Skip to content

Commit

Permalink
feat: add options to set initial focus within modal (#476)
Browse files Browse the repository at this point in the history
  • Loading branch information
megawebmaster committed Jun 1, 2021
1 parent 23cccc6 commit 5bdc362
Show file tree
Hide file tree
Showing 10 changed files with 328 additions and 206 deletions.
2 changes: 1 addition & 1 deletion react-responsive-modal/__tests__/index.test.tsx
@@ -1,5 +1,5 @@
import * as React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { Modal } from '../src';

describe('modal', () => {
Expand Down
10 changes: 10 additions & 0 deletions react-responsive-modal/cypress/integration/modal.spec.ts
Expand Up @@ -65,4 +65,14 @@ describe('simple modal', () => {
cy.get('[data-testid=modal]').should('not.exist');
cy.get('body').should('not.have.css', 'overflow', 'hidden');
});

it('should focus first element within modal', () => {
cy.get('button').eq(3).click();
cy.get('[data-testid=modal] input').first().should('have.focus');
});

it('should focus on modal root', () => {
cy.get('button').eq(4).click();
cy.get('[data-testid=modal]').should('have.focus');
});
});
5 changes: 3 additions & 2 deletions react-responsive-modal/package.json
Expand Up @@ -47,14 +47,15 @@
"size-limit": [
{
"path": "dist/react-responsive-modal.cjs.production.min.js",
"limit": "3.8 KB"
"limit": "4.0 KB"
},
{
"path": "dist/react-responsive-modal.esm.js",
"limit": "3.8 KB"
"limit": "4.0 KB"
}
],
"dependencies": {
"@bedrock-layout/use-forwarded-ref": "^1.1.4",
"body-scroll-lock": "^3.1.5",
"classnames": "^2.2.6"
},
Expand Down
23 changes: 18 additions & 5 deletions react-responsive-modal/src/FocusTrap.tsx
Expand Up @@ -8,9 +8,10 @@ import {

interface FocusTrapProps {
container?: React.RefObject<HTMLElement> | null;
initialFocusRef?: React.RefObject<HTMLElement>;
}

export const FocusTrap = ({ container }: FocusTrapProps) => {
export const FocusTrap = ({ container, initialFocusRef }: FocusTrapProps) => {
const refLastFocus = useRef<HTMLElement | null>();
/**
* Handle focus lock on the modal
Expand All @@ -27,8 +28,7 @@ export const FocusTrap = ({ container }: FocusTrapProps) => {
}
// On mount we focus on the first focusable element in the modal if there is one
if (isBrowser && container?.current) {
const allTabbingElements = getAllTabbingElements(container.current);
if (allTabbingElements[0]) {
const savePreviousFocus = () => {
// First we save the last focused element
// only if it's a focusable element
if (
Expand All @@ -38,7 +38,20 @@ export const FocusTrap = ({ container }: FocusTrapProps) => {
) {
refLastFocus.current = document.activeElement as HTMLElement;
}
allTabbingElements[0].focus();
};

if (initialFocusRef) {
savePreviousFocus();
// We need to schedule focusing on a next frame - this allows to focus on the modal root
requestAnimationFrame(() => {
initialFocusRef.current?.focus();
});
} else {
const allTabbingElements = getAllTabbingElements(container.current);
if (allTabbingElements[0]) {
savePreviousFocus();
allTabbingElements[0].focus();
}
}
}
return () => {
Expand All @@ -48,7 +61,7 @@ export const FocusTrap = ({ container }: FocusTrapProps) => {
refLastFocus.current?.focus();
}
};
}, [container]);
}, [container, initialFocusRef]);

return null;
};

0 comments on commit 5bdc362

Please sign in to comment.