Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port over draft components #718

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 193 additions & 0 deletions content/components/dialog/react/draft.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
---
title: Dialog
description:
Dialog is a floating surface used to display transient content such as confirmation actions, selection options, and
more.
reactId: dialog
reactStatus: draft
---

import ReactComponentPage from '~/src/components/react-component-page'
import ReactPropsTable from '~/src/components/react-props-table'
import { graphql } from "gatsby"

export const query = graphql`
query DialogDraftQuery($componentId: String! = "dialog", $parentPath: String! = "/components/dialog", $status: String! = "draft") {
...ReactComponentInfo
}
`

export default function Layout({data, children}) {
return <ReactComponentPage data={data} showExamples={false} showProps={false} showImport={false} tocItems={[{url: "#import", title: "Import"}, {url: "#props", title: "Props"}, {url: "#confirmationdialog", title: "ConfirmationDialog"}]}>
{children}
</ReactComponentPage>
}

The dialog component is the Primer implementation of the ARIA design pattern [Dialog](https://www.w3.org/TR/wai-aria-practices-1.1/#dialog_modal). A dialog is a type of overlay that can be used for confirming actions, asking for disambiguation, and presenting small forms. They generally allow the user to focus on a quick task without having to navigate to a different page.

**Dialogs appear in the page after a direct user interaction**. Don't show dialogs on page load or as system alerts.

**Dialogs appear centered in the page**, with a visible backdrop that dims the rest of the window for focus.

**All dialogs should have a title and a close button**. Use the `title` prop to set the title. The close button is created automatically, but must be handled with an `onClose` prop.

**Dialogs are modal**. Dialogs can be dismissed by clicking on the close button, by interacting with another button in the overlay, or by clicking outside of the overlay.

Dialogs should not be dismissed by clicking outside the dialog's content area if used to add, update, or remove information. This would be the equivalent of a cancel action and may cause accidental data loss.

**(Coming soon) Make sure larger dialogs remain mobile-friendly**. Even large dialogs need to be responsive. A dialog's width and height will be readjusted depending on the screen size and should never exceed the available space. Use responsive solutions to adjust the UI so they behave well on smaller screens.

**(Coming soon) Simple and small dialogs can remain as-is on mobile**. As more elements are added to it, mobile-specific style variations such as **Bottom sheet** and **Full-screen** should be considered.
camertron marked this conversation as resolved.
Show resolved Hide resolved

## Import

```javascript
import {Dialog} from '@primer/react/drafts'
```

### State

The dialog component is completely stateless. The parent component must conditionally render a dialog based on the necessary criteria. The parent component can be notified by a gesture to close the dialog (e.g. by clicking the "X" button or by pressing the Escape key) by passing in the `onClose` prop.

### Accessibility

The dialog component is fully accessible out-of-the-box. The dialog's title is used for `aria-labelledby`, and the dialog's description is used for `aria-describedby`. The `aria-modal="true"` attribute is used to inform screen readers that the rest of the content on the page is inert.

#### Keyboard

The default keyboard API for a dialog employs three mechanisms:

1. The Escape key can be pressed to close the dialog.
2. Focus is trapped within the top-most dialog. When a dialog is opened, the close button receives initial focus by default, or on a button defined with `autoFocus: true`.
3. If there are buttons in the dialog footer, focus can be moved between them with left and right arrow keys or tab/shift+tab.
4. When a dialog is closed, it can optionally handle returning focus to the element that was focused immediately before the dialog was opened. Otherwise, it is the consumer's responsibility to move focus to a suitable element.
camertron marked this conversation as resolved.
Show resolved Hide resolved

### Custom rendering

**Note: using custom rendering allows breaking out of the defined design, UX, and accessibility patterns of the dialog. Use only as a last resort.**

By default, the Dialog component implements the design and interactions defined by the Primer design system. If necessary, you can provide custom renderers for the header, body, or footer using the `renderHeader`, `renderBody`, and `renderFooter` props, respectively. The JSX produced by these render props will render full-bleed into their respective areas of the dialog. If you are using the custom renderers, you should use the provided sub-components `Dialog.Header`, `Dialog.Title`, `Dialog.Subtitle`, `Dialog.CloseButton`, `Dialog.Body`, `Dialog.Footer`, and `Dialog.Buttons` to the extent possible.

### Example

```jsx live drafts
<State default={false}>
{([isOpen, setIsOpen]) => {
const openDialog = React.useCallback(() => setIsOpen(true), [setIsOpen])
const closeDialog = React.useCallback(() => setIsOpen(false), [setIsOpen])
return (
<>
<Button onClick={openDialog}>Open</Button>
{isOpen && (
<Dialog
title="Dialog example"
subtitle={
<>
This is a <b>description</b> of the dialog.
</>
}
footerButtons={[{content: 'Ok', onClick: closeDialog}]}
onClose={closeDialog}
>
<Text fontFamily="sans-serif">Some content</Text>
</Dialog>
)}
</>
)
}}
</State>
```

## Props
camertron marked this conversation as resolved.
Show resolved Hide resolved

<ReactPropsTable props={props.data.reactComponent.props} />

### DialogHeaderProps
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I correct in assuming that we have to manually render these tables in Markdown because we don't have a way to document these types in the current shape of the *.docs.json objects?

A good follow-up issue could be to solve this problem so we don't have to manually render stuff like this.

Copy link
Contributor Author

@camertron camertron Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, I just copy/pasted this from primer/react. The info for all these props exists in components.json, so I think we can remove this and let the layout render the props table(s) instead.


The `DialogHeaderProps` interface extends `DialogProps` and adds these additional properties:

| Prop name | Type | Description |
| :------------------ | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| dialogLabelId | `string` | ID of the element that will be used as the `aria-labelledby` attribute on the dialog. This ID should be set to the element that renders the dialog's title. |
| dialogDescriptionId | `string` | ID of the element that will be used as the `aria-describedby` attribute on the dialog. This ID should be set to the element that renders the dialog's subtitle. |

### DialogButtonProps

The `DialogButtonProps` interface extends `ButtonProps` (see Button) and adds these additional properties:

| Prop name | Type | Default | Description |
| :--------- | :-------------------------------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| buttonType | `"normal" │ "primary" │ "danger"` | `Button` | The type of button to render |
| content | `React.ReactNode` | | Required. The button's inner content. |
| autoFocus | `boolean` | false | If true, this button will be automatically focused when the dialog is first rendered. If `autoFocus` is true on subsequent button definitions, it will be ignored. |

## ConfirmationDialog

A `ConfirmationDialog` is a special kind of dialog with more rigid behavior. It is used to confirm a user action. `ConfirmationDialog`s always have exactly two buttons: one to cancel the action and one to confirm it. No custom rendering capabilities are provided for ConfirmationDialog.

### useConfirm hook

An alternate way to use `ConfirmationDialog` is with the `useConfirm()` hook. It takes no parameters and returns an `async` function, `confirm`. When `confirm` is called (see ConfirmOptions below for its argument), it shows the confirmation dialog. When the dialog is dismissed, a promise is resolved with `true` or `false` depending on whether or not the confirm button was used.

### Example (with `useConfirm` hook)

```javascript live noinline
function ShorthandExample2() {
const confirm = useConfirm()
const buttonClick = React.useCallback(
async function (e) {
if (await confirm({title: 'Are you sure?', content: 'You must confirm this action to continue.'})) {
e.target.textContent = 'Confirmed!'
}
},
[confirm],
)
return (
<>
<Button onClick={buttonClick}>Confirmable action</Button>
</>
)
}
render(<ShorthandExample2 />)
```
Comment on lines +128 to +152
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this to the useConfirm() hook docs and just link there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm yeah that's a good idea.


### Example (using the full `ConfirmationDialog` component)

```jsx live
<State default={false}>
{([isOpen, setIsOpen]) => {
const openDialog = React.useCallback(() => setIsOpen(true), [setIsOpen])
const closeDialog = React.useCallback(() => setIsOpen(false), [setIsOpen])
return (
<>
<Button onClick={openDialog}>Open</Button>
{isOpen && (
<ConfirmationDialog title="Confirm action?" onClose={closeDialog}>
Are you sure you want to confirm this action?
</ConfirmationDialog>
)}
</>
)
}}
</State>
```

### ConfirmationDialogProps
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### ConfirmationDialogProps
### Props


| Prop name | Type | Default | Description |
| :------------------- | :-------------------------------------------------------------------- | :--------- | :---------------------------------------------------------------------------------------------------------------------------- |
| title | `React.ReactNode` | | Required. Sets the title of the dialog, which by default is also used as the `aria-labelledby` attribute. |
| onClose | `(gesture: 'confirm' │ 'cancel' │ 'close-button' │ 'escape') => void` | | Required. This callback is invoked when a gesture to close the dialog is performed. The first argument indicates the gesture. |
| cancelButtonContent | `React.ReactNode` | `"Cancel"` | The content to use for the cancel button. |
| confirmButtonContent | `React.ReactNode` | `"OK"` | The content to use for the confirm button. |
| confirmButtonType | `"normal" │ "primary" │ "danger"` | `Button` | The type of button to use for the confirm button. |

### ConfirmOptions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### ConfirmOptions
#### ConfirmOptions


| Prop name | Type | Default | Description |
| :------------------- | :-------------------------------- | :--------- | :-------------------------------------------------------------------------------------------------------- |
| title | `React.ReactNode` | | Required. Sets the title of the dialog, which by default is also used as the `aria-labelledby` attribute. |
| content | `React.ReactNode` | | Required. Used as the body of the ConfirmationDialog that is displayed. |
| cancelButtonContent | `React.ReactNode` | `"Cancel"` | The content to use for the cancel button. |
| confirmButtonContent | `React.ReactNode` | `"OK"` | The content to use for the confirm button. |
| confirmButtonType | `"normal" │ "primary" │ "danger"` | `Button` | The type of button to use for the confirm button. |
22 changes: 22 additions & 0 deletions content/components/hidden.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
title: Hidden
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this component is only used internally in @primer/react. Can we remove this page and just keep content/components/hidden/react/draft.mdx?

I think we still need to figure out how we want to handle docs for internal components.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's currently documented on the old docsite, will it be potentially confusing for it to not get ported over?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's safe to remove. I'd prefer we don't add it to the sidebar on the root primer.style/

description: Use Hidden to responsively hide or show content in narrow, regular and wide viewports.
reactId: hidden
---

import ComponentLayout from '~/src/layouts/component-layout'
import {Link, Text} from '@primer/react'
import {Link as GatsbyLink} from 'gatsby'
export default ComponentLayout
import {AccessibilityLink} from '~/src/components/accessibility-link'

<Note variant="warning">
<Text sx={{display: 'block', fontWeight: 'bold', mb: 2}}>Guidelines for this component are in progress</Text>
<Text>Interested in contributing? Review the guidelines for <Link as={GatsbyLink} to="/guides/contribute/documentation#documenting-components">writing component documentation</Link> and open a pull request in our <Link as={GatsbyLink} to="https://github.com/primer/design">documentation repository.</Link></Text>
</Note>

## Accessibility

### Known accessibility issues (GitHub staff only)

<AccessibilityLink label="Hidden"/>
41 changes: 41 additions & 0 deletions content/components/hidden/react/draft.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: Hidden
description: Use Hidden to responsively hide or show content in narrow, regular and wide viewports.
reactId: hidden
reactStatus: draft
---

import ReactComponentPage from '~/src/components/react-component-page'
import { graphql } from "gatsby"

export const query = graphql`
query HiddenDraftQuery($componentId: String! = "hidden", $parentPath: String! = "/components/hidden", $status: String! = "draft") {
...ReactComponentInfo
}
`

export default function Layout({data, children}) {
return <ReactComponentPage data={data}>{children}</ReactComponentPage>
}

The `Hidden` component is a utility component that helps hide or show content/components in different viewports.

<Note>Use Hidden only to control behaviour in a responsive manner. It does not take any `sx` props.</Note>

## Examples
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we just want to render a "default" example and rely on Storybook for everything else. For example: https://primer.style/components/action-list/react/beta


### Single viewport value provided

```jsx drafts
<Hidden when="narrow">
<Placeholder height="80px" label="This is not visible in narrow viewport" />
</Hidden>
```

### Multiple viewport values provided

```jsx drafts
<Hidden when={['narrow', 'wide']}>
<Placeholder height="80px" label="This is not visible in narrow and wide viewport" />
</Hidden>
```
Loading
Loading