fix(accordion): allow interactive elements in header and content (#DS-4614) - #1842
Conversation
…-4614) Move keydown handling from the accordion host onto each trigger, so buttons, dropdowns and form controls placed beside the trigger or inside the expanded content keep their own keys instead of toggling or navigating the accordion. Scope the item content query to the accordion that owns each item: a descendant query also matched a nested accordion's items, which sent arrow-key focus into the nested headers and reported nested values through valueChange. Name the header heading after its trigger, so the labels of the actions beside it no longer leak into the section title announced when navigating by heading.
|
Visit the preview URL for this PR (updated for commit d22106f): https://koobiq-next--prs-1842-nijpmihv.web.app (expires Sat, 08 Aug 2026 11:56:20 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: c9e37e518febda70d0317d07e8ceb35ac43c534c |
There was a problem hiding this comment.
Pull request overview
This PR improves KbqAccordion keyboard handling and accessibility when accordion headers/content contain additional interactive elements, and fixes nested-accordion behavior by ensuring an accordion only manages its own items.
Changes:
- Move keydown handling from the accordion host to each trigger so non-trigger interactive elements keep their native keyboard behavior.
- Filter descendant item queries to exclude nested accordions’ items, preventing focus/value leakage across accordions.
- Improve heading accessible naming by labeling the header heading from the trigger only; add docs + examples demonstrating the recommended markup.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/public_api_guard/components/accordion.api.md | Updates API snapshot for new/changed members (allItems, trigger keydown, header labeling). |
| packages/docs-examples/example-module.ts | Registers the new interactive-elements accordion example for docs. |
| packages/docs-examples/components/accordion/index.ts | Exports and includes the new interactive-elements example in the accordion examples bundle. |
| packages/docs-examples/components/accordion/accordion-interactive-elements/accordion-interactive-elements-example.ts | Adds a new docs example component demonstrating interactive elements in header/content. |
| packages/docs-examples/components/accordion/accordion-interactive-elements/accordion-interactive-elements-example.html | Adds example template showing header actions + content controls. |
| packages/docs-examples/components/accordion/accordion-header/accordion-header-example.html | Refactors header example to place actions beside the trigger (not inside). |
| packages/components/accordion/accordion.ts | Removes host keydown handler; introduces filtered items computed to exclude nested accordions. |
| packages/components/accordion/accordion.spec.ts | Updates keydown tests to dispatch on triggers; adds coverage for interactive content and nested accordions; adds a11y assertions. |
| packages/components/accordion/accordion.scss | Adds header clipping and styles for .kbq-accordion-header__actions layout. |
| packages/components/accordion/accordion.ru.md | Documents the interactive-elements guidance and adds the new example to RU docs. |
| packages/components/accordion/accordion.en.md | Documents the interactive-elements guidance and adds the new example to EN docs. |
| packages/components/accordion/accordion-trigger.scss | Deprecates trigger-nested actions styling and improves layout behavior for long labels. |
| packages/components/accordion/accordion-trigger.directive.ts | Adds trigger-scoped keydown routing to accordion handler. |
| packages/components/accordion/accordion-tokens.scss | Adds tokens for header actions spacing/padding. |
| packages/components/accordion/accordion-item.ts | Exposes the owning accordion reference for ownership filtering. |
| packages/components/accordion/accordion-header.ts | Sets aria-labelledby on the heading to reference the trigger’s ID. |
| packages/components/accordion/_accordion-theme.scss | Adjusts focus styling to key off trigger focus rather than :focus-within on the whole item. |
| packages/components-dev/accordion/template.html | Shows the new interactive-elements example in the dev app template. |
| packages/components-dev/accordion/module.ts | Imports the new example into the accordion dev app. |
Suppressed comments (1)
packages/docs-examples/components/accordion/accordion-interactive-elements/accordion-interactive-elements-example.html:22
- Same issue as above:
attr.aria-labeldoes not setaria-label, so screen readers won't get an accessible name for this control.
<button
attr.aria-label="More actions for {{ section.title }}"
kbq-icon-button="kbq-ellipsis-vertical_16"
type="button"
[kbqDropdownTriggerFor]="dropdown"
></button>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @Component({ | ||
| selector: 'dev-app', | ||
| imports: [KbqAccordionModule, KbqIconModule], | ||
| imports: [KbqAccordionModule, KbqIconModule, AccordionInteractiveElementsExample], |
There was a problem hiding this comment.
давай сделаем по аналогии с другими dev компонентами, через DevDocsExamples
| * The accordion this item belongs to — always the nearest one, because it is injected. | ||
| * The accordion reads it back to tell its own items apart from a nested accordion's. | ||
| */ | ||
| readonly accordion = inject(KbqAccordion); |
There was a problem hiding this comment.
Должен ли фокус заходить в контент item в закрытом состоянии?
Если нет, можно для KbqAccordionContentDirective добавить атрибут inert
🚨 E2E tests failedReview the report for details. 💡 Comment |
|
/approve-snapshots |
|
🔄 Updating snapshots. |
|
✅ Snapshots updated! |
Move keydown handling from the accordion host onto each trigger, so buttons, dropdowns and form controls placed beside the trigger or inside the expanded content keep their own keys instead of toggling or navigating the accordion.
Scope the item content query to the accordion that owns each item: a descendant query also matched a nested accordion's items, which sent arrow-key focus into the nested headers and reported nested values through valueChange.
Name the header heading after its trigger, so the labels of the actions beside it no longer leak into the section title announced when navigating by heading.