-
Notifications
You must be signed in to change notification settings - Fork 71
LG-4952: Updates Modal component to use dialog element #2579
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
Changes from 20 commits
01353ba
84b68db
6bed50e
49b4479
46bfa16
ef96bd0
dea1576
554012a
fbbcd03
3a456a5
ea44dad
ebdccd7
d745726
60500f6
bb78e5a
34b03b1
2c5eb46
1f45e50
da9e572
cb6aaa3
044e9df
078576c
bab4129
a82531a
d0bee20
c5aad57
59c46e2
0ff3c17
3306e4d
4427ddc
85b8632
6e9fab7
7f23dfa
cce69e7
f166348
b39f9d6
d70631f
20c96e9
2901037
e59a668
2eca88e
0a82bc9
3c6d6ac
75615d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
'@leafygreen-ui/confirmation-modal': major | ||
'@leafygreen-ui/marketing-modal': major | ||
'@leafygreen-ui/modal': major | ||
--- | ||
|
||
Upgrades components to use the native `dialog` HTML element. This means we no longer have to handle focus trapping ourselves, and can rely on the browser to do that for us. The API for all modal components is not changing, but the DOM structure of the Modal components themselves have changed drastically, as well as where they are placed within the DOM itself. | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,48 +37,40 @@ function ExampleComponent() { | |
} | ||
``` | ||
|
||
**Output HTML** | ||
|
||
```html | ||
<button>Open Modal</button> | ||
<div aria-modal="true" role="dialog" tabindex="-1" class="leafygreen-ui-2e4yhj"> | ||
<button | ||
tabindex="0" | ||
aria-disabled="false" | ||
aria-label="Close modal" | ||
class="leafygreen-ui-zndd6x" | ||
> | ||
<div class="leafygreen-ui-xhlipt"> | ||
<svg | ||
class="leafygreen-ui-19fdo3o" | ||
height="20" | ||
width="20" | ||
viewBox="0 0 16 16" | ||
role="img" | ||
> | ||
<g | ||
id="X-Copy" | ||
stroke="none" | ||
stroke-width="1" | ||
fill="none" | ||
fill-rule="evenodd" | ||
> | ||
<path | ||
d="M9,7 L13.5,7 L13.5,9 L9,9 L9,13.5 L7,13.5 L7,9 L2.5,9 L2.5,7 L7,7 L7,2.5 L9,2.5 L9,7 Z" | ||
id="Combined-Shape-Copy" | ||
fill="currentColor" | ||
transform="translate(8.000000, 8.000000) rotate(45.000000) translate(-8.000000, -8.000000) " | ||
></path> | ||
</g> | ||
</svg> | ||
</div></button | ||
>Modal Content goes here. | ||
</div> | ||
## Notes | ||
|
||
Portaled Elements within a Modal | ||
|
||
Recommended Approach: `renderMode="top-layer"` | ||
When using any LeafyGreen components that wrap a Portal or Popover component, we recommend setting the `renderMode` prop to the value "top-layer". This is the browser's default way of handling hierarchy, without worrying about the DOM or z-index collisions. | ||
bruugey marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
Example: | ||
|
||
```js | ||
<Modal> | ||
<Select renderMode="top-layer" /> | ||
</Modal> | ||
``` | ||
|
||
## Notes | ||
Fallback: | ||
bruugey marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
If for some reason you must use a Portal with an element rendered inside of a Modal component, you can access the Modal's DOM structure by passing a ref to the `portalRef` property in the Modal component. We will set the current value of your ref to an element inside of the Modal itself. | ||
|
||
Example: | ||
|
||
It is HIGHLY encouraged that any children inside of `Modal` should refrain from using `usePortal={false}` because this can cause stacking context/z-indexing issues since the popover element will render relative to the parent rather than rendering in a `React portal` which is automatically appended to the `Modal`. By default any component that can use a `React portal`, like `Tooltip` or `Select`, will have `usePortal` set to `true`. | ||
```js | ||
const portalRef = useRef<HTMLDivElement | null>(null) | ||
const [containerState, setContainerState] = useState<React.RefObject<HTMLDivElement>>(null) | ||
|
||
useEffect(() => { | ||
if (portalRef.current) { | ||
setContainerState(portalRef) | ||
} | ||
}, [portalRef]) | ||
|
||
<Modal portalRef={portalRef}> | ||
<Select renderMode="portal" portalContainer={containerState.current}/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we continue using a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to what I think makes sense but let me know |
||
</Modal> | ||
``` | ||
|
||
## Properties | ||
|
||
|
@@ -94,6 +86,7 @@ It is HIGHLY encouraged that any children inside of `Modal` should refrain from | |
| `initialFocus` | `string` | A selector string for the element to move focus to when the modal is opened. The first focusable element in the modal will receive focus by default. | | | ||
| `darkMode` | `boolean` | Determines if the component will appear in dark mode. | `false` | | ||
| `closeIconColor` | `'default'`, `'dark'`, `'light'` | Determines the color of the close icon. | `default` | | ||
| `portalRef` | `React.RefObject<HTMLDivElement>` | Current property gets set with an element inside the Modal, in order to safely portal elements inside of the dialog element | | | ||
|
||
## Using `Clipboard.js` inside `Modal` | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { css } from '@leafygreen-ui/emotion'; | ||
import { Theme } from '@leafygreen-ui/lib'; | ||
import { palette } from '@leafygreen-ui/palette'; | ||
|
||
import { CloseIconColor } from '../Modal/Modal.types'; | ||
|
||
const getColor = (theme: Theme, customColor: CloseIconColor) => { | ||
switch (customColor) { | ||
case 'dark': | ||
return palette.black; | ||
|
||
case 'light': | ||
return palette.gray.light2; | ||
|
||
default: | ||
return theme === Theme.Light ? palette.gray.dark1 : palette.gray.light2; | ||
} | ||
}; | ||
|
||
export const closeButtonStyles = ( | ||
theme: Theme, | ||
customColor: CloseIconColor, | ||
) => css` | ||
position: absolute; | ||
cursor: pointer; | ||
// x-icon should be 24px from edge. IconButton is 28x28 and Icon is 16x16 | ||
// so there's already (28 - 16) / 2 = 6px of spacing. 24 - 6 = 18. | ||
right: 18px; | ||
top: 18px; | ||
color: ${getColor(theme, customColor)}; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import React from 'react'; | ||
|
||
import { useIdAllocator } from '@leafygreen-ui/hooks'; | ||
import XIcon from '@leafygreen-ui/icon/dist/X'; | ||
import IconButton from '@leafygreen-ui/icon-button'; | ||
import { useDarkMode } from '@leafygreen-ui/leafygreen-provider'; | ||
|
||
import { LGIDS_MODAL } from '../constants'; | ||
import { CloseIconColor } from '../Modal/Modal.types'; | ||
|
||
import { closeButtonStyles } from './CloseButton.styles'; | ||
import { CloseButtonProps } from './CloseButton.types'; | ||
|
||
export default function CloseButton({ | ||
closeIconColor = CloseIconColor.Default, | ||
handleClose, | ||
}: CloseButtonProps) { | ||
const { theme } = useDarkMode(); | ||
const closeId = useIdAllocator({ prefix: 'modal' }); | ||
bruugey marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
return ( | ||
<IconButton | ||
id={closeId} | ||
data-testid={LGIDS_MODAL.close} | ||
onClick={handleClose} | ||
aria-label="Close modal" | ||
className={closeButtonStyles(theme, closeIconColor)} | ||
> | ||
<XIcon /> | ||
</IconButton> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { CloseIconColor } from '../Modal/Modal.types'; | ||
|
||
export interface CloseButtonProps { | ||
closeIconColor?: CloseIconColor; | ||
handleClose?: () => void; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './CloseButton'; |
Uh oh!
There was an error while loading. Please reload this page.