Skip to content

Commit fa18b70

Browse files
jcfrancobenelan
authored andcommitted
fix(panel): tweak focusable content area (#10141)
**Related Issue:** #10022 ## Summary Updates `panel` to have a single focusable container when it has scrolling content. ### Noteworthy changes * Improves test coverage for both scrolling and non-scrolling content. * Updates `disabled` test helper to clear focus targets between tab and click tests
1 parent 52081ca commit fa18b70

File tree

10 files changed

+326
-85
lines changed

10 files changed

+326
-85
lines changed

packages/calcite-components/src/components/checkbox/checkbox.e2e.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,15 @@ describe("calcite-checkbox", () => {
3636
});
3737

3838
describe("disabled", () => {
39-
disabled("calcite-checkbox");
39+
disabled("calcite-checkbox", {
40+
focusTarget: {
41+
tab: "calcite-checkbox",
42+
click: {
43+
pointer: "calcite-checkbox",
44+
method: "calcite-checkbox",
45+
},
46+
},
47+
});
4048
});
4149

4250
it("renders with correct default attributes", async () => {

packages/calcite-components/src/components/combobox/combobox.e2e.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,15 @@ describe("calcite-combobox", () => {
164164
});
165165

166166
describe("disabled", () => {
167-
disabled("calcite-combobox");
167+
disabled("calcite-combobox", {
168+
focusTarget: {
169+
tab: "calcite-combobox",
170+
click: {
171+
pointer: "calcite-combobox",
172+
method: "calcite-combobox",
173+
},
174+
},
175+
});
168176
});
169177

170178
const simpleComboboxHTML = html`

packages/calcite-components/src/components/flow-item/flow-item.e2e.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from "../../tests/commonTests";
1414
import { html } from "../../../support/formatting";
1515
import { GlobalTestProps } from "../../tests/utils";
16+
import { scrollingContentHtml, scrollingHeightStyle } from "../panel/panel.e2e";
1617
import { IDS as PanelIDS } from "../panel/resources";
1718
import { CSS, SLOTS } from "./resources";
1819

@@ -124,7 +125,24 @@ describe("calcite-flow-item", () => {
124125
});
125126

126127
describe("disabled", () => {
127-
disabled(`<calcite-flow-item closable>scrolling content</calcite-flow-item>`);
128+
disabled(html`<calcite-flow-item style="${scrollingHeightStyle}">${scrollingContentHtml}</calcite-flow-item>`, {
129+
focusTarget: {
130+
tab: "calcite-flow-item",
131+
click: "body",
132+
},
133+
});
134+
135+
describe("closable", () => {
136+
disabled(
137+
html`<calcite-flow-item closable style="${scrollingHeightStyle}">${scrollingContentHtml}</calcite-flow-item>`,
138+
{
139+
focusTarget: {
140+
tab: "calcite-flow-item",
141+
click: "body",
142+
},
143+
},
144+
);
145+
});
128146
});
129147

130148
describe("accessible", () => {

packages/calcite-components/src/components/input-date-picker/input-date-picker.e2e.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,12 @@ describe("calcite-input-date-picker", () => {
6666
});
6767

6868
describe("disabled", () => {
69-
disabled("calcite-input-date-picker");
69+
disabled("calcite-input-date-picker", {
70+
focusTarget: {
71+
tab: "calcite-input-date-picker",
72+
click: "calcite-input-date-picker",
73+
},
74+
});
7075
});
7176

7277
describe("openClose", () => {

packages/calcite-components/src/components/panel/panel.e2e.ts

Lines changed: 188 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,55 @@ const panelTemplate = (scrollable = false) =>
3030
</calcite-panel>
3131
</div>`;
3232

33+
export const scrollingContentHtml = html`
34+
<p>
35+
Enim nascetur erat faucibus ornare varius arcu fames bibendum habitant felis elit ante. Nibh morbi massa curae; leo
36+
semper diam aenean congue taciti eu porta. Varius faucibus ridiculus donec. Montes sit ligula purus porta ante lacus
37+
habitasse libero cubilia purus! In quis congue arcu maecenas felis cursus pellentesque nascetur porta donec non.
38+
Quisque, rutrum ligula pharetra justo habitasse facilisis rutrum neque. Magnis nostra nec nulla dictumst taciti
39+
consectetur. Non porttitor tempor orci dictumst magna porta vitae.
40+
</p>
41+
<p>
42+
Ipsum nostra tempus etiam augue ullamcorper scelerisque sapien potenti erat nisi gravida. Vehicula sem tristique
43+
sed. Nullam, sociis imperdiet ullamcorper? Dapibus fames primis ridiculus vulputate, habitant inceptos! Nunc
44+
torquent lorem urna vehicula volutpat donec nec. Orci massa eu nec donec enim fames, faucibus quam aenean. Laoreet
45+
tellus tempor quisque ornare lobortis praesent erat senectus natoque consectetur donec imperdiet. Quis sem cum
46+
gravida dictumst a pretium purus aptent amet id. Orci habitasse, praesent facilisis condimentum. Nec elit turpis
47+
leo.
48+
</p>
49+
<p>
50+
Tempus per volutpat diam tempor mauris parturient vulputate leo id libero quisque. Mattis aliquam dictum venenatis
51+
fringilla. Taciti venenatis, ultrices sollicitudin consequat. Sapien fusce est iaculis potenti ut auctor potenti.
52+
Nisi malesuada feugiat vulputate vitae porttitor. Nullam nullam nullam accumsan quis magna in. Elementum, nascetur
53+
gravida cras scelerisque inceptos aenean inceptos potenti. Lobortis condimentum accumsan posuere curabitur fermentum
54+
diam, natoque quisque. Eget placerat sed aptent orci urna fusce magnis. Vel lacus magnis nunc.
55+
</p>
56+
<p>
57+
Enim nascetur erat faucibus ornare varius arcu fames bibendum habitant felis elit ante. Nibh morbi massa curae; leo
58+
semper diam aenean congue taciti eu porta. Varius faucibus ridiculus donec. Montes sit ligula purus porta ante lacus
59+
habitasse libero cubilia purus! In quis congue arcu maecenas felis cursus pellentesque nascetur porta donec non.
60+
Quisque, rutrum ligula pharetra justo habitasse facilisis rutrum neque. Magnis nostra nec nulla dictumst taciti
61+
consectetur. Non porttitor tempor orci dictumst magna porta vitae.
62+
</p>
63+
<p>
64+
Ipsum nostra tempus etiam augue ullamcorper scelerisque sapien potenti erat nisi gravida. Vehicula sem tristique
65+
sed. Nullam, sociis imperdiet ullamcorper? Dapibus fames primis ridiculus vulputate, habitant inceptos! Nunc
66+
torquent lorem urna vehicula volutpat donec nec. Orci massa eu nec donec enim fames, faucibus quam aenean. Laoreet
67+
tellus tempor quisque ornare lobortis praesent erat senectus natoque consectetur donec imperdiet. Quis sem cum
68+
gravida dictumst a pretium purus aptent amet id. Orci habitasse, praesent facilisis condimentum. Nec elit turpis
69+
leo.
70+
</p>
71+
<p>
72+
Tempus per volutpat diam tempor mauris parturient vulputate leo id libero quisque. Mattis aliquam dictum venenatis
73+
fringilla. Taciti venenatis, ultrices sollicitudin consequat. Sapien fusce est iaculis potenti ut auctor potenti.
74+
Nisi malesuada feugiat vulputate vitae porttitor. Nullam nullam nullam accumsan quis magna in. Elementum, nascetur
75+
gravida cras scelerisque inceptos aenean inceptos potenti. Lobortis condimentum accumsan posuere curabitur fermentum
76+
diam, natoque quisque. Eget placerat sed aptent orci urna fusce magnis. Vel lacus magnis nunc.
77+
</p>
78+
`;
79+
80+
export const scrollingHeightStyle = "height: 200px;";
81+
3382
describe("calcite-panel", () => {
3483
describe("renders", () => {
3584
renders("calcite-panel", { display: "flex" });
@@ -102,7 +151,38 @@ describe("calcite-panel", () => {
102151
});
103152

104153
describe("disabled", () => {
105-
disabled(`<calcite-panel closable>scrolling content</calcite-panel>`);
154+
describe("with scrolling content", () => {
155+
disabled(html`<calcite-panel style="${scrollingHeightStyle}">${scrollingContentHtml}</calcite-panel>`, {
156+
focusTarget: {
157+
tab: "calcite-panel",
158+
click: "body",
159+
},
160+
});
161+
162+
describe("closable", () => {
163+
disabled(
164+
html`<calcite-panel closable style="${scrollingHeightStyle}">${scrollingContentHtml}</calcite-panel>`,
165+
{
166+
focusTarget: {
167+
tab: "calcite-panel",
168+
click: "body",
169+
},
170+
},
171+
);
172+
});
173+
});
174+
175+
describe("without scrolling content", () => {
176+
disabled(html`<calcite-panel>non-scrolling content</calcite-panel>`, {
177+
focusTarget: "none",
178+
});
179+
180+
describe("closable", () => {
181+
disabled(html`<calcite-panel closable>non-scrolling content</calcite-panel>`, {
182+
focusTarget: "none",
183+
});
184+
});
185+
});
106186
});
107187

108188
describe("translation support", () => {
@@ -252,9 +332,9 @@ describe("calcite-panel", () => {
252332
<calcite-panel>
253333
<calcite-action-bar slot="${SLOTS.actionBar}">
254334
<calcite-action-group>
255-
<calcite-action text="Add" icon="plus"> </calcite-action>
256-
<calcite-action text="Save" icon="save"> </calcite-action>
257-
<calcite-action text="Layers" icon="layers"> </calcite-action>
335+
<calcite-action text="Add" icon="plus"></calcite-action>
336+
<calcite-action text="Save" icon="save"></calcite-action>
337+
<calcite-action text="Layers" icon="layers"></calcite-action>
258338
</calcite-action-group>
259339
</calcite-action-bar>
260340
<div slot="${SLOTS.headerActionsStart}">test start</div>
@@ -270,9 +350,9 @@ describe("calcite-panel", () => {
270350
<calcite-panel collapsible closable>
271351
<calcite-action-bar slot="${SLOTS.actionBar}">
272352
<calcite-action-group>
273-
<calcite-action text="Add" icon="plus"> </calcite-action>
274-
<calcite-action text="Save" icon="save"> </calcite-action>
275-
<calcite-action text="Layers" icon="layers"> </calcite-action>
353+
<calcite-action text="Add" icon="plus"></calcite-action>
354+
<calcite-action text="Save" icon="save"></calcite-action>
355+
<calcite-action text="Layers" icon="layers"></calcite-action>
276356
</calcite-action-group>
277357
</calcite-action-bar>
278358
<div slot="${SLOTS.headerActionsStart}">test start</div>
@@ -285,15 +365,36 @@ describe("calcite-panel", () => {
285365
`);
286366
});
287367

288-
describe("should focus on close button", () => {
289-
focusable(`<calcite-panel closable>test</calcite-panel>`, {
290-
shadowFocusTargetSelector: "calcite-action",
368+
describe("is focusable", () => {
369+
describe("with scrolling content", () => {
370+
describe("closable", () => {
371+
focusable(
372+
html`<calcite-panel closable style="${scrollingHeightStyle}">${scrollingContentHtml}</calcite-panel>`,
373+
{
374+
shadowFocusTargetSelector: "calcite-action",
375+
},
376+
);
377+
});
378+
379+
describe("should focus on container", () => {
380+
focusable(html`<calcite-panel style="${scrollingHeightStyle}">${scrollingContentHtml}</calcite-panel>`, {
381+
shadowFocusTargetSelector: `.${CSS.contentWrapper}`,
382+
});
383+
});
291384
});
292-
});
293385

294-
describe("should focus on container", () => {
295-
focusable(`<calcite-panel>test</calcite-panel>`, {
296-
shadowFocusTargetSelector: "article",
386+
describe("without scrolling content", () => {
387+
describe("closable", () => {
388+
focusable(html`<calcite-panel closable>non-scrolling content</calcite-panel>`, {
389+
shadowFocusTargetSelector: "calcite-action",
390+
});
391+
});
392+
393+
describe("should not focus on container", () => {
394+
focusable(html`<calcite-panel>non-scrolling-content</calcite-panel>`, {
395+
focusTargetSelector: "body",
396+
});
397+
});
297398
});
298399
});
299400

@@ -456,51 +557,83 @@ describe("calcite-panel", () => {
456557
expect(await scrollEl.getProperty("scrollTop")).toBe(100);
457558
});
458559

459-
it("should close when Escape key is pressed and closable is true", async () => {
460-
const page = await newE2EPage();
461-
await page.setContent("<calcite-panel>test</calcite-panel>");
462-
const panel = await page.find("calcite-panel");
463-
const calcitePanelClose = await panel.spyOnEvent("calcitePanelClose");
464-
const container = await page.find(`calcite-panel >>> .${CSS.container}`);
465-
expect(await panel.getProperty("closed")).toBe(false);
466-
expect(await container.isVisible()).toBe(true);
467-
await container.press("Escape");
468-
await page.waitForChanges();
469-
expect(await panel.getProperty("closed")).toBe(false);
470-
expect(await container.isVisible()).toBe(true);
471-
panel.setProperty("closable", true);
472-
await page.waitForChanges();
473-
await container.press("Escape");
474-
await page.waitForChanges();
475-
expect(await panel.getProperty("closed")).toBe(true);
476-
expect(await container.isVisible()).toBe(false);
477-
expect(calcitePanelClose).toHaveReceivedEventTimes(1);
478-
});
479-
480-
it("should not close when Escape key is prevented and closable is true", async () => {
481-
const page = await newE2EPage();
482-
await page.setContent("<calcite-panel closable>test</calcite-panel>");
483-
const panel = await page.find("calcite-panel");
484-
const calcitePanelClose = await panel.spyOnEvent("calcitePanelClose");
485-
const container = await page.find(`calcite-panel >>> .${CSS.container}`);
486-
487-
expect(await panel.getProperty("closed")).toBe(false);
488-
expect(await container.isVisible()).toBe(true);
560+
describe("closable", () => {
561+
describe("with scrollable content (Escape emits from scroll container)", () => {
562+
it("should close when Escape key is pressed and closable is true", async () => {
563+
const page = await newE2EPage();
564+
await page.setContent(
565+
html`<calcite-panel style="${scrollingHeightStyle}">${scrollingContentHtml}</calcite-panel>`,
566+
);
567+
const panel = await page.find("calcite-panel");
568+
const calcitePanelClose = await panel.spyOnEvent("calcitePanelClose");
569+
const contentWrapper = await page.find(`calcite-panel >>> .${CSS.contentWrapper}`);
570+
const container = await page.find(`calcite-panel >>> .${CSS.container}`);
571+
expect(await panel.getProperty("closed")).toBe(false);
572+
expect(await container.isVisible()).toBe(true);
573+
await contentWrapper.press("Escape");
574+
await page.waitForChanges();
575+
expect(await panel.getProperty("closed")).toBe(false);
576+
expect(await container.isVisible()).toBe(true);
577+
panel.setProperty("closable", true);
578+
await page.waitForChanges();
579+
580+
await contentWrapper.press("Escape");
581+
await page.waitForChanges();
582+
expect(await panel.getProperty("closed")).toBe(true);
583+
expect(await container.isVisible()).toBe(false);
584+
expect(calcitePanelClose).toHaveReceivedEventTimes(1);
585+
});
489586

490-
await page.$eval("calcite-panel", (panel: HTMLCalcitePanelElement) => {
491-
panel.addEventListener("keydown", (event) => {
492-
if (event.key === "Escape") {
493-
event.preventDefault();
494-
}
587+
it("should not close when Escape key is prevented and closable is true", async () => {
588+
const page = await newE2EPage();
589+
await page.setContent(
590+
html`<calcite-panel closable style="${scrollingHeightStyle}">${scrollingContentHtml}</calcite-panel>`,
591+
);
592+
const panel = await page.find("calcite-panel");
593+
const calcitePanelClose = await panel.spyOnEvent("calcitePanelClose");
594+
const container = await page.find(`calcite-panel >>> .${CSS.container}`);
595+
596+
expect(await panel.getProperty("closed")).toBe(false);
597+
expect(await container.isVisible()).toBe(true);
598+
599+
await page.$eval("calcite-panel", (panel: HTMLCalcitePanelElement) => {
600+
panel.addEventListener("keydown", (event) => {
601+
if (event.key === "Escape") {
602+
event.preventDefault();
603+
}
604+
});
605+
});
606+
607+
await panel.press("Escape");
608+
await page.waitForChanges();
609+
610+
expect(await panel.getProperty("closed")).toBe(false);
611+
expect(await container.isVisible()).toBe(true);
612+
expect(calcitePanelClose).toHaveReceivedEventTimes(0);
495613
});
496614
});
497615

498-
await panel.press("Escape");
499-
await page.waitForChanges();
500-
501-
expect(await panel.getProperty("closed")).toBe(false);
502-
expect(await container.isVisible()).toBe(true);
503-
expect(calcitePanelClose).toHaveReceivedEventTimes(0);
616+
describe("without scrollable content (Escape emits from close button)", () => {
617+
it("should close when Escape key is pressed and closable is true", async () => {
618+
const page = await newE2EPage();
619+
await page.setContent(html`<calcite-panel closable>non-scrolling content</calcite-panel>`);
620+
const panel = await page.find("calcite-panel");
621+
const calcitePanelClose = await panel.spyOnEvent("calcitePanelClose");
622+
const closeButton = await page.find(`calcite-panel >>> #${IDS.close}`);
623+
const container = await page.find(`calcite-panel >>> .${CSS.container}`);
624+
expect(await panel.getProperty("closed")).toBe(false);
625+
expect(await container.isVisible()).toBe(true);
626+
expect(calcitePanelClose).toHaveReceivedEventTimes(0);
627+
628+
await closeButton.callMethod("setFocus");
629+
await closeButton.press("Escape");
630+
await page.waitForChanges();
631+
632+
expect(await panel.getProperty("closed")).toBe(true);
633+
expect(await container.isVisible()).toBe(false);
634+
expect(calcitePanelClose).toHaveReceivedEventTimes(1);
635+
});
636+
});
504637
});
505638

506639
describe("theme", () => {

0 commit comments

Comments
 (0)