diff --git a/CHANGELOG.md b/CHANGELOG.md index c58ccaffd2..3cd4554ce9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,37 @@ +## [123.7.0](https://github.com/Sage/carbon/compare/v123.6.0...v123.7.0) (2023-11-08) + + +### Features + +* **select:** add selectionConfirmed property to event emitted on change ([748a17b](https://github.com/Sage/carbon/commit/748a17b8185a2d9a2bd58843efbd4a10739c2e8d)), closes [#6330](https://github.com/Sage/carbon/issues/6330) + + +### Bug Fixes + +* **filterable-select, multi-select:** handle when filter text has no match and enter pressed ([84ebef7](https://github.com/Sage/carbon/commit/84ebef7df4573d3205db9ebd424d88c2a26f4c3e)) +* **option:** ensure fill prop is not passed to underlying DOM element ([2a8cfc1](https://github.com/Sage/carbon/commit/2a8cfc11bae33c52496a65741ad02a339989beff)) + +## [123.6.0](https://github.com/Sage/carbon/compare/v123.5.1...v123.6.0) (2023-11-08) + + +### Features + +* **toast:** add alignY prop to allow vertical alignment to be configurable ([64203e1](https://github.com/Sage/carbon/commit/64203e1a618294c9c8c71964963377ae2891e134)), closes [#6301](https://github.com/Sage/carbon/issues/6301) + +### [123.5.1](https://github.com/Sage/carbon/compare/v123.5.0...v123.5.1) (2023-11-08) + + +### Bug Fixes + +* **multi-action-button, split-button:** fix aria role ([c16a1e4](https://github.com/Sage/carbon/commit/c16a1e4357518a122681675875999d61408a60b8)), closes [#6383](https://github.com/Sage/carbon/issues/6383) + +## [123.5.0](https://github.com/Sage/carbon/compare/v123.4.4...v123.5.0) (2023-11-07) + + +### Features + +* **icon:** 4 icons added to the icon font ([33f979a](https://github.com/Sage/carbon/commit/33f979ad1690fba0229a73e5999744168814378b)) + ### [123.4.4](https://github.com/Sage/carbon/compare/v123.4.3...v123.4.4) (2023-11-06) diff --git a/cypress/components/global-header/global-header.cy.tsx b/cypress/components/global-header/global-header.cy.tsx deleted file mode 100644 index 7f9ee0a3a0..0000000000 --- a/cypress/components/global-header/global-header.cy.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* eslint-disable jest/valid-expect, jest/valid-expect-in-promise */ -import React from "react"; -import GlobalHeader from "../../../src/components/global-header"; -import { - FullMenuExample, - GlobalHeaderWithErrorHandler, -} from "../../../src/components/global-header/global-header-test.stories"; -import CypressMountWithProviders from "../../support/component-helper/cypress-mount"; - -import carbonLogo from "../../../logo/carbon-logo.png"; -import navigationBar from "../../locators/navigation-bar"; -import { globalHeader, globalHeaderLogo } from "../../locators/global-header"; - -context("Testing Global Header component", () => { - it("should not cause a ResizeObserver-related error to occur", () => { - CypressMountWithProviders(); - cy.wait(500); - cy.get("#error-div").should("have.text", ""); - }); - - it("should check that z-index of component is greater than that of NavigationBar", () => { - CypressMountWithProviders(); - globalHeader().invoke("css", "zIndex").as("globalHeaderZIndex"); - - navigationBar().invoke("css", "zIndex").as("navigationBarZIndex"); - const globalIndex = parseInt(cy.get("@globalHeaderZIndex").toString()); - const NavIndex = parseInt(cy.get("@navigationBarZIndex").toString()); - - expect(globalIndex > NavIndex); - }); - - it("should check when logo prop is passed, the height of the logo element never exceeds the maximum height of the component", () => { - const logoHeight = 41; - const expectedHeight = 40; - - const logo = ( - Carbon logo - ); - CypressMountWithProviders(Example); - - globalHeaderLogo().should("have.css", "height", `${expectedHeight}px`); - }); - - describe("Accessibility tests for Global-Header component", () => { - it("should pass accessibility tests for Global-Header FullMenuExample", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Global-Header when logo prop is passed", () => { - const logoHeight = 41; - - const logo = ( - Carbon logo - ); - CypressMountWithProviders( - Example - ); - - cy.checkAccessibility(); - }); - }); -}); diff --git a/cypress/components/multi-action-button/multi-action-button.cy.tsx b/cypress/components/multi-action-button/multi-action-button.cy.tsx index 0ec9430e69..e4da62af0c 100644 --- a/cypress/components/multi-action-button/multi-action-button.cy.tsx +++ b/cypress/components/multi-action-button/multi-action-button.cy.tsx @@ -720,6 +720,15 @@ context("Tests for MultiActionButton component", () => { cy.checkAccessibility(); }); + // TODO: test passes even when it shouldn't, see FE-6267 + it("should pass accessibility tests for MultiActionButton when open", () => { + CypressMountWithProviders(); + + multiActionButton().eq(0).trigger("keydown", keyCode("Enter")); + + cy.checkAccessibility(); + }); + it("should pass accessibility tests for MultiActionButton disabled prop", () => { CypressMountWithProviders(); diff --git a/cypress/components/select/filterable-select/filterable-select.cy.tsx b/cypress/components/select/filterable-select/filterable-select.cy.tsx index 3b0c855ee7..77901b2ac4 100644 --- a/cypress/components/select/filterable-select/filterable-select.cy.tsx +++ b/cypress/components/select/filterable-select/filterable-select.cy.tsx @@ -1018,6 +1018,7 @@ context("Tests for FilterableSelect component", () => { selectOption(positionOfElement(position)).click(); cy.get("@onChange").should("have.been.calledWith", { target: { value: option }, + selectionConfirmed: true, }); }); @@ -1147,6 +1148,93 @@ context("Tests for FilterableSelect component", () => { }); }); + describe("selection confirmed", () => { + it("is set on the event when options are clicked", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectListText("One").click(); + cy.get('[data-element="confirmed-selection-1"]').should("exist"); + dropdownButton().click(); + selectListText("Five").click(); + cy.get('[data-element="confirmed-selection-1"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-5"]').should("exist"); + dropdownButton().click(); + selectListText("Seven").click(); + cy.get('[data-element="confirmed-selection-5"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-7"]').should("exist"); + }); + + it("is set on the event when Enter key is pressed on an option using ArrowDown key to navigate", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-1"]').should("exist"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-1"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-3"]').should("exist"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-3"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-5"]').should("exist"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-5"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-6"]').should("exist"); + }); + + it("is set on the event when Enter key is pressed on an option using ArrowUp key to navigate", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-9"]').should("exist"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-9"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-7"]').should("exist"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-7"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-5"]').should("exist"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-5"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-4"]').should("exist"); + }); + + it("is set on the event when Enter key is pressed on an option after filtering", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + commonDataElementInputPreview().click().type("th"); + cy.get('[data-element="confirmed-selection-3"]').should("not.exist"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-3"]').should("exist"); + }); + }); + + it("should not throw when filter text does not match option text", () => { + CypressMountWithProviders( + + ); + + commonDataElementInputPreview().type("abc"); + selectInput().realPress("Enter"); + getDataElementByValue("input").should("have.attr", "value", ""); + }); + describe("Accessibility tests for FilterableSelect component", () => { it("should pass accessibilty tests for FilterableSelect", () => { CypressMountWithProviders(); diff --git a/cypress/components/select/multi-select/multi-select.cy.tsx b/cypress/components/select/multi-select/multi-select.cy.tsx index f489129f92..149152d81c 100644 --- a/cypress/components/select/multi-select/multi-select.cy.tsx +++ b/cypress/components/select/multi-select/multi-select.cy.tsx @@ -915,6 +915,7 @@ context("Tests for MultiSelect component", () => { selectOption(positionOfElement(position)).click(); cy.get("@onChange").should("have.been.calledWith", { target: { value: option }, + selectionConfirmed: true, }); }); @@ -1099,6 +1100,141 @@ context("Tests for MultiSelect component", () => { }); }); + describe("selection confirmed", () => { + it("is set on the event when options are clicked", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectListText("One").click(); + selectListText("Five").click(); + selectListText("Seven").click(); + + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 3); + cy.get('[data-element="confirmed-selection-1"]').should("exist"); + cy.get('[data-element="confirmed-selection-5"]').should("exist"); + cy.get('[data-element="confirmed-selection-7"]').should("exist"); + }); + + it("is set on the event when Enter key is pressed on an option using ArrowDown key to navigate", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 4); + cy.get('[data-element="confirmed-selection-1"]').should("exist"); + cy.get('[data-element="confirmed-selection-3"]').should("exist"); + cy.get('[data-element="confirmed-selection-5"]').should("exist"); + cy.get('[data-element="confirmed-selection-6"]').should("exist"); + }); + + it("is set on the event when Enter key is pressed on an option using ArrowUp key to navigate", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 4); + cy.get('[data-element="confirmed-selection-9"]').should("exist"); + cy.get('[data-element="confirmed-selection-7"]').should("exist"); + cy.get('[data-element="confirmed-selection-5"]').should("exist"); + cy.get('[data-element="confirmed-selection-4"]').should("exist"); + }); + + it("is set on the event when the selected options are removed via Backspace key", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 4); + + selectInput().realPress("Backspace"); + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 3); + selectInput().realPress("Backspace"); + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 2); + selectInput().realPress("Backspace"); + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 1); + selectInput().realPress("Backspace"); + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 0); + }); + + it("is set on the event when the selected options are removed via clicking close icon of Pills", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectListText("One").click(); + selectListText("Five").click(); + selectListText("Seven").click(); + + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 3); + pillCloseIcon().eq(2).click(); + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 2); + pillCloseIcon().eq(1).click(); + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 1); + pillCloseIcon().eq(0).click(); + cy.get('[data-element="confirmed-selections"]') + .children() + .should("have.length", 0); + }); + }); + + it("should not add an empty Pill when filter text does not match option text", () => { + CypressMountWithProviders(); + + multiSelectPill().should("not.exist"); + commonDataElementInputPreview().type("abc"); + selectInput().realPress("Enter"); + multiSelectPill().should("not.exist"); + }); + describe("Accessibility tests for MultiSelect component", () => { it("should pass accessibilty tests for MultiSelect", () => { CypressMountWithProviders(); diff --git a/cypress/components/select/simple-select/simple-select.cy.tsx b/cypress/components/select/simple-select/simple-select.cy.tsx index 7a96cd582f..2222149372 100644 --- a/cypress/components/select/simple-select/simple-select.cy.tsx +++ b/cypress/components/select/simple-select/simple-select.cy.tsx @@ -1007,6 +1007,80 @@ context("Tests for SimpleSelect component", () => { }); }); + describe("selection confirmed", () => { + it("is set on the event when options are clicked", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectListText("One").click(); + cy.get('[data-element="confirmed-selection-1"]').should("exist"); + dropdownButton().click(); + selectListText("Five").click(); + cy.get('[data-element="confirmed-selection-1"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-5"]').should("exist"); + dropdownButton().click(); + selectListText("Seven").click(); + cy.get('[data-element="confirmed-selection-5"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-7"]').should("exist"); + }); + + it("is set on the event when Enter key is pressed on an option using ArrowDown key to navigate", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-1"]').should("exist"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-1"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-3"]').should("exist"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-3"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-5"]').should("exist"); + selectInput().realPress("ArrowDown"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-5"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-6"]').should("exist"); + }); + + it("is set on the event when Enter key is pressed on an option using ArrowUp key to navigate", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-9"]').should("exist"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-9"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-7"]').should("exist"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-7"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-5"]').should("exist"); + selectInput().realPress("ArrowUp"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-5"]').should("not.exist"); + cy.get('[data-element="confirmed-selection-4"]').should("exist"); + }); + + it("is set on the event when Enter key is pressed on an option after using alpha key", () => { + CypressMountWithProviders(); + + dropdownButton().click(); + selectInput().type("t", { force: true }); + cy.get('[data-element="confirmed-selection-2"]').should("not.exist"); + selectInput().realPress("Enter"); + cy.get('[data-element="confirmed-selection-2"]').should("exist"); + }); + }); + describe("Accessibility tests for SimpleSelect component", () => { it("should pass accessibility tests for SimpleSelect", () => { CypressMountWithProviders(); diff --git a/cypress/components/split-button/split-button.cy.tsx b/cypress/components/split-button/split-button.cy.tsx index cccd269bb0..f285530a86 100644 --- a/cypress/components/split-button/split-button.cy.tsx +++ b/cypress/components/split-button/split-button.cy.tsx @@ -754,6 +754,15 @@ context("Tests for SplitButton component", () => { cy.checkAccessibility(); }); + // TODO: test passes even when it shouldn't, see FE-6267 + it("should pass accessibility tests for SplitButton Default story when open", () => { + CypressMountWithProviders(); + + splitToggleButton().eq(0).trigger("keydown", keyCode("downarrow")); + + cy.checkAccessibility(); + }); + it("should pass accessibility tests for SplitButton Default story when the additional buttons are opened", () => { CypressMountWithProviders(); diff --git a/cypress/components/textbox/textbox.cy.tsx b/cypress/components/textbox/textbox.cy.tsx deleted file mode 100644 index 61e98c0f72..0000000000 --- a/cypress/components/textbox/textbox.cy.tsx +++ /dev/null @@ -1,984 +0,0 @@ -/* eslint-disable no-unused-expressions, jest/valid-expect */ -import React from "react"; -import { TextboxProps } from "components/textbox"; -import * as stories from "../../../src/components/textbox/textbox-test.stories"; -import * as defaultStories from "../../../src/components/textbox/textbox.stories"; -import Box from "../../../src/components/box"; -import CypressMountWithProviders from "../../support/component-helper/cypress-mount"; -import { - verifyRequiredAsteriskForLabel, - checkGoldenOutline, - assertCssValueIsApproximately, -} from "../../support/component-helper/common-steps"; - -import { - getDataElementByValue, - tooltipPreview, - characterCount, - body, - getComponent, - cyRoot, - fieldHelpPreview, - getElement, -} from "../../locators"; -import { - CHARACTERS, - SIZE, - VALIDATION, -} from "../../support/component-helper/constants"; - -import { - textbox, - textboxInput, - textboxPrefix, - visuallyHiddenCharacterCount, - visuallyHiddenHint, -} from "../../locators/textbox"; - -import { keyCode } from "../../support/helper"; -import Button from "../../../src/components/button"; -import { ICON } from "../../locators/locators"; - -const testData = [CHARACTERS.DIACRITICS, CHARACTERS.SPECIALCHARACTERS]; -const keysToTrigger = ["Enter", "Space"] as const; - -const verifyOptional = (element: Cypress.Chainable>) => - element.then(($els) => { - // get Window reference from element - const win = $els[0].ownerDocument.defaultView; - // use getComputedStyle to read the pseudo selector - const after = win?.getComputedStyle($els[0], "after"); - // read the value of the `content` CSS property - const contentValue = after?.getPropertyValue("content"); - // the returned value will have double quotes around it, but this is correct - expect(contentValue).to.eq('"(optional)"'); - }); - -context("Tests for Textbox component", () => { - describe("check props for Textbox component", () => { - it.each([ - [SIZE.SMALL, "32px", "--sizing400"], - [SIZE.MEDIUM, "40px", "--sizing500"], - [SIZE.LARGE, "48px", "--sizing600"], - ] as [TextboxProps["size"], string, string][])( - "should use %s as size and render Textbox with %s as height", - (size, height, token) => { - CypressMountWithProviders(); - - textbox().should("have.css", "min-height", height); - textbox() - .getDesignTokensByCssProperty("min-height") - .should(($el) => { - expect($el[0]).to.equal(token); - }); - } - ); - - it.each([ - ["background", "--colorsUtilityYang100"], - ["border", "--colorsUtilityMajor300"], - ])("should check %s token for Textbox", (cssProp, token) => { - CypressMountWithProviders(); - - textbox() - .getDesignTokensByCssProperty(cssProp) - .should(($el) => { - expect($el[0]).to.equal(token); - }); - }); - - it("should render Textbox with isOptional prop", () => { - CypressMountWithProviders(); - - verifyOptional(getDataElementByValue("label").parent()); - }); - - it("should render Textbox with data-component prop", () => { - CypressMountWithProviders( - - ); - - getComponent(CHARACTERS.STANDARD).should("be.visible"); - }); - - it("should render Textbox with data-element prop", () => { - CypressMountWithProviders( - - ); - cyRoot() - .children() - .children() - .should("have.attr", "data-element", CHARACTERS.STANDARD); - }); - - it("should render Textbox with data-role prop", () => { - CypressMountWithProviders( - - ); - - cyRoot() - .children() - .children() - .should("have.attr", "data-role", CHARACTERS.STANDARD); - }); - - it("should render Textbox with id prop", () => { - CypressMountWithProviders( - - ); - - textboxInput().should("have.attr", "id", CHARACTERS.STANDARD); - }); - - it.each(testData)( - "should render Textbox with label prop set to %s", - (label) => { - CypressMountWithProviders(); - - getDataElementByValue("label").should("have.text", label); - } - ); - - it("should render Textbox with labelInline prop", () => { - CypressMountWithProviders(); - - getDataElementByValue("label") - .parent() - .should("have.css", "justify-content", "flex-end"); - }); - - it.each([ - ["left", "start"], - ["right", "end"], - ] as [TextboxProps["labelAlign"], string][])( - "should render Textbox with labelAlign prop set to %s", - (labelAlign, cssValue) => { - CypressMountWithProviders( - - ); - - getDataElementByValue("label") - .parent() - .should(($element) => - expect($element).to.have.css("justify-content", `flex-${cssValue}`) - ); - } - ); - - it.each(testData)( - "should render Textbox with labelHelp prop", - (labelHelp) => { - CypressMountWithProviders( - - ); - - getDataElementByValue("question").trigger("mouseover"); - tooltipPreview().should("have.text", labelHelp); - } - ); - - it.each([ - [1, "8px"], - [2, "16px"], - ] as [TextboxProps["labelSpacing"], string][])( - "should render Textbox with labelSpacing prop set to %s", - (spacing, padding) => { - CypressMountWithProviders( - - ); - - getDataElementByValue("label") - .parent() - .should("have.css", "padding-right", padding); - } - ); - - it.each([ - [10, 90, 135, 1229], - [30, 70, 409, 956], - [80, 20, 1092, 273], - ] as [TextboxProps["labelWidth"], TextboxProps["inputWidth"], number, number][])( - "should use %s as labelWidth, %s as inputWidth and render it with correct label and input width ratios", - (label, input, labelRatio, inputRatio) => { - CypressMountWithProviders( - - ); - - getDataElementByValue("label") - .parent() - .then(($el) => { - assertCssValueIsApproximately($el, "width", labelRatio); - }); - - getDataElementByValue("input") - .parent() - .then(($el) => { - assertCssValueIsApproximately($el, "width", inputRatio); - }); - } - ); - - it.each([ - ["foo", "exist"], - ["", "not.exist"], - ])( - "input hint should be conditionally rendered", - (inputHint, renderStatus) => { - CypressMountWithProviders( - - ); - - getDataElementByValue("input-hint").should(renderStatus); - } - ); - - it.each([ - [5, "exist"], - [null, "not.exist"], - ] as [TextboxProps["characterLimit"], string][])( - "character counter should be conditionally rendered", - (characterLimit, renderStatus) => { - CypressMountWithProviders( - - ); - - characterCount().should(renderStatus); - } - ); - - it.each([ - [5, "exist"], - [null, "not.exist"], - ] as [TextboxProps["characterLimit"], string][])( - "visually hidden character count should be conditionally rendered", - (characterLimit, renderStatus) => { - CypressMountWithProviders( - - ); - - visuallyHiddenCharacterCount().should(renderStatus); - } - ); - - it.each([ - [5, "exist"], - [null, "not.exist"], - ] as [TextboxProps["characterLimit"], string][])( - "visually hidden hint should be conditionally rendered", - (characterLimit, renderStatus) => { - CypressMountWithProviders( - - ); - - visuallyHiddenHint().should(renderStatus); - } - ); - - it.each(["10%", "30%", "50%", "80%", "100%"] as TextboxProps["maxWidth"][])( - "should check maxWidth as %s for TextBox component", - (maxWidth) => { - CypressMountWithProviders( - - ); - - getDataElementByValue("input") - .parent() - .parent() - .should("have.css", "max-width", maxWidth); - } - ); - - it("when maxWidth has no value it should render as 100%", () => { - CypressMountWithProviders(); - - getDataElementByValue("input") - .parent() - .parent() - .should("have.css", "max-width", "100%"); - }); - - it("should render Textbox with required prop", () => { - CypressMountWithProviders(); - - verifyRequiredAsteriskForLabel(); - }); - - it.each([ - [11, 11, "rgba(0, 0, 0, 0.55)"], - [11, 10, "rgb(203, 55, 74)"], - ])( - "should input %s characters and warn if over character limit of %s in Textbox", - (charactersUsed, limit, color) => { - const inputValue = "12345678901"; - const underCharacters = - limit - charactersUsed === 1 ? "character" : "characters"; - const overCharacters = - charactersUsed - limit === 1 ? "character" : "characters"; - - CypressMountWithProviders( - - ); - - textboxInput() - .type(inputValue) - .then(() => { - characterCount() - .should( - "have.text", - `${ - charactersUsed - limit - ? `${charactersUsed - limit} ${overCharacters} too many` - : `${charactersUsed - limit} ${underCharacters} left` - }` - ) - .and("have.css", "color", color); - - visuallyHiddenCharacterCount().should( - "have.text", - `${ - charactersUsed - limit - ? `${charactersUsed - limit} ${overCharacters} too many` - : `${charactersUsed - limit} ${underCharacters} left` - }` - ); - }); - } - ); - - it("should render Textbox with name prop", () => { - CypressMountWithProviders( - - ); - - textboxInput().should("have.attr", "name", CHARACTERS.STANDARD); - }); - - it("should render Textbox with disabled prop", () => { - CypressMountWithProviders(); - - textboxInput().should("be.disabled").and("have.attr", "disabled"); - }); - - it("should render Textbox icon with disabled style", () => { - CypressMountWithProviders( - - ); - - textbox() - .find(ICON) - .should("be.visible") - .and("have.css", "color", "rgba(0, 0, 0, 0.3)"); - }); - - it.each(testData)( - "should render Textbox with placeholder prop set to %s", - (placeholder) => { - CypressMountWithProviders( - - ); - - textboxInput().should("have.attr", "placeholder", placeholder); - } - ); - - it("should render Textbox with autoFocus prop and correct styling when focusRedesignOptOut is false", () => { - CypressMountWithProviders( - , - undefined, - undefined, - { - focusRedesignOptOut: false, - } - ); - - body().tab(); - textboxInput() - .should("be.focused") - .parent() - .should( - "have.css", - "box-shadow", - "rgb(255, 188, 25) 0px 0px 0px 3px, rgba(0, 0, 0, 0.9) 0px 0px 0px 6px" - ) - .and("have.css", "outline", "rgba(0, 0, 0, 0) solid 3px"); - }); - - it("should render Textbox with autoFocus prop and correct styling when focusRedesignOptOut is true", () => { - CypressMountWithProviders( - , - undefined, - undefined, - { - focusRedesignOptOut: true, - } - ); - - body().tab(); - textboxInput() - .should("be.focused") - .then(($el) => { - checkGoldenOutline($el.parent()); - }); - }); - - it("should render Textbox with readOnly prop", () => { - CypressMountWithProviders(); - - textboxInput().and("have.attr", "readOnly"); - }); - - it("should render Textbox icon with readOnly style", () => { - CypressMountWithProviders( - - ); - - textbox() - .find(ICON) - .should("be.visible") - .and("have.css", "color", "rgba(0, 0, 0, 0.3)"); - }); - - it.each(["error", "warning", "info"])( - "should verify Textbox is displayed with %s validation icon on input", - (type) => { - CypressMountWithProviders( - - ); - - textbox().find(ICON).should("have.attr", "data-element", type); - } - ); - - it.each(["error", "warning", "info"])( - "should verify Textbox is displayed with %s validation icon on label", - (type) => { - CypressMountWithProviders( - - ); - - getDataElementByValue("label") - .parent() - .find(ICON) - .should("have.attr", "data-element", type); - } - ); - - it.each([ - [VALIDATION.ERROR, "error", true], - [VALIDATION.WARNING, "warning", true], - [VALIDATION.INFO, "info", true], - ["rgb(102, 132, 148)", "error", false], - ["rgb(102, 132, 148)", "warning", false], - ["rgb(102, 132, 148)", "info", false], - ])( - "should verify Textbox input border colour is %s when validation is %s and boolean prop is %s", - (borderColor, type, bool) => { - CypressMountWithProviders( - - ); - - textbox().should("have.css", "border-bottom-color", borderColor); - } - ); - - it("should render Textbox with leftChildren prop", () => { - CypressMountWithProviders( - Test} /> - ); - - textbox() - .children() - .should("have.attr", "data-component", "button") - .and("be.visible"); - }); - - it.each(testData)( - "should render Textbox with prefix prop set to %s", - (prefix) => { - CypressMountWithProviders(); - - textboxPrefix() - .should("have.text", prefix) - .and("have.css", "font-size", "14px") - .and("have.css", "font-weight", "900") - .and("have.css", "margin-left", "12px"); - } - ); - - it("when prefix prop is set, and align is set to 'right', 'flex-direction' should be 'row'", () => { - CypressMountWithProviders( - - ); - - getDataElementByValue("input") - .parent() - .should("have.css", "flex-direction", "row"); - }); - - it.each([ - [true, "input", "label"], - [false, "label", "input"], - ])( - "should render Textbox with reverse prop set to %s", - (boolean, firstElement, secondElement) => { - CypressMountWithProviders( - - ); - - getDataElementByValue("label").parent().parent().as("startPoint"); - - cy.get("@startPoint") - .children() - .eq(0) - .find(firstElement) - .should("be.visible"); - cy.get("@startPoint") - .children() - .eq(1) - .find(secondElement) - .should("be.visible"); - } - ); - - it("should render Textbox with positionedChildren prop", () => { - CypressMountWithProviders( - Test} /> - ); - - textbox() - .parent() - .children() - .should("have.attr", "data-component", "button") - .and("be.visible"); - }); - - it.each([ - ["flex", 399], - ["flex", 400], - ["block", 401], - ] as [string, TextboxProps["adaptiveLabelBreakpoint"]][])( - "should check Textbox label alignment is %s with adaptiveLabelBreakpoint %s and viewport 400", - (displayValue, breakpoint) => { - cy.viewport(400, 300); - - CypressMountWithProviders( - - ); - - getDataElementByValue("label") - .parent() - .parent() - .should("have.css", "display", displayValue); - } - ); - - it("should render Textbox with labelId prop", () => { - CypressMountWithProviders( - - ); - - getDataElementByValue("label") - .should("have.attr", "id", `${CHARACTERS.STANDARD}_cy-label`) - .and("have.attr", "for", `${CHARACTERS.STANDARD}_cy`); - }); - - it.each(testData)( - "should render Textbox with fieldHelp prop set to %s", - (fieldHelp) => { - CypressMountWithProviders( - - ); - - fieldHelpPreview().should("have.text", fieldHelp); - } - ); - - it("should render Textbox with formattedValue prop", () => { - CypressMountWithProviders( - - ); - - textboxInput().should("have.attr", "value", CHARACTERS.STANDARD); - }); - - it.each(["add", "filter", "play"] as TextboxProps["inputIcon"][])( - "should render Textbox with inputIcon prop set to %s", - (icon) => { - CypressMountWithProviders( - - ); - - getElement(icon).and("be.visible"); - } - ); - - it("should render Textbox with iconTabIndex prop", () => { - CypressMountWithProviders( - - ); - - getDataElementByValue("add").parent().should("have.attr", "tabindex", 25); - }); - - it.each([ - "top", - "bottom", - "left", - "right", - ] as TextboxProps["tooltipPosition"][])( - "should render Textbox component with tooltip positioned to the %s", - (position) => { - CypressMountWithProviders( - - - - ); - getDataElementByValue("error").trigger("mouseover"); - tooltipPreview() - .should("have.text", CHARACTERS.STANDARD) - .should("have.attr", "data-placement", position); - } - ); - - it("should render Textbox with helpAriaLabel prop", () => { - CypressMountWithProviders( - - ); - - getComponent("help").should( - "have.attr", - "aria-label", - CHARACTERS.STANDARD - ); - }); - - it.each(["left", "right"] as TextboxProps["align"][])( - "should render Textbox with align prop set to %s", - (align) => { - CypressMountWithProviders(); - - textboxInput().should("have.css", "text-align", align); - } - ); - - it("should render Textbox with inputRef prop and focus the input via click on ref component", () => { - CypressMountWithProviders(); - - getComponent("button").click(); - textboxInput().should("be.focused"); - }); - - it.each(testData)( - "should input into Textbox and verify the value", - (input) => { - CypressMountWithProviders(); - - textboxInput().type(input).should("have.attr", "value", input); - } - ); - }); - - describe("check events for Textbox component", () => { - const inputValue = "1"; - - it("should call onChange callback when a type event is triggered", () => { - const callback: TextboxProps["onChange"] = cy.stub(); - - CypressMountWithProviders( - - ); - - textboxInput() - .type(inputValue) - .then(() => { - expect(callback).to.have.been.calledOnce; - }); - }); - - it("should call onBlur callback when a blur event is triggered", () => { - const callback: TextboxProps["onBlur"] = cy.stub(); - - CypressMountWithProviders(); - - textboxInput() - .click() - .blur() - .then(() => { - expect(callback).to.have.been.calledOnce; - }); - }); - - it("should call onClick callback when a click event is triggered", () => { - const callback: TextboxProps["onClick"] = cy.stub(); - - CypressMountWithProviders( - - ); - - textboxInput() - .click() - .then(() => { - expect(callback).to.have.been.calledOnce; - }); - }); - - it("should call onFocus callback when a focus event is triggered", () => { - const callback: TextboxProps["onFocus"] = cy.stub(); - - CypressMountWithProviders( - - ); - - textboxInput() - .focus() - .then(() => { - expect(callback).to.have.been.calledOnce; - }); - }); - - it("should call onMouseDown callback when a mousedown event is triggered", () => { - const callback: TextboxProps["onMouseDown"] = cy.stub(); - - CypressMountWithProviders( - - ); - - textboxInput() - .trigger("mousedown") - .then(() => { - expect(callback).to.have.been.calledOnce; - }); - }); - - it("should call iconOnMouseDown callback when a click event is triggered", () => { - const callback: TextboxProps["iconOnMouseDown"] = cy.stub(); - - CypressMountWithProviders( - - ); - - getComponent("icon") - .click() - .then(() => { - expect(callback).to.have.been.calledOnce; - }); - }); - - it("should call iconOnClick callback when a click event is triggered", () => { - const callback: TextboxProps["iconOnClick"] = cy.stub(); - - CypressMountWithProviders( - - ); - - getComponent("icon") - .click() - .then(() => { - expect(callback).to.have.been.calledOnce; - }); - }); - - it.each([keysToTrigger[0], keysToTrigger[1]])( - "should call iconOnClick callback when %s key is triggered", - (key) => { - const callback: TextboxProps["iconOnClick"] = cy.stub(); - - CypressMountWithProviders( - - ); - - getComponent("icon") - .trigger("keydown", { ...keyCode(key), force: true }) - .then(() => { - expect(callback).to.have.been.calledOnce; - }); - } - ); - - it.each([keysToTrigger[0], keysToTrigger[1]])( - "should call onKeyDown callback when %s key is triggered", - (key) => { - const callback: TextboxProps["onKeyDown"] = cy.stub(); - - CypressMountWithProviders( - - ); - - textboxInput() - .focus() - .trigger("keydown", { ...keyCode(key), force: true }) - .then(() => { - expect(callback).to.have.been.calledOnce; - }); - } - ); - }); - - describe("Accessibility tests for Textbox component", () => { - it("should pass accessibility tests for Textbox default story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox autoFocus story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox characterCounter story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox disabled story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox LabelAlign story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox Margins story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox NewDesignsValidation story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox Prefix story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox ReadOnly story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox Required story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox Sizes story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox ValidationsAsABoolean story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox ValidationsAsAString story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox ValidationsAsAStringDisplayedOnLabel story", () => { - CypressMountWithProviders( - - ); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox ValidationsAsAStringWithTooltipCustom story", () => { - CypressMountWithProviders( - - ); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox ValidationsAsAStringWithTooltipDefault story", () => { - CypressMountWithProviders( - - ); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox WithCustomLabelWidthAndInputWidth story", () => { - CypressMountWithProviders( - - ); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox WithCustomMaxWidth story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox WithFieldHelp story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox WithLabelHelp story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Textbox WithLabelInline story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - }); - - it("should have the expected border radius styling", () => { - CypressMountWithProviders(); - getElement("input").should("have.css", "border-radius", "4px"); - }); -}); diff --git a/cypress/components/toast/toast.cy.tsx b/cypress/components/toast/toast.cy.tsx index b3e9f053da..328da3e318 100644 --- a/cypress/components/toast/toast.cy.tsx +++ b/cypress/components/toast/toast.cy.tsx @@ -1,6 +1,9 @@ import React from "react"; import { ToastProps } from "components/toast"; -import { ToastComponent } from "../../../src/components/toast/toast-test.stories"; +import { + ToastComponent, + AllAlign, +} from "../../../src/components/toast/toast-test.stories"; import { TOAST_COLORS } from "../../../src/components/toast/toast.config"; import CypressMountWithProviders from "../../support/component-helper/cypress-mount"; import toastComponent from "../../locators/toast"; @@ -204,6 +207,19 @@ context("Testing Toast component", () => { .should("have.css", "justify-content", align); } ); + it.each(["top", "bottom"] as ToastProps["alignY"][])( + "should render Toast component alignY prop set to %s", + (alignY) => { + CypressMountWithProviders(); + + toastComponent() + .parent() + .parent() + .parent() + .parent() + .should("have.css", alignY, "0px"); + } + ); }); describe("check events for Toast component", () => { @@ -248,6 +264,11 @@ context("Testing Toast component", () => { cy.checkAccessibility(); }); + + it("should render Toast components with all align combinations and check accessibility", () => { + CypressMountWithProviders(); + cy.checkAccessibility(); + }); }); it("render with the expected border radius", () => { diff --git a/package-lock.json b/package-lock.json index 4e5e450dde..bc48fe16fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "carbon-react", - "version": "123.4.4", + "version": "123.7.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "carbon-react", - "version": "123.4.4", + "version": "123.7.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index 77362c5033..8b4ebf9e24 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "carbon-react", - "version": "123.4.4", + "version": "123.7.0", "description": "A library of reusable React components for easily building user interfaces.", "files": [ "lib", diff --git a/playwright/components/global-header/index.ts b/playwright/components/global-header/index.ts new file mode 100644 index 0000000000..03d3d90a4d --- /dev/null +++ b/playwright/components/global-header/index.ts @@ -0,0 +1,6 @@ +import { Page } from "playwright-core"; +import { GLOBAL_HEADER, GLOBAL_HEADER_LOGO_WRAPPER } from "./locators"; + +export const globalHeader = (page: Page) => page.locator(GLOBAL_HEADER); +export const globalHeaderLogo = (page: Page) => + page.locator(GLOBAL_HEADER_LOGO_WRAPPER).nth(0).first(); diff --git a/playwright/components/global-header/locators.ts b/playwright/components/global-header/locators.ts new file mode 100644 index 0000000000..82090117b6 --- /dev/null +++ b/playwright/components/global-header/locators.ts @@ -0,0 +1,4 @@ +// component preview locators +export const GLOBAL_HEADER = "[data-component='global-header']"; +export const GLOBAL_HEADER_LOGO_WRAPPER = + "[data-element='global-header-logo-wrapper']"; diff --git a/playwright/components/index.ts b/playwright/components/index.ts index 2949a58c58..82fb9eb01b 100644 --- a/playwright/components/index.ts +++ b/playwright/components/index.ts @@ -8,6 +8,7 @@ import { DLS_ROOT, FIELD_HELP_PREVIEW, LABEL, + COMMON_INPUT_CHARACTER_LIMIT, STICKY_FOOTER, COMMMON_DATA_ELEMENT_INPUT, PORTAL, @@ -30,6 +31,14 @@ export const button = (page: Page) => { return page.locator(BUTTON); }; +export const getDataComponentByValue = (page: Page, element: string) => { + return page.locator(`[data-component="${element}"]`); +}; + +export const getDataRoleByValue = (page: Page, element: string) => { + return page.locator(`[data-role="${element}"]`); +}; + export const closeIconButton = (page: Page) => { return page.locator(CLOSE_ICON_BUTTON); }; @@ -70,6 +79,10 @@ export const label = (page: Page) => { return page.locator(LABEL); }; +export const characterLimit = (page: Page) => { + return page.locator(COMMON_INPUT_CHARACTER_LIMIT); +}; + export const legendSpan = (page: Page) => { return page.locator("legend > span"); }; diff --git a/playwright/components/locators.ts b/playwright/components/locators.ts index 40d67c7e82..8c0cf4c454 100644 --- a/playwright/components/locators.ts +++ b/playwright/components/locators.ts @@ -18,7 +18,7 @@ export const COMMON_INPUT = ".common-input__"; export const BUTTON = 'button[type="button"]'; export const COMMON_INPUT_PREFIX = '[data-element="textbox-prefix"]'; export const COMMON_INPUT_CHARACTER_LIMIT = - 'div[data-element="character-limit"]'; + 'div[data-element="character-count"]'; export const PORTAL = ".carbon-portal"; export const LEGEND = "legend"; export const STICKY_FOOTER = '[data-component="sticky-footer"]'; diff --git a/playwright/components/textbox/index.ts b/playwright/components/textbox/index.ts index 1e22797c51..822515f65c 100644 --- a/playwright/components/textbox/index.ts +++ b/playwright/components/textbox/index.ts @@ -16,5 +16,5 @@ export const textboxPrefix = (page: Page) => { }; export const textboxInput = (page: Page) => { - return page.locator(TEXTBOX).locator("div").first(); + return page.locator(TEXTBOX).locator("input"); }; diff --git a/src/components/button/button.component.tsx b/src/components/button/button.component.tsx index 7572ee4907..e82fbf9d06 100644 --- a/src/components/button/button.component.tsx +++ b/src/components/button/button.component.tsx @@ -293,7 +293,7 @@ const Button = React.forwardRef( buttonType={buttonType} disabled={disabled} destructive={destructive} - role={inSplitButton ? "menu-item" : "button"} + role={inSplitButton ? "menuitem" : "button"} type={href ? undefined : "button"} iconType={iconType} size={size} diff --git a/src/components/global-header/component.test-pw.tsx b/src/components/global-header/component.test-pw.tsx new file mode 100644 index 0000000000..c4e9f3b9b4 --- /dev/null +++ b/src/components/global-header/component.test-pw.tsx @@ -0,0 +1,83 @@ +import React, { useState, useEffect } from "react"; +import carbonlogo from "../../../logo/carbon-logo.png"; +import GlobalHeader, { GlobalHeaderProps } from "./global-header.component"; +import { Menu, MenuItem, MenuDivider } from "../menu"; +import NavigationBar from "../navigation-bar"; + +export const FullMenuExample = () => ( + <> + + + + Product A + + + Child Item 1 + Child Item 2 + Child Item 3 + + + Child Item + + + + + + Menu Item One + + Menu Item Two + + + Item Submenu One + Item Submenu Two + + + Item Submenu Three + + Item Submenu Four + + + Item Submenu One + Item Submenu Two + + + + +); + +export const GlobalHeaderWithErrorHandler = ({ + ...props +}: GlobalHeaderProps) => { + const [error, setError] = useState(""); + useEffect(() => { + const handleError = (e: ErrorEvent) => { + setError(e.message); + }; + window.addEventListener("error", handleError); + + return () => window.removeEventListener("error", handleError); + }); + return ( + <> + +
{error}
+ + ); +}; + +export const GlobalHeaderWithLogo = () => { + const logoHeight = 41; + const logo = ( + Carbon logo + ); + return ( + <> + Example + + ); +}; diff --git a/src/components/global-header/global-header-test.stories.tsx b/src/components/global-header/global-header-test.stories.tsx index bcff7fd3a5..1d0cab8c68 100644 --- a/src/components/global-header/global-header-test.stories.tsx +++ b/src/components/global-header/global-header-test.stories.tsx @@ -1,10 +1,9 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { ComponentMeta, ComponentStory } from "@storybook/react"; -import GlobalHeader, { GlobalHeaderProps } from "./global-header.component"; -import { Menu, MenuItem, MenuDivider } from "../menu"; +import GlobalHeader from "./global-header.component"; +import { Menu, MenuItem } from "../menu"; import VerticalDivider from "../vertical-divider"; -import NavigationBar from "../navigation-bar"; import carbonLogo from "../../../logo/carbon-logo.png"; export default { @@ -55,64 +54,3 @@ MenuWithIconOnlyButtonsStory.parameters = { }, }, }; - -export const FullMenuExample = () => ( - <> - - - - Product A - - - Child Item 1 - Child Item 2 - Child Item 3 - - - Child Item - - - - - - Menu Item One - - Menu Item Two - - - Item Submenu One - Item Submenu Two - - - Item Submenu Three - - Item Submenu Four - - - Item Submenu One - Item Submenu Two - - - - -); - -export const GlobalHeaderWithErrorHandler = ({ - ...props -}: GlobalHeaderProps) => { - const [error, setError] = useState(""); - useEffect(() => { - const handleError = (e: ErrorEvent) => { - setError(e.message); - }; - window.addEventListener("error", handleError); - - return () => window.removeEventListener("error", handleError); - }); - return ( - <> - -
{error}
- - ); -}; diff --git a/src/components/global-header/global-header.pw.tsx b/src/components/global-header/global-header.pw.tsx new file mode 100644 index 0000000000..0a727ee4d1 --- /dev/null +++ b/src/components/global-header/global-header.pw.tsx @@ -0,0 +1,77 @@ +import React from "react"; +import { test, expect } from "@playwright/experimental-ct-react17"; +import { + FullMenuExample, + GlobalHeaderWithErrorHandler, + GlobalHeaderWithLogo, +} from "./component.test-pw"; +import navigationBar from "../../../playwright/components/navigation-bar"; +import { + globalHeader, + globalHeaderLogo, +} from "../../../playwright/components/global-header"; +import { checkAccessibility } from "../../../playwright/support/helper"; + +test.describe("Global Header component", () => { + test("should render without causing a ResiveObserver related error", async ({ + mount, + page, + }) => { + await mount(); + + await page.waitForTimeout(500); + + const errorState = page.locator("#error-div"); + await expect(errorState).toHaveText(""); + }); + + test("should render with z-index of component greater than z-index of NavigationBar", async ({ + mount, + page, + }) => { + await mount(); + + const globalHeaderZIndex = await globalHeader(page).evaluate((element) => { + const style = getComputedStyle(element); + return style.zIndex; + }); + + const navigationBarZIndex = await navigationBar(page).evaluate( + (element) => { + const style = getComputedStyle(element); + return style.zIndex; + } + ); + + const globalIndex = parseInt(globalHeaderZIndex.toString()); + const NavIndex = parseInt(navigationBarZIndex.toString()); + + expect(globalIndex).toBeGreaterThan(NavIndex); + }); + + test("should render with height of logo element never exceeding the max height of the component when logo prop is passed", async ({ + mount, + page, + }) => { + await mount(); + + await expect(globalHeaderLogo(page)).toHaveCSS("height", `40px`); + }); + + test.describe("Accessibility tests", () => { + test("should pass tests for FullMenuExample", async ({ mount, page }) => { + await mount(); + + await checkAccessibility(page); + }); + + test("should pass tests for GlobalHeaderWithLogo example", async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); +}); diff --git a/src/components/icon/__snapshots__/icon-unicodes.spec.js.snap b/src/components/icon/__snapshots__/icon-unicodes.spec.js.snap index f3b89dec12..83d5363489 100644 --- a/src/components/icon/__snapshots__/icon-unicodes.spec.js.snap +++ b/src/components/icon/__snapshots__/icon-unicodes.spec.js.snap @@ -85,12 +85,14 @@ Object { "circles_connection": "\\\\e979", "clock": "\\\\e919", "close": "\\\\e91e", + "cloud_co2": "\\\\f060", "coins": "\\\\e96a", "collaborate": "\\\\e946", "computer_clock": "\\\\e997", "connect": "\\\\e955", "connect_off": "\\\\f053", "construction": "\\\\f008", + "contact_card": "\\\\f059", "contacts": "\\\\e93b", "copy": "\\\\e91b", "create": "\\\\e940", @@ -166,12 +168,14 @@ Object { "job_seeked": "\\\\f037", "key": "\\\\e92b", "laptop": "\\\\f012", + "leaf": "\\\\f061", "ledger": "\\\\e973", "ledger_arrow_left": "\\\\e971", "ledger_arrow_right": "\\\\e972", "lightbulb_off": "\\\\e95c", "lightbulb_on": "\\\\e95d", "like": "\\\\f056", + "like_no": "\\\\f058", "link": "\\\\e92d", "link_cloud": "\\\\f036", "link_on": "\\\\f043", diff --git a/src/components/icon/fonts/carbon-icons-webfont.woff b/src/components/icon/fonts/carbon-icons-webfont.woff index 972be7184c..8d1d40d862 100644 Binary files a/src/components/icon/fonts/carbon-icons-webfont.woff and b/src/components/icon/fonts/carbon-icons-webfont.woff differ diff --git a/src/components/icon/icon-config.ts b/src/components/icon/icon-config.ts index c858802323..b109d1dd2b 100644 --- a/src/components/icon/icon-config.ts +++ b/src/components/icon/icon-config.ts @@ -131,6 +131,7 @@ export const ICONS: IconType[] = [ "circles_connection", "clock", "close", + "cloud_co2", "coins", "collaborate", "computer_clock", @@ -138,6 +139,7 @@ export const ICONS: IconType[] = [ "connect_off", "construction", "contacts", + "contact_card", "copy", "create", "credit_card", @@ -212,12 +214,14 @@ export const ICONS: IconType[] = [ "job_seeked", "key", "laptop", + "leaf", "ledger", "ledger_arrow_left", "ledger_arrow_right", "lightbulb_off", "lightbulb_on", "like", + "like_no", "link", "link_cloud", "link_on", diff --git a/src/components/icon/icon-type.ts b/src/components/icon/icon-type.ts index 390769abe4..1604d0badb 100644 --- a/src/components/icon/icon-type.ts +++ b/src/components/icon/icon-type.ts @@ -82,6 +82,7 @@ export type IconType = | "circles_connection" | "clock" | "close" + | "cloud_co2" | "coins" | "collaborate" | "computer_clock" @@ -89,6 +90,7 @@ export type IconType = | "connect_off" | "construction" | "contacts" + | "contact_card" | "copy" | "create" | "credit_card" @@ -163,10 +165,12 @@ export type IconType = | "job_seeked" | "key" | "laptop" + | "leaf" | "ledger" | "ledger_arrow_left" | "ledger_arrow_right" | "like" + | "like_no" | "link" | "lightbulb_off" | "lightbulb_on" diff --git a/src/components/icon/icon-unicodes.js b/src/components/icon/icon-unicodes.js index a5512c5137..b6394ecdb0 100644 --- a/src/components/icon/icon-unicodes.js +++ b/src/components/icon/icon-unicodes.js @@ -250,6 +250,10 @@ const misc = { volunteering: "\\f039", website: "\\f051", welfare: "\\f034", + contact_card: "\\f059", + cloud_co2: "\\f060", + leaf: "\\f061", + like_no: "\\f058", }; const legacyNames = { diff --git a/src/components/select/filterable-select/filterable-select-test.stories.tsx b/src/components/select/filterable-select/filterable-select-test.stories.tsx index 4953773c01..e5aace0979 100644 --- a/src/components/select/filterable-select/filterable-select-test.stories.tsx +++ b/src/components/select/filterable-select/filterable-select-test.stories.tsx @@ -1,6 +1,11 @@ -import React, { useState } from "react"; +import React, { useState, useRef } from "react"; import partialAction from "../../../../.storybook/utils/partial-action"; -import { FilterableSelect, Option, FilterableSelectProps } from ".."; +import { + FilterableSelect, + Option, + FilterableSelectProps, + CustomSelectChangeEvent, +} from ".."; import OptionRow from "../option-row/option-row.component"; import Dialog from "../../dialog"; import Button from "../../button"; @@ -8,6 +13,7 @@ import Button from "../../button"; export default { component: FilterableSelect, title: "Select/Filterable/Test", + excludeStories: ["SelectionConfirmed"], parameters: { info: { disable: true }, chromatic: { @@ -49,7 +55,7 @@ DefaultStory.storyName = "default"; export const FilterableSelectComponent = ( props: Partial ) => { - const [value, setValue] = React.useState(""); + const [value, setValue] = useState(""); function onChangeHandler(event: React.ChangeEvent) { setValue(event.target.value); @@ -87,9 +93,9 @@ export const FilterableSelectComponent = ( export const FilterableSelectWithLazyLoadingComponent = ( props: Partial ) => { - const preventLoading = React.useRef(false); - const [value, setValue] = React.useState("black"); - const [isLoading, setIsLoading] = React.useState(true); + const preventLoading = useRef(false); + const [value, setValue] = useState("black"); + const [isLoading, setIsLoading] = useState(true); const asyncList = [