From 336e95a0fec039985a44e5d7b84246499c57c28c Mon Sep 17 00:00:00 2001 From: Mohammer5 Date: Wed, 23 Oct 2024 08:39:19 +0800 Subject: [PATCH] chore(select a11y): make position cucumber test into a vanilla cypress test --- collections/forms/i18n/en.pot | 4 +- .../features/menu-positioning.test.e2e.js | 187 ++++++++++++++++++ .../features/position.feature | 26 --- .../features/position/index.js | 114 ----------- .../single-select-a11y.prod.stories.js | 3 +- 5 files changed, 191 insertions(+), 143 deletions(-) create mode 100644 components/select/src/single-select-a11y/features/menu-positioning.test.e2e.js delete mode 100644 components/select/src/single-select-a11y/features/position.feature delete mode 100644 components/select/src/single-select-a11y/features/position/index.js diff --git a/collections/forms/i18n/en.pot b/collections/forms/i18n/en.pot index c99afb8eb0..599e729bf8 100644 --- a/collections/forms/i18n/en.pot +++ b/collections/forms/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-10-22T03:25:41.899Z\n" -"PO-Revision-Date: 2024-10-22T03:25:41.902Z\n" +"POT-Creation-Date: 2024-10-23T00:35:22.918Z\n" +"PO-Revision-Date: 2024-10-23T00:35:22.918Z\n" msgid "Upload file" msgstr "Upload file" diff --git a/components/select/src/single-select-a11y/features/menu-positioning.test.e2e.js b/components/select/src/single-select-a11y/features/menu-positioning.test.e2e.js new file mode 100644 index 0000000000..5ea33c8aa7 --- /dev/null +++ b/components/select/src/single-select-a11y/features/menu-positioning.test.e2e.js @@ -0,0 +1,187 @@ +describe('SingleSelectA11y: Menu positioning', () => { + it('should open in the default position', () => { + // Given there is enough space below the anchor to fit the SingleSelect menu + cy.visitStory('Single Select A11y', 'Default position') + + // When the SingleSelect is clicked + cy.findByRole('combobox').click() + + // Then the top of the menu is aligned with the bottom of the input + cy.all( + () => cy.findByRole('combobox'), + () => cy.findByRole('listbox') + ).should(([selectedValue, menu]) => { + expect(selectedValue.length).to.equal(1) + expect(menu.length).to.equal(1) + + const $selectedValue = selectedValue[0] + const $menu = menu[0] + + const selectedValueRect = $selectedValue.getBoundingClientRect() + const menuRect = $menu.getBoundingClientRect() + + // The listbox is inside a container with a border, + // hence the increased delta + expect(menuRect.top).to.be.closeTo(selectedValueRect.bottom, 2) + }) + + // And the left of the SingleSelect is aligned with the left of the anchor + cy.all( + () => cy.findByRole('combobox'), + () => cy.findByRole('listbox') + ).should(([selectedValue, menu]) => { + expect(selectedValue.length).to.equal(1) + expect(menu.length).to.equal(1) + + const $selectedValue = selectedValue[0] + const $menu = menu[0] + + const selectedValueRect = $selectedValue.getBoundingClientRect() + const menuRect = $menu.getBoundingClientRect() + + // The listbox is inside a container with a border, + // hence the increased delta + expect(selectedValueRect.left).to.be.closeTo(menuRect.left, 2) + }) + }) + + it('should flipp the position when insufficient space below', () => { + // Given there is not enough space below the anchor to fit the SingleSelect menu + cy.visitStory('Single Select A11y', 'Flipped position') + + // When the SingleSelect is clicked + cy.findByRole('combobox').click() + + // Then the bottom of the menu is aligned with the top of the input + cy.all( + () => cy.findByRole('combobox'), + // We need to get the parent as the menu itself + // extends withing the div container + () => cy.findByRole('listbox').invoke('parent') + ).should(([selectedValue, menu]) => { + expect(selectedValue.length).to.equal(1) + expect(menu.length).to.equal(1) + + const $selectedValue = selectedValue[0] + const $menu = menu[0] + + const selectedValueRect = $selectedValue.getBoundingClientRect() + const menuRect = $menu.getBoundingClientRect() + + expect(selectedValueRect.top).to.be.closeTo(menuRect.bottom, 1) + }) + + // And the left of the SingleSelect is aligned with the left of the anchor + cy.all( + () => cy.findByRole('combobox'), + () => cy.findByRole('listbox') + ).should(([selectedValue, menu]) => { + expect(selectedValue.length).to.equal(1) + expect(menu.length).to.equal(1) + + const $selectedValue = selectedValue[0] + const $menu = menu[0] + + const selectedValueRect = $selectedValue.getBoundingClientRect() + const menuRect = $menu.getBoundingClientRect() + + // The listbox is inside a container with a border, + // hence the increased delta + expect(selectedValueRect.left).to.be.closeTo(menuRect.left, 2) + }) + }) + + it('should shift the menu into view when insufficient space below and above', () => { + // Given there is not enough space above or below the anchor to fit the SingleSelect menu + cy.visitStory('Single Select A11y', 'Shifted into view') + + // When the SingleSelect is clicked + cy.findByRole('combobox').click() + + // Then it is rendered on top of the SingleSelect + cy.all( + () => cy.findByRole('combobox'), + () => cy.findByRole('listbox') + ).should(([selectedValue, menu]) => { + expect(selectedValue.length).to.equal(1) + expect(menu.length).to.equal(1) + + const $selectedValue = selectedValue[0] + const $menu = menu[0] + + const selectedValueRect = $selectedValue.getBoundingClientRect() + const menuRect = $menu.getBoundingClientRect() + + expect(selectedValueRect.top).to.be.greaterThan(menuRect.top) + expect(menuRect.bottom).to.be.greaterThan(selectedValueRect.bottom) + }) + + // And the left of the SingleSelect is aligned with the left of the anchor + cy.all( + () => cy.findByRole('combobox'), + () => cy.findByRole('listbox') + ).should(([selectedValue, menu]) => { + expect(selectedValue.length).to.equal(1) + expect(menu.length).to.equal(1) + + const $selectedValue = selectedValue[0] + const $menu = menu[0] + + const selectedValueRect = $selectedValue.getBoundingClientRect() + const menuRect = $menu.getBoundingClientRect() + + expect(selectedValueRect.top).to.be.greaterThan(menuRect.top) + expect(menuRect.bottom).to.be.greaterThan(selectedValueRect.bottom) + }) + }) + + it('should keep the menu in position while the window is scrolled', () => { + // Given there is enough space below the anchor to fit the SingleSelect menu + cy.visitStory('Single Select A11y', 'Default position') + + // When the SingleSelect is clicked + cy.findByRole('combobox').click() + + // And the window is scrolled down + cy.get('body').then(($body) => $body.height('5000px')) // Ensure the body can scroll first + cy.scrollTo(0, 800) + + // Then the top of the menu is aligned with the bottom of the input + cy.all( + () => cy.findByRole('combobox'), + () => cy.findByRole('listbox') + ).should(([selectedValue, menu]) => { + expect(selectedValue.length).to.equal(1) + expect(menu.length).to.equal(1) + + const $selectedValue = selectedValue[0] + const $menu = menu[0] + + const selectedValueRect = $selectedValue.getBoundingClientRect() + const menuRect = $menu.getBoundingClientRect() + + // The listbox is inside a container with a border, + // hence the increased delta + expect(menuRect.top).to.be.closeTo(selectedValueRect.bottom, 2) + }) + + // And the left of the SingleSelect is aligned with the left of the anchor + cy.all( + () => cy.findByRole('combobox'), + () => cy.findByRole('listbox') + ).should(([selectedValue, menu]) => { + expect(selectedValue.length).to.equal(1) + expect(menu.length).to.equal(1) + + const $selectedValue = selectedValue[0] + const $menu = menu[0] + + const selectedValueRect = $selectedValue.getBoundingClientRect() + const menuRect = $menu.getBoundingClientRect() + + // The listbox is inside a container with a border, + // hence the increased delta + expect(selectedValueRect.left).to.be.closeTo(menuRect.left, 2) + }) + }) +}) diff --git a/components/select/src/single-select-a11y/features/position.feature b/components/select/src/single-select-a11y/features/position.feature deleted file mode 100644 index 791f229299..0000000000 --- a/components/select/src/single-select-a11y/features/position.feature +++ /dev/null @@ -1,26 +0,0 @@ -Feature: Position of SingleSelect menu dropdown - - Scenario: Default rendering - Given there is enough space below the anchor to fit the SingleSelect menu - When the SingleSelect is clicked - Then the top of the menu is aligned with the bottom of the input - And the left of the SingleSelect is aligned with the left of the anchor - - Scenario: Flipped rendering when insufficient space below - Given there is not enough space below the anchor to fit the SingleSelect menu - When the SingleSelect is clicked - Then the bottom of the menu is aligned with the top of the input - And the left of the SingleSelect is aligned with the left of the anchor - - Scenario: Shifting into view when insufficient space below and above - Given there is not enough space above or below the anchor to fit the SingleSelect menu - When the SingleSelect is clicked - Then it is rendered on top of the SingleSelect - And the left of the SingleSelect is aligned with the left of the anchor - - Scenario: Staying in position during when the window is scrolled - Given there is enough space below the anchor to fit the SingleSelect menu - When the SingleSelect is clicked - And the window is scrolled down - Then the top of the menu is aligned with the bottom of the input - And the left of the SingleSelect is aligned with the left of the anchor diff --git a/components/select/src/single-select-a11y/features/position/index.js b/components/select/src/single-select-a11y/features/position/index.js deleted file mode 100644 index f775d754c5..0000000000 --- a/components/select/src/single-select-a11y/features/position/index.js +++ /dev/null @@ -1,114 +0,0 @@ -import { Given, Then, When } from '@badeball/cypress-cucumber-preprocessor' - -Given( - 'there is enough space below the anchor to fit the SingleSelect menu', - () => { - cy.visitStory('Single Select A11y', 'Default position') - } -) - -Given( - 'there is not enough space below the anchor to fit the SingleSelect menu', - () => { - cy.visitStory('Single Select A11y', 'Flipped position') - } -) - -Given( - 'there is not enough space above or below the anchor to fit the SingleSelect menu', - () => { - cy.visitStory('Single Select A11y', 'Shifted into view') - } -) - -When('the SingleSelect is clicked', () => { - cy.findByRole('combobox').click() -}) - -When('the window is scrolled down', () => { - // Ensure the body can scroll first - cy.get('body').then(($body) => $body.height('5000px')) - cy.scrollTo(0, 800) -}) - -Then('the top of the menu is aligned with the bottom of the input', () => { - cy.all( - () => cy.findByRole('combobox'), - () => cy.findByRole('listbox') - ).should(([selectedValue, menu]) => { - expect(selectedValue.length).to.equal(1) - expect(menu.length).to.equal(1) - - const $selectedValue = selectedValue[0] - const $menu = menu[0] - - const selectedValueRect = $selectedValue.getBoundingClientRect() - const menuRect = $menu.getBoundingClientRect() - - // The listbox is inside a container with a border, - // hence the increased delta - expect(menuRect.top).to.be.closeTo(selectedValueRect.bottom, 2) - }) -}) - -Then('the bottom of the menu is aligned with the top of the input', () => { - cy.all( - () => cy.findByRole('combobox'), - // We need to get the parent as the menu itself - // extends withing the div container - () => cy.findByRole('listbox').invoke('parent') - ).should(([selectedValue, menu]) => { - expect(selectedValue.length).to.equal(1) - expect(menu.length).to.equal(1) - - const $selectedValue = selectedValue[0] - const $menu = menu[0] - - const selectedValueRect = $selectedValue.getBoundingClientRect() - const menuRect = $menu.getBoundingClientRect() - - expect(selectedValueRect.top).to.be.closeTo(menuRect.bottom, 1) - }) -}) - -Then('it is rendered on top of the SingleSelect', () => { - cy.all( - () => cy.findByRole('combobox'), - () => cy.findByRole('listbox') - ).should(([selectedValue, menu]) => { - expect(selectedValue.length).to.equal(1) - expect(menu.length).to.equal(1) - - const $selectedValue = selectedValue[0] - const $menu = menu[0] - - const selectedValueRect = $selectedValue.getBoundingClientRect() - const menuRect = $menu.getBoundingClientRect() - - expect(selectedValueRect.top).to.be.greaterThan(menuRect.top) - expect(menuRect.bottom).to.be.greaterThan(selectedValueRect.bottom) - }) -}) - -Then( - 'the left of the SingleSelect is aligned with the left of the anchor', - () => { - cy.all( - () => cy.findByRole('combobox'), - () => cy.findByRole('listbox') - ).should(([selectedValue, menu]) => { - expect(selectedValue.length).to.equal(1) - expect(menu.length).to.equal(1) - - const $selectedValue = selectedValue[0] - const $menu = menu[0] - - const selectedValueRect = $selectedValue.getBoundingClientRect() - const menuRect = $menu.getBoundingClientRect() - - // The listbox is inside a container with a border, - // hence the increased delta - expect(selectedValueRect.left).to.be.closeTo(menuRect.left, 2) - }) - } -) diff --git a/components/select/src/single-select-a11y/single-select-a11y.prod.stories.js b/components/select/src/single-select-a11y/single-select-a11y.prod.stories.js index a1c11677f9..51236f436f 100644 --- a/components/select/src/single-select-a11y/single-select-a11y.prod.stories.js +++ b/components/select/src/single-select-a11y/single-select-a11y.prod.stories.js @@ -571,6 +571,7 @@ export const WithRTL = () => { export const WithPlaceholder = () => { const [value, setValue] = useState('') + const withoutEmptyOptions = options.slice(1) return ( { : '' } onChange={(nextValue) => setValue(nextValue)} - options={fiveOptions} + options={withoutEmptyOptions} /> ) }