diff --git a/lib/src/chip/Chip.accessibility.test.tsx b/lib/src/chip/Chip.accessibility.test.tsx index 6baff07fa..cc5076941 100644 --- a/lib/src/chip/Chip.accessibility.test.tsx +++ b/lib/src/chip/Chip.accessibility.test.tsx @@ -28,13 +28,22 @@ c-10.663,0-17.467,1.853-20.417,5.568c-2.949,3.711-4.428,10.23-4.428,19.558v31.11 describe("Chip component accessibility tests", () => { it("Should not have basic accessibility issues", async () => { - const { container } = render(); + const { container } = render( + {}} suffixIcon={iconSVG} label="Chip" /> + ); const results = await axe(container); expect(results).toHaveNoViolations(); }); it("Should not have basic accessibility issues for disabled mode", async () => { const { container } = render( - + {}} + suffixIcon={iconSVG} + label="Chip" + disabled + /> ); const results = await axe(container); expect(results).toHaveNoViolations(); diff --git a/lib/src/chip/Chip.stories.tsx b/lib/src/chip/Chip.stories.tsx index b7d99141c..a228aa226 100644 --- a/lib/src/chip/Chip.stories.tsx +++ b/lib/src/chip/Chip.stories.tsx @@ -33,12 +33,6 @@ c-10.663,0-17.467,1.853-20.417,5.568c-2.949,3.711-4.428,10.23-4.428,19.558v31.11 ); -const smallIconSVG = ( - - - -); - const opinionatedTheme = { chip: { baseColor: "#e6e6e6", @@ -50,33 +44,79 @@ const opinionatedTheme = { export const Chromatic = () => ( <> - - <DxcChip label="Default Chip" /> + <Title title="Default" theme="light" level={4} /> + <DxcChip label="Default" /> + </ExampleContainer> + <ExampleContainer> + <Title title="With prefix (SVG)" theme="light" level={4} /> + <DxcChip label="Prefix" prefixIcon={iconSVG} /> </ExampleContainer> <ExampleContainer> - <Title title="Chip with prefix SVG (small icon)" theme="light" level={4} /> - <DxcChip label="Chip with prefix" prefixIcon={smallIconSVG} /> + <Title title="With action prefix (SVG)" theme="light" level={4} /> + <DxcChip label="Action prefix" prefixIcon={iconSVG} onClickPrefix={() => {}} /> </ExampleContainer> <ExampleContainer> - <Title title="Chip with suffix SVG (large icon)" theme="light" level={4} /> - <DxcChip label="Chip with suffix" suffixIcon={iconSVG} /> + <Title title="Disabled action prefix (SVG)" theme="light" level={4} /> + <DxcChip label="Disabled action prefix" prefixIcon={iconSVG} onClickPrefix={() => {}} disabled /> </ExampleContainer> <ExampleContainer> - <Title title="Chip with prefix (SVG) and suffix (URL)" theme="light" level={4} /> - <DxcChip label="Chip with prefix and suffix" prefixIcon={iconSVG} suffixIcon="filled_check_circle" /> + <Title title="With suffix (Material icon)" theme="light" level={4} /> + <DxcChip label="Suffix" suffixIcon="filled_check_circle" /> + </ExampleContainer> + <ExampleContainer> + <Title title="With action suffix (Material icon)" theme="light" level={4} /> + <DxcChip label="Action suffix" suffixIcon="filled_check_circle" onClickSuffix={() => {}} /> + </ExampleContainer> + <ExampleContainer> + <Title title="Action prefix and suffix" theme="light" level={4} /> + <DxcChip + label="Action prefix and suffix" + prefixIcon="filled_check_circle" + onClickPrefix={() => {}} + suffixIcon={iconSVG} + /> </ExampleContainer> <ExampleContainer> - <Title title="Disabled chip" theme="light" level={4} /> - <DxcChip label="Disabled" disabled prefixIcon={iconSVG} suffixIcon="filled_check_circle" /> + <Title title="Prefix and action suffix" theme="light" level={4} /> + <DxcChip + label="Prefix and action suffix" + prefixIcon="filled_check_circle" + suffixIcon={iconSVG} + onClickSuffix={() => {}} + /> + </ExampleContainer> + <ExampleContainer> + <Title title="Disabled action suffix (Material icon)" theme="light" level={4} /> + <DxcChip label="Disabled action suffix" suffixIcon="filled_check_circle" onClickSuffix={() => {}} disabled /> + </ExampleContainer> + <ExampleContainer> + <Title title="Disabled action prefix and suffix" theme="light" level={4} /> + <DxcChip + label="Disabled action prefix" + disabled + prefixIcon={iconSVG} + onClickPrefix={() => {}} + suffixIcon="filled_check_circle" + /> + </ExampleContainer> + <ExampleContainer> + <Title title="Prefix and disabled action suffix" theme="light" level={4} /> + <DxcChip + label="Disabled action suffix" + disabled + prefixIcon={iconSVG} + suffixIcon="filled_check_circle" + onClickSuffix={() => {}} + /> </ExampleContainer> <ExampleContainer> - <Title title="Chip with ellipsis" theme="light" level={4} /> + <Title title="With ellipsis" theme="light" level={4} /> <div style={{ width: "200px" }}> <DxcChip label="With ellipsis asdfasdf asdf asdfasdf asdf asdfasdf asdfasdf asdf asdf adfasrfasf afsdg afgasfg asdf asdf asdf asdf asdf asdf asdf afdg asfg asdfg asdf asdf asdf asdfasdf asd fas df asd asdf asdf asdfasd fg ssssssssssss ssss" /> </div> </ExampleContainer> <ExampleContainer> - <Title title="Chip with ellipsis and suffix" theme="light" level={4} /> + <Title title="With ellipsis and suffix" theme="light" level={4} /> <div style={{ width: "200px" }}> <DxcChip suffixIcon={iconSVG} @@ -85,7 +125,7 @@ export const Chromatic = () => ( </div> </ExampleContainer> <ExampleContainer> - <Title title="Chip with ellipsis and prefix" theme="light" level={4} /> + <Title title="With ellipsis and prefix" theme="light" level={4} /> <div style={{ width: "200px" }}> <DxcChip prefixIcon={iconSVG} @@ -94,10 +134,11 @@ export const Chromatic = () => ( </div> </ExampleContainer> <ExampleContainer> - <Title title="Chip with ellipsis, suffix and prefix" theme="light" level={4} /> + <Title title="With ellipsis, action prefix and suffix" theme="light" level={4} /> <div style={{ width: "200px" }}> <DxcChip prefixIcon={iconSVG} + onClickPrefix={() => {}} suffixIcon={iconSVG} label="With ellipsis asdfasdf asdf asdfasdf asdf asdfasdf asdfasdf asdf asdf adfasrfasf afsdg afgasfg asdf asdf asdf asdf asdf asdf asdf afdg asfg asdfg asdf asdf asdf asdfasdf asd fas df asd asdf asdf asdfasdf" /> @@ -134,57 +175,48 @@ export const Chromatic = () => ( </ExampleContainer> <Title title="Opinionated theme" theme="light" level={2} /> <ExampleContainer> - <Title title="Chip with prefix and suffix" theme="light" level={4} /> + <Title title="With prefix and suffix" theme="light" level={4} /> <HalstackProvider theme={opinionatedTheme}> - <DxcChip label="Chip" prefixIcon={iconSVG} suffixIcon="filled_check_circle" /> + <DxcChip label="Chip" prefixIcon={iconSVG} onClickPrefix={() => {}} suffixIcon="filled_check_circle" /> </HalstackProvider> </ExampleContainer> <ExampleContainer> - <Title title="Chip with prefix and suffix" theme="light" level={4} /> + <Title title="With prefix and suffix" theme="light" level={4} /> <HalstackProvider theme={opinionatedTheme}> - <DxcChip label="Chip" disabled prefixIcon={iconSVG} suffixIcon="filled_check_circle" /> + <DxcChip + label="Disabled" + disabled + prefixIcon={iconSVG} + suffixIcon="filled_check_circle" + onClickSuffix={() => {}} + /> </HalstackProvider> </ExampleContainer> <ExampleContainer pseudoState="pseudo-hover"> <Title title="Hovered" theme="light" level={4} /> <HalstackProvider theme={opinionatedTheme}> - <DxcChip - label="Chip" - prefixIcon={iconSVG} - suffixIcon={iconSVG} - onClickPrefix={() => {}} - onClickSuffix={() => {}} - /> + <DxcChip label="Chip" prefixIcon={iconSVG} suffixIcon={iconSVG} onClickPrefix={() => {}} /> </HalstackProvider> </ExampleContainer> <ExampleContainer pseudoState="pseudo-active"> <Title title="Actived" theme="light" level={4} /> <HalstackProvider theme={opinionatedTheme}> - <DxcChip - label="Chip" - prefixIcon={iconSVG} - suffixIcon={iconSVG} - onClickPrefix={() => {}} - onClickSuffix={() => {}} - /> + <DxcChip label="Chip" prefixIcon={iconSVG} suffixIcon={iconSVG} onClickPrefix={() => {}} /> </HalstackProvider> </ExampleContainer> </> ); + const ChipPrefixFocused = () => ( <ExampleContainer> - <Title title="Chip with prefix" theme="light" level={4} /> - <DxcChip - label="Chip with prefix" - prefixIcon={iconSVG} - onClickPrefix={() => {}} - /> + <Title title="With prefix" theme="light" level={4} /> + <DxcChip label="Prefix" prefixIcon={iconSVG} onClickPrefix={() => {}} /> </ExampleContainer> ); const ChipSuffixFocused = () => ( <ExampleContainer> - <Title title="Chip with suffix" theme="light" level={4} /> - <DxcChip label="Chip with suffix" suffixIcon="filled_delete" onClickSuffix={() => {}} /> + <Title title="With suffix" theme="light" level={4} /> + <DxcChip label="Suffix" suffixIcon="filled_delete" onClickSuffix={() => {}} /> </ExampleContainer> ); diff --git a/lib/src/chip/Chip.tsx b/lib/src/chip/Chip.tsx index 7d3ef522d..22a4dd8ae 100644 --- a/lib/src/chip/Chip.tsx +++ b/lib/src/chip/Chip.tsx @@ -20,7 +20,7 @@ const DxcChip = ({ return ( <ThemeProvider theme={colorsTheme.chip}> - <Chip disabled={disabled} margin={margin}> + <Chip margin={margin}> {prefixIcon && ( <IconContainer role={typeof onClickPrefix === "function" ? "button" : undefined} @@ -28,12 +28,12 @@ const DxcChip = ({ disabled={disabled} interactuable={typeof onClickPrefix === "function" && !disabled} tabIndex={typeof onClickPrefix === "function" && !disabled ? tabIndex : -1} - onClick={() => onClickPrefix && !disabled && onClickPrefix()} + onClick={onClickPrefix} > {typeof prefixIcon === "string" ? <DxcIcon icon={prefixIcon} /> : prefixIcon} </IconContainer> )} - {label && <LabelContainer disabled={disabled}>{label}</LabelContainer>} + {label && <LabelContainer>{label}</LabelContainer>} {suffixIcon && ( <IconContainer role={typeof onClickSuffix === "function" ? "button" : undefined} @@ -41,7 +41,7 @@ const DxcChip = ({ disabled={disabled} interactuable={typeof onClickSuffix === "function" && !disabled} tabIndex={typeof onClickSuffix === "function" && !disabled ? tabIndex : -1} - onClick={() => !disabled && onClickSuffix?.()} + onClick={onClickSuffix} > {typeof suffixIcon === "string" ? <DxcIcon icon={suffixIcon} /> : suffixIcon} </IconContainer> @@ -54,15 +54,14 @@ const DxcChip = ({ const calculateWidth = (margin: ChipPropsType["margin"]) => `calc(100% - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})`; -const Chip = styled.div<{ margin: ChipPropsType["margin"]; disabled: ChipPropsType["disabled"] }>` +const Chip = styled.div<{ margin: ChipPropsType["margin"] }>` box-sizing: border-box; display: inline-flex; align-items: center; gap: ${(props) => props.theme.iconSpacing}; min-height: 40px; max-width: ${(props) => calculateWidth(props.margin)}; - background-color: ${(props) => - (props.disabled && props.theme.disabledBackgroundColor) || props.theme.backgroundColor}; + background-color: ${(props) => props.theme.backgroundColor}; border-radius: ${(props) => props.theme.borderRadius}; border-width: ${(props) => props.theme.borderThickness}; border-style: ${(props) => props.theme.borderStyle}; @@ -81,15 +80,14 @@ const Chip = styled.div<{ margin: ChipPropsType["margin"]; disabled: ChipPropsTy props.margin && typeof props.margin === "object" && props.margin.bottom ? spaces[props.margin.bottom] : ""}; margin-left: ${(props) => props.margin && typeof props.margin === "object" && props.margin.left ? spaces[props.margin.left] : ""}; - cursor: ${({ disabled }) => disabled && "not-allowed"}; `; -const LabelContainer = styled.span<{ disabled: ChipPropsType["disabled"] }>` +const LabelContainer = styled.span` font-size: ${(props) => props.theme.fontSize}; font-family: ${(props) => props.theme.fontFamily}; font-weight: ${(props) => props.theme.fontWeight}; font-style: ${(props) => props.theme.fontStyle}; - color: ${(props) => (props.disabled ? props.theme.disabledFontColor : props.theme.fontColor)}; + color: ${(props) => props.theme.fontColor}; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; @@ -97,13 +95,15 @@ const LabelContainer = styled.span<{ disabled: ChipPropsType["disabled"] }>` const IconContainer = styled.div<{ disabled: ChipPropsType["disabled"]; + onClick?: ChipPropsType["onClickPrefix"]; interactuable: boolean; }>` display: flex; border-radius: 0.25rem; - color: ${(props) => (props.disabled ? props.theme.disabledIconColor : props.theme.iconColor)}; - ${({ interactuable }) => interactuable && "cursor: pointer;"} - + color: ${(props) => + props.disabled && (props.onClick || props.onClick) ? props.theme.disabledIconColor : props.theme.iconColor}; + cursor: ${(props) => + props.disabled && (props.onClick || props.onClick) ? "not-allowed" : props.interactuable ? "pointer" : ""}; ${(props) => props.interactuable && ` diff --git a/lib/src/chip/types.ts b/lib/src/chip/types.ts index f79af26ca..cd9c3cb18 100644 --- a/lib/src/chip/types.ts +++ b/lib/src/chip/types.ts @@ -8,31 +8,60 @@ type Margin = { type SVG = React.ReactNode & React.SVGProps<SVGSVGElement>; -type Props = { +type Icon = string | SVG; + +type PrefixIconProps = + | { + /** + * Element or path used as icon to be placed before the chip label. + */ + prefixIcon: Icon; + /** + * This function will be called when the prefix is clicked. + */ + onClickPrefix: () => void; + /** + * Element or path used as icon to be placed after the chip label. + */ + suffixIcon?: Icon; + /** + * This function will be called when the suffix is clicked. + */ + onClickSuffix?: never; + /** + * If true, the action icon will be disabled. + */ + disabled?: boolean; + } + | { + prefixIcon?: Icon; + onClickPrefix?: never; + suffixIcon?: never; + onClickSuffix?: never; + disabled?: never; + }; + +type SuffixIconProps = + | { + suffixIcon: Icon; + onClickSuffix: () => void; + prefixIcon?: Icon; + onClickPrefix?: never; + disabled?: boolean; + } + | { + suffixIcon?: Icon; + onClickSuffix?: never; + prefixIcon?: never; + onClickPrefix?: never; + disabled?: never; + }; + +type CommonProps = { /** * Text to be placed on the chip. */ label?: string; - /** - * Element or path used as icon to be placed after the chip label. - */ - suffixIcon?: string | SVG; - /** - * Element or path used as icon to be placed before the chip label. - */ - prefixIcon?: string | SVG; - /** - * This function will be called when the suffix is clicked. - */ - onClickSuffix?: () => void; - /** - * This function will be called when the prefix is clicked. - */ - onClickPrefix?: () => void; - /** - * If true, the component will be disabled. - */ - disabled?: boolean; /** * Size of the margin to be applied to the component ('xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge'). * You can pass an object with 'top', 'bottom', 'left' and 'right' properties in order to specify different margin sizes. @@ -44,4 +73,6 @@ type Props = { tabIndex?: number; }; +type Props = (PrefixIconProps | SuffixIconProps) & CommonProps; + export default Props; diff --git a/lib/src/common/variables.ts b/lib/src/common/variables.ts index fd745a7d3..c28e34dd7 100644 --- a/lib/src/common/variables.ts +++ b/lib/src/common/variables.ts @@ -193,13 +193,11 @@ export const componentTokens = { }, chip: { backgroundColor: CoreTokens.color_grey_200, - disabledBackgroundColor: CoreTokens.color_grey_100, fontFamily: CoreTokens.type_sans, fontSize: CoreTokens.type_scale_03, fontStyle: CoreTokens.type_normal, fontWeight: CoreTokens.type_regular, fontColor: CoreTokens.color_black, - disabledFontColor: CoreTokens.color_grey_500, borderColor: CoreTokens.color_transparent, borderRadius: "80px", borderThickness: CoreTokens.border_width_0,