diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c133f9b5f..f104b5dd7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [v10.3.50](https://github.com/Workday/canvas-kit/releases/tag/v10.3.50) (2024-08-05) + +### Components + +- fix(combobox): Use correct state for aria-selected ([#2849](https://github.com/Workday/canvas-kit/pull/2849)) ([@NicholasBoll](https://github.com/NicholasBoll)) + This change fixes `aria-selected` in `Combobox.Menu.Item` components, but this does change the visuals of what is considered "selected". If you have any visual tests that have a screenshot of a selected state, the visual regression will have to be updated. The same is true for DOM-based snapshot tests. `aria-selected="true"` will now be added when an item is selected and not just when the virtual cursor is on the item. If your snapshot captures this DOM state, the snapshot will have to be updated. ## [v11.0.24](https://github.com/Workday/canvas-kit/releases/tag/v11.0.24) (2024-08-05) ### Components diff --git a/cypress/integration/Autocomplete.spec.ts b/cypress/integration/Autocomplete.spec.ts index 9f56743dbd..b611e16885 100644 --- a/cypress/integration/Autocomplete.spec.ts +++ b/cypress/integration/Autocomplete.spec.ts @@ -67,6 +67,10 @@ describe('Autocomplete', () => { cy.findByRole('combobox').should('not.have.attr', 'aria-activedescendant'); }); + it('should not have visual focus on any element', () => { + cy.get('[role="option"].focus').should('not.exist'); + }); + it('should not have aria-selected=true on any elements', () => { cy.get('[aria-selected=true]').should('not.exist'); }); @@ -81,12 +85,16 @@ describe('Autocomplete', () => { cy.findAllByRole('combobox').should('not.have.attr', 'aria-activedescendant'); }); + it('should not have visual focus on the first item', () => { + cy.findAllByRole('option').eq(0).should('not.have.class', 'focus'); + }); + it('should not set aria-selected to the first option', () => { cy.findAllByRole('option').eq(0).should('have.not.attr', 'aria-selected'); }); }); - context('when a value is entered', () => { + context('when "Red" is typed', () => { beforeEach(() => { cy.findByRole('combobox').type('Red', {delay: 1}); waitForAutocompleteReady(); @@ -139,8 +147,8 @@ describe('Autocomplete', () => { ); }); - it('should set aria-selected to the first option', () => { - cy.findAllByRole('option').eq(0).should('have.attr', 'aria-selected', 'true'); + it('should set visual focus to the first option', () => { + cy.findAllByRole('option').eq(0).should('have.class', 'focus'); }); context('when the user presses the enter key', () => { @@ -156,7 +164,7 @@ describe('Autocomplete', () => { cy.findByRole('listbox').should('not.exist'); }); - context('when the use hits the "2" key', () => { + context('when the user hits the "2" key', () => { beforeEach(() => { cy.findAllByRole('combobox').type('2'); waitForAutocompleteReady(); @@ -173,6 +181,10 @@ describe('Autocomplete', () => { it.skip('should change the filtered results', () => { cy.findByRole('option', {name: 'Red Apple 121'}).should('be.visible'); }); + + it('should set aria-selected to the first option', () => { + cy.findAllByRole('option').eq(0).should('have.attr', 'aria-selected', 'true'); + }); }); }); @@ -230,8 +242,12 @@ describe('Autocomplete', () => { ); }); - it('should set aria-selected to the second option', () => { - cy.findAllByRole('option').eq(1).should('have.attr', 'aria-selected', 'true'); + it('should set visual focus on the second option', () => { + cy.findAllByRole('option').eq(1).should('have.class', 'focus'); + }); + + it('should not have aria-selected=true on any elements', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); @@ -253,8 +269,12 @@ describe('Autocomplete', () => { ); }); - it('should set aria-selected to the first option', () => { - cy.findAllByRole('option').eq(0).should('have.attr', 'aria-selected', 'true'); + it('should set visual focus on the first option', () => { + cy.findAllByRole('option').eq(0).should('have.class', 'focus'); + }); + + it('should not have aria-selected=true on any elements', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); @@ -269,8 +289,12 @@ describe('Autocomplete', () => { ); }); - it('should set aria-selected to the last option', () => { - cy.findAllByRole('option').eq(3).should('have.attr', 'aria-selected', 'true'); + it('should set visual focus on the last option', () => { + cy.findAllByRole('option').eq(3).should('have.class', 'focus'); + }); + + it('should not have aria-selected=true on any elements', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); @@ -285,8 +309,12 @@ describe('Autocomplete', () => { ); }); - it('should set aria-selected to the third option', () => { - cy.findAllByRole('option').eq(2).should('have.attr', 'aria-selected', 'true'); + it('should set visual focus on the third option', () => { + cy.findAllByRole('option').eq(2).should('have.class', 'focus'); + }); + + it('should not have aria-selected=true on any elements', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); @@ -300,8 +328,12 @@ describe('Autocomplete', () => { ); }); - it('should set aria-selected to the first option', () => { - cy.findAllByRole('option').eq(0).should('have.attr', 'aria-selected', 'true'); + it('should set visual focus on the first option', () => { + cy.findAllByRole('option').eq(0).should('have.class', 'focus'); + }); + + it('should not have aria-selected=true on any elements', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); }); diff --git a/cypress/integration/Select.spec.ts b/cypress/integration/Select.spec.ts index e9a6ec2459..b8f761441c 100644 --- a/cypress/integration/Select.spec.ts +++ b/cypress/integration/Select.spec.ts @@ -115,11 +115,11 @@ describe('Select', () => { context('the menu', () => { it('should scroll so that the "San Francisco (United States)" option is fully visible', () => { - cy.findByText('San Francisco (United States)').should( - 'have.attr', - 'aria-selected', - 'true' - ); + cy.findByText('San Francisco (United States)').should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); }); @@ -132,11 +132,11 @@ describe('Select', () => { context('the menu', () => { it('should scroll so that the "San Mateo (United States)" option is fully visible', () => { - cy.findByText('San Mateo (United States)').should( - 'have.attr', - 'aria-selected', - 'true' - ); + cy.findByText('San Mateo (United States)').should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); }); @@ -149,11 +149,11 @@ describe('Select', () => { context('the menu', () => { it('should scroll so that the "Dallas (United States)" option is fully visible', () => { - cy.findByText('Dallas (United States)').should( - 'have.attr', - 'aria-selected', - 'true' - ); + cy.findByText('Dallas (United States)').should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); }); @@ -170,11 +170,11 @@ describe('Select', () => { context('the menu', () => { it('should set assistive focus to the "San Francisco (United States)" option', () => { - cy.findByText('San Francisco (United States)').should( - 'have.attr', - 'aria-selected', - 'true' - ); + cy.findByText('San Francisco (United States)').should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); }); @@ -186,11 +186,11 @@ describe('Select', () => { context('the select input', () => { it('should set assistive focus to the "San Francisco (United States)" option', () => { - cy.findByText('San Francisco (United States)').should( - 'have.attr', - 'aria-selected', - 'true' - ); + cy.findByText('San Francisco (United States)').should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); }); @@ -203,11 +203,11 @@ describe('Select', () => { context('the select input', () => { it('should set assistive focus to the "San Mateo (United States)" option', () => { - cy.findByText('San Mateo (United States)').should( - 'have.attr', - 'aria-selected', - 'true' - ); + cy.findByText('San Mateo (United States)').should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); }); @@ -231,6 +231,10 @@ describe('Select', () => { 'true' ); }); + + it('should set assistive focus to the "Dallas (United States)" option', () => { + cy.findByText('Dallas (United States)').should('have.class', 'focus'); + }); }); }); } @@ -266,7 +270,11 @@ describe('Select', () => { context('the menu', () => { it('should set assistive focus to the first option ("E-mail")', () => { - cy.findAllByRole('option').eq(0).should('have.attr', 'aria-selected', 'true'); + cy.findAllByRole('option').eq(0).should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); @@ -277,7 +285,11 @@ describe('Select', () => { context('the menu', () => { it('should set assistive focus to the second option ("Phone")', () => { - cy.findAllByRole('option').eq(1).should('have.attr', 'aria-selected', 'true'); + cy.findAllByRole('option').eq(1).should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); @@ -292,6 +304,10 @@ describe('Select', () => { cy.findByRole('combobox').should('not.have.attr', 'aria-activedescendant'); }); + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); + }); + context('when the menu is re-opened AFTER it has fully closed', () => { beforeEach(() => { // Wait for menu to fully close before we open it again (so we @@ -303,7 +319,11 @@ describe('Select', () => { context('the menu', () => { it('should set assistive focus to the second option ("Phone") that is where the cursor was', () => { - cy.findAllByRole('option').eq(1).should('have.attr', 'aria-selected', 'true'); + cy.findByRole('option', {name: 'Phone'}).should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); }); @@ -336,7 +356,11 @@ describe('Select', () => { context('the menu', () => { it('should set assistive focus to second enabled option ("Phone")', () => { - cy.findAllByRole('option').eq(1).should('have.attr', 'aria-selected', 'true'); + cy.findAllByRole('option').eq(1).should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); @@ -347,7 +371,11 @@ describe('Select', () => { context('the menu', () => { it('should set assistive focus to the fourth option down ("Mail") since focus will have skipped one disabled option ("Fax")', () => { - cy.findAllByRole('option').eq(3).should('have.attr', 'aria-selected', 'true'); + cy.findByRole('option', {name: 'Mail'}).should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); }); @@ -409,9 +437,13 @@ describe('Select', () => { }); context('when Boulder is reached via the arrow key', () => { it('should show Boulder (United States)', () => { - cy.findByText('Boulder (United States)').should('have.attr', 'aria-selected', 'true'); + cy.findByText('Boulder (United States)').should('have.class', 'focus'); cy.findByText('Boulder (United States)').should('be.visible'); }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); + }); }); }); }); @@ -461,8 +493,12 @@ describe('Select', () => { }); context('the first option ("E-Mail")', () => { - it('should have an aria-selected attribute set to "true"', () => { - cy.findAllByRole('option').eq(0).should('have.attr', 'aria-selected', 'true'); + it('should set accessible focus on the "E-Mail" option', () => { + cy.findAllByRole('option').eq(0).should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); @@ -495,6 +531,10 @@ describe('Select', () => { context('the menu', () => { it('should set assistive focus to the "Phone" option', () => { + cy.findByText('Phone').should('have.class', 'focus'); + }); + + it('should set assistive selection on the "Phone" option', () => { cy.findByText('Phone').should('have.attr', 'aria-selected', 'true'); }); }); @@ -526,7 +566,7 @@ describe('Select', () => { context('the menu', () => { it('should set assistive focus to the "Phone" option', () => { - cy.findAllByRole('option').eq(1).should('have.attr', 'aria-selected', 'true'); + cy.findByRole('option', {name: 'Phone'}).should('have.class', 'focus'); }); }); @@ -537,14 +577,18 @@ describe('Select', () => { context('the menu', () => { it('should set assistive focus to the "Mail" option and skip disabled fax', () => { - cy.findAllByRole('option').eq(3).should('have.attr', 'aria-selected', 'true'); + cy.findByRole('option', {name: 'Mail'}).should('have.class', 'focus'); + }); + + it('should not have any item selected', () => { + cy.get('[aria-selected=true]').should('not.exist'); }); }); }); }); }); - context('when the enter key is pressed', () => { + context('when the down arrow key is pressed', () => { beforeEach(() => { cy.findByRole('combobox').focus().realType('{downarrow}'); }); @@ -554,8 +598,8 @@ describe('Select', () => { cy.findByRole('listbox').should('be.visible'); }); - it('should have E-Mail selected', () => { - cy.findAllByRole('option').eq(0).should('have.attr', 'aria-selected', 'true'); + it('should set accessible focus on the "E-Mail" option', () => { + cy.findAllByRole('option').eq(0).should('have.class', 'focus'); }); }); diff --git a/modules/react/combobox/lib/ComboboxMenuItem.tsx b/modules/react/combobox/lib/ComboboxMenuItem.tsx index 047ec8b7bb..ebf9ced1ab 100644 --- a/modules/react/combobox/lib/ComboboxMenuItem.tsx +++ b/modules/react/combobox/lib/ComboboxMenuItem.tsx @@ -45,7 +45,7 @@ export const useComboboxMenuItem = composeHooks( event.preventDefault(); }; - const selected = model.state.cursorId === id; + const selected = model.state.selectedIds[0] === id; return { role: 'option', diff --git a/modules/styling/package.json b/modules/styling/package.json index d8b73f66d8..904245cc5b 100644 --- a/modules/styling/package.json +++ b/modules/styling/package.json @@ -54,8 +54,8 @@ "@emotion/serialize": "^1.0.2", "@emotion/styled": "^11.6.0", "@workday/canvas-kit-react": "^11.0.24", - "@workday/canvas-system-icons-web": "^3.0.0", "@workday/canvas-tokens-web": "^2.0.0", + "@workday/canvas-system-icons-web": "^3.0.0", "typescript": "4.2" } }