-
Notifications
You must be signed in to change notification settings - Fork 224
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
fix(modal): Use React portals for accessibility fixes #419
fix(modal): Use React portals for accessibility fixes #419
Conversation
Modal now uses a portal for content to not be constrained by a parent container. This also allows for the hiding of non-modal content (visibly covered by modal overlay) to assistive technology. Fixes Workday#414 Fixes Workday#330
d21a395
to
7ebf3f0
Compare
@@ -6,7 +6,6 @@ function getModalTargetButton() { | |||
|
|||
describe('Modal', () => { | |||
before(() => { | |||
cy.viewport(500, 300); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not actually sure why this is here.
Test summaryRun details
View run in Cypress Dashboard ➡️ This comment has been generated by cypress-bot as a result of this project's GitHub integration settings. You can manage this integration in this project's settings in the Cypress Dashboard |
return ( | ||
React.useEffect(() => { | ||
const siblings = [...((document.body.children as any) as HTMLElement[])].filter( | ||
el => el !== modalRef.current!.parentElement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should actually check if current is not undefined?
const observed = useRef(null);
useEffect(() => {
if(observed.current) console.log(observed.current);
}, [observed]);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting... Will useEffect
ever be called before the ref is applied? Technically this is an unsupported use-case. If this case happens, we cannot guarantee accessibility of the Modal
<a href="#">Link</a> | ||
</p> | ||
|
||
<button type="button">Button</button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need all this extra content?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accessibility requested this for testing if content was really being hidden from assistive technology
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should i add this back in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe do that in an accessible story? Maybe behind a knob setting called extraElements
that defaults to false
? I don't think it is strictly necessary. We don't have a way to test this automatically, but it is nice for manual verification.
@NicholasBoll Should we be adding portals to all of our popups? If it happens for modals, I would also expect it to happen for Popup, Toast, etc. |
Test summaryRun details
View run in Cypress Dashboard ➡️ This comment has been generated by cypress-bot as a result of this project's GitHub integration settings. You can manage this integration in this project's settings in the Cypress Dashboard |
Something worth noting that I just noticed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates @mannycarrera4 Do we want to make the portal changes to other popups in another PR? I wonder if we should just split these into two.
modules/modal/react/lib/Modal.tsx
Outdated
const Modal = ({open, ...modalContentProps}: ModalProps): JSX.Element | null => | ||
open ? <ModalContent {...modalContentProps} /> : null; | ||
const Modal = ({open, ...modalContentProps}: ModalProps): JSX.Element | null => { | ||
return open ? <ModalContent {...modalContentProps} /> : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Can we revert this change?
This is an open question for all things we do in Canvas Kit that effect element outside a component's render tree. This current change set |
I think this should happen for all Popups. I think portalling should be the default unless we have a good reason not to. Portalling can complicate focus management if the portalled content has focusable elements inside. For the most part, focusable elements should be avoided due to complications with accessibility anyways. If a focusable element is not avoidable, have a conversation with accessibility about how to handle this. One compromise was capturing a If browsers came with a focus manager, things would be different: https://discourse.wicg.io/t/proposal-focus-traversal-api/3427 |
@@ -24,50 +24,7 @@ const DefaultModalExample = () => { | |||
<> | |||
<DeleteButton buttonRef={buttonRef} onClick={openModal}> | |||
Delete Item | |||
</Button> | |||
<p> | |||
<a href="#">Link</a> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooo. Removed in a merge commit. Sneaky
@@ -172,7 +173,28 @@ const ModalContent = ({ | |||
} | |||
}; | |||
|
|||
return ( | |||
React.useEffect(() => { | |||
const siblings = [...((document.body.children as any) as HTMLElement[])].filter( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot to make this line and https://github.com/Workday/canvas-kit/pull/419/files#diff-3fe25d4d6af6314872e682fe13b7f7ccR212 use a configurable container instead of just document.body
. Applications may need a different container than document.body
@NicholasBoll are we going to look at that in this PR or in a followup? If the latter, we should create an issue for it |
I think the latter. |
Summary
Modal now uses a portal for content to not be constrained by a parent container. This also allows for the hiding of non-modal content (visibly covered by modal overlay) to assistive technology.
Fixes #414
Fixes #330
Checklist
yarn test
passespackage.json