Skip to content

Commit df144dc

Browse files
authored
fix(modal): no longer loses focus trap after clicking inside the component. (#6434)
**Related Issue:** #6281 ## Summary This PR will fix `focusTrap` issue when user clicks inside the `modal`. When user clicks inside the modal, `focusTrap` behavior wont be affected.
1 parent df132ba commit df144dc

File tree

3 files changed

+90
-3
lines changed

3 files changed

+90
-3
lines changed

src/components/modal/modal.e2e.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,46 @@ describe("calcite-modal accessibility checks", () => {
382382
expect(document.activeElement).toEqual($button1);
383383
});
384384

385+
it("traps focus within the modal when user clicks inside the modal", async () => {
386+
const page = await newE2EPage();
387+
await page.setContent(
388+
html`
389+
<calcite-modal aria-labelledby="modal-title" close-button-disabled open>
390+
<div slot="header" id="modal-title">Modal title</div>
391+
<div slot="content">
392+
<p>Modal content.</p>
393+
<calcite-button icon-start="plus" id="plus" round></calcite-button>
394+
</div>
395+
<calcite-button slot="back" color="neutral" width="full">Back</calcite-button>
396+
<calcite-button slot="secondary" width="full" appearance="outline">Cancel</calcite-button>
397+
<calcite-button slot="primary" width="full">Save</calcite-button>
398+
</calcite-modal>
399+
`
400+
);
401+
402+
const contentEl = await page.find(`div[slot="content"]`);
403+
const backButtonEl = await page.find(`calcite-button[slot="back"]`);
404+
405+
await page.keyboard.press("Tab");
406+
expect(await page.evaluate(() => document.activeElement.id)).toBe("plus");
407+
408+
await page.keyboard.press("Tab");
409+
expect(await page.evaluate(() => document.activeElement.slot)).toBe("back");
410+
411+
await page.keyboard.press("Tab");
412+
expect(await page.evaluate(() => document.activeElement.slot)).toBe("secondary");
413+
414+
await page.keyboard.press("Tab");
415+
expect(await page.evaluate(() => document.activeElement.slot)).toBe("primary");
416+
417+
await contentEl.click();
418+
await page.waitForChanges();
419+
await backButtonEl.click();
420+
await page.waitForChanges();
421+
422+
expect(await page.evaluate(() => document.activeElement.slot)).toBe("back");
423+
});
424+
385425
describe("setFocus", () => {
386426
const createModalHTML = (contentHTML?: string, attrs?: string) =>
387427
`<calcite-modal open ${attrs}>${contentHTML}</calcite-modal>`;

src/components/popover/popover.e2e.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,4 +734,48 @@ describe("calcite-popover", () => {
734734
shadowFocusTargetSelector: `.${CSS.closeButton}`
735735
}));
736736
});
737+
738+
it("should focus input element in the page with popover when user click", async () => {
739+
// refer to https://github.com/Esri/calcite-components/issues/5993 for context
740+
const page = await newE2EPage();
741+
await page.setContent(html` <calcite-shell content-behind>
742+
<calcite-shell-panel slot="panel-end">
743+
<calcite-label>
744+
value
745+
<calcite-input-number value="0" min="0" max="10" id="shell-input"></calcite-input-number>
746+
</calcite-label>
747+
<calcite-button id="button">open popover</calcite-button>
748+
</calcite-shell-panel>
749+
</calcite-shell>
750+
751+
<calcite-popover reference-element="button">
752+
<calcite-panel heading="popover panel header" closable="true" style="height: 400px">
753+
<calcite-input-number value="5" min="0" max="10" id="popover-input"></calcite-input-number>
754+
</calcite-panel>
755+
</calcite-popover>`);
756+
757+
const popover = await page.find("calcite-popover");
758+
expect(await popover.getProperty("open")).toBe(false);
759+
760+
const referenceElement = await page.find("calcite-button#button");
761+
await referenceElement.click();
762+
await page.waitForChanges();
763+
expect(await popover.getProperty("open")).toBe(true);
764+
765+
const inputElInPopover = await page.find("calcite-input-number#popover-input");
766+
await inputElInPopover.click();
767+
expect(await page.evaluate(() => document.activeElement.id)).toBe("popover-input");
768+
769+
await page.keyboard.press("Backspace");
770+
await page.keyboard.type("12345");
771+
expect(await inputElInPopover.getProperty("value")).toBe("12345");
772+
773+
const inputElInShell = await page.find("calcite-input-number#shell-input");
774+
await inputElInShell.click();
775+
expect(await page.evaluate(() => document.activeElement.id)).toBe("shell-input");
776+
777+
await page.keyboard.press("Backspace");
778+
await page.keyboard.type("12345");
779+
expect(await inputElInShell.getProperty("value")).toBe("12345");
780+
});
737781
});

src/utils/focusTrapComponent.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,15 @@ export function connectFocusTrap(component: FocusTrapComponent): void {
4242
return;
4343
}
4444

45-
if (focusTrapEl.tabIndex == null) {
46-
focusTrapEl.tabIndex = -1;
45+
if (!focusTrapEl.hasAttribute("tabindex")) {
46+
focusTrapEl.setAttribute("tabindex", "-1");
4747
}
4848

4949
const focusTrapOptions: FocusTrapOptions = {
50-
clickOutsideDeactivates: true,
50+
allowOutsideClick: true,
51+
clickOutsideDeactivates: (event: MouseEvent | TouchEvent) => {
52+
return !event.composedPath().find((el) => (el as HTMLElement) === focusTrapEl);
53+
},
5154
document: focusTrapEl.ownerDocument,
5255
escapeDeactivates: false,
5356
fallbackFocus: focusTrapEl,

0 commit comments

Comments
 (0)