From 2c691011688c5186a7d20f8af7e6acd768e5a0e8 Mon Sep 17 00:00:00 2001 From: DipperTheDan Date: Wed, 25 Oct 2023 16:07:36 +0100 Subject: [PATCH 01/10] feat(confirm): make data tag props available on cancel and confirm buttons This enhancement makes `data-component`, `data-role` and `data-element` tags available on the cancel and confirm buttons in this component fixes #6374 --- src/components/confirm/confirm.component.tsx | 17 +++++++++-- src/components/confirm/confirm.pw.tsx | 32 ++++++++++++++++++++ src/components/confirm/confirm.spec.tsx | 32 ++++++++++++++++++++ src/components/confirm/confirm.stories.mdx | 6 ++++ src/components/confirm/confirm.stories.tsx | 27 +++++++++++++++++ 5 files changed, 112 insertions(+), 2 deletions(-) diff --git a/src/components/confirm/confirm.component.tsx b/src/components/confirm/confirm.component.tsx index 76050757cd..6c5b35045c 100644 --- a/src/components/confirm/confirm.component.tsx +++ b/src/components/confirm/confirm.component.tsx @@ -8,6 +8,7 @@ import Button from "../button/button.component"; import Icon, { IconType } from "../icon"; import Loader from "../loader"; import useLocale from "../../hooks/__internal__/useLocale"; +import tagComponent, { TagProps } from "../../__internal__/utils/helpers/tags"; export interface ConfirmProps extends Omit< @@ -55,6 +56,10 @@ export interface ConfirmProps confirmButtonIconPosition?: "before" | "after"; /** Defines an Icon type within the confirm button (see Icon for options) */ confirmButtonIconType?: IconType; + /** Data tag prop bag for cancelButton */ + cancelButtonDataProps?: TagProps; + /** Data tag prop bag for confirmButton */ + confirmButtonDataProps?: TagProps; /** Makes cancel button disabled */ disableCancel?: boolean; /** Makes confirm button disabled */ @@ -81,6 +86,8 @@ export const Confirm = ({ cancelButtonIconPosition, confirmButtonIconType, confirmButtonIconPosition, + cancelButtonDataProps, + confirmButtonDataProps, cancelLabel, onCancel, disableCancel, @@ -122,12 +129,15 @@ export const Confirm = ({ ev: React.MouseEvent ) => void } - data-element="cancel" buttonType={cancelButtonType} destructive={cancelButtonDestructive} disabled={disableCancel} iconType={cancelButtonIconType} iconPosition={cancelButtonIconPosition} + {...tagComponent("cancel", { + "data-element": "cancel", + ...cancelButtonDataProps, + })} > {cancelLabel || l.confirm.no()} @@ -142,13 +152,16 @@ export const Confirm = ({ ev: React.MouseEvent ) => void } - data-element="confirm" buttonType={confirmButtonType} destructive={confirmButtonDestructive} disabled={isLoadingConfirm || disableConfirm} ml={2} iconType={confirmButtonIconType} iconPosition={confirmButtonIconPosition} + {...tagComponent("confirm", { + "data-element": "confirm", + ...confirmButtonDataProps, + })} > {isLoadingConfirm ? ( diff --git a/src/components/confirm/confirm.pw.tsx b/src/components/confirm/confirm.pw.tsx index aa02dbd6fe..6f3da3858a 100644 --- a/src/components/confirm/confirm.pw.tsx +++ b/src/components/confirm/confirm.pw.tsx @@ -495,6 +495,38 @@ test.describe("should render Confirm component", () => { "rgb(255, 188, 25) 0px 0px 0px 3px, rgba(0, 0, 0, 0.9) 0px 0px 0px 6px" ); }); + + test(`should check custom data tags are correctly applied to their respective buttons`, async ({ + mount, + page, + }) => { + await mount( + {}} + onConfirm={() => {}} + cancelButtonDataProps={{ + "data-element": "bang", + "data-role": "wallop", + }} + confirmButtonDataProps={{ + "data-element": "bar", + "data-role": "wiz", + }} + open + /> + ); + + const cancelButton = getDataElementByValue(page, "bang"); + const confirmButton = getDataElementByValue(page, "bar"); + + await expect(cancelButton).toHaveAttribute("data-component", "cancel"); + await expect(cancelButton).toHaveAttribute("data-element", "bang"); + await expect(cancelButton).toHaveAttribute("data-role", "wallop"); + + await expect(confirmButton).toHaveAttribute("data-component", "confirm"); + await expect(confirmButton).toHaveAttribute("data-element", "bar"); + await expect(confirmButton).toHaveAttribute("data-role", "wiz"); + }); }); test.describe("should render Confirm component for event tests", () => { diff --git a/src/components/confirm/confirm.spec.tsx b/src/components/confirm/confirm.spec.tsx index 434783f602..2d451b69b3 100644 --- a/src/components/confirm/confirm.spec.tsx +++ b/src/components/confirm/confirm.spec.tsx @@ -14,6 +14,7 @@ import IconButton from "../icon-button"; import StyledIconButton from "../icon-button/icon-button.style"; import { StyledDialog } from "../dialog/dialog.style"; import Logger from "../../__internal__/utils/logger"; +import { rootTagTest } from "../../__internal__/utils/helpers/tags/tags-specs"; // mock Logger.deprecate so that no console warnings occur while running the tests const loggerSpy = jest.spyOn(Logger, "deprecate"); @@ -490,4 +491,35 @@ describe("Confirm", () => { ) ); }); + + it("has proper data attributes applied to elements", () => { + wrapper = mount( + {}} + onConfirm={() => {}} + cancelButtonDataProps={{ + "data-element": "bang", + "data-role": "wallop", + }} + confirmButtonDataProps={{ + "data-element": "bar", + "data-role": "wiz", + }} + open + /> + ); + const cancelButton = wrapper + .find(Button) + .filter('[data-component="cancel"]') + .at(0); + + const confirmButton = wrapper + .find(Button) + .filter('[data-component="confirm"]') + .at(0); + + rootTagTest(cancelButton, "cancel", "bang", "wallop"); + rootTagTest(confirmButton, "confirm", "bar", "wiz"); + }); }); diff --git a/src/components/confirm/confirm.stories.mdx b/src/components/confirm/confirm.stories.mdx index 2d609af717..57eb29a0db 100644 --- a/src/components/confirm/confirm.stories.mdx +++ b/src/components/confirm/confirm.stories.mdx @@ -106,6 +106,12 @@ Allows to set variant which is supported in ` + setIsOpen(false)} + onCancel={() => setIsOpen(false)} + cancelButtonDataProps={{ + "data-element": "bang", + "data-role": "wallop", + }} + confirmButtonDataProps={{ + "data-element": "bar", + "data-role": "wiz", + }} + > + Content + + + ); +}; + export const SingleAction = () => { const [isOpen, setIsOpen] = useState(defaultOpenState); return ( From 208148b2d5009db64fa3ce4d8ad86250c860c523 Mon Sep 17 00:00:00 2001 From: DipperTheDan Date: Thu, 5 Oct 2023 11:49:58 +0100 Subject: [PATCH 02/10] feat(action-popover): disabled items can no longer be focused --- .../action-popover-item.component.tsx | 1 + .../action-popover-menu.component.tsx | 107 ++++++-- .../action-popover-test.stories.tsx | 100 ++++++- .../action-popover.component.tsx | 5 +- .../action-popover/action-popover.pw.tsx | 244 ++++++++++++++++-- .../action-popover/action-popover.spec.tsx | 158 +++++++++--- .../action-popover/action-popover.stories.mdx | 2 +- .../action-popover/action-popover.stories.tsx | 26 +- .../action-popover/action-popover.style.ts | 8 + .../action-popover/components.test-pw.tsx | 96 +++++++ 10 files changed, 665 insertions(+), 82 deletions(-) diff --git a/src/components/action-popover/action-popover-item/action-popover-item.component.tsx b/src/components/action-popover/action-popover-item/action-popover-item.component.tsx index 3ab0dde7f4..c599902df8 100644 --- a/src/components/action-popover/action-popover-item/action-popover-item.component.tsx +++ b/src/components/action-popover/action-popover-item/action-popover-item.component.tsx @@ -212,6 +212,7 @@ export const ActionPopoverItem = ({ } }, [alignSubmenu, submenu]); + // focuses item on opening of actionPopover submenu useEffect(() => { if (focusItem) { ref.current?.focus(); diff --git a/src/components/action-popover/action-popover-menu/action-popover-menu.component.tsx b/src/components/action-popover/action-popover-menu/action-popover-menu.component.tsx index 5a69efcd5a..0d54db4d98 100644 --- a/src/components/action-popover/action-popover-menu/action-popover-menu.component.tsx +++ b/src/components/action-popover/action-popover-menu/action-popover-menu.component.tsx @@ -1,4 +1,10 @@ -import React, { useCallback, useMemo, useContext, useState } from "react"; +import React, { + useCallback, + useMemo, + useContext, + useState, + useEffect, +} from "react"; import invariant from "invariant"; import { Menu } from "../action-popover.style"; @@ -87,8 +93,10 @@ const ActionPopoverMenu = React.forwardRef< } return ( - child.type !== ActionPopoverItem && - child.type !== ActionPopoverDivider + (child.type as React.FunctionComponent).displayName !== + ActionPopoverItem.displayName && + (child.type as React.FunctionComponent).displayName !== + ActionPopoverDivider.displayName ); } ); @@ -108,46 +116,100 @@ const ActionPopoverMenu = React.forwardRef< }); }, [children]); + const isItemDisabled = useCallback( + (value: number) => { + const item = items[value]; + // The invariant will be triggered before this else path can be explored, hence the ignore else. + // istanbul ignore else + return React.isValidElement(item) && item.props.disabled; + }, + [items] + ); + + const firstFocusableItem = items.findIndex( + (_, index) => !isItemDisabled(index) + ); + + // FIX-ME: FE-6248 + // Once we no longer support Node 16, this function can be removed and `findLastIndex()` can be used in it's place. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex + function findLastFocusableItem() { + let lastFocusableItem = -1; + for (let i = items.length - 1; i >= 0; i--) { + if (!isItemDisabled(i)) { + lastFocusableItem = i; + break; + } + } + return lastFocusableItem; + } + + const lastFocusableItem = findLastFocusableItem(); + + useEffect(() => { + if (isOpen && firstFocusableItem !== -1) + setFocusIndex(firstFocusableItem); + }, [isOpen, firstFocusableItem, setFocusIndex]); + const onKeyDown = useCallback( (e) => { if (Events.isTabKey(e)) { e.preventDefault(); - // TAB: close menu and allow focus to change to next focusable element + // TAB: close menu and allow focus to change to the next focusable element focusButton(); setOpen(false); } else if (Events.isDownKey(e)) { - // DOWN: focus next item or first + // DOWN: focus on the next item or first non-disabled item e.preventDefault(); e.stopPropagation(); - const indexValue = focusIndex < items.length - 1 ? focusIndex + 1 : 0; + let indexValue = focusIndex + 1; + while (indexValue < items.length && isItemDisabled(indexValue)) { + indexValue += 1; + } + if (indexValue >= items.length) { + indexValue = firstFocusableItem; + } setFocusIndex(indexValue); } else if (Events.isUpKey(e)) { - // UP: focus previous item or last + // UP: focus on the previous item or last non-disabled item e.preventDefault(); e.stopPropagation(); - const indexValue = focusIndex > 0 ? focusIndex - 1 : items.length - 1; + let indexValue = focusIndex - 1; + while ( + indexValue >= firstFocusableItem && + isItemDisabled(indexValue) + ) { + indexValue -= 1; + } + if (indexValue < firstFocusableItem) { + indexValue = lastFocusableItem; + } setFocusIndex(indexValue); } else if (Events.isHomeKey(e)) { - // HOME: focus first item + // HOME: focus on the first non-disabled item e.preventDefault(); e.stopPropagation(); - setFocusIndex(0); + const indexValue = firstFocusableItem; + setFocusIndex(indexValue); } else if (Events.isEndKey(e)) { - // END: focus last item + // END: focus on the last non-disabled item e.preventDefault(); e.stopPropagation(); - setFocusIndex(items.length - 1); + const indexValue = lastFocusableItem; + setFocusIndex(indexValue); } else if (e.key.length === 1) { - // any printable character: focus the next item on the list that starts with that character - // selection should wrap to the start of the list + // Any printable character: focus on the next non-disabled item on the list that starts with that character + // Selection should wrap to the start of the list e.stopPropagation(); let firstMatch: number | undefined; let nextMatch: number | undefined; - React.Children.forEach(items, (child, index) => { + items.forEach((item, index) => { if ( - React.isValidElement(child) && - child.props.children.toLowerCase().startsWith(e.key.toLowerCase()) + React.isValidElement(item) && + !isItemDisabled(index) && + item.props.children.toLowerCase().startsWith(e.key.toLowerCase()) ) { + // istanbul ignore else if (firstMatch === undefined) { firstMatch = index; } @@ -164,7 +226,16 @@ const ActionPopoverMenu = React.forwardRef< } } }, - [focusButton, setOpen, focusIndex, items, setFocusIndex] + [ + focusButton, + setOpen, + focusIndex, + items, + isItemDisabled, + setFocusIndex, + firstFocusableItem, + lastFocusableItem, + ] ); const [childHasSubmenu, setChildHasSubmenu] = useState(false); diff --git a/src/components/action-popover/action-popover-test.stories.tsx b/src/components/action-popover/action-popover-test.stories.tsx index a3223d2078..a27f87a77e 100644 --- a/src/components/action-popover/action-popover-test.stories.tsx +++ b/src/components/action-popover/action-popover-test.stories.tsx @@ -63,7 +63,7 @@ export const Default = () => { First Name Last Name -   + Options @@ -177,7 +177,7 @@ export const ActionPopoverCustom = ({ ...props }) => { First Name Last Name -   + Options @@ -610,3 +610,99 @@ export const ActionPopoverPropsComponent = ( ); }; + +export const ActionPopoverPropsComponentWithFirstAndLastDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; + +export const ActionPopoverPropsComponentWithOnlyFirstAndLastNotDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; + +export const ActionPopoverPropsComponentWithOnlyFirstDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; + +export const ActionPopoverPropsComponentWithOnlyLastDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; + +export const ActionPopoverPropsComponentWithSomeDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; + +export const ActionPopoverPropsComponentAllDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; diff --git a/src/components/action-popover/action-popover.component.tsx b/src/components/action-popover/action-popover.component.tsx index 87db208b9a..3a66c5128e 100644 --- a/src/components/action-popover/action-popover.component.tsx +++ b/src/components/action-popover/action-popover.component.tsx @@ -199,9 +199,8 @@ export const ActionPopover = ({ // There will be multiple document click listeners but we cant prevent propagation because it will interfere with // other instances on the same page - const isInMenu = menu.current && menu.current.contains(target as Node); - const isInButton = - buttonRef.current && buttonRef.current.contains(target as Node); + const isInMenu = menu?.current?.contains(target as Node); + const isInButton = buttonRef?.current?.contains(target as Node); if (!isInMenu && !isInButton) { setOpen(false); diff --git a/src/components/action-popover/action-popover.pw.tsx b/src/components/action-popover/action-popover.pw.tsx index a1d14c8cbd..4b72de5319 100644 --- a/src/components/action-popover/action-popover.pw.tsx +++ b/src/components/action-popover/action-popover.pw.tsx @@ -63,6 +63,10 @@ import { ActionPopoverWithSubmenusAndSomeIcons, ActionPopoverWithVariableChildren, ActionPopoverWithRenderProp, + ActionPopoverPropsComponentAllDisabled, + ActionPopoverPropsComponentWithSomeDisabled, + ActionPopoverPropsComponentWithOnlyFirstAndLastNotDisabled, + ActionPopoverPropsComponentWithFirstAndLastDisabled, } from "../../../src/components/action-popover/components.test-pw"; const keyToTrigger = ["Enter", " ", "End", "ArrowDown", "ArrowUp"] as const; @@ -78,12 +82,11 @@ test.describe("check functionality for ActionPopover component", () => { }); ([ - [0, "Business"], - [1, "Email Invoice"], - [2, "Print Invoice"], - [3, "Download PDF"], - [4, "Download CSV"], - [5, "Delete"], + [0, "Email Invoice"], + [1, "Print Invoice"], + [2, "Download PDF"], + [3, "Download CSV"], + [4, "Delete"], ] as [number, string][]).forEach(([times, elementText]) => { test(`should be able to press downarrow ${times} times and get button ${elementText} focused`, async ({ mount, @@ -109,13 +112,13 @@ test.describe("check functionality for ActionPopover component", () => { ).first(); await actionPopoverButtonElement.press(key); const focusedElement = await page.locator("*:focus"); - await expect(focusedElement).toContainText("Business"); + await expect(focusedElement).toContainText("Email Invoice"); const actionPopoverElement = await actionPopover(page).first(); await expect(actionPopoverElement).toBeVisible(); }); }); - test("should focus the first element Business using Home key", async ({ + test("should focus the first element Email Invoice using Home key", async ({ mount, page, }) => { @@ -128,7 +131,7 @@ test.describe("check functionality for ActionPopover component", () => { } const focusedElement = await page.locator("*:focus"); await focusedElement.press("Home"); - await expect(focusedElement).toContainText("Business"); + await expect(focusedElement).toContainText("Email Invoice"); }); test("should focus the first sub menu 1 element using Home key", async ({ @@ -165,7 +168,7 @@ test.describe("check functionality for ActionPopover component", () => { }); }); - test("should focus the last sub menu 3 element using End keyboard key", async ({ + test("should focus the last sub menu 2 element using End keyboard key", async ({ mount, page, }) => { @@ -179,7 +182,7 @@ test.describe("check functionality for ActionPopover component", () => { const focusedElement = await page.locator("*:focus"); await focusedElement.press("ArrowLeft"); await focusedElement.press("End"); - await expect(focusedElement).toContainText("Sub Menu 3"); + await expect(focusedElement).toContainText("Sub Menu 2"); }); test("should close using Tab key", async ({ mount, page }) => { @@ -292,7 +295,6 @@ test.describe("check functionality for ActionPopover component", () => { ([ [subMenuOption[0], 0], [subMenuOption[1], 1], - [subMenuOption[2], 2], ] as [typeof subMenuOption[number], number][]).forEach( ([innerText, times]) => { test(`should focus ${innerText} element`, async ({ mount, page }) => { @@ -315,6 +317,28 @@ test.describe("check functionality for ActionPopover component", () => { } ); + ([[subMenuOption[2], 2]] as [typeof subMenuOption[number], number][]).forEach( + ([innerText, times]) => { + test(`should not focus ${innerText} element`, async ({ mount, page }) => { + await mount(); + const actionPopoverButtonElementEq0 = await actionPopoverButton( + page + ).nth(0); + await actionPopoverButtonElementEq0.click(); + for (let i = 0; i < 2; i++) { + const focusedElement = await page.locator("*:focus"); + await focusedElement.press("ArrowDown"); + } + const focusedElement = await page.locator("*:focus"); + await focusedElement.press("ArrowLeft"); + for (let i = 0; i < times; i++) { + await focusedElement.press("ArrowDown"); + } + await expect(focusedElement).not.toContainText(innerText); + }); + } + ); + ([ [subMenuOption[0], 0], [subMenuOption[1], 1], @@ -401,10 +425,7 @@ test.describe("check functionality for ActionPopover component", () => { } ); - ([ - [subMenuOption[0], 0], - [subMenuOption[1], 1], - ] as [typeof subMenuOption[number], number][]).forEach(([name, item]) => { + [[subMenuOption[0]], [subMenuOption[1]]].forEach(([name]) => { test(`should close ${name} and ActionPopover after clicking on the submenu`, async ({ mount, page, @@ -421,8 +442,7 @@ test.describe("check functionality for ActionPopover component", () => { for (let i = 0; i < 2; i++) { await focusedElement.press("ArrowDown"); } - const submenuItem = await actionPopoverSubmenu(page, item); - await submenuItem.click(); + await focusedElement.click(); const actionPopoverElement = await actionPopover(page).first(); await expect(actionPopoverElement).not.toBeVisible(); }); @@ -522,6 +542,190 @@ test.describe("check functionality for ActionPopover component", () => { ); }); }); + + ([ + [0, "Item 2"], + [1, "Item 3"], + [2, "Item 4"], + [3, "Item 5"], + [4, "Item 6"], + ] as [number, string][]).forEach(([times, elementText]) => { + test(`should be able to press downarrow ${times} times and get button ${elementText} focused when first and last items are disabled`, async ({ + mount, + page, + }) => { + await mount(); + + const actionPopoverButtonElement = await actionPopoverButton(page).nth(0); + await actionPopoverButtonElement.click(); + for (let i = 0; i < times; i++) { + const focusedElement = await page.locator("*:focus"); + await focusedElement.press("ArrowDown"); + } + const focusedElement = await page.locator("*:focus"); + await expect(focusedElement).toContainText(elementText); + }); + }); + + ([ + [0, "Item 2"], + [1, "Item 6"], + [2, "Item 5"], + [3, "Item 4"], + [4, "Item 3"], + ] as [number, string][]).forEach(([times, elementText]) => { + test(`should be able to press up ${times} times and get button ${elementText} focused when first and last items are disabled`, async ({ + mount, + page, + }) => { + await mount(); + + const actionPopoverButtonElement = await actionPopoverButton(page).nth(0); + await actionPopoverButtonElement.click(); + for (let i = 0; i < times; i++) { + const focusedElement = await page.locator("*:focus"); + await focusedElement.press("ArrowUp"); + } + const focusedElement = await page.locator("*:focus"); + await expect(focusedElement).toContainText(elementText); + }); + }); + + ([ + [0, "Item 1"], + [1, "Item 7"], + ] as [number, string][]).forEach(([times, elementText]) => { + test(`should be able to press downarrow ${times} times and get button ${elementText} focused when only the first and last items are not disabled`, async ({ + mount, + page, + }) => { + await mount( + + ); + + const actionPopoverButtonElement = await actionPopoverButton(page).nth(0); + await actionPopoverButtonElement.click(); + for (let i = 0; i < times; i++) { + const focusedElement = await page.locator("*:focus"); + await focusedElement.press("ArrowDown"); + } + const focusedElement = await page.locator("*:focus"); + await expect(focusedElement).toContainText(elementText); + }); + }); + + ([ + [0, "Item 1"], + [1, "Item 7"], + ] as [number, string][]).forEach(([times, elementText]) => { + test(`should be able to press up ${times} times and get button ${elementText} focused`, async ({ + mount, + page, + }) => { + await mount( + + ); + + const actionPopoverButtonElement = await actionPopoverButton(page).nth(0); + await actionPopoverButtonElement.click(); + for (let i = 0; i < times; i++) { + const focusedElement = await page.locator("*:focus"); + await focusedElement.press("ArrowUp"); + } + const focusedElement = await page.locator("*:focus"); + await expect(focusedElement).toContainText(elementText); + }); + }); + + ([ + [0, "Item 1"], + [1, "Item 4"], + [2, "Item 6"], + ] as [number, string][]).forEach(([times, elementText]) => { + test(`should be able to press downarrow ${times} times and get button ${elementText} focused when only a few items are disabled`, async ({ + mount, + page, + }) => { + await mount(); + + const actionPopoverButtonElement = await actionPopoverButton(page).nth(0); + await actionPopoverButtonElement.click(); + for (let i = 0; i < times; i++) { + const focusedElement = await page.locator("*:focus"); + await focusedElement.press("ArrowDown"); + } + const focusedElement = await page.locator("*:focus"); + await expect(focusedElement).toContainText(elementText); + }); + }); + + ([ + [0, "Item 1"], + [1, "Item 6"], + [2, "Item 4"], + ] as [number, string][]).forEach(([times, elementText]) => { + test(`should be able to press up ${times} times and get button ${elementText} focused when only a few items are disabled`, async ({ + mount, + page, + }) => { + await mount(); + + const actionPopoverButtonElement = await actionPopoverButton(page).nth(0); + await actionPopoverButtonElement.click(); + for (let i = 0; i < times; i++) { + const focusedElement = await page.locator("*:focus"); + await focusedElement.press("ArrowUp"); + } + const focusedElement = await page.locator("*:focus"); + await expect(focusedElement).toContainText(elementText); + }); + }); + + ([ + [1, "Item 2"], + [2, "Item 3"], + [3, "Item 4"], + [4, "Item 5"], + ] as [number, string][]).forEach(([times, elementText]) => { + test(`should be able to press down ${times} times and not get button ${elementText} focused when all items are disabled`, async ({ + mount, + page, + }) => { + await mount(); + + const actionPopoverButtonElement = await actionPopoverButton(page).nth(0); + await actionPopoverButtonElement.click(); + for (let i = 0; i < times; i++) { + const focusedElement = await page.locator("*:focus"); + await focusedElement.press("ArrowDown"); + } + const focusedElement = await page.locator("*:focus"); + await expect(focusedElement).not.toContainText(elementText); + }); + }); + + ([ + [1, "Item 2"], + [2, "Item 3"], + [3, "Item 4"], + [4, "Item 5"], + ] as [number, string][]).forEach(([times, elementText]) => { + test(`should be able to press up ${times} times and not get button ${elementText} focused when all items are disabled`, async ({ + mount, + page, + }) => { + await mount(); + + const actionPopoverButtonElement = await actionPopoverButton(page).nth(0); + await actionPopoverButtonElement.click(); + for (let i = 0; i < times; i++) { + const focusedElement = await page.locator("*:focus"); + await focusedElement.press("ArrowUp"); + } + const focusedElement = await page.locator("*:focus"); + await expect(focusedElement).not.toContainText(elementText); + }); + }); }); test.describe("check props for ActionPopover component", () => { @@ -1090,7 +1294,7 @@ test.describe("when focused", () => { "rgb(255, 188, 25) solid 3px" ); await actionPopoverButtonElement.click(); - const focusedItem = await actionPopoverInnerItem(page, 0); + const focusedItem = await actionPopoverInnerItem(page, 1); await expect(focusedItem).toHaveCSS( "outline", "rgb(255, 188, 25) solid 3px" @@ -1109,7 +1313,7 @@ test.describe("when focused", () => { "rgb(255, 188, 25) 0px 0px 0px 3px, rgba(0, 0, 0, 0.9) 0px 0px 0px 6px" ); await actionPopoverButtonElement.click(); - const focusedItem = await actionPopoverInnerItem(page, 0); + const focusedItem = await actionPopoverInnerItem(page, 1); await expect(focusedItem).toHaveCSS( "box-shadow", "rgb(255, 188, 25) 0px 0px 0px 3px, rgba(0, 0, 0, 0.9) 0px 0px 0px 6px" diff --git a/src/components/action-popover/action-popover.spec.tsx b/src/components/action-popover/action-popover.spec.tsx index 3dce97b470..d53faa9e95 100644 --- a/src/components/action-popover/action-popover.spec.tsx +++ b/src/components/action-popover/action-popover.spec.tsx @@ -409,6 +409,49 @@ describe("ActionPopover", () => { ); } + function renderActionPopoverWithMultipleDisabledItems( + props = {}, + renderer = mount + ) { + const defaultProps = { + children: [ + {}}> + Business + , + {}}> + Email Invoice + , + {}}> + Print Invoice + , + {}}> + Download PDF + , + {}}> + Download CSV + , + , + {}}> + Delete + , + {}}> + Return Home + , + null, + undefined, + ], + ...props, + }; + + renderer( + <> + + + + + ); + } + function getElements() { return { items: wrapper.find(ActionPopoverItem), @@ -683,10 +726,10 @@ describe("ActionPopover", () => { expect(stopPropagation).toHaveBeenCalled(); }); - it("Clicking focuses the first element", () => { + it("Clicking focuses the first focusable element", () => { const { items } = getElements(); - expect(items.at(0).find(StyledMenuItem)).toBeFocused(); + expect(items.at(1).find(StyledMenuItem)).toBeFocused(); }); it("Clicking closes the menu", () => { @@ -773,7 +816,7 @@ describe("ActionPopover", () => { ); it.each(["ArrowDown", "Space", "Enter"] as const)( - "Pressing %s key selects the first item", + "Pressing %s key selects the first focusable item", (key) => { render(); const { menubutton } = getElements(); @@ -781,17 +824,17 @@ describe("ActionPopover", () => { jest.runAllTimers(); const { items } = getElements(); - expect(items.first().find(StyledMenuItem)).toBeFocused(); + expect(items.at(1).find(StyledMenuItem)).toBeFocused(); } ); - it("Pressing UpArrow selects the last item", () => { + it("Pressing UpArrow selects the last focusable item", () => { render(); - const { menubutton } = getElements(); - simulate.keydown.pressArrowUp(menubutton); + openMenu(); + const { items } = getElements(); + simulate.keydown.pressArrowUp(items.at(1).find(StyledMenuItem).at(0)); jest.runAllTimers(); - const { items } = getElements(); expect(items.last().find(StyledMenuItem)).toBeFocused(); }); }); @@ -865,9 +908,6 @@ describe("ActionPopover", () => { "next", "", (items: ReactWrapper) => { - simulate.keydown.pressArrowDown( - items.first().find(StyledMenuItem).at(0) - ); jest.runAllTimers(); expect(items.at(1).find(StyledMenuItem)).toBeFocused(); @@ -888,8 +928,8 @@ describe("ActionPopover", () => { items.at(3).find(StyledMenuItem).at(0) ); jest.runAllTimers(); - expect(items.at(0).find(StyledMenuItem)).toBeFocused(); - // we're checking that we can focus the disabled item, this is intentional behaviour + // we're checking that we cannot focus the first disabled item + expect(items.at(0).find(StyledMenuItem)).not.toBeFocused(); expect(items.at(0).prop("disabled")).toBe(true); expect( items @@ -903,9 +943,9 @@ describe("ActionPopover", () => { [ "Down", "first", - "if the focus on the last item", + "if the focus on the last focusable item", (items: ReactWrapper) => { - simulate.keydown.pressEnd(items.first().find(StyledMenuItem).at(0)); + simulate.keydown.pressEnd(items.at(1).find(StyledMenuItem).at(0)); jest.runAllTimers(); expect(items.last().find(StyledMenuItem)).toBeFocused(); @@ -913,7 +953,7 @@ describe("ActionPopover", () => { items.last().find(StyledMenuItem).at(0) ); jest.runAllTimers(); - expect(items.first().find(StyledMenuItem)).toBeFocused(); + expect(items.at(1).find(StyledMenuItem)).toBeFocused(); }, ], [ @@ -921,9 +961,6 @@ describe("ActionPopover", () => { "previous", "", (items: ReactWrapper) => { - simulate.keydown.pressArrowDown( - items.first().find(StyledMenuItem).at(0) - ); simulate.keydown.pressArrowDown( items.at(1).find(StyledMenuItem).at(0) ); @@ -949,16 +986,17 @@ describe("ActionPopover", () => { items.at(1).find(StyledMenuItem).at(0) ); jest.runAllTimers(); - expect(items.first().find(StyledMenuItem)).toBeFocused(); + expect(items.at(3).find(StyledMenuItem)).toBeFocused(); + expect(items.first().find(StyledMenuItem)).not.toBeFocused(); }, ], [ "Up", "last", - "if the focus is on the first item", + "if the focus is on the first focusable item", (items: ReactWrapper) => { simulate.keydown.pressArrowUp( - items.first().find(StyledMenuItem).at(0) + items.at(1).find(StyledMenuItem).at(0) ); jest.runAllTimers(); expect(items.last().find(StyledMenuItem)).toBeFocused(); @@ -969,13 +1007,9 @@ describe("ActionPopover", () => { "first", "", (items: ReactWrapper) => { - simulate.keydown.pressArrowDown( - items.first().find(StyledMenuItem).at(0) - ); - simulate.keydown.pressHome(items.at(1)); - expect(items.first().find(StyledMenuItem)).toBeFocused(); + expect(items.at(1).find(StyledMenuItem)).toBeFocused(); }, ], [ @@ -1004,10 +1038,10 @@ describe("ActionPopover", () => { openMenu(); const { items } = getElements(); - simulate.keydown.pressSpace(items.at(0).find(StyledMenuItem)); + simulate.keydown.pressSpace(items.at(1).find(StyledMenuItem)); const { menu } = getElements(); - expect(items.at(0).find(StyledMenuItem)).toBeFocused(); + expect(items.at(1).find(StyledMenuItem)).toBeFocused(); expect(menu.exists()).toBe(true); }); @@ -1018,7 +1052,7 @@ describe("ActionPopover", () => { const { items } = getElements(); // moves to first element starting with P - simulate.keydown.pressP(items.first().find(StyledMenuItem).at(0)); + simulate.keydown.pressP(items.at(1).find(StyledMenuItem).at(0)); jest.runAllTimers(); expect(items.at(2).find(StyledMenuItem)).toBeFocused(); @@ -1028,10 +1062,11 @@ describe("ActionPopover", () => { expect(items.at(3).find(StyledMenuItem)).toBeFocused(); // moves to next element starting with D, it loops to the start - // we're checking that we can focus the disabled item, this is intentional behaviour simulate.keydown.pressD(items.at(3).find(StyledMenuItem).at(0)); jest.runAllTimers(); - expect(items.at(0).find(StyledMenuItem)).toBeFocused(); + expect(items.at(3).find(StyledMenuItem)).toBeFocused(); + // we're checking that we cannot focus the disabled item. + expect(items.at(0).find(StyledMenuItem)).not.toBeFocused(); expect(items.at(0).prop("disabled")).toBe(true); expect( items @@ -1042,9 +1077,9 @@ describe("ActionPopover", () => { ).toBe("true"); // does nothing when there are no matches - simulate.keydown.pressZ(items.at(0).find(StyledMenuItem).at(0)); + simulate.keydown.pressZ(items.at(3).find(StyledMenuItem).at(0)); jest.runAllTimers(); - expect(items.at(0).find(StyledMenuItem)).toBeFocused(); + expect(items.at(3).find(StyledMenuItem)).toBeFocused(); }); it("does nothing when a non printable character key is pressed", () => { @@ -1053,9 +1088,9 @@ describe("ActionPopover", () => { const { items } = getElements(); - items.at(0).simulate("keydown", { key: "F1" }); + items.at(1).simulate("keydown", { key: "F1" }); jest.runAllTimers(); - expect(items.at(0).find(StyledMenuItem)).toBeFocused(); + expect(items.at(1).find(StyledMenuItem)).toBeFocused(); }); }); }); @@ -2113,4 +2148,55 @@ describe("ActionPopover", () => { } ); }); + + describe("When ActionPopoverMenu contains multiple disabled items", () => { + it("should focus the next focusable item when down arrow is pressed", () => { + renderActionPopoverWithMultipleDisabledItems(); + openMenu(); + const { items } = getElements(); + + simulate.keydown.pressArrowDown(items.at(0).find(StyledMenuItem).at(0)); + jest.runAllTimers(); + + expect(items.at(3).find(StyledMenuItem)).toBeFocused(); + }); + + it("should focus the next focusable item when up arrow is pressed", () => { + renderActionPopoverWithMultipleDisabledItems(); + openMenu(); + const { items } = getElements(); + + simulate.keydown.pressArrowDown(items.at(0).find(StyledMenuItem).at(0)); + jest.runAllTimers(); + + expect(items.at(3).find(StyledMenuItem)).toBeFocused(); + + simulate.keydown.pressArrowUp(items.at(3).find(StyledMenuItem).at(0)); + jest.runAllTimers(); + + expect(items.at(0).find(StyledMenuItem)).toBeFocused(); + }); + + it("should focus the first focusable item when Home is pressed", () => { + renderActionPopoverWithMultipleDisabledItems(); + openMenu(); + const { items } = getElements(); + + simulate.keydown.pressHome(items.at(5).find(StyledMenuItem).at(0)); + jest.runAllTimers(); + + expect(items.at(0).find(StyledMenuItem)).toBeFocused(); + }); + + it("should focus the last focusable item when End is pressed", () => { + renderActionPopoverWithMultipleDisabledItems(); + openMenu(); + const { items } = getElements(); + + simulate.keydown.pressEnd(items.at(0).find(StyledMenuItem).at(0)); + jest.runAllTimers(); + + expect(items.at(5).find(StyledMenuItem)).toBeFocused(); + }); + }); }); diff --git a/src/components/action-popover/action-popover.stories.mdx b/src/components/action-popover/action-popover.stories.mdx index cada646c03..992ff7085a 100644 --- a/src/components/action-popover/action-popover.stories.mdx +++ b/src/components/action-popover/action-popover.stories.mdx @@ -83,7 +83,7 @@ Each item is clickable and represents an action to be taken on a given row. ### With disabled item -ActionPopoverItem's can be disabled and will not dispatch an action but can still be navigated with the keyboard. +ActionPopoverItem's can be disabled. These items are not clickable and can not be navigated to with the keyboard. diff --git a/src/components/action-popover/action-popover.stories.tsx b/src/components/action-popover/action-popover.stories.tsx index fb4bbfb35c..56e6ebd49e 100644 --- a/src/components/action-popover/action-popover.stories.tsx +++ b/src/components/action-popover/action-popover.stories.tsx @@ -121,13 +121,29 @@ export const ActionPopoverComponentDisabledItems: ComponentStory<
- {}}> + {}}> Email Invoice {}} icon="delete"> Delete + + {}} icon="add"> + Add + + {}} icon="delete"> + Delete + + {}} icon="tick"> + Tick + + {}} icon="delete"> + Delete + + {}} icon="none"> + None +
@@ -253,7 +269,10 @@ export const ActionPopoverComponentSubmenu: ComponentStory< onClick={() => {}} submenu={ - {}}>CSV + {}}> + CSV + + {}}>PDF {}}>PDF } @@ -261,6 +280,9 @@ export const ActionPopoverComponentSubmenu: ComponentStory< Print + {}} icon="add"> + Add + {}} icon="delete"> Delete diff --git a/src/components/action-popover/action-popover.style.ts b/src/components/action-popover/action-popover.style.ts index f329b28dcb..0af8e51c48 100644 --- a/src/components/action-popover/action-popover.style.ts +++ b/src/components/action-popover/action-popover.style.ts @@ -188,6 +188,14 @@ const StyledMenuItem = styled.button>` cursor: not-allowed; color: var(--colorsUtilityYin030); } + + :focus { + border: none; + outline: none; + -webkit-appearance: none; + -webkit-box-shadow: none; + box-shadow: none; + } `} ${({ isDisabled }) => diff --git a/src/components/action-popover/components.test-pw.tsx b/src/components/action-popover/components.test-pw.tsx index bfddac14a4..3263e6149e 100644 --- a/src/components/action-popover/components.test-pw.tsx +++ b/src/components/action-popover/components.test-pw.tsx @@ -507,3 +507,99 @@ export const ActionPopoverPropsComponent = (
); }; + +export const ActionPopoverPropsComponentWithFirstAndLastDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; + +export const ActionPopoverPropsComponentWithOnlyFirstAndLastNotDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; + +export const ActionPopoverPropsComponentWithOnlyFirstDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; + +export const ActionPopoverPropsComponentWithOnlyLastDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; + +export const ActionPopoverPropsComponentWithSomeDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; + +export const ActionPopoverPropsComponentAllDisabled = ( + props: Partial +) => { + return ( + + Item 1 + Item 2 + Item 3 + Item 4 + Item 5 + Item 6 + Item 7 + + ); +}; From 312b4c95cf2093d257eb017efcac399d4abe4dd5 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 27 Oct 2023 13:53:49 +0000 Subject: [PATCH 03/10] chore(release): 123.1.0 ## [123.1.0](https://github.com/Sage/carbon/compare/v123.0.1...v123.1.0) (2023-10-27) ### Features * **action-popover:** disabled items can no longer be focused ([208148b](https://github.com/Sage/carbon/commit/208148b2d5009db64fa3ce4d8ad86250c860c523)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5fc2c32e7..7318f48e5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [123.1.0](https://github.com/Sage/carbon/compare/v123.0.1...v123.1.0) (2023-10-27) + + +### Features + +* **action-popover:** disabled items can no longer be focused ([208148b](https://github.com/Sage/carbon/commit/208148b2d5009db64fa3ce4d8ad86250c860c523)) + ### [123.0.1](https://github.com/Sage/carbon/compare/v123.0.0...v123.0.1) (2023-10-27) diff --git a/package-lock.json b/package-lock.json index b88d14079b..0820e42b25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "carbon-react", - "version": "123.0.1", + "version": "123.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "carbon-react", - "version": "123.0.1", + "version": "123.1.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/package.json b/package.json index 86267e357d..77aae2f621 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "carbon-react", - "version": "123.0.1", + "version": "123.1.0", "description": "A library of reusable React components for easily building user interfaces.", "files": [ "lib", From ab1c49df2050938ad8cdf12e4476bc734483625b Mon Sep 17 00:00:00 2001 From: DipperTheDan Date: Fri, 27 Oct 2023 15:21:13 +0100 Subject: [PATCH 04/10] chore: address various spelling mistakes and some code formatting --- cypress/components/flat-table/flat-table.cy.tsx | 4 ++-- .../grouped-character/grouped-character.cy.tsx | 2 +- cypress/components/menu/menu.cy.tsx | 8 ++++---- .../multi-action-button.cy.tsx | 4 ++-- cypress/components/number/number.cy.tsx | 2 +- cypress/components/pager/pager.cy.tsx | 6 +++--- cypress/components/password/password.cy.tsx | 2 +- cypress/components/switch/switch.cy.tsx | 2 +- cypress/components/tooltip/tooltip.cy.tsx | 16 ++++++++-------- .../vertical-menu/vertical-menu.cy.tsx | 16 ++++++++-------- playwright/components/step-sequence/index.ts | 2 +- .../action-popover/action-popover.pw.tsx | 10 ++++++---- .../action-popover/action-popover.stories.tsx | 4 ++-- src/components/button-bar/button-bar.spec.tsx | 2 +- src/components/button-minor/button-minor.pw.tsx | 2 +- .../button-toggle/button-toggle.spec.tsx | 2 +- .../flat-table/flat-table-test.stories.tsx | 2 +- src/components/loader/loader.pw.tsx | 2 +- src/components/menu/menu.spec.tsx | 2 +- .../progress-tracker/progress-tracker.spec.tsx | 2 +- src/components/text-editor/text-editor.spec.tsx | 2 +- src/components/tooltip/tooltip.stories.mdx | 2 +- src/components/tooltip/tooltip.stories.tsx | 2 +- .../useCharacterCount/useCharacterCount.spec.tsx | 4 ++-- 24 files changed, 52 insertions(+), 50 deletions(-) diff --git a/cypress/components/flat-table/flat-table.cy.tsx b/cypress/components/flat-table/flat-table.cy.tsx index ace2d473ad..b729fca4f8 100644 --- a/cypress/components/flat-table/flat-table.cy.tsx +++ b/cypress/components/flat-table/flat-table.cy.tsx @@ -526,7 +526,7 @@ context("Tests for Flat Table component", () => { it.skip("should render Flat Table with multiple sticky row headers, stickyAlignment set to right", () => { cy.viewport(700, 700); - CypressMountWithProviders(); + CypressMountWithProviders(); flatTableBodyRowByPosition(1) .find("td") @@ -2932,7 +2932,7 @@ context("Tests for Flat Table component", () => { it.skip("should render Flat Table with multiple sticky row headers for accessibility tests", () => { cy.viewport(700, 700); - CypressMountWithProviders(); + CypressMountWithProviders(); cy.checkAccessibility(); }); diff --git a/cypress/components/grouped-character/grouped-character.cy.tsx b/cypress/components/grouped-character/grouped-character.cy.tsx index efbd8f72b3..d8febd130f 100644 --- a/cypress/components/grouped-character/grouped-character.cy.tsx +++ b/cypress/components/grouped-character/grouped-character.cy.tsx @@ -148,7 +148,7 @@ context("Tests for GroupedCharacter component", () => { ["right", "end"], ["left", "start"], ] as [GroupedCharacterProps["labelAlign"], string][])( - "should use %s as labelAligment and render it with %s as css properties", + "should use %s as labelAlignment and render it with %s as css properties", (alignment, cssProp) => { CypressMountWithProviders( diff --git a/cypress/components/menu/menu.cy.tsx b/cypress/components/menu/menu.cy.tsx index 519cf0c60d..dd9854e6ca 100644 --- a/cypress/components/menu/menu.cy.tsx +++ b/cypress/components/menu/menu.cy.tsx @@ -333,12 +333,12 @@ context("Testing Menu component", () => { searchDefaultInput().tab(); searchCrossIcon().parent().should("have.focus"); - const bouding = (element: JQuery) => { + const bounding = (element: JQuery) => { return element[0].getBoundingClientRect(); }; searchCrossIcon() - .then(($el) => bouding($el)) + .then(($el) => bounding($el)) .as("position"); cy.get("@position") @@ -627,7 +627,7 @@ context("Testing Menu component", () => { "center", "flex-start", "flex-end", - ])("should verify Menu alignItmes is %s", (alignment) => { + ])("should verify Menu alignItems is %s", (alignment) => { CypressMountWithProviders(); menu().should("have.css", "align-items", alignment); @@ -1501,7 +1501,7 @@ context("Testing Menu component", () => { "text-top", "top", ])( - "should pass accessibility tests for Menu when alignItmes is %s", + "should pass accessibility tests for Menu when alignItems is %s", (alignment) => { CypressMountWithProviders(); 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 94e8225019..0ec9430e69 100644 --- a/cypress/components/multi-action-button/multi-action-button.cy.tsx +++ b/cypress/components/multi-action-button/multi-action-button.cy.tsx @@ -229,7 +229,7 @@ context("Tests for MultiActionButton component", () => { }); }); - describe("user interactions with MultiActionutton", () => { + describe("user interactions with MultiActionButton", () => { describe("pressing ArrowUp while MultiActionButton is open", () => { it("should move focus to previous child button and should not loop to last button when first is focused", () => { CypressMountWithProviders( @@ -448,7 +448,7 @@ context("Tests for MultiActionButton component", () => { }); }); - describe("user interactions with MultiActionutton when wrapping the child buttons in a custom component", () => { + describe("user interactions with MultiActionButton when wrapping the child buttons in a custom component", () => { describe("pressing ArrowUp while MultiActionButton is open", () => { it("should move focus to previous child button and should not loop to last button when first is focused", () => { CypressMountWithProviders( diff --git a/cypress/components/number/number.cy.tsx b/cypress/components/number/number.cy.tsx index c071bb7c9b..33f472cd16 100644 --- a/cypress/components/number/number.cy.tsx +++ b/cypress/components/number/number.cy.tsx @@ -178,7 +178,7 @@ context("Tests for Number component", () => { ["right", "end"], ["left", "start"], ] as [NumberProps["labelAlign"], string][])( - "should use %s as labelAligment and render it with flex-%s as css properties", + "should use %s as labelAlignment and render it with flex-%s as css properties", (alignment, cssProp) => { CypressMountWithProviders( diff --git a/cypress/components/pager/pager.cy.tsx b/cypress/components/pager/pager.cy.tsx index a41fa68ba7..47de7e6729 100644 --- a/cypress/components/pager/pager.cy.tsx +++ b/cypress/components/pager/pager.cy.tsx @@ -323,7 +323,7 @@ context("Test for Pager component", () => { }); }); - describe("check funtionality for Pager component", () => { + describe("check functionality for Pager component", () => { it.each([-1, -10, -100, ...testData])( "should set totalRecords out of scope to %s", (totalRecords) => { @@ -379,14 +379,14 @@ context("Test for Pager component", () => { viewportWidth, showItemsAssertion, firstAndLastArrowsAssertion, - totalRecordsAssetion + totalRecordsAssertion ) => { cy.viewport(viewportWidth, 768); CypressMountWithProviders(); showLabelBefore().should(showItemsAssertion); - pagerSummary().should(totalRecordsAssetion); + pagerSummary().should(totalRecordsAssertion); firstArrow().should(firstAndLastArrowsAssertion); lastArrow().should(firstAndLastArrowsAssertion); nextArrow().should("be.visible"); diff --git a/cypress/components/password/password.cy.tsx b/cypress/components/password/password.cy.tsx index 0bd2de5821..9b5a8a0b08 100644 --- a/cypress/components/password/password.cy.tsx +++ b/cypress/components/password/password.cy.tsx @@ -305,7 +305,7 @@ context("Tests for Password component", () => { ["right", "end"], ["left", "start"], ] as [PasswordProps["labelAlign"], string][])( - "should use %s as labelAligment and render it with %s as css properties", + "should use %s as labelAlignment and render it with %s as css properties", (alignment, cssProp) => { CypressMountWithProviders( diff --git a/cypress/components/switch/switch.cy.tsx b/cypress/components/switch/switch.cy.tsx index 2934318169..5ce15ffbfd 100644 --- a/cypress/components/switch/switch.cy.tsx +++ b/cypress/components/switch/switch.cy.tsx @@ -314,7 +314,7 @@ context("Testing Switch component", () => { ["warning", VALIDATION.WARNING], ["info", VALIDATION.INFO], ])( - "verify Switch component is verifyed with appropriate border color for validations", + "verify Switch component is verified with appropriate border color for validations", (type, validation) => { CypressMountWithProviders(); diff --git a/cypress/components/tooltip/tooltip.cy.tsx b/cypress/components/tooltip/tooltip.cy.tsx index 8cea855472..b4c876db14 100644 --- a/cypress/components/tooltip/tooltip.cy.tsx +++ b/cypress/components/tooltip/tooltip.cy.tsx @@ -208,14 +208,14 @@ context("Tests for Tooltip component", () => { }); describe("Accessibility tests for Tooltip component", () => { - it("should pass accessibilty tests for Tooltip Default story", () => { + it("should pass accessibility tests for Tooltip Default story", () => { CypressMountWithProviders(); getDataElementByValue("main-text").click(); cy.checkAccessibility(); }); - it("should pass accessibilty tests for Tooltip Controlled story", () => { + it("should pass accessibility tests for Tooltip Controlled story", () => { CypressMountWithProviders(); getDataElementByValue("main-text").eq(0).click(); @@ -228,7 +228,7 @@ context("Tests for Tooltip component", () => { ["left", 2], ["right", 3], ])( - "should pass accessibilty tests for Tooltip Positioning story %s position", + "should pass accessibility tests for Tooltip Positioning story %s position", (position, button) => { CypressMountWithProviders(); @@ -237,20 +237,20 @@ context("Tests for Tooltip component", () => { } ); - it("should pass accessibilty tests for Tooltip FlipBehviourOverrides story", () => { - CypressMountWithProviders(); + it("should pass accessibility tests for Tooltip FlipBehaviourOverrides story", () => { + CypressMountWithProviders(); cy.checkAccessibility(); }); - it("should pass accessibilty tests for Tooltip LargeTooltip story", () => { + it("should pass accessibility tests for Tooltip LargeTooltip story", () => { CypressMountWithProviders(); getDataElementByValue("main-text").click(); cy.checkAccessibility(); }); - it("should pass accessibilty tests for Tooltip Types story", () => { + it("should pass accessibility tests for Tooltip Types story", () => { CypressMountWithProviders(); getDataElementByValue("main-text").eq(1).click(); @@ -259,7 +259,7 @@ context("Tests for Tooltip component", () => { cy.checkAccessibility(); }); - it("should pass accessibilty tests for Tooltip ColorOverrides story", () => { + it("should pass accessibility tests for Tooltip ColorOverrides story", () => { CypressMountWithProviders(); getDataElementByValue("main-text").click(); diff --git a/cypress/components/vertical-menu/vertical-menu.cy.tsx b/cypress/components/vertical-menu/vertical-menu.cy.tsx index 373ea6e42c..b756ae3961 100644 --- a/cypress/components/vertical-menu/vertical-menu.cy.tsx +++ b/cypress/components/vertical-menu/vertical-menu.cy.tsx @@ -487,20 +487,20 @@ context("Testing Vertical Menu component", () => { }); describe("should check the accessibility tests", () => { - it("should check accessiblity for verticalMenuComponent", () => { + it("should check accessibility for verticalMenuComponent", () => { CypressMountWithProviders(); cy.checkAccessibility(); }); - it("should check accessiblity for verticalMenuComponent open", () => { + it("should check accessibility for verticalMenuComponent open", () => { CypressMountWithProviders(); verticalMenuItem().tab().tab().click(); cy.checkAccessibility(); }); - it("should check accessiblity for verticalMenuComponent Active", () => { + it("should check accessibility for verticalMenuComponent Active", () => { CypressMountWithProviders( !isOpen} /> ); @@ -508,32 +508,32 @@ context("Testing Vertical Menu component", () => { cy.checkAccessibility(); }); - it("should check accessiblity for verticalMenuComponent Adornment", () => { + it("should check accessibility for verticalMenuComponent Adornment", () => { CypressMountWithProviders(); cy.checkAccessibility(); }); - it("should check accessiblity for verticalMenuComponent CustomItemHeight", () => { + it("should check accessibility for verticalMenuComponent CustomItemHeight", () => { CypressMountWithProviders(); cy.checkAccessibility(); }); - it("should check accessiblity for verticalMenuComponent CustomItemPadding", () => { + it("should check accessibility for verticalMenuComponent CustomItemPadding", () => { CypressMountWithProviders(); cy.checkAccessibility(); }); - it("should check accessiblity for verticalMenuComponent FullScreen", () => { + it("should check accessibility for verticalMenuComponent FullScreen", () => { cy.viewport(320, 599); CypressMountWithProviders(); cy.checkAccessibility(); }); - it("should check accessiblity for verticalMenuComponent FullScreen open", () => { + it("should check accessibility for verticalMenuComponent FullScreen open", () => { cy.viewport(320, 599); CypressMountWithProviders(); diff --git a/playwright/components/step-sequence/index.ts b/playwright/components/step-sequence/index.ts index 671aa7bb5b..3607b1b76b 100644 --- a/playwright/components/step-sequence/index.ts +++ b/playwright/components/step-sequence/index.ts @@ -11,6 +11,6 @@ export const stepSequenceItemIndicator = (page: Page) => export const stepSequenceDataComponentItem = (page: Page) => page.locator(STEP_SEQUENCE_DATA_COMPONENT_ITEM); - + export const stepSequenceDataComponent = (page: Page) => page.locator(STEP_SEQUENCE_DATA_COMPONENT); diff --git a/src/components/action-popover/action-popover.pw.tsx b/src/components/action-popover/action-popover.pw.tsx index a1d14c8cbd..639a93d0b0 100644 --- a/src/components/action-popover/action-popover.pw.tsx +++ b/src/components/action-popover/action-popover.pw.tsx @@ -38,8 +38,8 @@ import { ActionPopoverComponentIcons, ActionPopoverComponentInFlatTable, ActionPopoverComponentInOverflowHiddenContainer, - ActionPopoverComponentKeyboardNaviationLeftAlignedSubmenu, - ActionPopoverComponentKeyboardNaviationRightAlignedSubmenu, + ActionPopoverComponentKeyboardNavigationLeftAlignedSubmenu, + ActionPopoverComponentKeyboardNavigationRightAlignedSubmenu, ActionPopoverComponentKeyboardNavigation, ActionPopoverComponentMenuOpeningAbove, ActionPopoverComponentMenuRightAligned, @@ -1272,7 +1272,7 @@ test.describe("Accessibility tests for ActionPopover", () => { mount, page, }) => { - await mount(); + await mount(); const actionPopoverButtonElement = await actionPopoverButton(page).nth(0); await actionPopoverButtonElement.click(); const submenuTrigger = await actionPopoverInnerItem(page, 0); @@ -1284,7 +1284,9 @@ test.describe("Accessibility tests for ActionPopover", () => { mount, page, }) => { - await mount(); + await mount( + + ); const actionPopoverButtonElement = await actionPopoverButton(page).nth(0); await actionPopoverButtonElement.click(); const submenuTrigger = await actionPopoverInnerItem(page, 0); diff --git a/src/components/action-popover/action-popover.stories.tsx b/src/components/action-popover/action-popover.stories.tsx index fb4bbfb35c..bfe39d6cf6 100644 --- a/src/components/action-popover/action-popover.stories.tsx +++ b/src/components/action-popover/action-popover.stories.tsx @@ -384,7 +384,7 @@ export const ActionPopoverComponentKeyboardNavigation: ComponentStory< ); }; -export const ActionPopoverComponentKeyboardNaviationLeftAlignedSubmenu: ComponentStory< +export const ActionPopoverComponentKeyboardNavigationLeftAlignedSubmenu: ComponentStory< typeof ActionPopover > = () => { return ( @@ -433,7 +433,7 @@ export const ActionPopoverComponentKeyboardNaviationLeftAlignedSubmenu: Componen ); }; -export const ActionPopoverComponentKeyboardNaviationRightAlignedSubmenu: ComponentStory< +export const ActionPopoverComponentKeyboardNavigationRightAlignedSubmenu: ComponentStory< typeof ActionPopover > = () => { return ( diff --git a/src/components/button-bar/button-bar.spec.tsx b/src/components/button-bar/button-bar.spec.tsx index 668004b6bf..0591c6083b 100644 --- a/src/components/button-bar/button-bar.spec.tsx +++ b/src/components/button-bar/button-bar.spec.tsx @@ -47,7 +47,7 @@ describe("Button Bar", () => { }); }); - describe("when props are passed to the compontent", () => { + describe("when props are passed to the component", () => { it("renders proper props and children", () => { const wrapper = renderButtonBar("Large", 3, { size: "large", diff --git a/src/components/button-minor/button-minor.pw.tsx b/src/components/button-minor/button-minor.pw.tsx index 17f47d9a0c..da504e4683 100644 --- a/src/components/button-minor/button-minor.pw.tsx +++ b/src/components/button-minor/button-minor.pw.tsx @@ -541,7 +541,7 @@ test.describe("accessibility tests", () => { await checkAccessibility(page); }); - test("should check accessibility for secondary destrictive Button Minor", async ({ + test("should check accessibility for secondary destructive Button Minor", async ({ mount, page, }) => { diff --git a/src/components/button-toggle/button-toggle.spec.tsx b/src/components/button-toggle/button-toggle.spec.tsx index 061990a31f..0b3c4d8f2b 100644 --- a/src/components/button-toggle/button-toggle.spec.tsx +++ b/src/components/button-toggle/button-toggle.spec.tsx @@ -317,7 +317,7 @@ describe("ButtonToggle", () => { ); - // Uses snapshot as jest/enzyme doesnt support :first-of-type + // Uses snapshot as jest/enzyme doesn't support :first-of-type expect(wrapper).toMatchSnapshot(); }); }); diff --git a/src/components/flat-table/flat-table-test.stories.tsx b/src/components/flat-table/flat-table-test.stories.tsx index 30f1304b15..92518beab2 100644 --- a/src/components/flat-table/flat-table-test.stories.tsx +++ b/src/components/flat-table/flat-table-test.stories.tsx @@ -893,7 +893,7 @@ export const FlatTableCellRowSpanComponent = ( ); }; -export const FlatTableMutipleStickyComponent = ( +export const FlatTableMultipleStickyComponent = ( props: Partial ) => { return ( diff --git a/src/components/loader/loader.pw.tsx b/src/components/loader/loader.pw.tsx index 712b1b7703..2fb884d7cb 100644 --- a/src/components/loader/loader.pw.tsx +++ b/src/components/loader/loader.pw.tsx @@ -182,7 +182,7 @@ test.describe("check props for Loader component test", () => { }); test.describe("Accessibility tests for Loader component", async () => { - test("should pass accessibilty tests for Loader default story", async ({ + test("should pass accessibility tests for Loader default story", async ({ mount, page, }) => { diff --git a/src/components/menu/menu.spec.tsx b/src/components/menu/menu.spec.tsx index b79d88ef8b..f14378756a 100644 --- a/src/components/menu/menu.spec.tsx +++ b/src/components/menu/menu.spec.tsx @@ -197,7 +197,7 @@ describe("Menu", () => { }); describe("with multiple submenus", () => { - it("when a sumenu is opened, any previously open submenu is closed", () => { + it("when a submenu is opened, any previously open submenu is closed", () => { wrapper = mount( menu item diff --git a/src/components/progress-tracker/progress-tracker.spec.tsx b/src/components/progress-tracker/progress-tracker.spec.tsx index ae344cde9b..cc3f26015e 100644 --- a/src/components/progress-tracker/progress-tracker.spec.tsx +++ b/src/components/progress-tracker/progress-tracker.spec.tsx @@ -246,7 +246,7 @@ describe("ProgressTracker", () => { }); }); - describe("get a correct background of inner and outter bar color, when progress is 100 or the error occurs", () => { + describe("get a correct background of inner and outer bar color, when progress is 100 or the error occurs", () => { it("applies correct background color if progress is 100", () => { wrapper = mount(); assertStyleMatch( diff --git a/src/components/text-editor/text-editor.spec.tsx b/src/components/text-editor/text-editor.spec.tsx index e5f7bc09e1..1806c70877 100644 --- a/src/components/text-editor/text-editor.spec.tsx +++ b/src/components/text-editor/text-editor.spec.tsx @@ -1188,7 +1188,7 @@ describe("TextEditor", () => { ); }); - it("applies error styling when hasError is true and focusRedesignOptOut and isForcused are also true", () => { + it("applies error styling when hasError is true and focusRedesignOptOut and isFocused are also true", () => { const focusRedesignWrapper = mount( diff --git a/src/components/tooltip/tooltip.stories.mdx b/src/components/tooltip/tooltip.stories.mdx index d7c0375075..b3c7e8d183 100644 --- a/src/components/tooltip/tooltip.stories.mdx +++ b/src/components/tooltip/tooltip.stories.mdx @@ -78,7 +78,7 @@ Tooltip "bottom" intitially, then flip to "right" when there is not enough room when there is no space to render to the right anymore. - + ### Large tooltip diff --git a/src/components/tooltip/tooltip.stories.tsx b/src/components/tooltip/tooltip.stories.tsx index a223094c01..74921f3319 100644 --- a/src/components/tooltip/tooltip.stories.tsx +++ b/src/components/tooltip/tooltip.stories.tsx @@ -73,7 +73,7 @@ export const Positioning = () => { ); }; -export const FlipBehviourOverrides = () => { +export const FlipBehaviourOverrides = () => { const Component = forwardRef( ({ children }: ButtonProps, ref) => (