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: Modal component #1622

Merged
merged 42 commits into from
Oct 7, 2021
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
92e25d0
Modal base component, minimum Storybook examples identified, plus tes…
SirenaBorracha May 13, 2021
3585102
Add example content to use in Storybook examples
SirenaBorracha May 13, 2021
c30913f
Add ModalDescription folder and files, passing one test
SirenaBorracha May 17, 2021
a474a51
Update ModalDeescription to include USWDS class, update tests
SirenaBorracha May 17, 2021
fdd3078
Add folder and files for ModalHeading
SirenaBorracha May 17, 2021
b289aa4
Skeleton for ModalHeading, passing one test
SirenaBorracha May 17, 2021
948ba74
Flesh out default Modal Storybook example
SirenaBorracha May 17, 2021
e77c335
Export ModalFooter, ModalDescription from index.ts
SirenaBorracha May 17, 2021
49a916b
Add ModalFooter component
SirenaBorracha May 18, 2021
7ac0c35
Remove ModalCloseButton from Storybook example with a forced action
SirenaBorracha May 18, 2021
421923d
Merge branch 'V2-cherry-picked' into ak-new-component-modal-1233
SirenaBorracha Jun 14, 2021
7f4c414
Add data-close-modal attribute to close button, comment for unique ID…
SirenaBorracha Jun 15, 2021
344ecfd
Add skeleton for ModalWrapper component, passing one test
SirenaBorracha Jun 15, 2021
08ebb2f
Rendering placeholder Storybook example for ModalWrapper
SirenaBorracha Jun 16, 2021
11aa30e
Add isVisible prop to ModalWrapper
SirenaBorracha Jun 22, 2021
e633f9b
Modal storybook update
SirenaBorracha Jun 28, 2021
cefff0d
2.11 update
Sep 29, 2021
9944911
Update thumbs down icon
Sep 29, 2021
b0b274a
Merge branch 'ak-new-component-modal-1233' of github.com:trussworks/r…
Sep 30, 2021
519bf2c
Simplify modal components
Sep 30, 2021
89528ea
Update tests, remove unused files
Sep 30, 2021
ac72177
Move Modal to ModalWindow component
Sep 30, 2021
3ae7aeb
Setting up modal functionality
Sep 30, 2021
5604686
Modal tests
Sep 30, 2021
4c35f5d
Working on modal effects and tests
Oct 1, 2021
dac357e
Hide other elements test, add modalRoot prop
Oct 1, 2021
7a4aeff
Style body padding when modal opens
Oct 1, 2021
6d9758e
Add complete Modal stories
Oct 1, 2021
9c7cf34
Hook up click on overlay
Oct 1, 2021
6cff778
Adding focus, event handlers, tests
Oct 4, 2021
3a0f100
Fix yarn.lock merge conflicts
Oct 4, 2021
777e4d0
Revert button forward ref change
Oct 4, 2021
1075eb1
Fix toggle event propagation, cleanup
Oct 4, 2021
a334ebf
Change Modal API to use forwardRef
Oct 5, 2021
b2c562e
Replace closeModal, openModal with toggleModal
Oct 5, 2021
059b589
Update modal exports
Oct 5, 2021
00b05da
Merge branch 'main' of github.com:trussworks/react-uswds into sr-1233…
Oct 6, 2021
f10dc55
Add ModalToggleButton, ModalOpenLink components
Oct 6, 2021
b49836c
Update ModalOpenLink test
Oct 6, 2021
129ffa1
Fix custom link implementation
Oct 6, 2021
ded43ce
Add open modal stories
Oct 7, 2021
d76d583
Fix Storybook action lag by replacing close handler with noop
Oct 7, 2021
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
"eslint-plugin-react": "^7.16.0",
"eslint-plugin-react-hooks": "^4.0.0",
"eslint-plugin-security": "^1.4.0",
"focus-trap-react": "^8.8.1",
"happo-plugin-storybook": "^2.7.0",
"happo.io": "^6.0.0",
"husky": "^4.3.8",
Expand Down
2 changes: 1 addition & 1 deletion src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import classnames from 'classnames'
import { deprecationWarning } from '../../deprecation'

