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(Tearsheet): implement focus trapping within tearsheets #4129

Merged

Conversation

makafsal
Copy link
Member

@makafsal makafsal commented Jan 22, 2024

Contributes to #3499

This is to fix the focus trap issue in the Tearsheet component

What did you change?

  1. File: packages/ibm-products/src/components/Tearsheet/TearsheetShell.js

Implement a 'keydown' event handler handleKeyDown() function that handles the 'Tab' key operations. The function checks for focusable elements inside the Tearsheet component and properly navigates the users to the focusable elements in the Tearsheet.

Change in the logic of focus claim function claimFocus()

  1. File: packages/ibm-products/src/components/CreateTearsheet/preview-components/MultiStepWithIntro.js

Add tabIndex property conditionally to the RadioTile component

How did you test and verify your work?

Storybook tested in Chrome, Safari, Firefox, and Edge

makafsal and others added 5 commits January 19, 2024 12:25
Use 'keydown' event listener to implement focus trapping.
Handle focus in code.

One more issue resolve in the Narrow Tearsheet:
 - Update in Narrow Tearsheet storybook.
 - Add 'selectorPrimaryFocus' property to TearsheetNarrow component.
 - Assign tabIndex and id to the main content div inside TearsheetNarrow.
Logic change in 'keydown' event handler function.
introduce conditional tabIndex for RadioTile component in MultiStepWithIntro.js file to resolve focus issue in selected RadioTile component
@makafsal makafsal requested a review from elycheea January 22, 2024 06:55
@makafsal makafsal requested a review from a team as a code owner January 22, 2024 06:55
Copy link

netlify bot commented Jan 22, 2024

Deploy Preview for carbon-for-ibm-products ready!

Name Link
🔨 Latest commit 7be0c42
🔍 Latest deploy log https://app.netlify.com/sites/carbon-for-ibm-products/deploys/65c538b60c83560008ea2f23
😎 Deploy Preview https://deploy-preview-4129--carbon-for-ibm-products.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

amal-k-joy
amal-k-joy previously approved these changes Jan 30, 2024
Copy link
Contributor

@amal-k-joy amal-k-joy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks fine and seems to be working.

@makafsal
Copy link
Member Author

makafsal commented Feb 6, 2024

@Ratheeshrajan @elycheea @lee-chase

I have tried this wrapFocus() utility function. Here are my observations from that:

  1. When using the usePortalTarget hook to render the ComposedModal component, the onBlur() event is not firing, so I used the onKeydown() event to experiment with the wrapFocus() function. It is working in a few cases, but it is not solving the issue in stacked narrow tearsheet and tearsheet with error state.
  2. In addition, focusing on dummy 'focus sentinal' elements may confuse users using screen readers.

However, I am trying to improve my current solution to make it more efficient, and reusable. We can hold this PR till I refactor and improve the code.

@makafsal
Copy link
Member Author

makafsal commented Feb 6, 2024

@elycheea @matthewgallo @Ratheeshrajan @amal-k-joy @lee-chase
Reworked the entire code 🥳 and created a useFocus() hook. So we can reuse the hook with the SidePanel component as well. I've tried it with SidePanel, and it works perfectly! 🤩.

How to use the hook:

    import { useFocus } from '../../global/js/hooks/useFocus';

    export const TearsheetShell = React.forwardRef(
    ...
        // call the hook like this
        const { firstElement, keyDownListener } = useFocus(modalRef);
    ...
        // to focus the first element
        useEffect(() => {
            if (open) {
                setTimeout(() => {
                    firstElement?.focus();
                }, 0);
            }
        }, [open, firstElement]);
    ...
        <ComposedModal
            ...
            // just call the listener in the onKeyDown event
            onKeyDown={keyDownListener}
        >
        ...
        </ComposedModal>

davidmenendez
davidmenendez previously approved these changes Feb 6, 2024
@elycheea elycheea added this pull request to the merge queue Feb 9, 2024
Merged via the queue into carbon-design-system:main with commit 3403941 Feb 9, 2024
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
version: 2 Carbon 11 / v2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants