From b479e3fdd0b2ea252d7b83b62a3d9ed479e1db67 Mon Sep 17 00:00:00 2001 From: terminalchai <213856599+terminalchai@users.noreply.github.com> Date: Wed, 25 Mar 2026 01:16:00 +0530 Subject: [PATCH 1/2] fix: remove invalid dropdown aria-expanded --- src/scripts/components/dropdown.ts | 2 -- src/scripts/templates.ts | 1 - test/scripts/components/dropdown.test.ts | 8 ++++---- test/scripts/templates.test.ts | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/scripts/components/dropdown.ts b/src/scripts/components/dropdown.ts index ce8ac73f0..da447b598 100644 --- a/src/scripts/components/dropdown.ts +++ b/src/scripts/components/dropdown.ts @@ -31,7 +31,6 @@ export default class Dropdown { */ show(): this { addClassesToElement(this.element, this.classNames.activeState); - this.element.setAttribute('aria-expanded', 'true'); this.isActive = true; return this; @@ -42,7 +41,6 @@ export default class Dropdown { */ hide(): this { removeClassesFromElement(this.element, this.classNames.activeState); - this.element.setAttribute('aria-expanded', 'false'); this.isActive = false; return this; diff --git a/src/scripts/templates.ts b/src/scripts/templates.ts index ddbb1b4d3..0b51f7337 100644 --- a/src/scripts/templates.ts +++ b/src/scripts/templates.ts @@ -355,7 +355,6 @@ const templates: TemplatesInterface = { addClassesToElement(div, list); addClassesToElement(div, listDropdown); - div.setAttribute('aria-expanded', 'false'); return div; }, diff --git a/test/scripts/components/dropdown.test.ts b/test/scripts/components/dropdown.test.ts index 2c09d9068..e53d975f7 100644 --- a/test/scripts/components/dropdown.test.ts +++ b/test/scripts/components/dropdown.test.ts @@ -69,12 +69,12 @@ describe('components/dropdown', () => { }); }); - it('sets expanded attribute', () => { + it('does not set an expanded attribute', () => { expect(instance).to.not.be.null; if (!instance) { return; } - expect(instance.element.getAttribute('aria-expanded')).to.equal('true'); + expect(instance.element.hasAttribute('aria-expanded')).to.equal(false); }); it('sets isActive instance flag', () => { @@ -119,12 +119,12 @@ describe('components/dropdown', () => { }); }); - it('sets expanded attribute', () => { + it('does not set an expanded attribute', () => { expect(instance).to.not.be.null; if (!instance) { return; } - expect(instance.element.getAttribute('aria-expanded')).to.equal('false'); + expect(instance.element.hasAttribute('aria-expanded')).to.equal(false); }); it('sets isActive instance flag', () => { diff --git a/test/scripts/templates.test.ts b/test/scripts/templates.test.ts index 86ca4fa2c..29c0ff3a9 100644 --- a/test/scripts/templates.test.ts +++ b/test/scripts/templates.test.ts @@ -611,7 +611,7 @@ describe('templates', () => { it('returns expected html', () => { const expectedOutput = strToEl( - ``, + `
`, ); const actualOutput = templates.dropdown(dropdownOptions); From dcffbb09bbc2392722612e2228e085eeb7f70ac2 Mon Sep 17 00:00:00 2001 From: terminalchai <213856599+terminalchai@users.noreply.github.com> Date: Wed, 27 May 2026 00:52:59 +0530 Subject: [PATCH 2/2] fix: keep dropdown aria-expanded for select elements --- src/scripts/choices.ts | 2 +- src/scripts/components/dropdown.ts | 8 +++++++- src/scripts/interfaces/templates.ts | 2 +- src/scripts/templates.ts | 10 ++++++++-- test/scripts/components/dropdown.test.ts | 24 ++++++++++++++++++++++++ test/scripts/templates.test.ts | 13 +++++++++++-- 6 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/scripts/choices.ts b/src/scripts/choices.ts index f15efa2b8..7d5e70233 100644 --- a/src/scripts/choices.ts +++ b/src/scripts/choices.ts @@ -2271,7 +2271,7 @@ class Choices { }); this.dropdown = new Dropdown({ - element: templating.dropdown(config), + element: templating.dropdown(config, elementType), classNames, type: elementType, }); diff --git a/src/scripts/components/dropdown.ts b/src/scripts/components/dropdown.ts index da447b598..3ff556d85 100644 --- a/src/scripts/components/dropdown.ts +++ b/src/scripts/components/dropdown.ts @@ -1,5 +1,5 @@ import { ClassNames } from '../interfaces/class-names'; -import { PassedElementType } from '../interfaces/passed-element-type'; +import { PassedElementType, PassedElementTypes } from '../interfaces/passed-element-type'; import { addClassesToElement, removeClassesFromElement } from '../lib/utils'; export default class Dropdown { @@ -31,6 +31,9 @@ export default class Dropdown { */ show(): this { addClassesToElement(this.element, this.classNames.activeState); + if (this.type !== PassedElementTypes.Text) { + this.element.setAttribute('aria-expanded', 'true'); + } this.isActive = true; return this; @@ -41,6 +44,9 @@ export default class Dropdown { */ hide(): this { removeClassesFromElement(this.element, this.classNames.activeState); + if (this.type !== PassedElementTypes.Text) { + this.element.setAttribute('aria-expanded', 'false'); + } this.isActive = false; return this; diff --git a/src/scripts/interfaces/templates.ts b/src/scripts/interfaces/templates.ts index 279ac3a0f..1676d0266 100644 --- a/src/scripts/interfaces/templates.ts +++ b/src/scripts/interfaces/templates.ts @@ -58,7 +58,7 @@ export interface Templates { input(options: TemplateOptions, placeholderValue: string | null): HTMLInputElement; - dropdown(options: TemplateOptions): HTMLDivElement; + dropdown(options: TemplateOptions, passedElementType: PassedElementType): HTMLDivElement; notice(options: TemplateOptions, innerText: string, type: NoticeType): HTMLDivElement; diff --git a/src/scripts/templates.ts b/src/scripts/templates.ts index 0b51f7337..c2b739fca 100644 --- a/src/scripts/templates.ts +++ b/src/scripts/templates.ts @@ -6,7 +6,7 @@ import { ChoiceFull } from './interfaces/choice-full'; import { GroupFull } from './interfaces/group-full'; -import { PassedElementType } from './interfaces/passed-element-type'; +import { PassedElementType, PassedElementTypes } from './interfaces/passed-element-type'; import { StringPreEscaped } from './interfaces/string-pre-escaped'; import { getClassNames, @@ -350,11 +350,17 @@ const templates: TemplatesInterface = { return inp; }, - dropdown({ classNames: { list, listDropdown } }: TemplateOptions): HTMLDivElement { + dropdown( + { classNames: { list, listDropdown } }: TemplateOptions, + passedElementType: PassedElementType, + ): HTMLDivElement { const div = document.createElement('div'); addClassesToElement(div, list); addClassesToElement(div, listDropdown); + if (passedElementType !== PassedElementTypes.Text) { + div.setAttribute('aria-expanded', 'false'); + } return div; }, diff --git a/test/scripts/components/dropdown.test.ts b/test/scripts/components/dropdown.test.ts index e53d975f7..881e55360 100644 --- a/test/scripts/components/dropdown.test.ts +++ b/test/scripts/components/dropdown.test.ts @@ -77,6 +77,18 @@ describe('components/dropdown', () => { expect(instance.element.hasAttribute('aria-expanded')).to.equal(false); }); + it('sets expanded attribute for select dropdowns', () => { + const selectInstance = new Dropdown({ + element: choicesElement, + type: 'select-one', + classNames: DEFAULT_CLASSNAMES, + }); + + selectInstance.show(); + + expect(selectInstance.element.getAttribute('aria-expanded')).to.equal('true'); + }); + it('sets isActive instance flag', () => { expect(instance).to.not.be.null; if (!instance) { @@ -127,6 +139,18 @@ describe('components/dropdown', () => { expect(instance.element.hasAttribute('aria-expanded')).to.equal(false); }); + it('sets expanded attribute for select dropdowns', () => { + const selectInstance = new Dropdown({ + element: choicesElement, + type: 'select-one', + classNames: DEFAULT_CLASSNAMES, + }); + + selectInstance.hide(); + + expect(selectInstance.element.getAttribute('aria-expanded')).to.equal('false'); + }); + it('sets isActive instance flag', () => { expect(instance).to.not.be.null; if (!instance) { diff --git a/test/scripts/templates.test.ts b/test/scripts/templates.test.ts index 29c0ff3a9..02c0f1a42 100644 --- a/test/scripts/templates.test.ts +++ b/test/scripts/templates.test.ts @@ -609,11 +609,20 @@ describe('templates', () => { listDropdown: 'class-2', }); - it('returns expected html', () => { + it('returns expected html for select elements', () => { + const expectedOutput = strToEl( + ``, + ); + const actualOutput = templates.dropdown(dropdownOptions, 'select-one'); + + expectEqualElements(actualOutput, expectedOutput); + }); + + it('returns expected html for text inputs', () => { const expectedOutput = strToEl( `
`, ); - const actualOutput = templates.dropdown(dropdownOptions); + const actualOutput = templates.dropdown(dropdownOptions, 'text'); expectEqualElements(actualOutput, expectedOutput); });