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

fix(modal): Use React portals for accessibility fixes #419

Merged

Conversation

NicholasBoll
Copy link
Member

@NicholasBoll NicholasBoll commented Jan 23, 2020

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

  • tests are changed or added
  • yarn test passes
  • all (dev)dependencies that the module needs is added to its package.json
  • code has been documented and, if applicable, usage described in README.md
  • design approved final implementation
  • a11y approved final implementation
  • code adheres to the API & Pattern guidelines

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
@NicholasBoll NicholasBoll force-pushed the fix/modal-accessibility-touch branch from d21a395 to 7ebf3f0 Compare January 23, 2020 06:15
@@ -6,7 +6,6 @@ function getModalTargetButton() {

describe('Modal', () => {
before(() => {
cy.viewport(500, 300);
Copy link
Member Author

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.

@cypress
Copy link

cypress bot commented Jan 23, 2020



Test summary

140 0 0 0


Run details

Project canvas-kit
Status Passed
Commit e8237ba
Started Feb 6, 2020 4:20 AM
Ended Feb 6, 2020 4:22 AM
Duration 02:37 💡
OS Linux Ubuntu Linux - 16.04
Browser Electron 78

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

@anicholls anicholls added the bug Something isn't working label Jan 25, 2020
@NicholasBoll NicholasBoll changed the title DNM: fix(modal): Hide background content from assistive technology fix(modal): Hide background content from assistive technology Feb 6, 2020
@NicholasBoll NicholasBoll marked this pull request as ready for review February 6, 2020 04:00
return (
React.useEffect(() => {
const siblings = [...((document.body.children as any) as HTMLElement[])].filter(
el => el !== modalRef.current!.parentElement
Copy link
Contributor

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]);

Copy link
Member Author

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

@lychyi lychyi added the 4.x label Feb 18, 2020
<a href="#">Link</a>
</p>

<button type="button">Button</button>
Copy link
Contributor

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?

Copy link
Member Author

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

Copy link
Contributor

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?

Copy link
Member Author

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.

@anicholls anicholls changed the base branch from master to prerelease/v4 February 24, 2020 18:16
@anicholls anicholls mentioned this pull request Feb 24, 2020
12 tasks
@anicholls
Copy link
Contributor

@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.

anicholls
anicholls previously approved these changes Mar 11, 2020
@anicholls anicholls dismissed their stale review March 11, 2020 17:52

Approved the wrong PR haha

@jpante jpante assigned mannycarrera4 and unassigned NicholasBoll Mar 12, 2020
@cypress
Copy link

cypress bot commented Mar 12, 2020



Test summary

198 0 0 0


Run details

Project canvas-kit
Status Passed
Commit 34dd6bf ℹ️
Started Mar 17, 2020 6:37 PM
Ended Mar 17, 2020 6:40 PM
Duration 02:57 💡
OS Linux Ubuntu Linux - 16.04
Browser Electron 78

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

@anicholls anicholls changed the title fix(modal): Hide background content from assistive technology fix(modal): Use React portals for accessibility fixes Mar 12, 2020
@anicholls
Copy link
Contributor

Something worth noting that I just noticed. InputProvider will not work with any portalled components because they are no longer a child of the element that it controls. Maybe we need to consider making InputProvider set its data attributes on the body element.

Copy link
Contributor

@anicholls anicholls left a 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.

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;
Copy link
Contributor

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?

@NicholasBoll
Copy link
Member Author

Maybe we need to consider making InputProvider set its data attributes on the body element.

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 aria-hidden="true" on other elements outside the control of the Modal, but that is required to work properly. I made a compromise by allowing the "container" to be set to anything and defaulting to document.body. Hopefully that is a good compromise. Documentation should state this is happening in case it causes issues.

@NicholasBoll
Copy link
Member Author

@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.

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 blur event outside the portalled content and forcing focus back on the target element and closing the portalled popup. That way the next tab key is the native browser focus management.

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>
Copy link
Member Author

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(
Copy link
Member Author

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

@anicholls
Copy link
Contributor

I think this should happen for all Popups. I think portalling should be the default unless we have a good reason not to.

@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

@NicholasBoll
Copy link
Member Author

I think this should happen for all Popups. I think portalling should be the default unless we have a good reason not to.

@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.

@NicholasBoll NicholasBoll merged commit 7895f09 into Workday:prerelease/v4 Mar 17, 2020
@NicholasBoll NicholasBoll deleted the fix/modal-accessibility-touch branch March 17, 2020 19:18
@anicholls anicholls added this to the v4.0.0 milestone Mar 31, 2020
@anicholls anicholls mentioned this pull request Jun 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
4.x bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Modal is not fully accessible Modal: Component is constrained by overflow content
5 participants