From 7c7e3b36704121db33818de206adb6c6ec484814 Mon Sep 17 00:00:00 2001 From: Masoud Amjadi Date: Thu, 14 Sep 2023 14:41:36 -0400 Subject: [PATCH] feat(menuitem): add determinate/indeterminate variant to the MenuItem (#602) * feat(menuitem): add determinate/indeterminate variant to the MenuItem component * fix(jest): new jest config * fix(a11y): fix storybook:axe timeouts * fix(jest): fix jest test timeouts --- package.json | 4 +- .../core/MenuItem/MenuItem.namespace-test.tsx | 2 +- .../src/core/MenuItem/index.stories.tsx | 77 ++++++++++++++++++- .../components/src/core/MenuItem/index.tsx | 34 ++++++-- .../components/src/core/MenuItem/style.ts | 29 ++++++- 5 files changed, 129 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 8eec7f77a..acc23b0bf 100644 --- a/package.json +++ b/package.json @@ -110,9 +110,9 @@ "build-storybook": "storybook build -o docs-build", "test-storybook": "test-storybook", "storybook:axe": "yarn build-storybook && yarn storybook:axeOnly", - "storybook:axeOnly": "axe-storybook --build-dir docs-build", + "storybook:axeOnly": "axe-storybook --build-dir docs-build --timeout 10000", "test": "lerna run test -- --detectOpenHandles", - "test:updateSnapshots": "lerna run test -- -u", + "test:updateSnapshots": "lerna run test -- -u --detectOpenHandles", "namespace-check": "lerna run namespace-check", "prepare": "husky install", "lint": "lerna run lint", diff --git a/packages/components/src/core/MenuItem/MenuItem.namespace-test.tsx b/packages/components/src/core/MenuItem/MenuItem.namespace-test.tsx index 83163273b..f599fc601 100644 --- a/packages/components/src/core/MenuItem/MenuItem.namespace-test.tsx +++ b/packages/components/src/core/MenuItem/MenuItem.namespace-test.tsx @@ -3,7 +3,6 @@ import { MenuItem, MenuItemProps, } from "@czi-sds/components"; -import React from "react"; import { noop } from "src/common/utils"; const MenuNameSpaceTest = ( @@ -18,6 +17,7 @@ const MenuNameSpaceTest = ( isMultiSelect disabled={false} selected + sdsStyle="determinate" > Contact us diff --git a/packages/components/src/core/MenuItem/index.stories.tsx b/packages/components/src/core/MenuItem/index.stories.tsx index 4fcdcf38e..15d70debe 100644 --- a/packages/components/src/core/MenuItem/index.stories.tsx +++ b/packages/components/src/core/MenuItem/index.stories.tsx @@ -1,6 +1,6 @@ /* eslint-disable no-use-before-define */ import { Args, Meta } from "@storybook/react"; -import RawMenuItem from "./index"; +import RawMenuItem, { MenuItemProps } from "./index"; import { DemoWrapper } from "./style"; const MenuItem = (props: Args): JSX.Element => { @@ -37,7 +37,15 @@ export default { "flagCheck", ], }, - sdsIconProps: { control: { type: "object" } }, + sdsIconProps: { + control: { type: "object" }, + }, + sdsStyle: { + control: { + type: "radio", + }, + options: ["determinate", "indeterminate"], + }, selected: { control: { type: "boolean" }, }, @@ -56,13 +64,19 @@ export default { export const Default = { args: { column: "column value here", + disabled: false, + isMultiSelect: false, name: "text here", + sdsIconProps: {}, + sdsStyle: "determinate", + selected: false, }, }; // Screenshot test const MULTI_SELECT_OPTIONS = [false, true]; +const SDS_STYLE_OPTIONS = ["determinate", "indeterminate"]; const COLUMN_OPTIONS = [undefined, "Column"]; /** * (thuang): Add `as const` to make sure the type is not widened to `string` @@ -229,6 +243,48 @@ const ScreenshotTestDemo = (props: Args): JSX.Element => {

Selected: {selected ? "true" : "false"}

+ {SDS_STYLE_OPTIONS.map((sdsStyle) => { + return ( + + ); + })} + + ); + } + + // loop through all SDS_STYLE_OPTIONS + create headers for DITERMINISTIC_OPTIONS + function MenuItemStyles({ + isMultiSelect, + column, + sdsIcon, + selected, + sdsStyle, + }: { + isMultiSelect: (typeof MULTI_SELECT_OPTIONS)[number]; + column: (typeof COLUMN_OPTIONS)[number]; + sdsIcon: (typeof ICON_OPTIONS)[number]; + selected: (typeof SELECTED_OPTIONS)[number]; + sdsStyle: (typeof SDS_STYLE_OPTIONS)[number]; + }) { + const LABEL_STYLE: React.CSSProperties = { + ...MID_LABEL, + alignSelf: "end", + borderWidth: "1px", + fontSize: "0.83em", + margin: "0 0 10px 0", + }; + return ( +
+

