diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c9ebfdd3f..cb5f9c3350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,94 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [v11.1.3](https://github.com/Workday/canvas-kit/releases/tag/v11.1.3) (2024-08-20) + +### Components + +- fix: Update select to trigger onChange ([#2874](https://github.com/Workday/canvas-kit/pull/2874)) ([@mannycarrera4](https://github.com/mannycarrera4), manuel.carrera) + + +## [v10.3.53](https://github.com/Workday/canvas-kit/releases/tag/v10.3.53) (2024-08-20) + +### Components + +- fix: Update select to trigger onChange ([#2874](https://github.com/Workday/canvas-kit/pull/2874)) ([@mannycarrera4](https://github.com/mannycarrera4), manuel.carrera) +## [v11.1.2](https://github.com/Workday/canvas-kit/releases/tag/v11.1.2) (2024-08-20) + + + + +## [v11.1.1](https://github.com/Workday/canvas-kit/releases/tag/v11.1.1) (2024-08-20) + +### Components + +- fix(select): Clicking the input closes the menu ([#2869](https://github.com/Workday/canvas-kit/pull/2869)) ([@NicholasBoll](https://github.com/NicholasBoll)) + + +## [v10.3.52](https://github.com/Workday/canvas-kit/releases/tag/v10.3.52) (2024-08-14) + +### Components + +- fix(select): Clicking the input closes the menu ([#2869](https://github.com/Workday/canvas-kit/pull/2869)) ([@NicholasBoll](https://github.com/NicholasBoll)) +## [v11.1.0](https://github.com/Workday/canvas-kit/releases/tag/v11.1.0) (2024-08-14) + + + + +## [v11.0.27](https://github.com/Workday/canvas-kit/releases/tag/v11.0.27) (2024-08-12) + +### Components + +- fix(Checkbox): Remove console warning ([#2863](https://github.com/Workday/canvas-kit/pull/2863)) ([@thunguyen19](https://github.com/thunguyen19)) + + +## [v11.0.26](https://github.com/Workday/canvas-kit/releases/tag/v11.0.26) (2024-08-05) + +### Accessibility + +- fix: Set aria-modal to false for better accessibility ([#2855](https://github.com/Workday/canvas-kit/pull/2855)) ([@mannycarrera4](https://github.com/mannycarrera4), manuel.carrera) + + +## [v10.3.51](https://github.com/Workday/canvas-kit/releases/tag/v10.3.51) (2024-08-05) + +### Accessibility + +- fix: Set aria-modal to false for better accessibility ([#2855](https://github.com/Workday/canvas-kit/pull/2855)) ([@mannycarrera4](https://github.com/mannycarrera4), manuel.carrera) +## [v11.0.25](https://github.com/Workday/canvas-kit/releases/tag/v11.0.25) (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. + + +## [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 + +- fix(SearchForm): Suppress forwarding props warning ([#2850](https://github.com/Workday/canvas-kit/pull/2850)) ([@thunguyen19](https://github.com/thunguyen19), Thu Nguyen) + + +## [v11.0.23](https://github.com/Workday/canvas-kit/releases/tag/v11.0.23) (2024-07-30) + +### Components + +- fix: Support marginTop and marginBottom on ListBox ([#2844](https://github.com/Workday/canvas-kit/pull/2844)) ([@NicholasBoll](https://github.com/NicholasBoll)) + + +## [v10.3.49](https://github.com/Workday/canvas-kit/releases/tag/v10.3.49) (2024-07-29) + +### Components + +- fix: Support marginTop and marginBottom on ListBox ([#2844](https://github.com/Workday/canvas-kit/pull/2844)) ([@NicholasBoll](https://github.com/NicholasBoll)) + + ## [v11.0.22](https://github.com/Workday/canvas-kit/releases/tag/v11.0.22) (2024-07-29) ### Documentation @@ -10,6 +98,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - docs: Fix stencil docs typo ([#2847](https://github.com/Workday/canvas-kit/pull/2847)) ([@alanbsmith](https://github.com/alanbsmith)) +## [v10.3.48](https://github.com/Workday/canvas-kit/releases/tag/v10.3.48) (2024-07-24) ## [v11.0.21](https://github.com/Workday/canvas-kit/releases/tag/v11.0.21) (2024-07-24) ### Documentation diff --git a/cypress/integration/Autocomplete.spec.ts b/cypress/integration/Autocomplete.spec.ts index 9f56743dbd..c9d36026a4 100644 --- a/cypress/integration/Autocomplete.spec.ts +++ b/cypress/integration/Autocomplete.spec.ts @@ -55,6 +55,16 @@ describe('Autocomplete', () => { cy.findByRole('combobox').click(); }); + context('when the combobox is clicked', () => { + beforeEach(() => { + cy.findByRole('combobox').click(); + }); + + it('should close the menu', () => { + cy.findByRole('listbox').should('not.exist'); + }); + }); + it('should set the aria-owns to reference the listbox element', () => { cy.findByRole('combobox').should(haveAttrMatchingIdOf('aria-controls', '[role=listbox]')); }); @@ -67,6 +77,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 +95,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 +157,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 +174,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 +191,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 +252,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 +279,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 +299,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 +319,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 +338,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/Modal.spec.ts b/cypress/integration/Modal.spec.ts index cc43f75bbc..5b9c9aca44 100644 --- a/cypress/integration/Modal.spec.ts +++ b/cypress/integration/Modal.spec.ts @@ -69,11 +69,11 @@ describe('Modal', () => { cy.findByRole('dialog', {name: 'MIT License'}).should('have.attr', 'aria-labelledby'); }); - it('should have an aria-modal=true', () => { + it('should have an aria-modal=false', () => { cy.findByRole('dialog', {name: 'MIT License'}).should( 'have.attr', 'aria-modal', - 'true' + 'false' ); }); @@ -384,8 +384,8 @@ describe('Modal', () => { cy.findByRole('dialog', {name: 'Delete Item'}).should('have.attr', 'aria-labelledby'); }); - it('should have an aria-modal=true', () => { - cy.findByRole('dialog', {name: 'Delete Item'}).should('have.attr', 'aria-modal', 'true'); + it('should have an aria-modal=false', () => { + cy.findByRole('dialog', {name: 'Delete Item'}).should('have.attr', 'aria-modal', 'false'); }); it('should contain the title', () => { @@ -483,11 +483,11 @@ describe('Modal', () => { ); }); - it('should have an aria-modal=true', () => { + it('should have an aria-modal=false', () => { cy.findByRole('dialog', {name: 'Acknowledge License'}).should( 'have.attr', 'aria-modal', - 'true' + 'false' ); }); diff --git a/cypress/integration/Select.spec.ts b/cypress/integration/Select.spec.ts index e9a6ec2459..9933d3a076 100644 --- a/cypress/integration/Select.spec.ts +++ b/cypress/integration/Select.spec.ts @@ -104,6 +104,16 @@ describe('Select', () => { cy.findByRole('combobox').click(); }); + context('when the combobox is clicked', () => { + beforeEach(() => { + cy.findByRole('combobox').click(); + }); + + it('should close the menu', () => { + cy.findByRole('listbox').should('not.exist'); + }); + }); + context( 'when a character is typed (provided no other characters have been typed in the last 500ms), the select should advance assistive focus to the first matching option beyond the currently selected option (cycling back to the beginning of the options if necessary) and scroll that option into view', () => { @@ -115,11 +125,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 +142,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 +159,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 +180,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 +196,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 +213,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 +241,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 +280,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 +295,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 +314,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 +329,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'); }); }); }); @@ -311,6 +341,18 @@ describe('Select', () => { ); }); }); + context('when a value is selected', () => { + beforeEach(() => { + cy.findByRole('combobox').focus(); + cy.focused().realType('{downarrow}'); + }); + it('should select phone and the selected value should be visible', () => { + // Select Phone + cy.focused().realType('ph'); + cy.focused().realType('{enter}'); + cy.findByText('Selected Value: Phone'); + }); + }); }); context('given the "Disabled Options" story with a disabled option', () => { @@ -336,7 +378,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 +393,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'); }); }); }); @@ -371,6 +421,23 @@ describe('Select', () => { }); }); + context(`given the "Complex" story is rendered`, () => { + beforeEach(() => { + h.stories.load('Components/Inputs/Select', 'Complex'); + cy.findByRole('combobox').focus(); + cy.focused().realType('{downarrow}'); + }); + + context('when a value is selected with an id and text', () => { + it('should display the correct id and value of the selected Phone', () => { + cy.focused().realType('ph'); + cy.focused().realType('{enter}'); + cy.findByText('Id: phone'); + cy.findByText('Value: Phone'); + }); + }); + }); + context(`given the "FetchingDynamicItems" story is rendered`, () => { beforeEach(() => { h.stories.load('Components/Inputs/Select', 'FetchingDynamicItems'); @@ -409,9 +476,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 +532,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 +570,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 +605,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 +616,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 +637,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/lerna.json b/lerna.json index bc2c8d32c4..7bbbe9ab5f 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,7 @@ "packages": [ "modules/**" ], - "version": "11.0.22", + "version": "11.1.3", "npmClient": "yarn", "useWorkspaces": true, "command": { diff --git a/modules/codemod/package.json b/modules/codemod/package.json index 733a04f564..7c67600f6d 100644 --- a/modules/codemod/package.json +++ b/modules/codemod/package.json @@ -2,7 +2,7 @@ "name": "@workday/canvas-kit-codemod", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", - "version": "11.0.22", + "version": "11.1.3", "description": "A collection of codemods for use on Workday Canvas Kit packages.", "main": "dist/es6/index.js", "sideEffects": false, diff --git a/modules/css/package.json b/modules/css/package.json index 5074d45e51..60e1ab50d5 100644 --- a/modules/css/package.json +++ b/modules/css/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-css", - "version": "11.0.22", + "version": "11.1.3", "description": "The parent module that contains all Workday Canvas Kit CSS components", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", diff --git a/modules/docs/package.json b/modules/docs/package.json index 2b024702bb..327fc2dd8f 100644 --- a/modules/docs/package.json +++ b/modules/docs/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-docs", - "version": "11.0.22", + "version": "11.1.3", "description": "Documentation components of Canvas Kit components", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", @@ -44,10 +44,10 @@ "dependencies": { "@emotion/styled": "^11.6.0", "@storybook/csf": "0.0.1", - "@workday/canvas-kit-labs-react": "^11.0.22", - "@workday/canvas-kit-preview-react": "^11.0.22", - "@workday/canvas-kit-react": "^11.0.22", - "@workday/canvas-kit-styling": "^11.0.22", + "@workday/canvas-kit-labs-react": "^11.1.3", + "@workday/canvas-kit-preview-react": "^11.1.3", + "@workday/canvas-kit-react": "^11.1.3", + "@workday/canvas-kit-styling": "^11.1.3", "@workday/canvas-system-icons-web": "^3.0.0", "@workday/canvas-tokens-web": "^2.0.0", "markdown-to-jsx": "^7.2.0", diff --git a/modules/labs-css/package.json b/modules/labs-css/package.json index 5bc11ead54..b386edfd52 100644 --- a/modules/labs-css/package.json +++ b/modules/labs-css/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-labs-css", - "version": "11.0.22", + "version": "11.1.3", "description": "The parent module that contains all Workday Canvas Kit Labs CSS components", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", diff --git a/modules/labs-react/package.json b/modules/labs-react/package.json index 1665482e40..90337194ed 100644 --- a/modules/labs-react/package.json +++ b/modules/labs-react/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-labs-react", - "version": "11.0.22", + "version": "11.1.3", "description": "Canvas Kit Labs is an incubator for new and experimental components. Since we have a rather rigorous process for getting components in at a production level, it can be valuable to make them available earlier while we continuously iterate on the API/functionality. The Labs modules allow us to do that as needed.", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", @@ -46,7 +46,7 @@ "dependencies": { "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", - "@workday/canvas-kit-react": "^11.0.22", + "@workday/canvas-kit-react": "^11.1.3", "@workday/canvas-system-icons-web": "^3.0.0", "@workday/design-assets-types": "^0.2.8", "chroma-js": "^2.1.0", diff --git a/modules/labs-react/search-form/lib/SearchForm.tsx b/modules/labs-react/search-form/lib/SearchForm.tsx index a1b21ae877..441a3f65ab 100644 --- a/modules/labs-react/search-form/lib/SearchForm.tsx +++ b/modules/labs-react/search-form/lib/SearchForm.tsx @@ -178,7 +178,7 @@ const SearchCombobox = styled(Combobox)({ }); const SearchIcon = styled(TertiaryButton, { - shouldForwardProp: filterOutProps(['isHidden']), + shouldForwardProp: filterOutProps(['isHidden', 'isCollapsed']), }) & {isHidden: boolean}>(({isCollapsed, isHidden}) => { return { position: `absolute`, @@ -192,28 +192,30 @@ const SearchIcon = styled(TertiaryButton, { }; }); -const CloseButton = styled(TertiaryButton)< - Pick & Pick ->(({isCollapsed, showForm}) => { - const collapseStyles: CSSObject = - isCollapsed && showForm - ? { - display: 'inline-block', - } - : { - display: 'none', - }; +const CloseButton = styled(TertiaryButton, { + shouldForwardProp: filterOutProps(['isCollapsed', 'showForm']), +}) & Pick>( + ({isCollapsed, showForm}) => { + const collapseStyles: CSSObject = + isCollapsed && showForm + ? { + display: 'inline-block', + } + : { + display: 'none', + }; - return { - position: `absolute`, - top: 0, - bottom: 0, - right: 0, - margin: `auto ${space.xxs}`, - zIndex: 3, - ...collapseStyles, - }; -}); + return { + position: `absolute`, + top: 0, + bottom: 0, + right: 0, + margin: `auto ${space.xxs}`, + zIndex: 3, + ...collapseStyles, + }; + } +); const SearchField = styled(FormField)< Pick & Pick diff --git a/modules/popup-stack/package.json b/modules/popup-stack/package.json index 16c82bf1c0..19ca479f24 100644 --- a/modules/popup-stack/package.json +++ b/modules/popup-stack/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-popup-stack", - "version": "11.0.22", + "version": "11.1.3", "description": "Stack for managing popup UIs to coordinate global concerns like escape key handling and rendering order", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", diff --git a/modules/preview-css/package.json b/modules/preview-css/package.json index 188e6c8cb8..d85b74724a 100644 --- a/modules/preview-css/package.json +++ b/modules/preview-css/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-preview-css", - "version": "11.0.22", + "version": "11.1.3", "description": "The parent module that contains all Workday Canvas Kit Preview CSS components", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", diff --git a/modules/preview-react/divider/LICENSE b/modules/preview-react/divider/LICENSE new file mode 100644 index 0000000000..10966086f1 --- /dev/null +++ b/modules/preview-react/divider/LICENSE @@ -0,0 +1,52 @@ +Apache License, Version 2.0 Apache License Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +©2021 Workday, Inc. All rights reserved. Workday and the Workday logo are registered trademarks of Workday, Inc. All other brand and product names are trademarks or registered trademarks of their respective holders. + + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/modules/preview-react/divider/README.md b/modules/preview-react/divider/README.md new file mode 100644 index 0000000000..ccdf924c06 --- /dev/null +++ b/modules/preview-react/divider/README.md @@ -0,0 +1,11 @@ + +# Canvas Kit Divider + + + PREVIEW: Beta + This component is work in progress and currently in prerelease. + +A divider to segment and visually organize content + +View the [documentation for Divider](https://workday.github.io/canvas-kit/?path=/docs/preview-divider-react) +on Storybook. diff --git a/modules/preview-react/divider/index.ts b/modules/preview-react/divider/index.ts new file mode 100644 index 0000000000..fa2b087ab5 --- /dev/null +++ b/modules/preview-react/divider/index.ts @@ -0,0 +1 @@ +export * from './lib/Divider'; diff --git a/modules/preview-react/divider/lib/Divider.tsx b/modules/preview-react/divider/lib/Divider.tsx new file mode 100644 index 0000000000..d201186c5a --- /dev/null +++ b/modules/preview-react/divider/lib/Divider.tsx @@ -0,0 +1,60 @@ +import * as React from 'react'; +import {createComponent} from '@workday/canvas-kit-react/common'; +import { + createStencil, + handleCsProp, + CSProps, + calc, + cssVar, + px2rem, +} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +export const dividerStencil = createStencil({ + vars: { + space: cssVar(system.space.x4), + }, + base: ({space}) => { + return { + display: 'block', + height: px2rem(1), + border: 'none', + borderTop: `1px solid ${system.color.border.divider}`, + margin: `${calc.divide(space, 2)} 0`, + }; + }, +}); + +export interface DividerProps extends CSProps { + /** + * Applies top and bottom margin evenly. It divides the provided value by two and applies half to each end. + * E.g. `space="2rem"` would apply `1rem` margin to the top, and `1rem` margin to the bottom. + * @default `system.space.x4` (1rem) + */ + space?: string; +} + +/** + * # Divider + * A divider to segment and visually organize content. By default, it renders a semantic `[
](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr)` element. + * + * [View Docs](https://workday.github.io/canvas-kit/?path=/docs/preview-divider-react) + * + * The `space` prop will equally apply top and bottom margin styles. + * In the example below, `x2` (0.5rem), adds `0.25rem` margin to the top and `0.25rem` to the bottom + * + * @example + * ```tsx + * import { Divider } from '@workday/canvas-kit-preview-react/divider'; + * import {system} from '@workday/canvas-tokens-web'; + * + * + * + * ``` + */ +export const Divider = createComponent('hr')({ + displayName: 'Divider', + Component: ({space, ...elemProps}: DividerProps, ref, Element) => ( + + ), +}); diff --git a/modules/preview-react/divider/spec/SSR.spec.tsx b/modules/preview-react/divider/spec/SSR.spec.tsx new file mode 100644 index 0000000000..415048b4c0 --- /dev/null +++ b/modules/preview-react/divider/spec/SSR.spec.tsx @@ -0,0 +1,13 @@ +/** + * @jest-environment node + */ +import React from 'react'; +import {renderToString} from 'react-dom/server'; +import {Divider} from '../'; + +describe('Divider', () => { + it('should render on a server without crashing', () => { + const ssrRender = () => renderToString(); + expect(ssrRender).not.toThrow(); + }); +}); diff --git a/modules/preview-react/divider/spec/tsconfig.json b/modules/preview-react/divider/spec/tsconfig.json new file mode 100644 index 0000000000..d079d9f251 --- /dev/null +++ b/modules/preview-react/divider/spec/tsconfig.json @@ -0,0 +1,4 @@ + +{ + "extends": "../../../../tsconfig.spec.json" +} diff --git a/modules/preview-react/divider/stories/Divider.stories.mdx b/modules/preview-react/divider/stories/Divider.stories.mdx new file mode 100644 index 0000000000..4f3dcbe11d --- /dev/null +++ b/modules/preview-react/divider/stories/Divider.stories.mdx @@ -0,0 +1,38 @@ + +import {SymbolDoc, Specifications} from '@workday/canvas-kit-docs'; + +import {Divider} from '@workday/canvas-kit-preview-react/divider'; +import {Basic} from './examples/Basic'; +import {CustomSpace} from './examples/CustomSpace'; + + + +# Divider + +A `Divider` segments and visually organizes content. + +## Installation + +```sh +yarn add @workday/canvas-kit-preview-react +``` + +## Usage + +Use a `Divider` to separate content and create visual hierarchy. Typically they are used between paragraph sections to indicate a break or shift in content. However, they can also be used as decorative elements to provide greater emphasis and visual hierarchy. + +### Basic Example + +By default, `Divider` renders a `
` (horizontal rule) element with `0.5rem` of margin on top and bottom. The `space` prop allows you to adjust the vertical margin evenly. In the example below, the `Divider`s provide a subtle deliniation between each profile card without being as visually prominent as a `Card`. The `space` is adjusted to `0.25rem` which applied `0.125rem` to the top and bottom margin. + + + +### Custom Space + +You might also want to apply custom space to `Divider` where the top and bottom margin are not equal. The best way to achieve this is with `createStyles` and the `cs` property. In the example below, the `Divider` is applied as a decorative element to add emphasis to the section heading. Custom styles are defined in the `createStyles` function outside the component and are passed to `Divider`'s `cs` prop. These styles remove the top margin and set the bottom margin to `1rem`. + + + +## Component API + + diff --git a/modules/preview-react/divider/stories/examples/Basic.tsx b/modules/preview-react/divider/stories/examples/Basic.tsx new file mode 100644 index 0000000000..15d4034f48 --- /dev/null +++ b/modules/preview-react/divider/stories/examples/Basic.tsx @@ -0,0 +1,105 @@ +import React from 'react'; +import {Divider} from '@workday/canvas-kit-preview-react/divider'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; +import {Avatar} from '@workday/canvas-kit-react/avatar'; + +const sectionStyles = createStyles({ + display: 'flex', + flexDirection: 'column', + gap: system.space.x4, + maxWidth: '40rem', +}); + +export const Basic = () => { + const lastIndex = maintainerList.length - 1; + return ( +
+ {maintainerList.map((maintainerData, index) => ( + <> + + {index !== lastIndex && } + + ))} +
+ ); +}; + +const maintainerList = [ + { + id: '44883293', + name: 'Josh Bagwell', + bio: 'Software Development Engineer', + }, + { + id: '338257', + name: 'Nicholas Boll', + bio: 'Principal Software Development Engineer', + }, + { + id: '7966550', + name: 'Manuel Carrera', + bio: 'Sr. Software Development Engineer', + }, + { + id: '146020', + name: 'James Fan', + bio: 'Sr. Software Development Engineer', + }, + { + id: '48605821', + name: 'Raisa Primerova', + bio: 'Software Development Engineer', + }, + { + id: '4818182', + name: 'Alan Smith', + bio: 'Principal Software Development Engineer', + }, +]; + +const profileCardStyles = createStyles({ + display: 'grid', + gridGap: '0.5rem', + gridTemplateColumns: '5rem 1fr', + gridTemplateRows: '1fr 1fr', +}); + +const profileCardAvatarStyles = createStyles({ + gridColumn: '1', + gridRow: '1 / 3', +}); + +const profileCardHeadingStyles = createStyles({ + ...system.type.body.large, + fontWeight: system.fontWeight.bold, + gridColumn: '2/3', + gridRow: '1', + margin: 0, +}); + +const profileCardBodyStyles = createStyles({ + ...system.type.body.small, + gridColumn: '2', + gridRow: '2', + margin: 0, +}); + +interface ProfileCardProps { + id: string; + name: string; + bio: string; +} + +const ProfileCard = ({id, name, bio}: ProfileCardProps) => ( +
+ +

{name}

+

{bio}

+
+); diff --git a/modules/preview-react/divider/stories/examples/CustomSpace.tsx b/modules/preview-react/divider/stories/examples/CustomSpace.tsx new file mode 100644 index 0000000000..8752c01fd1 --- /dev/null +++ b/modules/preview-react/divider/stories/examples/CustomSpace.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import {Divider} from '@workday/canvas-kit-preview-react/divider'; +import {createStyles} from '@workday/canvas-kit-styling'; +import {system} from '@workday/canvas-tokens-web'; + +const headingStyles = createStyles({ + ...system.type.body.large, + fontWeight: system.fontWeight.bold, + margin: 0, +}); + +const bodyStyles = createStyles({ + ...system.type.body.small, + margin: 0, +}); + +const customDividerSpace = createStyles({ + margin: `0 0 ${system.space.x4}`, +}); + +export const CustomSpace = () => { + return ( +
+

Quote of the Day

+ +

+ "It is not our differences that divide us. It is our inability to recognize, accept, and + celebrate those differences." – Audre Lorde +

+
+ ); +}; diff --git a/modules/preview-react/divider/stories/tsconfig.json b/modules/preview-react/divider/stories/tsconfig.json new file mode 100644 index 0000000000..721d5175e2 --- /dev/null +++ b/modules/preview-react/divider/stories/tsconfig.json @@ -0,0 +1,4 @@ + +{ + "extends": "../../../../tsconfig.stories.json" +} diff --git a/modules/preview-react/divider/stories/visual-testing/stories_Divider.tsx b/modules/preview-react/divider/stories/visual-testing/stories_Divider.tsx new file mode 100644 index 0000000000..b0f0d0a038 --- /dev/null +++ b/modules/preview-react/divider/stories/visual-testing/stories_Divider.tsx @@ -0,0 +1,45 @@ +import React from 'react'; + +import {StaticStates, ComponentStatesTable} from '@workday/canvas-kit-react/testing'; +import {withSnapshotsEnabled} from '../../../../../utils/storybook'; + +import {Divider} from '@workday/canvas-kit-preview-react/divider'; +import {system} from '@workday/canvas-tokens-web'; + +export default withSnapshotsEnabled({ + title: 'Testing/Preview/Divider', + component: Divider, +}); + +export const DividerStates = () => { + return ( + + + {props => { + return ( +
+

Quote of the Day

+ +

+ "It is not our differences that divide us. It is our inability to recognize, accept, + and celebrate those differences." – Audre Lorde +

+
+ ); + }} +
+
+ ); +}; diff --git a/modules/preview-react/index.ts b/modules/preview-react/index.ts index 7ee2c454db..3388de841d 100644 --- a/modules/preview-react/index.ts +++ b/modules/preview-react/index.ts @@ -1,11 +1,12 @@ export * from './color-picker'; +export * from './divider'; export * from './form-field'; export * from './loading-sparkles'; export * from './menu'; export * from './pill'; +export * from './radio'; export * from './segmented-control'; export * from './side-panel'; export * from './status-indicator'; export * from './text-area'; export * from './text-input'; -export * from './radio'; diff --git a/modules/preview-react/package.json b/modules/preview-react/package.json index 8f73a47961..d16802ea2a 100644 --- a/modules/preview-react/package.json +++ b/modules/preview-react/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-preview-react", - "version": "11.0.22", + "version": "11.1.3", "description": "Canvas Kit Preview is made up of components that have the full design and a11y review, are part of the DS ecosystem and are approved for use in product. The API's could be subject to change, but not without strong communication and migration strategies.", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", @@ -46,8 +46,8 @@ "dependencies": { "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", - "@workday/canvas-kit-react": "^11.0.22", - "@workday/canvas-kit-styling": "^11.0.22", + "@workday/canvas-kit-react": "^11.1.3", + "@workday/canvas-kit-styling": "^11.1.3", "@workday/canvas-system-icons-web": "^3.0.0", "@workday/canvas-tokens-web": "^2.0.0", "@workday/design-assets-types": "^0.2.8" diff --git a/modules/react-fonts/package.json b/modules/react-fonts/package.json index 1b14e6cfc0..6acc37cd59 100644 --- a/modules/react-fonts/package.json +++ b/modules/react-fonts/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-react-fonts", - "version": "11.0.22", + "version": "11.1.3", "description": "Fonts for canvas-kit-react", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", diff --git a/modules/react/checkbox/lib/CheckboxInput.tsx b/modules/react/checkbox/lib/CheckboxInput.tsx index 3ebe0665e8..5ad88ef6cb 100644 --- a/modules/react/checkbox/lib/CheckboxInput.tsx +++ b/modules/react/checkbox/lib/CheckboxInput.tsx @@ -223,8 +223,8 @@ const checkboxInputStencil = createStencil({ export const CheckboxInput = createComponent('input')({ displayName: 'CheckboxInput', - Component: ({variant, error, ...elemProps}: CheckboxProps, ref, Element) => { - const {checked, disabled, indeterminate} = elemProps; + Component: ({variant, error, indeterminate, ...elemProps}: CheckboxProps, ref, Element) => { + const {checked, disabled} = elemProps; return ( extends Omit, 'children'> { +export interface ListBoxProps extends Omit { children?: React.ReactNode | ((item: T, index: number) => React.ReactNode); + /** + * Set the margin top of the list box. You must use this prop and not style any other way. The + * `Menu` uses virtualization and needs margins to be set on the correct element. This ensure + * proper rendering. If a `marginTop` is not provided, the value falls back to `marginY`. + */ + marginTop?: FlexProps['marginTop']; + /** + * Set the margin bottom of the list box. You must use this prop and not style any other way. The + * `Menu` uses virtualization and needs margins to be set on the correct element. This ensure + * proper rendering. If a `marginBottom` is not provided, the value falls back to `marginY`. + */ + marginBottom?: FlexProps['marginBottom']; + /** + * Set the margin top and bottom of the list box. You must use this prop and not style any other way. The + * `Menu` uses virtualization and needs margins to be set on the correct element. This ensure + * proper rendering. + */ + marginY?: FlexProps['marginY']; } export const ListBoxItem = createSubcomponent('li')({ displayName: 'Item', modelHook: useListModel, elemPropsHook: useListItemRegister, -})>((elemProps, Element) => { - return ; +})((elemProps, Element) => { + return ; }); export const useListBox = createElemPropsHook(useListModel)(model => { @@ -50,6 +67,7 @@ const listBoxContainerStencil = createStencil({ const listBoxStencil = createStencil({ base: { + display: 'flex', flexDirection: 'column', marginTop: system.space.zero, marginBottom: system.space.zero, @@ -104,16 +122,16 @@ export const ListBox = createContainer('ul')({ { style: { maxHeight, - marginBottom: marginY || marginBottom, - marginTop: marginY || marginBottom, + marginBottom: marginBottom ?? marginY, + marginTop: marginTop ?? marginY, }, }, listBoxContainerStencil({orientation: model.state.orientation}) )} > - + {useListRenderItems(model, elemProps.children)} - + ); } 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/react/menu/lib/MenuList.tsx b/modules/react/menu/lib/MenuList.tsx index 1f44edc12b..79cf428f24 100644 --- a/modules/react/menu/lib/MenuList.tsx +++ b/modules/react/menu/lib/MenuList.tsx @@ -10,9 +10,8 @@ import {ListBox, ListProps} from '@workday/canvas-kit-react/collection'; import {useReturnFocus, useFocusRedirect} from '@workday/canvas-kit-react/popup'; import {useMenuModel} from './useMenuModel'; -import {createStencil, cssVar} from '@workday/canvas-kit-styling'; +import {createStencil, cssVar, handleCsProp} from '@workday/canvas-kit-styling'; import {base, system} from '@workday/canvas-tokens-web'; -import {mergeStyles} from '@workday/canvas-kit-react/layout'; export interface MenuListProps extends Omit, 'children'> { @@ -62,8 +61,7 @@ export const MenuList = createSubcomponent('div')({ as={Element} model={model} marginY={cssVar(system.space.x2)} - overflowY="auto" - {...mergeStyles(elemProps, menuListStencil({orientation: model.state.orientation}))} + {...handleCsProp(elemProps, menuListStencil({orientation: model.state.orientation}))} > {children} diff --git a/modules/react/modal/lib/Modal.tsx b/modules/react/modal/lib/Modal.tsx index 9ff391cbc4..faafc5d751 100644 --- a/modules/react/modal/lib/Modal.tsx +++ b/modules/react/modal/lib/Modal.tsx @@ -42,7 +42,7 @@ export const Modal = createContainer()({ /** * The `Modal.Card` is wraps a {@link PopupCard Popup.Card} which wraps a {@link Card}. It is * the `role="dialog"` element and is uses `useModalCard` behavior hook which sets - * `aria-modal="true"` and sets the `aria-labelledby` that points to the `id` of the + * `aria-modal="false"` and sets the `aria-labelledby` that points to the `id` of the * {@link ModalHeading Modal.Heading}. If you don't use a `Modal.Heading`, add an `aria-label` * instead. The default element is a `div` and can be changed via the `as` prop. */ diff --git a/modules/react/modal/lib/hooks/useModalCard.ts b/modules/react/modal/lib/hooks/useModalCard.ts index ab18e7ffda..9bcdf86c06 100644 --- a/modules/react/modal/lib/hooks/useModalCard.ts +++ b/modules/react/modal/lib/hooks/useModalCard.ts @@ -5,7 +5,14 @@ export const useModalCard = composeHooks( usePopupCard, createElemPropsHook(usePopupModel)(() => { return { - 'aria-modal': true, + /** + * `aria-modal` was added with the intent that screen readers would add support to properly define content within a modal, + * including popups like a Select. However, when aria-modal is true, it will hide other elements from screen readers, + * including popups owned by the modal because these popups are siblings to the modal and not ancestors. There is no defined time when Apple/VoiceOver + * might change this behavior and better support aria-owns, so for the time being we'll explicitly set this to false to provide a better + * VoiceOver experience. + */ + 'aria-modal': false, }; }) ); diff --git a/modules/react/package.json b/modules/react/package.json index 474d668d4a..01a9ac8fbd 100644 --- a/modules/react/package.json +++ b/modules/react/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-react", - "version": "11.0.22", + "version": "11.1.3", "description": "The parent module that contains all Workday Canvas Kit React components", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", @@ -49,8 +49,8 @@ "@emotion/styled": "^11.6.0", "@popperjs/core": "^2.5.4", "@workday/canvas-colors-web": "^2.0.0", - "@workday/canvas-kit-popup-stack": "^11.0.22", - "@workday/canvas-kit-styling": "^11.0.22", + "@workday/canvas-kit-popup-stack": "^11.1.3", + "@workday/canvas-kit-styling": "^11.1.3", "@workday/canvas-system-icons-web": "^3.0.0", "@workday/canvas-tokens-web": "^2.0.0", "@workday/design-assets-types": "^0.2.8", diff --git a/modules/react/select/lib/Select.tsx b/modules/react/select/lib/Select.tsx index 359df958cc..1519a1abd5 100644 --- a/modules/react/select/lib/Select.tsx +++ b/modules/react/select/lib/Select.tsx @@ -14,7 +14,6 @@ import { ExtractProps, createContainer, Themeable, - useLocalRef, } from '@workday/canvas-kit-react/common'; import {system} from '@workday/canvas-tokens-web'; @@ -79,27 +78,6 @@ export const SelectInput = createSubcomponent(TextInput)({ Element, model ) => { - const {localRef, elementRef} = useLocalRef(ref); - - // We need to create a proxy between the multiple inputs. We need to redirect a few methods to - // the visible input - React.useImperativeHandle( - elementRef, - () => { - if (localRef.current) { - localRef.current.focus = (options?: FocusOptions) => { - textInputProps.ref.current!.focus(options); - }; - localRef.current.blur = () => { - textInputProps.ref.current!.blur(); - }; - } - - return localRef.current!; - }, - [textInputProps.ref, localRef] - ); - return ( {inputStartIcon && model.state.selectedIds.length > 0 && ( @@ -117,7 +95,7 @@ export const SelectInput = createSubcomponent(TextInput)({ onInput={onInput} value={value} name={name} - ref={elementRef} + ref={ref} {...hiddenSelectInputStencil()} /> {/* Visual input */} diff --git a/modules/react/select/lib/hooks/useSelectInput.ts b/modules/react/select/lib/hooks/useSelectInput.ts index 1705d0e24c..72a6b97d71 100644 --- a/modules/react/select/lib/hooks/useSelectInput.ts +++ b/modules/react/select/lib/hooks/useSelectInput.ts @@ -28,8 +28,33 @@ export const useSelectInput = composeHooks( useComboboxMoveCursorToSelected, createElemPropsHook(useSelectModel)( (model, ref, elemProps: {keySofar?: string; placeholder?: string; value?: string} = {}) => { - const {elementRef} = useLocalRef(ref as any); - const textInputRef = React.useRef(null); + const {elementRef: textInputElementRef, localRef: textInputRef} = useLocalRef( + // PopupModel says the targetRef is a `HTMLButtonElement`, but it is a `HTMLInputElement` + model.state.targetRef as any as React.Ref + ); + + const {localRef, elementRef} = useLocalRef(ref as React.Ref); + + // We need to create a proxy between the multiple inputs. We need to redirect a few methods to + // the visible input + React.useImperativeHandle( + elementRef, + () => { + if (localRef.current) { + localRef.current.focus = (options?: FocusOptions) => { + textInputRef.current!.focus(options); + }; + localRef.current.blur = () => { + textInputRef.current!.blur(); + }; + } + + return localRef.current!; + }, + [textInputRef, localRef] + ); + + // Remap the Popup model's targetRef to be the visible ref. `ref` and `model.state.targetRef` are already linked. We have to override that. // Update the text value of the input const handleOnChange = (event: React.ChangeEvent) => { @@ -63,6 +88,7 @@ export const useSelectInput = composeHooks( model.state.selectedIds[0] ) { const value = model.navigation.getItem(model.state.selectedIds[0], model).id; + const oldValue = model.state.inputRef.current.value; // force the hidden input to have the correct value if (model.state.inputRef.current.value !== value) { @@ -77,8 +103,8 @@ export const useSelectInput = composeHooks( } if ( - model.state.selectedIds[0] !== value && - model.state.inputRef.current.value !== value + model.state.selectedIds[0] !== oldValue && + model.state.inputRef.current.value !== oldValue ) { // Programmatically dispatch an onChange once items are loaded. This account for when a consumer wants an initial selected item and they're loading them from a server. dispatchInputEvent(model.state.inputRef.current, value); @@ -151,14 +177,13 @@ export const useSelectInput = composeHooks( textInputRef.current?.focus(); }, textInputProps: { - ref: textInputRef, + ref: textInputElementRef, onChange: noop, value: model.state.selectedIds.length > 0 && model.state.items.length > 0 ? model.navigation.getItem(model.state.selectedIds[0], model).textValue : '', }, - ref: elementRef, 'aria-haspopup': 'menu', } as const; } diff --git a/modules/styling-transform/package.json b/modules/styling-transform/package.json index f52df50755..ed45eb615b 100644 --- a/modules/styling-transform/package.json +++ b/modules/styling-transform/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-styling-transform", - "version": "11.0.22", + "version": "11.1.3", "description": "The custom CSS in JS solution that takes JS styles and turns them into static CSS", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", @@ -34,7 +34,7 @@ ], "dependencies": { "@emotion/serialize": "^1.0.2", - "@workday/canvas-kit-styling": "^11.0.22", + "@workday/canvas-kit-styling": "^11.1.3", "@workday/canvas-tokens-web": "^2.0.0", "stylis": "4.0.13", "typescript": "4.2" diff --git a/modules/styling/package.json b/modules/styling/package.json index 0cfe19fe84..47b874712d 100644 --- a/modules/styling/package.json +++ b/modules/styling/package.json @@ -1,6 +1,6 @@ { "name": "@workday/canvas-kit-styling", - "version": "11.0.22", + "version": "11.1.3", "description": "The custom CSS in JS solution that takes JS styles and turns them into static CSS", "author": "Workday, Inc. (https://www.workday.com)", "license": "Apache-2.0", @@ -53,7 +53,7 @@ "@emotion/react": "^11.7.1", "@emotion/serialize": "^1.0.2", "@emotion/styled": "^11.6.0", - "@workday/canvas-kit-react": "^11.0.22", + "@workday/canvas-kit-react": "^11.1.3", "@workday/canvas-system-icons-web": "^3.0.0", "@workday/canvas-tokens-web": "^2.0.0", "typescript": "4.2" diff --git a/yarn.lock b/yarn.lock index a92aad20bd..e046147177 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5733,11 +5733,11 @@ axe-core@^3.0.3, axe-core@^3.1.2, axe-core@^3.4.0, axe-core@^3.5.1: integrity sha512-HZpLE7xu05+8AbpqXITGdxp1Xwk8ysAXrg7MiKRY27py3DAyEJpoJQo1727pWF3F+O79V3r+cTWhOzfB49P89w== axios@^1.0.0: - version "1.6.7" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" - integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== + version "1.7.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" + integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw== dependencies: - follow-redirects "^1.15.4" + follow-redirects "^1.15.6" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -8027,9 +8027,9 @@ electron-to-chromium@^1.4.17: integrity sha512-XIvFB1omSAxYgHYX48sC+HR8i/p7lx7R+0cX9faElg1g++h9IilCrJ12+bQuY+d96Wp7zkBiJwMOv+AhLtLrTg== elliptic@^6.0.0, elliptic@^6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== dependencies: bn.js "^4.11.9" brorand "^1.1.0" @@ -9064,7 +9064,7 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -follow-redirects@^1.15.4: +follow-redirects@^1.15.6: version "1.15.6" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==