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

feat!: Add Action Group component, e.g. to wrap Dialog buttons in #1592

Merged
merged 22 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7f636e4
Introduce ActionGroup subcomponent for Dialog buttons
VincentSmedinga Sep 18, 2024
a1ef7a0
Sort object properties in Stories
VincentSmedinga Sep 18, 2024
e436f52
Update documentation
VincentSmedinga Sep 19, 2024
130a67e
Reuse backdrop styles
VincentSmedinga Sep 19, 2024
5a19b84
Fix type
VincentSmedinga Sep 19, 2024
e373154
Merge branch 'develop' into feature/DES-961-dialog-action-group
VincentSmedinga Sep 20, 2024
fe03968
Merge branch 'develop' into feature/DES-961-dialog-action-group
VincentSmedinga Sep 25, 2024
8a24824
Merge branch 'develop' into feature/DES-961-dialog-action-group
VincentSmedinga Sep 26, 2024
c6d5a68
Scaffold new Action Group component
VincentSmedinga Sep 26, 2024
6a3bfd3
Describe component tests with pascal case name
VincentSmedinga Sep 26, 2024
1d5907a
Introduce resizing utiliy for examples
VincentSmedinga Sep 26, 2024
61ecbf0
Implement Action Group as a standalone component
VincentSmedinga Sep 26, 2024
678eb8f
Remove Action Group as subcomponent of Dialog
VincentSmedinga Sep 26, 2024
778b95a
Mention Action Group in Button documentation
VincentSmedinga Sep 26, 2024
df8dbc6
Fix token name
VincentSmedinga Sep 26, 2024
6a0ed8a
Allow Link in Action Group
VincentSmedinga Sep 26, 2024
fd90a13
Improve prop description
VincentSmedinga Sep 26, 2024
c6b53e3
Fully qualify urls in plain markdown
VincentSmedinga Sep 27, 2024
8d5a326
Add group role
VincentSmedinga Sep 27, 2024
03e5eb6
Clarify docs
VincentSmedinga Sep 27, 2024
08383a1
Merge branch 'develop' into feature/DES-961-dialog-action-group
VincentSmedinga Sep 30, 2024
27d7306
Merge branch 'develop' into feature/DES-961-dialog-action-group
VincentSmedinga Oct 1, 2024
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
17 changes: 8 additions & 9 deletions packages/css/src/components/dialog/dialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,22 @@ so do not apply these styles without an `open` attribute. */
}
}

.ams-dialog__body {
overflow-y: auto;
overscroll-behavior-y: contain;
}

.ams-dialog__header {
align-items: flex-start;
display: flex;
gap: var(--ams-dialog-header-gap);
justify-content: space-between;
}