+ SdsStyle: {sdsStyle} +

{DISABLED_OPTIONS.map((disabled) => { return ( { column={column} sdsIcon={sdsIcon} selected={selected} + sdsStyle={sdsStyle as MenuItemProps<"gear">["sdsStyle"]} disabled={disabled} key={String(disabled)} /> @@ -251,12 +308,14 @@ const ScreenshotTestDemo = (props: Args): JSX.Element => { column, sdsIcon, selected, + sdsStyle, disabled, }: { isMultiSelect: (typeof MULTI_SELECT_OPTIONS)[number]; column: (typeof COLUMN_OPTIONS)[number]; sdsIcon: (typeof ICON_OPTIONS)[number]; selected: (typeof SELECTED_OPTIONS)[number]; + sdsStyle: MenuItemProps<"gear">["sdsStyle"]; disabled: (typeof DISABLED_OPTIONS)[number]; }) { const LEVEL_STYLE: React.CSSProperties = { @@ -287,6 +346,7 @@ const ScreenshotTestDemo = (props: Args): JSX.Element => { column={column} sdsIcon={sdsIcon} selected={selected} + sdsStyle={sdsStyle} disabled={disabled} className={`pseudo-${state}`} key={state} @@ -316,6 +376,7 @@ export const ScreenshotTest = { "isMultiSelect", "sdsIcon", "sdsIconProps", + "sdsStyle", "selected", ], }, @@ -334,6 +395,18 @@ export const Test = { name: "test text", }, parameters: { + controls: { + exclude: [ + "name", + "column", + "disabled", + "isMultiSelect", + "sdsIcon", + "sdsIconProps", + "sdsStyle", + "selected", + ], + }, snapshot: { skip: true, }, diff --git a/packages/components/src/core/MenuItem/index.tsx b/packages/components/src/core/MenuItem/index.tsx index 899808b54..7e003240c 100644 --- a/packages/components/src/core/MenuItem/index.tsx +++ b/packages/components/src/core/MenuItem/index.tsx @@ -5,9 +5,10 @@ import { ColumnWrapper, ContentWrapper, StyledCheck, - StyledCheckIconWrapper, + StyledIconWrapper, StyledMenuItem, StyledMenuItemIcon, + StyledMinus, TextWrapper, } from "./style"; @@ -53,6 +54,7 @@ export interface MenuItemExtraProps< isMultiSelect?: boolean; sdsIcon?: IconName; sdsIconProps?: Partial>; + sdsStyle?: "determinate" | "indeterminate"; } export type MenuItemProps = @@ -72,23 +74,39 @@ const MenuItem = forwardRef(function MenuItem< isMultiSelect = false, sdsIcon, sdsIconProps, + sdsStyle = "determinate", ...originalMenuItemProps } = props; const { selected = false } = originalMenuItemProps as MenuItemProps; - return ( - - {isMultiSelect && ( - // TODO (mlila): replace with sds InputCheckbox class once complete - + const selectionIcon = () => { + if (sdsStyle === "determinate") { + return ( + - - )} + + ); + } + return ( + + + + ); + }; + + return ( + + {isMultiSelect && selectionIcon()} { const spacings = getSpaces(props); const iconSizes = getIconSizes(props); @@ -212,7 +212,28 @@ export const StyledCheckIconWrapper = styled("span")` export const StyledCheck = styled(Check, { shouldForwardProp: (prop) => prop !== "selected", -})` +})` + ${(props) => { + const { selected, disabled } = props; + const colors = getColors(props); + const iconSizes = getIconSizes(props); + const selectedColor = disabled ? colors?.gray[300] : colors?.primary[400]; + return ` + color: ${selected ? selectedColor : "transparent"}; + padding: 0; + height: ${iconSizes?.s.height}px; + width: ${iconSizes?.s.width}px; + + &.MuiCheckbox-colorPrimary.Mui-checked:hover { + background-color: transparent; + } + `; + }} +`; + +export const StyledMinus = styled(Remove, { + shouldForwardProp: (prop) => prop !== "selected", +})` ${(props) => { const { selected, disabled } = props; const colors = getColors(props);