interface ButtonProps {
export interface ButtonProps {
type: 'button' | 'submit' | 'reset'
children: React.ReactNode
secondary?: boolean
Expand Down
2 changes: 1 addition & 1 deletion src/components/Link/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function Link<FCProps = DefaultLinkProps>(
// 3. Therefore we know that removing those props leaves us
// with FCProps
//
const linkProps: FCProps = (remainingProps as unknown) as FCProps
const linkProps: FCProps = remainingProps as unknown as FCProps
const classes = linkClasses(variant, className)
return React.createElement(
asCustom,
Expand Down
213 changes: 213 additions & 0 deletions src/components/Modal/Modal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import React, { useRef } from 'react'

import { Modal, ModalRef } from './Modal'
import { ModalHeading } from './ModalHeading/ModalHeading'
import { ModalFooter } from './ModalFooter/ModalFooter'
import { ModalToggleButton } from './ModalToggleButton'

import { ButtonGroup } from '../ButtonGroup/ButtonGroup'

export default {
title: 'Components/Modal',
component: Modal,
parameters: {
docs: {
description: {
component: `
### USWDS 2.0 Modal component

Source: http://designsystem.digital.gov/components/modal

To use this component, you will need to create a ref and pass it into the rendered Modal component. This ref will expose several properties (also described by the exported ModalRef type):

- modalId: string
- the value of the id attribute given to the modal
- modalIsOpen: boolean
- true if the modal is currently open, otherwise false
- toggleModal: (e?: React.MouseEvent, open?: boolean) => boolean
- use this function to open or close the modal.
- if attached to an event handler, pass the event in
- if the second argument is provided, it will explicitly open the modal if true, or explicitly close it if false
- returns true if the toggle operation was successful, false if the event was prevented.

Follow the [USWDS](https://designsystem.digital.gov/components/modal/) guidance for using modals:

- Pass a unique ID into each modal component.
- Any component that opens the modal should be a button or anchor element, have the [data-open-modal] attribute, and [aria-controls] attribute with the value of the modal ID.
- Any component that closes the modal should be a button element, and have the [data-close-modal] attribute, and [aria-controls] attribute with the value of the modal ID.
- Use the forceAction prop on the modal component if the user should be forced to take an action before closing the modal.

You can also use the provided ModalToggleButton and/or ModalOpenLink components, which will adhere to the above guidelines for convenience.
`,
},
},
},
}

export const defaultModal = (): React.ReactElement => {
brandonlenz marked this conversation as resolved.
Show resolved Hide resolved
const modalRef = useRef<ModalRef>()

return (
<>
<ModalToggleButton modalRef={modalRef} opener>
Open default modal
</ModalToggleButton>
<Modal
ref={modalRef}
id="example-modal-1"
aria-labelledby="modal-1-heading"
aria-describedby="modal-1-description">
<ModalHeading id="modal-1-heading">
Are you sure you want to continue?
</ModalHeading>
<div className="usa-prose">
<p id="modal-1-description">
You have unsaved changes that will be lost.
</p>
</div>
brandonlenz marked this conversation as resolved.
Show resolved Hide resolved
<ModalFooter>
<ButtonGroup>
<ModalToggleButton modalRef={modalRef} closer>
Continue without saving
</ModalToggleButton>
<ModalToggleButton
modalRef={modalRef}
closer
unstyled
className="padding-105 text-center">
Go back
</ModalToggleButton>
</ButtonGroup>
</ModalFooter>
</Modal>
</>
)
}

export const largeModal = (): React.ReactElement => {
const modalRef = useRef<ModalRef>()

return (
<>
<ModalToggleButton modalRef={modalRef} opener>
Open large modal
</ModalToggleButton>
<Modal
ref={modalRef}
isLarge
aria-labelledby="modal-2-heading"
aria-describedby="modal-2-description"
id="example-modal-2">
<ModalHeading id="modal-2-heading">
Are you sure you want to continue?
</ModalHeading>
<div className="usa-prose">
<p id="modal-2-description">
You have unsaved changes that will be lost.
</p>
</div>
<ModalFooter>
<ButtonGroup>
<ModalToggleButton modalRef={modalRef} closer>
Continue without saving
</ModalToggleButton>
<ModalToggleButton
modalRef={modalRef}
closer
unstyled
className="padding-105 text-center">
Go back
</ModalToggleButton>
</ButtonGroup>
</ModalFooter>
</Modal>
</>
)
}

export const forceActionModal = (): React.ReactElement => {
const modalRef = useRef<ModalRef>()

return (
<>
<ModalToggleButton modalRef={modalRef} opener>
Open modal with forced action
</ModalToggleButton>
<Modal
ref={modalRef}
forceAction
aria-labelledby="modal-3-heading"
aria-describedby="modal-3-description"
id="example-modal-3">
<ModalHeading id="modal-3-heading">
Your session will end soon.
</ModalHeading>
<div className="usa-prose">
<p id="modal-3-description">
You’ve been inactive for too long. Please choose to stay signed in
or sign out. Otherwise, you’ll be signed out automatically in 5
minutes.
</p>
</div>
<ModalFooter>
<ButtonGroup>
<ModalToggleButton modalRef={modalRef} closer>
Yes, stay signed in
</ModalToggleButton>
<ModalToggleButton
modalRef={modalRef}
closer
unstyled
className="padding-105 text-center">
Sign out
</ModalToggleButton>
</ButtonGroup>
</ModalFooter>
</Modal>
</>
)
}

export const customFocusElementModal = (): React.ReactElement => {
const modalRef = useRef<ModalRef>()

return (
<>
<ModalToggleButton modalRef={modalRef} opener>
Open modal with custom initial focus element
</ModalToggleButton>
<Modal
ref={modalRef}
id="example-modal-1"
aria-labelledby="modal-1-heading"
aria-describedby="modal-1-description">
<ModalHeading id="modal-1-heading">
Are you sure you want to continue?
</ModalHeading>
<div className="usa-prose">
<p id="modal-1-description">
You have unsaved changes that will be lost.
</p>
<button type="button">Decoy button</button>
<button type="button" data-focus="true">
Focus me first
</button>
</div>
<ModalFooter>
<ButtonGroup>
<ModalToggleButton modalRef={modalRef} closer>
Continue without saving
</ModalToggleButton>
<ModalToggleButton
modalRef={modalRef}
closer
unstyled
className="padding-105 text-center">
Go back
</ModalToggleButton>
</ButtonGroup>
</ModalFooter>
</Modal>
</>
)
}
Loading