From fc6a76db3d031f449e49ae6b2c53cb8b5f6242de Mon Sep 17 00:00:00 2001 From: Steven Spriggs Date: Tue, 5 Nov 2024 23:51:36 -0500 Subject: [PATCH 1/4] test(accordion): expand tests --- .../rh-accordion/test/rh-accordion.spec.ts | 509 +++++++++++++++++- 1 file changed, 501 insertions(+), 8 deletions(-) diff --git a/elements/rh-accordion/test/rh-accordion.spec.ts b/elements/rh-accordion/test/rh-accordion.spec.ts index 8179db28c5..6b55f341d8 100644 --- a/elements/rh-accordion/test/rh-accordion.spec.ts +++ b/elements/rh-accordion/test/rh-accordion.spec.ts @@ -1,18 +1,254 @@ -import { expect, html, fixture } from '@open-wc/testing'; +import { expect, fixture, html, nextFrame, aTimeout } from '@open-wc/testing'; +import { sendKeys } from '@web/test-runner-commands'; + +import { allUpdates, clickElementAtCenter } from '@patternfly/pfe-tools/test/utils.js'; import { a11ySnapshot } from '@patternfly/pfe-tools/test/a11y-snapshot.js'; -import { RhAccordion } from '@rhds/elements/rh-accordion/rh-accordion.js'; + +import { RhAccordion, RhAccordionHeader, RhAccordionPanel } from '@rhds/elements/rh-accordion/rh-accordion.js'; describe('', function() { + let element: RhAccordion; + let headers: RhAccordionHeader[]; + let panels: RhAccordionPanel[]; + + function press(press: string) { + return async function() { + await sendKeys({ press }); + await allUpdates(element); + }; + } + it('should upgrade', async function() { - const el = document.createElement('rh-accordion'); + element = document.createElement('rh-accordion'); const klass = customElements.get('rh-accordion'); - expect(el) + expect(element) .to.be.an.instanceOf(klass) .and .to.be.an.instanceOf(RhAccordion); }); - describe('with slotted h5', function() { + it('imperatively instantiates', function() { + expect(document.createElement('rh-accordion')).to.be.an.instanceof(RhAccordion); + expect(document.createElement('rh-accordion-header')).to.be.an.instanceof(RhAccordionHeader); + expect(document.createElement('rh-accordion-panel')).to.be.an.instanceof(RhAccordionPanel); + }); + + describe('in typical usage', function() { + let header1: RhAccordionHeader; + let header2: RhAccordionHeader; + let header3: RhAccordionHeader; + + let panel1: RhAccordionPanel; + let panel2: RhAccordionPanel; + let panel3: RhAccordionPanel; + + beforeEach(async function() { + element = await fixture(html` + + +

Header1 Consetetur sadipscing elitr?

+
+ +

Panel1 Panel1 link Lorem ipsum dolor, sit amet consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt + ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea + rebum.

+
+ +

Header2 Labore et dolore magna aliquyam erat?

+
+ +

Panel2 Panel2 link Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt + ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea + rebum.

+
+ +

Header3 Incididunt in Lorem voluptate eiusmod dolor?

+
+ +

Panel3Panel3 link Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt + ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea + rebum.

+
+
+ `); + headers = Array.from(element.querySelectorAll('rh-accordion-header')); + panels = Array.from(element.querySelectorAll('rh-accordion-panel')); + [header1, header2, header3] = headers; + [panel1, panel2, panel3] = panels; + }); + + beforeEach(() => allUpdates(element)); + + it('is accessible', async function() { + await expect(element).to.be.accessible(); + }); + + it('preserves existing IDs', function() { + expect(element.querySelector('#header1')).to.exist; + expect(element.querySelector('#panel1')).to.exist; + }); + + describe('clicking the first header', function() { + beforeEach(async () => { + await clickElementAtCenter(header1); + }); + + it('expands first pair', async function() { + expect(header1.expanded).to.be.true; + expect(panel1.expanded).to.be.true; + expect(panel1.hidden).to.be.false; + }); + + describe('then clicking first header again', function() { + beforeEach(async () => { + await clickElementAtCenter(header1); + }); + + it('collapses first pair', function() { + expect(header1.expanded).to.be.false; + expect(panel1.expanded).to.be.false; + expect(panel1.hidden).to.be.true; + }); + }); + }); + + /* API TESTS */ + describe('calling toggle(1)', function() { + beforeEach(async () => { + element.toggle(1); + }); + + it('expands second pair', function() { + expect(header2.expanded).to.be.true; + expect(panel2.expanded).to.be.true; + expect(panel2.hidden).to.be.false; + }); + + describe('then calling toggle(1) again', function() { + beforeEach(async () => { + element.toggle(1); + }); + + it('collapses second pair', function() { + expect(header2.expanded).to.be.false; + expect(panel2.expanded).to.be.false; + expect(panel2.hidden).to.be.true; + }); + }); + }); + + describe('calling expand(1)', function() { + beforeEach(async () => { + element.expand(1); + }); + + it('expands second pair', function() { + expect(header2.expanded).to.be.true; + expect(panel2.expanded).to.be.true; + expect(panel2.hidden).to.be.false; + }); + + describe('then calling collapse(1)', function() { + beforeEach(async () => { + element.collapse(1); + }); + + it('collapses second pair', function() { + expect(header2.expanded).to.be.false; + expect(panel2.expanded).to.be.false; + expect(panel2.hidden).to.be.true; + }); + }); + }); + + describe('calling expandAll()', function() { + beforeEach(async () => { + element.expandAll(); + }); + + it('expands all pairs', function() { + for (const header of headers) { + expect(header.expanded).to.be.true; + } + + for (const panel of panels) { + expect(panel.expanded).to.be.true; + expect(panel.hidden).to.be.false; + } + }); + + describe('then calling collapseAll()', function() { + beforeEach(async () => { + element.collapseAll(); + }); + + it('collapses all pairs', function() { + for (const header of headers) { + expect(header.expanded).to.be.false; + } + + for (const panel of panels) { + expect(panel.expanded).to.be.false; + expect(panel.hidden).to.be.true; + } + }); + }); + }); + + /* ATTRIBUTE TESTS */ + describe('setting expanded-index attribute', function() { + const indices = '1,2'; + beforeEach(function() { + element.setAttribute('expanded-index', indices); + }); + beforeEach(() => allUpdates(element)); + beforeEach(nextFrame); + + it('expands the pairs listed in the expanded-index attribute', function() { + for (const idx of indices.split(',').map(x => parseInt(x))) { + const header = headers[idx]; + const panel = panels[idx]; + + expect(header.expanded, 'header expanded DOM property').to.be.true; + expect(header.expanded, 'header expanded attribute').to.be.true; + expect(panel.expanded, 'panel expanded DOM property').to.be.true; + expect(panel.expanded, 'panel expanded attribute').to.be.true; + } + }); + }); + + describe('dynamically adding pairs', function() { + beforeEach(function() { + const newHeader = document.createElement('rh-accordion-header'); + newHeader.id = 'newHeader'; + newHeader.innerHTML = `

New Header

`; + + const newPanel = document.createElement('rh-accordion-panel'); + newPanel.id = 'newPanel'; + newPanel.innerHTML = `New Panel`; + + element.appendChild(newHeader); + element.appendChild(newPanel); + }); + + beforeEach(() => allUpdates(element)); + + it('properly initializes new pairs', function() { + const newHeader = headers.at(-1); + const newPanel = panels.at(-1); + expect(newHeader?.hasAttribute('id'), 'header has an id').to.be.true; + expect(newHeader?.getAttribute('aria-controls'), 'header has aria-controls') + .to.equal(newPanel?.getAttribute('id')); + + expect(newPanel?.getAttribute('role'), 'panel has role').to.equal('region'); + expect(newPanel?.hasAttribute('id'), 'panel has id').to.be.true; + expect(newPanel?.getAttribute('aria-labelledby'), 'panel has aria-labelledby') + .to.equal(newHeader?.getAttribute('id')); + }); + }); + }); + + describe('with slotted headers elements', function() { let element: RhAccordion; beforeEach(async function() { @@ -21,13 +257,14 @@ describe('', function() {
5-b
+ panel
`); }); beforeEach(() => element.updateComplete); - it('removes the h5', async function() { + it('removes the h5', function() { expect(element.querySelector('h5')?.shadowRoot?.querySelector('h5')).to.not.be.ok; }); @@ -35,9 +272,265 @@ describe('', function() { it.skip('retains the level five heading', async function() { expect(await a11ySnapshot()).to.axContainQuery({ level: 5 }); }); + }); - it('is accessible', async function() { - await expect(element).to.be.accessible(); + describe('for assistive technology', function() { + let header1: RhAccordionHeader; + let header2: RhAccordionHeader; + let header3: RhAccordionHeader; + + let panel1: RhAccordionPanel; + let panel2: RhAccordionPanel; + let panel3: RhAccordionPanel; + + beforeEach(async function() { + element = await fixture(html` + + h1 + p1 + h2 + p2 + h3 + p3 + + `); + }); + + beforeEach(function() { + [header1, header2, header3] = element.querySelectorAll('rh-accordion-header'); + [panel1, panel2, panel3] = element.querySelectorAll('rh-accordion-panel'); + }); + + it('applies hidden attribute to all panels', function() { + expect(panel1.hidden, 'panel1').to.be.true; + expect(panel2.hidden, 'panel2').to.be.true; + expect(panel3.hidden, 'panel3').to.be.true; + }); + + describe('calling focus() on the first header', function() { + beforeEach(function() { + header1.focus(); + }); + + beforeEach(async () => { + await allUpdates(element); + }); + + beforeEach(nextFrame); + + describe('Pressing Space', function() { + beforeEach(press(' ')); + + beforeEach(async () => { + await allUpdates(element); + }); + + it('expands the first pair', function() { + expect(panel1.expanded).to.be.true; + expect(panel1.hidden, 'panel1').to.be.false; + expect(panel2.expanded).to.be.false; + expect(panel3.expanded).to.be.false; + expect(panel2.hidden, 'panel2').to.be.true; + expect(panel3.hidden, 'panel3').to.be.true; + }); + + describe('Pressing Enter', function() { + beforeEach(press('Enter')); + + beforeEach(async () => { + await allUpdates(element); + }); + + it('collapses the first panel', function() { + expect(panel1.expanded).to.be.false; + expect(panel2.expanded).to.be.false; + expect(panel3.expanded).to.be.false; + expect(panel1.hidden, 'panel1').to.be.true; + expect(panel2.hidden, 'panel2').to.be.true; + expect(panel3.hidden, 'panel3').to.be.true; + }); + }); + }); + + describe('Pressing Tab', function() { + beforeEach(press('Tab')); + + beforeEach(async () => { + await allUpdates(element); + }); + + it('header2 to have focus', async function() { + expect(await a11ySnapshot()).to.have.axTreeFocusOn(header2); + }); + }); + }); + + describe('calling focus() on the middle header', function() { + beforeEach(function() { + header2.focus(); + }); + + beforeEach(nextFrame); + + describe('Pressing Space', function() { + beforeEach(press(' ')); + + it('expands the middle pair', function() { + expect(panel1.expanded).to.be.false; + expect(panel2.expanded).to.be.true; + expect(panel3.expanded).to.be.false; + expect(panel1.hidden, 'panel1').to.be.true; + expect(panel2.hidden, 'panel2').to.be.false; + expect(panel3.hidden, 'panel3').to.be.true; + }); + + describe('Pressing Enter', function() { + beforeEach(press('Enter')); + + it('collapses the middle panel', function() { + expect(panel1.expanded).to.be.false; + expect(panel2.expanded).to.be.false; + expect(panel3.expanded).to.be.false; + expect(panel1.hidden, 'panel1').to.be.true; + expect(panel2.hidden, 'panel2').to.be.true; + expect(panel3.hidden, 'panel3').to.be.true; + }); + }); + }); + + describe('Pressing Tab', function() { + beforeEach(press('Tab')); + + it('moves focus to the last header', async function() { + expect(await a11ySnapshot()).to.have.axTreeFocusOn(header3); + }); + }); + }); + + describe('calling focus() on the last header', function() { + beforeEach(function() { + header3.focus(); + }); + + beforeEach(nextFrame); + + describe('Press Space', function() { + beforeEach(press(' ')); + + it('expands the last pair', function() { + expect(panel1.expanded).to.be.false; + expect(panel2.expanded).to.be.false; + expect(panel3.expanded).to.be.true; + expect(panel1.hidden, 'panel1').to.be.true; + expect(panel2.hidden, 'panel2').to.be.true; + expect(panel3.hidden, 'panel3').to.be.false; + }); + + describe('Press Space again', function() { + beforeEach(press(' ')); + + it('collapses the last pair', function() { + expect(panel1.expanded).to.be.false; + expect(panel2.expanded).to.be.false; + expect(panel3.expanded).to.be.false; + expect(panel1.hidden, 'panel1').to.be.true; + expect(panel2.hidden, 'panel2').to.be.true; + expect(panel3.hidden, 'panel3').to.be.true; + }); + }); + }); + + describe('Press Shift+Tab', function() { + beforeEach(press('Shift+Tab')); + + it('moves focus to the second header', async function() { + expect(await a11ySnapshot()).to.have.axTreeFocusOn(header2); + }); + }); + + describe('Press Tab', function() { + beforeEach(press('Tab')); + + it('moves focus to the body', async function() { + expect(await a11ySnapshot()).to.have.axTreeFocusOn(document.body); + }); + }); + }); + + describe('expand(0)', function() { + beforeEach(function() { + element.expand(0); + }); + + beforeEach(nextFrame); + + it('expand the first header', function() { + expect(header1.expanded).to.be.true; + expect(panel1.hidden).to.be.false; + }); + }); + + describe('expand(1)', function() { + beforeEach(function() { + element.expand(1); + }); + + beforeEach(nextFrame); + + it('expand the second header', function() { + expect(header2.expanded).to.be.true; + expect(panel2.hidden).to.be.false; + }); + }); + }); + + describe('with expanded attribute', function() { + let header1: RhAccordionHeader; + let header2: RhAccordionHeader; + let header3: RhAccordionHeader; + + let panel1: RhAccordionPanel; + let panel2: RhAccordionPanel; + let panel3: RhAccordionPanel; + + beforeEach(async function() { + element = await fixture(html` + +

h1

+

p1

+

h2

+

p2

+

h3

+

p3

+
+ `); + headers = Array.from(element.querySelectorAll('rh-accordion-header')); + panels = Array.from(element.querySelectorAll('rh-accordion-panel')); + [header1, header2, header3] = headers; + [panel1, panel2, panel3] = panels; + await element.updateComplete; + }); + + beforeEach(async () => { + await allUpdates(element); + }); + + it('first panel is expanded', function() { + expect(header1.expanded, 'header-1 expanded').to.be.true; + expect(panel1.expanded, 'panel-1 expanded').to.be.true; + expect(panel1.hidden, 'panel-1 hidden').to.be.false; + }); + + it('second panel is expanded', function() { + expect(header2.expanded, 'header-2 expanded').to.be.true; + expect(panel2.expanded, 'panel-2 expanded').to.be.true; + expect(panel2.hidden, 'panel-2 hidden').to.be.false; + }); + + it('third panel remains hidden', function() { + expect(header3.expanded, 'header-3 expanded').to.be.false; + expect(panel3.expanded, 'panel-3 expanded').to.be.false; + expect(panel3.hidden, 'panel-3 hidden').to.be.true; }); }); }); From ab7acf6d55eb04f2ac31a3ad36df18a4d77a66a0 Mon Sep 17 00:00:00 2001 From: Steven Spriggs Date: Tue, 5 Nov 2024 23:52:07 -0500 Subject: [PATCH 2/4] docs(accordion): improve demos --- ...-expanded.html => expanded-attribute.html} | 2 +- .../rh-accordion/demo/expanded-index.html | 21 +++++++++++++++++++ elements/rh-accordion/demo/rh-accordion.html | 6 +++--- 3 files changed, 25 insertions(+), 4 deletions(-) rename elements/rh-accordion/demo/{initially-expanded.html => expanded-attribute.html} (91%) create mode 100644 elements/rh-accordion/demo/expanded-index.html diff --git a/elements/rh-accordion/demo/initially-expanded.html b/elements/rh-accordion/demo/expanded-attribute.html similarity index 91% rename from elements/rh-accordion/demo/initially-expanded.html rename to elements/rh-accordion/demo/expanded-attribute.html index 48436520f2..6f381a6eb2 100644 --- a/elements/rh-accordion/demo/initially-expanded.html +++ b/elements/rh-accordion/demo/expanded-attribute.html @@ -5,7 +5,7 @@

Item One

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

-

Item Two

+

Item Two

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

diff --git a/elements/rh-accordion/demo/expanded-index.html b/elements/rh-accordion/demo/expanded-index.html new file mode 100644 index 0000000000..a81325efbb --- /dev/null +++ b/elements/rh-accordion/demo/expanded-index.html @@ -0,0 +1,21 @@ + + +

Item One

+ +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ +

Item Two

+ +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+ +

Item Three

+ +

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

+
+
+ + diff --git a/elements/rh-accordion/demo/rh-accordion.html b/elements/rh-accordion/demo/rh-accordion.html index c8cd4e0855..2af2d60601 100644 --- a/elements/rh-accordion/demo/rh-accordion.html +++ b/elements/rh-accordion/demo/rh-accordion.html @@ -1,15 +1,15 @@ - +

Item One

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

- +

Item Two

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

- +

Item Three

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

From 227be24e0ed10a64d4fd51b74a8f8822c045f5d2 Mon Sep 17 00:00:00 2001 From: Steven Spriggs Date: Tue, 5 Nov 2024 23:52:44 -0500 Subject: [PATCH 3/4] fix(accordion): correct expanded and expanded-index functionality --- elements/rh-accordion/rh-accordion-header.ts | 26 +++++--- elements/rh-accordion/rh-accordion-panel.ts | 10 ++- elements/rh-accordion/rh-accordion.ts | 65 ++++++++++++-------- 3 files changed, 66 insertions(+), 35 deletions(-) diff --git a/elements/rh-accordion/rh-accordion-header.ts b/elements/rh-accordion/rh-accordion-header.ts index 5400ae0398..78e793c994 100644 --- a/elements/rh-accordion/rh-accordion-header.ts +++ b/elements/rh-accordion/rh-accordion-header.ts @@ -9,16 +9,16 @@ import { property } from 'lit/decorators/property.js'; import { getRandomId } from '@patternfly/pfe-core/functions/random.js'; import { observes } from '@patternfly/pfe-core/decorators/observes.js'; +import { HeadingLevelController } from '@rhds/elements/lib/context/headings/controller.js'; import { InternalsController } from '@patternfly/pfe-core/controllers/internals-controller.js'; + import { DirController } from '../../lib/DirController.js'; import { colorContextConsumer, type ColorTheme } from '../../lib/context/color/consumer.js'; import { consume } from '@lit/context'; - import { context } from './context.js'; import styles from './rh-accordion-header.css'; -import { HeadingLevelController } from '@rhds/elements/lib/context/headings/controller.js'; export class AccordionHeaderChangeEvent extends Event { declare target: RhAccordionHeader; @@ -49,6 +49,12 @@ const isAccordion = (x: EventTarget): x is RhAccordion => export class RhAccordionHeader extends LitElement { static readonly styles = [styles]; + // Allow focus to apply to shadow button + static override readonly shadowRootOptions: ShadowRootInit = { + ...LitElement.shadowRootOptions, + delegatesFocus: true, + }; + @property({ type: Boolean, reflect: true }) expanded = false; @consume({ context, subscribe: true }) @@ -67,12 +73,14 @@ export class RhAccordionHeader extends LitElement { #heading = new HeadingLevelController(this); + #belongsTo?: RhAccordion | null; + override connectedCallback() { super.connectedCallback(); this.id ||= getRandomId(this.localName); - const accordion = this.closest('rh-accordion'); + this.#belongsTo = this.closest('rh-accordion'); const heading = this.closest('h1,h2,h3,h4,h5,h6'); - if (heading && accordion?.contains(heading)) { + if (heading && this.#belongsTo?.contains(heading)) { this.#internals.ariaLevel = heading.localName.replace('h', ''); heading.replaceWith(this); } else { @@ -104,16 +112,20 @@ export class RhAccordionHeader extends LitElement { `; } - #onClick(event: MouseEvent) { - const accordion = event.composedPath().find(isAccordion); + #onClick() { + this.expanded = !this.expanded; + } + + #dispatchChange(accordion?: RhAccordion | null) { if (accordion) { - this.dispatchEvent(new AccordionHeaderChangeEvent(!this.expanded, this, accordion)); + this.dispatchEvent(new AccordionHeaderChangeEvent(this.expanded, this, accordion)); } } @observes('expanded') private expandedChanged() { this.#internals.ariaExpanded = String(!!this.expanded) as 'true' | 'false'; + this.#dispatchChange(this.#belongsTo); } } diff --git a/elements/rh-accordion/rh-accordion-panel.ts b/elements/rh-accordion/rh-accordion-panel.ts index 62ed3ba47b..68c459066a 100644 --- a/elements/rh-accordion/rh-accordion-panel.ts +++ b/elements/rh-accordion/rh-accordion-panel.ts @@ -1,6 +1,5 @@ import { html, LitElement } from 'lit'; import { classMap } from 'lit/directives/class-map.js'; - import { customElement } from 'lit/decorators/custom-element.js'; import { property } from 'lit/decorators/property.js'; @@ -8,12 +7,12 @@ import { colorContextConsumer, type ColorTheme } from '../../lib/context/color/c import { colorContextProvider, type ColorPalette } from '../../lib/context/color/provider.js'; import { getRandomId } from '@patternfly/pfe-core/functions/random.js'; - -import styles from './rh-accordion-panel.css'; +import { observes } from '@patternfly/pfe-core/decorators/observes.js'; import { consume } from '@lit/context'; import { context, type RhAccordionContext } from './context.js'; +import styles from './rh-accordion-panel.css'; /** * Accordion Panel * @@ -55,6 +54,11 @@ export class RhAccordionPanel extends LitElement { `; } + + @observes('expanded') + private expandedChanged() { + this.hidden = !this.expanded; + } } declare global { diff --git a/elements/rh-accordion/rh-accordion.ts b/elements/rh-accordion/rh-accordion.ts index cd9533f6ec..ff5cc6b72d 100644 --- a/elements/rh-accordion/rh-accordion.ts +++ b/elements/rh-accordion/rh-accordion.ts @@ -17,6 +17,9 @@ import { RhAccordionPanel } from './rh-accordion-panel.js'; import { context, type RhAccordionContext } from './context.js'; +export * from './rh-accordion-header.js'; +export * from './rh-accordion-panel.js'; + import styles from './rh-accordion.css'; export class AccordionExpandEvent extends ComposedEvent { @@ -111,16 +114,6 @@ export class RhAccordion extends LitElement { set expandedIndex(value) { this.#expandedIndex = value; - this.#expanded = !!this.#expandedIndex.length; - this.headers.forEach((header, i) => { - const expanded = this.#expandedIndexSet.has(i); - header.expanded = expanded; - const panel = this.panels[i]; - if (panel) { - panel.expanded = expanded; - panel.hidden = !expanded; - } - }); } /** All headers for this accordion */ @@ -170,14 +163,14 @@ export class RhAccordion extends LitElement { return c && results.every(Boolean); } - override firstUpdated() { - this.headers.forEach((header, index) => { - if (header.expanded) { - this.#expandedIndexSet.add(index); + @observes('expandedIndex') + private updateExpanded() { + this.#expandedIndex.forEach(headerIndex => { + if (!this.headers[headerIndex]) { + return; } + this.#expand(headerIndex); }); - this.expandedIndex = [...this.#expandedIndexSet]; - this.#expanded = !!this.#expandedIndex.length; } @observes('accents') @@ -205,25 +198,47 @@ export class RhAccordion extends LitElement { #expand(index: number) { // If this index is not already listed in the expandedSets array, add it - this.expandedIndex = [...this.#expandedIndexSet.add(index)]; + if (this.#expandedIndexSet.has(index)) { + return; + } + + this.#expandedIndexSet.add(index); + + const header = this.headers[index]; + const panel = this.panels[index]; + + if (header && panel) { + header.expanded = true; + panel.expanded = true; + } } #collapse(index: number) { - if (this.#expandedIndexSet.delete(index)) { - this.expandedIndex = [...this.#expandedIndexSet]; + if (!this.#expandedIndexSet.has(index)) { + return; + } + + const header = this.headers[index]; + const panel = this.panels[index]; + if (header && panel) { + header.expanded = false; + panel.expanded = false; } + this.#expandedIndexSet.delete(index); } #onChange(event: AccordionHeaderChangeEvent) { if (RhAccordion.isAccordionChangeEvent(event)) { const index = this.#getIndex(event.target); + if (event.expanded) { - this.expand(index, event.accordion); - } else { - this.collapse(index); + this.#expand(index); + } + + if (!event.expanded) { + this.#collapse(index); } } - this.requestUpdate('expandedIndex'); } #allHeaders(accordion: RhAccordion = this): RhAccordionHeader[] { @@ -279,9 +294,9 @@ export class RhAccordion extends LitElement { const header = headers[index]; if (!header.expanded) { - await this.expand(index); + await this.#expand(index); } else { - await this.collapse(index); + await this.#collapse(index); } } From ab592a3082f1d97aa3a5ea7520dfb5a49a8c1cc7 Mon Sep 17 00:00:00 2001 From: Steven Spriggs Date: Fri, 13 Dec 2024 13:57:54 -0500 Subject: [PATCH 4/4] chore(accordion): add changeset --- .changeset/smart-swans-move.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/smart-swans-move.md diff --git a/.changeset/smart-swans-move.md b/.changeset/smart-swans-move.md new file mode 100644 index 0000000000..a625f644fc --- /dev/null +++ b/.changeset/smart-swans-move.md @@ -0,0 +1,5 @@ +--- +"@rhds/elements": patch +--- + +``: corrected use of `expanded-index` and `expanded`