.ams-dialog__footer {
display: flex;
.ams-dialog__body {
overflow-y: auto;
overscroll-behavior-y: contain;
}

.ams-dialog__action-group {
display: inline-flex;
flex-wrap: wrap; // [1]
gap: var(--ams-dialog-footer-gap);
margin-inline-end: auto; // [1]
gap: var(--ams-dialog-action-group-gap);

> * {
flex: auto; // [1]
Expand Down
4 changes: 3 additions & 1 deletion packages/react/src/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import { forwardRef, MouseEvent } from 'react'
import type { DialogHTMLAttributes, ForwardedRef, PropsWithChildren, ReactNode } from 'react'
import { Heading } from '../Heading'
import { IconButton } from '../IconButton'
import { DialogActionGroup } from './DialogActionGroup'

export type DialogProps = {
/** The label for the button that dismisses the Dialog. */
closeButtonLabel?: string
/** The button(s) in the footer. Start with a primary button. */
/** Content for the footer, often an Action Group with one or more buttons. */
footer?: ReactNode
/** The text for the Heading. */
heading: string
Expand Down Expand Up @@ -40,6 +41,7 @@ const DialogRoot = forwardRef(
DialogRoot.displayName = 'Dialog'

export const Dialog = Object.assign(DialogRoot, {
ActionGroup: DialogActionGroup,
close: closeDialog,
open: openDialog,
})
41 changes: 41 additions & 0 deletions packages/react/src/Dialog/DialogActionGroup.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { render } from '@testing-library/react'
import { createRef } from 'react'
import '@testing-library/jest-dom'
import { Dialog } from './Dialog'

describe('Dialog Action Group', () => {
it('renders', () => {
const { container } = render(<Dialog.ActionGroup />)

const component = container.querySelector(':only-child')

expect(component).toBeInTheDocument()
expect(component).toBeVisible()
})

it('renders a design system BEM class name', () => {
const { container } = render(<Dialog.ActionGroup />)

const component = container.querySelector(':only-child')

expect(component).toHaveClass('ams-dialog__action-group')
})

it('renders an additional class name', () => {
const { container } = render(<Dialog.ActionGroup className="extra" />)

const component = container.querySelector(':only-child')

expect(component).toHaveClass('ams-dialog__action-group extra')
})

it('supports ForwardRef in React', () => {
const ref = createRef<HTMLDivElement>()

const { container } = render(<Dialog.ActionGroup ref={ref} />)

const component = container.querySelector(':only-child')

expect(ref.current).toBe(component)
})
})
20 changes: 20 additions & 0 deletions packages/react/src/Dialog/DialogActionGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/

import clsx from 'clsx'
import { forwardRef } from 'react'
import type { ForwardedRef, HTMLAttributes, PropsWithChildren } from 'react'

export type DialogActionGroupProps = PropsWithChildren<HTMLAttributes<HTMLDivElement>>

export const DialogActionGroup = forwardRef(
({ children, className, ...restProps }: DialogActionGroupProps, ref: ForwardedRef<HTMLDivElement>) => (
<div {...restProps} ref={ref} className={clsx('ams-dialog__action-group', className)}>
{children}
</div>
),
)

DialogActionGroup.displayName = 'DialogActionGroup'
2 changes: 1 addition & 1 deletion proprietary/tokens/src/components/ams/dialog.tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"header": {
"gap": { "value": "{ams.space.md}" }
},
"footer": {
"action-group": {
"gap": { "value": "{ams.space.md}" }
}
}
Expand Down
37 changes: 19 additions & 18 deletions storybook/src/components/Dialog/Dialog.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,35 @@ import README from "../../../../packages/css/src/components/dialog/README.md?raw

## Examples

### Form in a Dialog
### Open and close

Set `method="dialog"` when using a form in Dialog.
This closes the Dialog when submitting the form.
Pass the submit Button to the `footer` prop,
and link it to the form by passing its `id` to the Buttons `form` attribute.
The Dialog returns the value of the submit Button, so you can check which Button was clicked.
For more information, see [Handling the return value from the dialog (MDN)](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog#handling_the_return_value_from_the_dialog).
To open the Dialog, use `Dialog.open(dialogId)` from the React package.
To close the Dialog, use either `Dialog.close`, or a `<form>` as in the following example.
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved

<Canvas of={DialogStories.FormDialog} />
<Canvas of={DialogStories.OpenAndClose} />

### With scrollbar
### Asking to confirm

Content taller than the dialog itself will scroll.
Use a `<form>` when asking to confirm an action, e.g. through ‘OK’ and ‘Cancel’ buttons.
Add `method="dialog"` to let the browser close the Dialog automatically when the form is submitted.

<Canvas of={DialogStories.WithScrollbar} />
Wrap one or more buttons in an Action Group and place it in the `footer`.
This ensures correct whitespace and scrolling behaviour.
At the same time, it positions the buttons outside the `form` element.
Create an `id` for the form and add it to the submit Button’s `form` attribute to connect the two.

### Trigger Button
If the Action Group needs to live inside of the `form`, add a medium bottom margin (`ams-mb--md`) to the element before it and make scrolling work correctly.
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved

Click or tap this Button to open the Dialog.
The form returns the `value` of the submit Button, which allows inferring which Button the user clicked.
For more information, see [Handling the return value from the dialog (MDN)](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog#handling_the_return_value_from_the_dialog).

<Canvas of={DialogStories.TriggerButton} />
<Canvas of={DialogStories.AskingToConfirm} />

#### Utility functions
### Tall content will scroll

To open the Dialog, use `Dialog.open(id)` from the React package.
Pass the Dialog’s `id` to the function to select it.
To close the Dialog, use `Dialog.close`.
Content that doesn’t fit entirely in the Dialog will scroll.

<Canvas of={DialogStories.WithScrollbar} />

### Vertically stacked Buttons

Expand Down
Loading