Skip to content

Commit

Permalink
chore: check-in work
Browse files Browse the repository at this point in the history
  • Loading branch information
joshblack committed Aug 17, 2021
1 parent 761bc45 commit e9c5b7f
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 79 deletions.
100 changes: 85 additions & 15 deletions packages/react/src/components/Dialog/Dialog-story.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
*/

import * as React from 'react';
import ReactDOM from 'react-dom';
import { FocusScope } from './FocusScope';
import { Dialog } from '../Dialog';
import { useId } from '../../internal/useId';

export default {
title: 'Experimental/unstable_Dialog',
Expand Down Expand Up @@ -67,38 +69,106 @@ export const Default = () => {
export const DialogExample = () => {
function Example() {
const [open, setOpen] = React.useState(false);
const id = useId();
return (
<>
<div>
<div>
<button>First</button>
</div>

{/* trigger */}
<button
type="button"
onClick={() => {
setOpen(true);
}}>
Open
</button>
<Dialog
open={open}
onDismiss={() => {
setOpen(false);
}}>
<h1>Hello</h1>
<button
type="button"
onClick={() => {
setOpen(false);

{open ? (
<Portal
style={{
position: 'fixed',
top: 0,
right: 0,
bottom: 0,
left: 0,
zIndex: 9998,
}}>
Close
</button>
</Dialog>
{/* full screen background */}
<FullPage
onClick={() => {
setOpen(false);
}}
/>

{/* dialog */}
<Dialog
aria-labelledby={id}
onDismiss={() => {
setOpen(false);
}}
style={{
position: 'relative',
zIndex: 9999,
padding: '1rem',
background: 'white',
}}>
<div>
<span id={id}>Hello</span>
</div>
<div>
<Example />
</div>
<button
type="button"
onClick={() => {
setOpen(false);
}}>
Close
</button>
</Dialog>
</Portal>
) : null}

<div>
<button>Last</button>
</div>
</>
</div>
);
}

return <Example />;
};

function FullPage(props) {
return (
<div
style={{
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
transform: 'translateZ(0)',
background: 'rgba(0, 0, 0, 0.5)',
}}
{...props}
/>
);
}

function Portal({ children }) {
const [mountNode, setMountNode] = React.useState(null);

React.useEffect(() => {
// TODO: should this be configurable????
setMountNode(document.body);
}, []);

if (mountNode) {
return ReactDOM.createPortal(children, mountNode);
}

return null;
}
6 changes: 5 additions & 1 deletion packages/react/src/components/Dialog/FocusScope.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ function useRestoreFocus(container) {
element.removeEventListener('focusout', onFocusOut);

if (containsFocus.current === true) {
focus(initialActiveElement);
setTimeout(() => {
focus(initialActiveElement);
}, 0);
}
};
}, []);
Expand Down Expand Up @@ -141,6 +143,7 @@ const FocusScope = React.forwardRef(function FocusScope(props, forwardRef) {
return (
<>
<span
data-dialog-bumper=""
tabIndex="0"
style={{
outline: 'none',
Expand All @@ -156,6 +159,7 @@ const FocusScope = React.forwardRef(function FocusScope(props, forwardRef) {
{children}
</BaseComponent>
<span
data-dialog-bumper=""
tabIndex="0"
style={{
outline: 'none',
Expand Down
Empty file.
126 changes: 63 additions & 63 deletions packages/react/src/components/Dialog/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const Dialog = React.forwardRef(function Dialog(props, forwardRef) {
'aria-labelledby': labelledBy,
children,
onDismiss,
open = false,
modal = true,
...rest
} = props;
Expand All @@ -30,56 +29,78 @@ const Dialog = React.forwardRef(function Dialog(props, forwardRef) {
const savedOnDismiss = useSavedCallback(onDismiss);

function onKeyDown(event) {
if (!open) {
return;
}

if (match(event, keys.Escape)) {
event.stopPropagation();
savedOnDismiss();
}
}

useEffect(() => {
if (open) {
//
const changes = [];
const queue = Array.from(document.body.childNodes);

while (queue.length !== 0) {
const node = queue.shift();

// If a node is the modal (dialogRef), do nothing
if (node === dialogRef.current) {
continue;
}

// If a tree contains our `dialogRef`, traverse its children
if (node.contains(dialogRef.current)) {
queue.push(...Array.from(node.childNodes));
continue;
}

// If a node is a bumper, do nothing
if (
node.hasAttribute('data-dialog-bumper') &&
(dialogRef.current.previousSibling === node ||
dialogRef.current.nextSibling === node)
) {
continue;
}

if (node.hasAttribute('aria-hidden') || node.hasAttribute('inert')) {
continue;
}

// what is aria-hidden === 'false'

// Otherwise, set it to inert and set aria-hidden to true
node.setAttribute('aria-hidden', 'true');
node.setAttribute('inert', '');

changes.push(node);
}
}, [open]);

if (open) {
return (
<Portal
style={{
position: 'fixed',
top: 0,
right: 0,
bottom: 0,
left: 0,
zIndex: 9998,
}}>
<FullPage />
<FocusScope
{...rest}
aria-labelledby={labelledBy}
aria-modal="true"
initialFocusRef={dialogRef}
onKeyDown={onKeyDown}
ref={ref}
role="dialog"
tabIndex="-1"
style={{
position: 'relative',
zIndex: 9999,
background: 'white',
padding: '1rem',
}}>
{children}
</FocusScope>
</Portal>
);
}

return null;
return () => {
changes.forEach((node) => {
node.removeAttribute('inert');
// This mutation needs to be asynchronous to allow the polyfill time to
// observe the change and allow mutations to occur
// https://github.com/WICG/inert#performance-and-gotchas
setTimeout(() => {
node.removeAttribute('aria-hidden');
}, 0);
});
};
}, []);

return (
<FocusScope
{...rest}
aria-labelledby={labelledBy}
aria-modal="true"
initialFocusRef={dialogRef}
onKeyDown={onKeyDown}
ref={ref}
role="dialog"
tabIndex="-1">
{children}
</FocusScope>
);
});

Dialog.propTypes = {};
Expand All @@ -88,25 +109,4 @@ if (__DEV__) {
Dialog.displayName = 'Dialog';
}

function Portal({ children, ...rest }) {
return <div {...rest}>{children}</div>;
}

function FullPage(props) {
return (
<div
style={{
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
transform: 'translateZ(0)',
background: 'rgba(0, 0, 0, 0.5)',
}}
{...props}
/>
);
}

export { Dialog };

0 comments on commit e9c5b7f

Please sign in to comment.