diff --git a/cypress/components/action-popover/action-popover.cy.js b/cypress/components/action-popover/action-popover.cy.js
index 198177daef..422ffb128a 100644
--- a/cypress/components/action-popover/action-popover.cy.js
+++ b/cypress/components/action-popover/action-popover.cy.js
@@ -1,4 +1,3 @@
-/* eslint-disable react/prop-types */
import React from "react";
import path from "path";
@@ -11,6 +10,9 @@ import {
actionPopoverButton,
actionPopover,
actionPopoverSubmenu,
+ actionPopoverMenuItemIcon,
+ actionPopoverMenuItemInnerText,
+ actionPopoverMenuItemChevron,
actionPopoverSubmenuByIndex,
actionPopoverInnerItem,
actionPopoverWrapper,
@@ -27,6 +29,7 @@ import {
ActionPopoverCustom,
ActionPopoverWithProps,
ActionPopoverMenuWithProps,
+ ActionPopoverWithDesignVariants,
ActionPopoverProps,
} from "../../../src/components/action-popover/action-popover-test.stories";
import {
@@ -35,11 +38,11 @@ import {
ActionPopoverComponentDisabledItems,
ActionPopoverComponentMenuRightAligned,
ActionPopoverComponentContentAlignedRight,
+ ActionPopoverComponentSubmenuPositionedRight,
ActionPopoverComponentNoIcons,
ActionPopoverComponentCustomMenuButton,
ActionPopoverComponentSubmenu,
ActionPopoverComponentDisabledSubmenu,
- ActionPopoverComponentSubmenuAlignedRight,
ActionPopoverComponentMenuOpeningAbove,
ActionPopoverComponentKeyboardNavigation,
ActionPopoverComponentKeyboardNaviationLeftAlignedSubmenu,
@@ -444,6 +447,15 @@ context("Test for ActionPopover component", () => {
.should("be.visible");
});
+ it("should render ActionPopover with icon's within a submenu", () => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton().eq(0).click();
+ actionPopoverMenuItemIcon().eq(0).should("exist");
+ });
+
it.each([
["left", "start"],
["right", "end"],
@@ -461,6 +473,24 @@ context("Test for ActionPopover component", () => {
}
);
+ it.each([
+ ["left", "chevron_left_thick"],
+ ["right", "chevron_right_thick"],
+ ])(
+ "should render ActionPopover with submenuPosition prop set to %s",
+ (position, chevronType) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton().eq(0).click();
+ actionPopoverMenuItemChevron().should("have.attr", "type", chevronType);
+ }
+ );
+
it("should render ActionPopoverMenu with menuID", () => {
CypressMountWithProviders(
@@ -574,6 +604,605 @@ context("Test for ActionPopover component", () => {
});
});
+ describe("padding checks on 'StyledMenuItemInnerText'", () => {
+ it.each([
+ ["left", "left"],
+ ["left", "right"],
+ ["right", "left"],
+ ["right", "right"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState, caretState, icon and submenu are all false is left and right padding is: --spacing100",
+ (alignment, position) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .should("have.css", "padding-left", "8px")
+ .getDesignTokensByCssProperty("padding-left")
+ .then(($el) => {
+ expect($el[0]).to.equal("--spacing100");
+ });
+
+ actionPopoverMenuItemInnerText()
+ .should("have.css", "padding-right", "8px")
+ .getDesignTokensByCssProperty("padding-right")
+ .then(($el) => {
+ expect($el[0]).to.equal("--spacing100");
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left", "--spacing400", "32px"],
+ ["left", "right", "--spacing100", "8px"],
+ ["right", "left", "--spacing100", "8px"],
+ ["right", "right", "--spacing100", "8px"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState is false, caretState is true, icon is false and submenu is false padding-left is: %s",
+ (alignment, position, spacingLeft, paddingLeft) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-left", paddingLeft)
+ .getDesignTokensByCssProperty("padding-left")
+ .then(($el) => {
+ expect($el[0]).to.equal(spacingLeft);
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left", "--spacing100", "8px"],
+ ["left", "right", "--spacing100", "8px"],
+ ["right", "left", "--spacing100", "8px"],
+ ["right", "right", "--spacing400", "32px"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState is false, caretState is true, icon is false and submenu is false padding-right is %s",
+ (alignment, position, spacingRight, paddingRight) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-right", paddingRight)
+ .getDesignTokensByCssProperty("padding-right")
+ .then(($el) => {
+ expect($el[0]).to.equal(spacingRight);
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left", "--spacing100", "8px"],
+ ["left", "right", "--spacing100", "8px"],
+ ["right", "left", "--spacing100", "8px"],
+ ["right", "right", "--spacing100", "8px"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState is false, caretState is true, icon is false and submenu is true padding-left is: %s and padding-right is --spacing100",
+ (alignment, position, spacingLeft, paddingLeft) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-left", paddingLeft)
+ .getDesignTokensByCssProperty("padding-left")
+ .then(($el) => {
+ expect($el[0]).to.equal(spacingLeft);
+ });
+
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-right", "8px")
+ .getDesignTokensByCssProperty("padding-right")
+ .then(($el) => {
+ expect($el[0]).to.equal("--spacing100");
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left"],
+ ["left", "right"],
+ ["right", "left"],
+ ["right", "right"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState is true, caretState is false, icon is false and submenu is false left and right padding is: --spacing100",
+ (alignment, position) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-left", "8px")
+ .getDesignTokensByCssProperty("padding-left")
+ .then(($el) => {
+ expect($el[0]).to.equal("--spacing100");
+ });
+
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-right", "8px")
+ .getDesignTokensByCssProperty("padding-right")
+ .then(($el) => {
+ expect($el[0]).to.equal("--spacing100");
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left", "--spacing000", "0px"],
+ ["left", "right", "--spacing100", "8px"],
+ ["right", "left", "--spacing100", "8px"],
+ ["right", "right", "--spacing100", "8px"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState is true, caretState is false, icon is true and submenu is false padding-left is: %s and padding-right is: --spacing100",
+ (alignment, position, spacingLeft, paddingLeft) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-left", paddingLeft)
+ .getDesignTokensByCssProperty("padding-left")
+ .then(($el) => {
+ expect($el[0]).to.equal(spacingLeft);
+ });
+
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-right", "8px")
+ .getDesignTokensByCssProperty("padding-right")
+ .then(($el) => {
+ expect($el[0]).to.equal("--spacing100");
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left", "--spacing000", "0px"],
+ ["left", "right", "--spacing100", "8px"],
+ ["right", "left", "--spacing100", "8px"],
+ ["right", "right", "--spacing100", "8px"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState, caretState, icon and submenu are all true, padding-left is: %s and padding-right is --spacing100",
+ (alignment, position, spacingLeft, paddingLeft) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-left", paddingLeft)
+ .getDesignTokensByCssProperty("padding-left")
+ .then(($el) => {
+ expect($el[0]).to.equal(spacingLeft);
+ });
+
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-right", "8px")
+ .getDesignTokensByCssProperty("padding-right")
+ .then(($el) => {
+ expect($el[0]).to.equal("--spacing100");
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left", "--spacing000", "0px"],
+ ["left", "right", "--spacing100", "8px"],
+ ["right", "left", "--spacing100", "8px"],
+ ["right", "right", "--spacing100", "8px"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState is true, caretState is true, icon is true and submenu is false padding-left is: %s and padding-right is: --spacing100",
+ (alignment, position, spacingLeft, paddingLeft) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-left", paddingLeft)
+ .getDesignTokensByCssProperty("padding-left")
+ .then(($el) => {
+ expect($el[0]).to.equal(spacingLeft);
+ });
+
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-right", "8px")
+ .getDesignTokensByCssProperty("padding-right")
+ .then(($el) => {
+ expect($el[0]).to.equal("--spacing100");
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left", "--spacing500", "40px"],
+ ["left", "right", "--spacing100", "8px"],
+ ["right", "left", "--spacing100", "8px"],
+ ["right", "right", "--spacing100", "8px"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState is true, caretState is true, icon is false and submenu is true padding-left is: %s",
+ (alignment, position, spacingLeft, paddingLeft) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-left", paddingLeft)
+ .getDesignTokensByCssProperty("padding-left")
+ .then(($el) => {
+ expect($el[0]).to.equal(spacingLeft);
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left", "--spacing100", "8px"],
+ ["left", "right", "--spacing100", "8px"],
+ ["right", "left", "--spacing100", "8px"],
+ ["right", "right", "--spacing600", "48px"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState is true, caretState is true, icon is false and submenu is true padding-right is: %s",
+ (alignment, position, spacingRight, paddingRight) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-right", paddingRight)
+ .getDesignTokensByCssProperty("padding-right")
+ .then(($el) => {
+ expect($el[0]).to.equal(spacingRight);
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left", "--spacing800", "64px"],
+ ["left", "right", "--spacing100", "8px"],
+ ["right", "left", "--spacing100", "8px"],
+ ["right", "right", "--spacing100", "8px"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState is true, caretState is true, icon is false and submenu is false padding-left is: %s",
+ (alignment, position, spacingLeft, paddingLeft) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-left", paddingLeft)
+ .getDesignTokensByCssProperty("padding-left")
+ .then(($el) => {
+ expect($el[0]).to.equal(spacingLeft);
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left", "--spacing100", "8px"],
+ ["left", "right", "--spacing100", "8px"],
+ ["right", "left", "--spacing100", "8px"],
+ ["right", "right", "--spacing900", "72px"],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and iconState is true, caretState is true, icon is false and submenu is false padding-right is: %s",
+ (alignment, position, spacingRight, paddingRight) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(0)
+ .should("have.css", "padding-right", paddingRight)
+ .getDesignTokensByCssProperty("padding-right")
+ .then(($el) => {
+ expect($el[0]).to.equal(spacingRight);
+ });
+ });
+ }
+ );
+
+ it.each([
+ ["left", "left", 1],
+ ["left", "right", 2],
+ ["right", "left", 3],
+ ["right", "right", 4],
+ ])(
+ "when horizontalAlignment is %s, submenuPosition is %s and child is a submenu, left and right padding is: --spacing000",
+ (alignment, position, index) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemInnerText()
+ .eq(index)
+ .should("have.css", "padding-left", "0px")
+ .getDesignTokensByCssProperty("padding-left")
+ .then(($el) => {
+ expect($el[0]).to.equal("--spacing000");
+ });
+
+ actionPopoverMenuItemInnerText()
+ .eq(index)
+ .should("have.css", "padding-right", "0px")
+ .getDesignTokensByCssProperty("padding-right")
+ .then(($el) => {
+ expect($el[0]).to.equal("--spacing000");
+ });
+ });
+ }
+ );
+ });
+
+ describe("justify-content checks on 'StyledMenuItem'", () => {
+ it.each([
+ ["left", "flex-start"],
+ ["right", "flex-end"],
+ ])(
+ "when horizontalAlignment is %s, content should be justified %s",
+ (alignment, itemAlignment) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton().eq(0).click();
+ getDataElementByValue("menu-item1").should(
+ "have.css",
+ "justify-content",
+ itemAlignment
+ );
+ }
+ );
+
+ it.each([
+ ["left", "right", "space-between"],
+ ["right", "left", "flex-end"],
+ ])(
+ "when horizontalAlignment is %s, and submenuPosition is %s without submenu, content should be justified %s",
+ (alignment, position, itemAlignment) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton().eq(0).click();
+ getDataElementByValue("menu-item1").should(
+ "have.css",
+ "justify-content",
+ itemAlignment
+ );
+ }
+ );
+
+ it.each([
+ ["left", "right", "space-between"],
+ ["right", "left", "space-between"],
+ ])(
+ "when horizontalAlignment is %s, and submenuPosition is %s with submenu, content should be justified %s",
+ (alignment, position, itemAlignment) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton().eq(0).click();
+ getDataElementByValue("menu-item1").should(
+ "have.css",
+ "justify-content",
+ itemAlignment
+ );
+ }
+ );
+ });
+
+ describe("padding checks on 'MenuItemIcon'", () => {
+ it.each([
+ [
+ "left",
+ "left",
+ "--spacing100 --spacing100 --spacing100 --spacing400",
+ "8px 8px 8px 32px",
+ ],
+ [
+ "left",
+ "right",
+ "--spacing100 --spacing100 --spacing100 --spacing100",
+ "8px",
+ ],
+ [
+ "right",
+ "left",
+ "--spacing100 --spacing100 --spacing100 --spacing100",
+ "8px",
+ ],
+ [
+ "right",
+ "right",
+ "--spacing100 --spacing400 --spacing100 --spacing100",
+ "8px 32px 8px 8px",
+ ],
+ ])(
+ "when iconState, caretState and icon are all is true and submenu is false, submenuPosition is %s and horizontalAlignment is %s, padding is %s",
+ (position, alignment, spacing, padding) => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemIcon()
+ .eq(0)
+ .should("have.css", "padding", padding)
+ .getDesignTokensByCssProperty("padding")
+ .then(($el) => {
+ expect($el.join(" ")).to.deep.equal(spacing);
+ });
+ });
+ }
+ );
+
+ it("when iconState, caretState and icon are all true and submenu is false, padding is: --spacing100", () => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton()
+ .eq(0)
+ .click()
+ .then(() => {
+ actionPopoverMenuItemIcon()
+ .eq(0)
+ .should("have.css", "padding", "8px")
+ .getDesignTokensByCssProperty("padding")
+ .then(($el) => {
+ expect($el.join(" ")).to.deep.equal(
+ "--spacing100 --spacing100 --spacing100 --spacing100"
+ );
+ });
+ });
+ });
+ });
+
describe("Accessibility tests for ActionPopover", () => {
it("should pass accessibility tests for ActionPopover with custom button", () => {
CypressMountWithProviders(
@@ -629,6 +1258,15 @@ context("Test for ActionPopover component", () => {
cy.checkAccessibility();
});
+ it("should pass accessibility tests for ActionPopover with a right submenu position", () => {
+ CypressMountWithProviders(
+
+ );
+
+ actionPopoverButton().eq(0).click();
+ cy.checkAccessibility();
+ });
+
it("should pass accessibility tests for ActionPopover with no icons", () => {
CypressMountWithProviders();
@@ -658,14 +1296,6 @@ context("Test for ActionPopover component", () => {
cy.checkAccessibility();
});
- it("should pass accessibility tests for ActionPopover with submenu aligned right", () => {
- CypressMountWithProviders();
-
- actionPopoverButton().eq(0).click();
- actionPopoverInnerItem(0).click();
- cy.checkAccessibility();
- });
-
it("should pass accessibility tests for ActionPopover with menu opening above", () => {
CypressMountWithProviders();
diff --git a/cypress/locators/action-popover/index.js b/cypress/locators/action-popover/index.js
index 05e3400b0c..a9dc1ffbc6 100644
--- a/cypress/locators/action-popover/index.js
+++ b/cypress/locators/action-popover/index.js
@@ -2,6 +2,9 @@ import {
ACTION_POPOVER_BUTTON,
ACTION_POPOVER_DATA_COMPONENT,
ACTION_POPOVER_SUBMENU,
+ ACTION_POPOVER_MENU_ITEM_ICON,
+ ACTION_POPOVER_MENU_ITEM_INNER_TEXT,
+ ACTION_POPOVER_MENU_ITEM_CHEVRON,
ACTION_POPOVER_WRAPPER,
} from "./locators";
@@ -18,6 +21,12 @@ export const actionPopoverInnerItem = (index) =>
.first();
export const actionPopoverSubmenu = (index) =>
cy.get(ACTION_POPOVER_SUBMENU).eq(1).children().eq(index).find("button");
+export const actionPopoverMenuItemIcon = () =>
+ cy.get(ACTION_POPOVER_MENU_ITEM_ICON);
+export const actionPopoverMenuItemInnerText = () =>
+ cy.get(ACTION_POPOVER_MENU_ITEM_INNER_TEXT);
+export const actionPopoverMenuItemChevron = () =>
+ cy.get(ACTION_POPOVER_MENU_ITEM_CHEVRON);
export const actionPopoverSubmenuByIndex = () =>
cy.get(ACTION_POPOVER_SUBMENU).eq(1);
export const actionPopoverWrapper = () => cy.get(ACTION_POPOVER_WRAPPER);
diff --git a/cypress/locators/action-popover/locators.js b/cypress/locators/action-popover/locators.js
index ffbdfed094..6cd044eb2f 100644
--- a/cypress/locators/action-popover/locators.js
+++ b/cypress/locators/action-popover/locators.js
@@ -3,5 +3,11 @@ export const ACTION_POPOVER_BUTTON = '[data-element="action-popover-button"]';
export const ACTION_POPOVER_DATA_COMPONENT =
'[data-component="action-popover"]';
export const ACTION_POPOVER_SUBMENU = '[data-element="action-popover-submenu"]';
+export const ACTION_POPOVER_MENU_ITEM_ICON =
+ '[data-element="action-popover-menu-item-icon"]';
+export const ACTION_POPOVER_MENU_ITEM_INNER_TEXT =
+ '[data-element="action-popover-menu-item-inner-text"]';
+export const ACTION_POPOVER_MENU_ITEM_CHEVRON =
+ '[data-element="action-popover-menu-item-chevron"]';
export const ACTION_POPOVER_WRAPPER =
'[data-component="action-popover-wrapper"]';
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 4b35bb9a14..e7a60238c0 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
@@ -11,6 +11,8 @@ import {
MenuItemIcon,
SubMenuItemIcon,
StyledMenuItem,
+ StyledMenuItemInnerText,
+ StyledMenuItemOuterContainer,
StyledMenuItemWrapper,
} from "../action-popover.style";
import Events from "../../../__internal__/utils/helpers/events";
@@ -48,15 +50,27 @@ export interface ActionPopoverItemProps {
focusItem?: boolean;
/** @ignore @private */
horizontalAlignment?: "left" | "right";
+ /** @ignore @private */
+ submenuPosition?: "left" | "right";
+ /** @ignore @private */
+ caretState?: boolean;
+ /** @ignore @private */
+ iconState?: boolean;
+ /** @ignore @private */
+ setCaretState?: (value: boolean) => void;
+ /** @ignore @private */
+ setIconState?: (value: boolean) => void;
+ /** @ignore @private */
+ isASubmenu?: boolean;
}
const INTERVAL = 150;
type ContainerPosition = {
- left: number;
+ left: string | number;
top?: string;
bottom?: string;
- right: "auto";
+ right: string | number;
};
function checkRef(ref: React.RefObject) {
@@ -76,28 +90,6 @@ function leftAlignSubmenu(
return left >= offsetWidth;
}
-function getContainerPosition(
- itemRef: React.RefObject,
- submenuRef: React.RefObject,
- placement: "bottom" | "top"
-): ContainerPosition | undefined {
- /* istanbul ignore if */
- if (!itemRef.current || !submenuRef.current) return undefined;
-
- const { offsetWidth: parentWidth } = itemRef.current;
- const { offsetWidth: submenuWidth } = submenuRef.current;
- const xPositionValue = leftAlignSubmenu(itemRef, submenuRef)
- ? -submenuWidth
- : parentWidth;
- const yPositionName = placement === "top" ? "bottom" : "top";
-
- return {
- left: xPositionValue,
- [yPositionName]: "calc(-1 * var(--spacing100))",
- right: "auto",
- };
-}
-
export const ActionPopoverItem = ({
children,
icon,
@@ -109,6 +101,12 @@ export const ActionPopoverItem = ({
download,
href,
horizontalAlignment,
+ submenuPosition,
+ caretState,
+ iconState,
+ setCaretState,
+ setIconState,
+ isASubmenu = false,
...rest
}: ActionPopoverItemProps) => {
const l = useLocale();
@@ -139,19 +137,94 @@ export const ActionPopoverItem = ({
const mouseEnterTimer = useRef(null);
const mouseLeaveTimer = useRef(null);
+ const currentSubmenuAlignment = () => {
+ if (submenuPosition === "left") {
+ if (isLeftAligned) {
+ return "left";
+ }
+ return "right";
+ }
+ return "right";
+ };
+
+ function getContainerPosition(
+ itemRef: React.RefObject,
+ containerSubmenuRef: React.RefObject,
+ containerPlacement: "bottom" | "top"
+ ): ContainerPosition | undefined {
+ /* istanbul ignore if */
+ if (!itemRef.current || !containerSubmenuRef.current) return undefined;
+
+ const { offsetWidth: submenuWidth } = containerSubmenuRef.current;
+ const horizontalAlignmentValue = currentSubmenuAlignment() === "left";
+ const leftValue = horizontalAlignmentValue ? -submenuWidth : "auto";
+ const rightValue = horizontalAlignmentValue ? "auto" : -submenuWidth;
+ const yPositionName = containerPlacement === "top" ? "bottom" : "top";
+
+ return {
+ left: leftValue,
+ [yPositionName]: "calc(-1 * var(--spacing100))",
+ right: rightValue,
+ };
+ }
+
+ const retrieveVariantType = () => {
+ if (!iconState && !caretState && !icon && !submenu) {
+ return 1;
+ }
+ if (!iconState && caretState && !icon && !submenu) {
+ return 2;
+ }
+ if (!iconState && caretState && !icon && submenu) {
+ return 3;
+ }
+ if (iconState && !caretState && !icon && !submenu) {
+ return 4;
+ }
+ if (iconState && !caretState && icon && !submenu) {
+ return 5;
+ }
+ if (iconState && caretState && icon && submenu) {
+ return 6;
+ }
+ if (iconState && caretState && icon && !submenu) {
+ return 7;
+ }
+ if (iconState && caretState && !icon && submenu) {
+ return 8;
+ }
+ if (iconState && caretState && !icon && !submenu) {
+ return 9;
+ }
+ return 1;
+ };
+
useEffect(() => {
if (!isOpenPopover) {
setOpen(false);
}
}, [isOpenPopover]);
+ useEffect(() => {
+ if (icon) {
+ setIconState?.(true);
+ }
+ if (submenu) {
+ setCaretState?.(true);
+ }
+ }, [icon, setCaretState, setIconState, submenu, focusIndex]);
+
const alignSubmenu = useCallback(() => {
+ const align = leftAlignSubmenu(ref, submenuRef);
+ setIsLeftAligned(align);
+ return checkRef(ref) && checkRef(submenuRef) && submenu;
+ }, [submenu, placement]);
+
+ useEffect(() => {
if (checkRef(ref) && checkRef(submenuRef) && submenu) {
- const align = leftAlignSubmenu(ref, submenuRef);
- setIsLeftAligned(align);
setContainerPosition(getContainerPosition(ref, submenuRef, placement));
}
- }, [submenu, placement]);
+ }, [submenu, getContainerPosition]);
useEffect(() => {
alignSubmenu();
@@ -205,7 +278,7 @@ export const ActionPopoverItem = ({
e.stopPropagation();
} else if (!disabled) {
if (submenu) {
- if (isLeftAligned) {
+ if (currentSubmenuAlignment() === "left") {
// LEFT: open if has submenu and left aligned otherwise close submenu
if (Events.isLeftKey(e) || Events.isEnterKey(e)) {
setOpen(true);
@@ -242,7 +315,15 @@ export const ActionPopoverItem = ({
e.stopPropagation();
}
},
- [disabled, download, isHref, isLeftAligned, onClick, submenu]
+ [
+ disabled,
+ download,
+ isHref,
+ isLeftAligned,
+ onClick,
+ submenu,
+ currentSubmenuAlignment,
+ ]
);
const itemSubmenuProps = {
@@ -283,7 +364,19 @@ export const ActionPopoverItem = ({
};
const renderMenuItemIcon = () => {
- return icon && ;
+ return (
+ icon && (
+
+ )
+ );
};
return (
@@ -298,18 +391,37 @@ export const ActionPopoverItem = ({
tabIndex={0}
isDisabled={disabled}
horizontalAlignment={horizontalAlignment}
+ submenuPosition={currentSubmenuAlignment()}
+ hasSubmenu={submenu}
+ caretState={caretState}
{...(disabled && { "aria-disabled": true })}
{...(isHref && { as: ("a" as unknown) as undefined, download, href })}
{...(submenu && itemSubmenuProps)}
>
- {submenu && checkRef(ref) && isLeftAligned ? (
-
+ {submenu && checkRef(ref) && currentSubmenuAlignment() === "left" ? (
+
) : null}
- {horizontalAlignment === "left" ? renderMenuItemIcon() : null}
- {children}
- {horizontalAlignment === "right" ? renderMenuItemIcon() : null}
- {submenu && checkRef(ref) && !isLeftAligned ? (
-
+
+ {horizontalAlignment === "left" ? renderMenuItemIcon() : null}
+
+ {children}
+
+ {horizontalAlignment === "right" ? renderMenuItemIcon() : null}
+
+ {submenu && checkRef(ref) && currentSubmenuAlignment() === "right" ? (
+
) : null}
{React.isValidElement(submenu)
@@ -325,6 +437,8 @@ export const ActionPopoverItem = ({
setOpen,
setFocusIndex,
focusIndex,
+ isASubmenu: true,
+ horizontalAlignment,
}
)
: null}
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 1ab095c8ab..39cc2acdea 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,4 @@
-import React, { useCallback, useMemo, useContext } from "react";
+import React, { useCallback, useMemo, useContext, useState } from "react";
import invariant from "invariant";
import { Menu } from "../action-popover.style";
@@ -24,18 +24,22 @@ export interface ActionPopoverMenuBaseProps {
parentID?: string;
/** Horizontal alignment of menu items content */
horizontalAlignment?: "left" | "right";
+ /** Sets submenu position */
+ submenuPosition?: "left" | "right";
/** Set whether the menu should open above or below the button */
placement?: "bottom" | "top";
/** @ignore @private */
role?: string;
/** @ignore @private */
+ isASubmenu?: boolean;
+ /** @ignore @private */
"data-element"?: string;
/** @ignore @private */
style?: {
- left: number;
+ left: string | number;
top?: string;
bottom?: string;
- right: "auto";
+ right: string | number;
};
}
@@ -58,6 +62,8 @@ const ActionPopoverMenu = React.forwardRef<
setFocusIndex,
placement = "bottom",
horizontalAlignment,
+ submenuPosition,
+ isASubmenu,
...rest
}: ActionPopoverMenuBaseProps,
ref
@@ -162,6 +168,9 @@ const ActionPopoverMenu = React.forwardRef<
[focusButton, setOpen, focusIndex, items, setFocusIndex]
);
+ const [caretState, setCaretState] = useState(undefined);
+ const [iconState, setIconState] = useState(undefined);
+
const clonedChildren = useMemo(() => {
let index = 0;
return React.Children.map(children, (child) => {
@@ -171,12 +180,30 @@ const ActionPopoverMenu = React.forwardRef<
focusItem: isOpen && focusIndex === index - 1,
placement: child.props.submenu ? placement : undefined,
horizontalAlignment,
+ submenuPosition,
+ caretState,
+ setCaretState,
+ iconState,
+ setIconState,
+ isASubmenu,
});
}
return child;
});
- }, [children, focusIndex, isOpen, placement, horizontalAlignment]);
+ }, [
+ children,
+ focusIndex,
+ isOpen,
+ placement,
+ horizontalAlignment,
+ submenuPosition,
+ caretState,
+ setCaretState,
+ iconState,
+ setIconState,
+ isASubmenu,
+ ]);
return (