diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8fac3fc02..225d511d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- `loadLayout` now copies only the serialized tile properties: the spans, the positions, the flags and `id`. A layout from storage or a server can no longer set `innerHTML` on a tile or replace its prototype through `__proto__`. A value that is not an array, and an entry that is not an object, are ignored.
### Fixed
+- #### Accordion
+ - The arrow keys, `Home` and `End` now skip a panel that does not render, such as a panel with the `hidden` attribute or `display: none`. Before, the focus stayed on the current panel.
- #### QR code
- Versions 30 to 40 at the `M` error correction level now use the data codeword counts of ISO/IEC 18004. Before, these codes had the wrong block structure.
- Without `error-level`, a logo larger than the safe area of level `M` now raises the error correction level to the smallest one that holds the logo, as documented. Before, the default `M` always applied, so the logo shrank. An explicit `error-level`, `M` included, still caps the logo.
diff --git a/src/components/accordion/accordion.spec.ts b/src/components/accordion/accordion.spec.ts
index eec935c60..2e0ec80be 100644
--- a/src/components/accordion/accordion.spec.ts
+++ b/src/components/accordion/accordion.spec.ts
@@ -150,6 +150,57 @@ describe('Accordion', () => {
expect(panels[2].matches(':focus')).to.be.true;
});
+ it('should skip a hidden panel on ArrowDown keypress', async () => {
+ panels[1].hidden = true;
+
+ simulateKeyboard(panels[0], arrowDown);
+ await elementUpdated(accordion);
+
+ expect(panels[2].matches(':focus')).to.be.true;
+ });
+
+ it('should skip a hidden panel on ArrowUp keypress', async () => {
+ panels[1].hidden = true;
+
+ simulateKeyboard(panels[2], arrowUp);
+ await elementUpdated(accordion);
+
+ expect(panels[0].matches(':focus')).to.be.true;
+ });
+
+ it('should skip a panel that its styles do not render', async () => {
+ panels[1].style.display = 'none';
+
+ simulateKeyboard(panels[0], arrowDown);
+ await elementUpdated(accordion);
+
+ expect(panels[2].matches(':focus')).to.be.true;
+ });
+
+ it('should navigate to the first rendered panel on Home keypress', async () => {
+ panels[0].hidden = true;
+
+ simulateClick(getPanelHeader(panels[2]));
+ await elementUpdated(accordion);
+
+ simulateKeyboard(panels[2], homeKey);
+ await elementUpdated(accordion);
+
+ expect(panels[1].matches(':focus')).to.be.true;
+ });
+
+ it('should navigate to the last rendered panel on End keypress', async () => {
+ panels[2].hidden = true;
+
+ simulateClick(getPanelHeader(panels[0]));
+ await elementUpdated(accordion);
+
+ simulateKeyboard(panels[0], endKey);
+ await elementUpdated(accordion);
+
+ expect(panels[1].matches(':focus')).to.be.true;
+ });
+
it('should navigate to the panel below on ArrowDown keypress', async () => {
for (let i = 0; i < panels.length; i++) {
simulateKeyboard(panels[i], arrowDown);
diff --git a/src/components/accordion/accordion.ts b/src/components/accordion/accordion.ts
index f78336986..5f4a56e14 100644
--- a/src/components/accordion/accordion.ts
+++ b/src/components/accordion/accordion.ts
@@ -47,6 +47,17 @@ export default class IgcAccordionComponent extends LitElement {
return this._panels.filter((panel) => !panel.disabled);
}
+ /**
+ * The interactive panels that the keyboard navigation can focus. A panel
+ * that is not rendered, for example with the `hidden` attribute, cannot
+ * take focus, so the navigation would stop at it.
+ */
+ private get _navigablePanels(): IgcExpansionPanelComponent[] {
+ return this._interactivePanels.filter((panel) =>
+ this._getPanelHeader(panel)?.checkVisibility({ visibilityProperty: true })
+ );
+ }
+
//#endregion
//#region Public attributes and properties
@@ -115,11 +126,11 @@ export default class IgcAccordionComponent extends LitElement {
}
private _navigateToFirst(): void {
- this._getPanelHeader(firstOf(this._interactivePanels))?.focus();
+ this._getPanelHeader(firstOf(this._navigablePanels))?.focus();
}
private _navigateToLast(): void {
- this._getPanelHeader(lastOf(this._interactivePanels))?.focus();
+ this._getPanelHeader(lastOf(this._navigablePanels))?.focus();
}
private _navigateToPrevious(event: KeyboardEvent): void {
@@ -177,7 +188,7 @@ export default class IgcAccordionComponent extends LitElement {
panel: IgcExpansionPanelComponent,
dir: 1 | -1 = 1
): IgcExpansionPanelComponent {
- const panels = this._interactivePanels;
+ const panels = this._navigablePanels;
const idx = panels.indexOf(panel);
return panels[idx + dir] || panel;
diff --git a/src/components/accordion/spec.md b/src/components/accordion/spec.md
index fa56ccc8d..1b540b278 100644
--- a/src/components/accordion/spec.md
+++ b/src/components/accordion/spec.md
@@ -36,9 +36,10 @@
## Revision history
-| Version | Date | Notes |
-| ------: | ---------- | --------------------- |
-| 1 | 2026-09-21 | Initial specification |
+| Version | Date | Notes |
+| ------: | ---------- | ---------------------------------------------- |
+| 1 | 2026-09-21 | Initial specification |
+| 2 | 2026-10-01 | The navigation skips panels that do not render |
## Overview
@@ -175,7 +176,8 @@ The keys apply while a panel header inside the accordion has focus.
| Shift + Alt + Arrow Down | Expands every panel, unless single expand is on. |
| Shift + Alt + Arrow Up | Collapses every panel. |
-Disabled panels are skipped by the navigation.
+The navigation skips disabled panels, and panels that do not render, for example a panel with the `hidden`
+attribute or `display: none`.
## API
@@ -231,7 +233,8 @@ with `@open-wc/testing` fixtures and assertions. The groups below mirror the `de
the last.
8. Alt with the arrow keys expands and collapses the focused panel.
9. Shift + Alt with the arrow keys expands and collapses every panel.
-10. Disabled panels are skipped by the navigation.
+10. The navigation skips disabled panels, and panels that do not render, with the arrow keys and with Home
+ and End.
### Nested
@@ -242,6 +245,8 @@ with `@open-wc/testing` fixtures and assertions. The groups below mirror the `de
- Only direct `igc-expansion-panel` children are members of the accordion.
- The accordion has no expansion events of its own; the panels emit them.
- The accordion does not persist which panels were expanded.
+- The navigation skips a panel that does not render, but Shift + Alt with the arrow keys,
+ `showAll` and `hideAll` still expand and collapse it.
## Accessibility
@@ -249,7 +254,8 @@ with `@open-wc/testing` fixtures and assertions. The groups below mirror the `de
- The accordion is a container; the header and region semantics come from the individual
[expansion panels](../expansion-panel/spec.md#aria-roles-and-properties).
-- The panel headers form the navigable set, and disabled panels are announced as disabled and skipped.
+- The panel headers form the navigable set. Disabled panels are announced as disabled and skipped, and panels that
+ do not render are skipped.
### Keyboard support