From 2ce4128453482d79bb5eb2ab239792388d14ddb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 7 Mar 2025 15:18:40 +0100 Subject: [PATCH 01/15] Toggle Group redesign --- .../pages/components/toggle-group/code.tsx | 17 + .../pages/components/toggle-group/index.tsx | 24 +- .../toggle-group/specifications.tsx | 21 - .../pages/components/toggle-group/usage.tsx | 21 - .../toggle-group/ToggleGroupPageLayout.tsx | 12 +- .../toggle-group/code/ToggleGroupCodePage.tsx | 120 ++-- .../ToggleGroupOverviewPage.tsx} | 22 +- .../{usage => overview}/examples/icons.tsx | 2 +- .../{usage => overview}/examples/variants.ts | 2 +- .../specs/ToggleGroupSpecsPage.tsx | 680 ------------------ .../specs/images/toggle_group_anatomy.png | Bin 16855 -> 0 bytes .../specs/images/toggle_group_specs.png | Bin 26339 -> 0 bytes .../specs/images/toggle_group_states.png | Bin 34661 -> 0 bytes .../src/toggle-group/ToggleGroup.stories.tsx | 131 ++-- .../lib/src/toggle-group/ToggleGroup.test.tsx | 15 - packages/lib/src/toggle-group/ToggleGroup.tsx | 335 ++++----- packages/lib/src/toggle-group/types.ts | 30 +- 17 files changed, 308 insertions(+), 1124 deletions(-) create mode 100644 apps/website/pages/components/toggle-group/code.tsx delete mode 100644 apps/website/pages/components/toggle-group/specifications.tsx delete mode 100644 apps/website/pages/components/toggle-group/usage.tsx rename apps/website/screens/components/toggle-group/{usage/ToggleGroupUsagePage.tsx => overview/ToggleGroupOverviewPage.tsx} (84%) rename apps/website/screens/components/toggle-group/{usage => overview}/examples/icons.tsx (97%) rename apps/website/screens/components/toggle-group/{usage => overview}/examples/variants.ts (94%) delete mode 100644 apps/website/screens/components/toggle-group/specs/ToggleGroupSpecsPage.tsx delete mode 100644 apps/website/screens/components/toggle-group/specs/images/toggle_group_anatomy.png delete mode 100644 apps/website/screens/components/toggle-group/specs/images/toggle_group_specs.png delete mode 100644 apps/website/screens/components/toggle-group/specs/images/toggle_group_states.png diff --git a/apps/website/pages/components/toggle-group/code.tsx b/apps/website/pages/components/toggle-group/code.tsx new file mode 100644 index 0000000000..7056fd0a90 --- /dev/null +++ b/apps/website/pages/components/toggle-group/code.tsx @@ -0,0 +1,17 @@ +import Head from "next/head"; +import type { ReactElement } from "react"; +import ToggleGroupPageLayout from "screens/components/toggle-group/ToggleGroupPageLayout"; +import ToggleGroupCodePage from "screens/components/toggle-group/code/ToggleGroupCodePage"; + +const Code = () => ( + <> + + Toggle group code — Halstack Design System + + + +); + +Code.getLayout = (page: ReactElement) => {page}; + +export default Code; diff --git a/apps/website/pages/components/toggle-group/index.tsx b/apps/website/pages/components/toggle-group/index.tsx index 57fd486e43..e22f16103c 100644 --- a/apps/website/pages/components/toggle-group/index.tsx +++ b/apps/website/pages/components/toggle-group/index.tsx @@ -1,21 +1,17 @@ import Head from "next/head"; import type { ReactElement } from "react"; import ToggleGroupPageLayout from "screens/components/toggle-group/ToggleGroupPageLayout"; -import ToggleGroupCodePage from "screens/components/toggle-group/code/ToggleGroupCodePage"; +import ToggleGroupOverviewPage from "screens/components/toggle-group/overview/ToggleGroupOverviewPage"; -const Index = () => { - return ( - <> - - Toggle Group — Halstack Design System - - - - ); -}; +const Index = () => ( + <> + + Toggle Group — Halstack Design System + + + +); -Index.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; +Index.getLayout = (page: ReactElement) => {page}; export default Index; diff --git a/apps/website/pages/components/toggle-group/specifications.tsx b/apps/website/pages/components/toggle-group/specifications.tsx deleted file mode 100644 index 07a9ecac8d..0000000000 --- a/apps/website/pages/components/toggle-group/specifications.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import ToggleGroupPageLayout from "screens/components/toggle-group/ToggleGroupPageLayout"; -import ToggleGroupSpecsPage from "screens/components/toggle-group/specs/ToggleGroupSpecsPage"; - -const Specifications = () => { - return ( - <> - - Toggle Group Specs — Halstack Design System - - - - ); -}; - -Specifications.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Specifications; diff --git a/apps/website/pages/components/toggle-group/usage.tsx b/apps/website/pages/components/toggle-group/usage.tsx deleted file mode 100644 index df03c22448..0000000000 --- a/apps/website/pages/components/toggle-group/usage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import Head from "next/head"; -import type { ReactElement } from "react"; -import ToggleGroupPageLayout from "screens/components/toggle-group/ToggleGroupPageLayout"; -import ToggleGroupUsagePage from "screens/components/toggle-group/usage/ToggleGroupUsagePage"; - -const Usage = () => { - return ( - <> - - ToggleGroup Usage — Halstack Design System - - - - ); -}; - -Usage.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - -export default Usage; diff --git a/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx b/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx index e38d6e79d9..bcf66467ca 100644 --- a/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx +++ b/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx @@ -6,26 +6,22 @@ import { ReactNode } from "react"; const ToggleGroupPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ - { label: "Code", path: "/components/toggle-group" }, - { label: "Usage", path: "/components/toggle-group/usage" }, - { - label: "Specifications", - path: "/components/toggle-group/specifications", - }, + { label: "Overview", path: "/components/toggle-group" }, + { label: "Code", path: "/components/toggle-group/code" }, ]; return ( - + Toggle buttons can be used to put together related options that share a common attribute modification. It allows the user to switch from one selected option to another in the same control, having one option selected at a time. Also, there can be another variation that allows selecting multiple options from the current toggle group. - + {children} diff --git a/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx b/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx index 86c4ef9c8c..b14c2d945d 100644 --- a/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx +++ b/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx @@ -1,4 +1,4 @@ -import { DxcFlex, DxcLink, DxcTable } from "@dxc-technology/halstack-react"; +import { DxcFlex, DxcTable } from "@dxc-technology/halstack-react"; import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; import QuickNavContainer from "@/common/QuickNavContainer"; import Code from "@/common/Code"; @@ -7,7 +7,16 @@ import Example from "@/common/example/Example"; import controlled from "./examples/controlled"; import uncontrolled from "./examples/uncontrolled"; import StatusBadge from "@/common/StatusBadge"; -import TableCode from "@/common/TableCode"; +import TableCode, { ExtendedTableCode } from "@/common/TableCode"; + +const optionTypeString = `{ + disabled?: boolean; + icon?: string | + (React.ReactNode + & React.SVGProps); + label?: string; + value: string; +}`; const sections = [ { @@ -32,15 +41,11 @@ const sections = [ - - value - - number | number[] - + helperText - The key(s) of the selected value(s). If the toggle group component doesn't allow multiple selection, it - must be one unique value. If the component allows multiple selection, value must be an array. If - undefined, the component will be uncontrolled and the value will be managed internally by the component. + string + Helper text to be placed above the component. - @@ -52,61 +57,16 @@ const sections = [ - - helperText - - string - - Helper text to be placed above the component. - - - - + margin - - - options - + 'xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge' | Margin - - { - "{ label?: string; icon: string | (React.ReactNode & React.SVGProps ); value: string; title?: string; }[]" - } - - - - An array of objects representing the selectable options. Each object has the following properties: -
    -
  • - label: String with the option display value. -
  • -
  • - icon:{" "} - - Material Symbol - {" "} - name or SVG element used as the icon of an option. -
  • -
  • - value: Number with the option inner value. -
  • -
  • - title: Text representing advisory information related to an option. Under the hood, it also - serves as an accessible label for the icon. -
  • -
+ Size of the margin to be applied to the component. You can pass an object with 'top', 'bottom', 'left' and + 'right' properties in order to specify different margin sizes. - - - disabled - - boolean - - If true, the component will be disabled. - - false - - multiple @@ -133,14 +93,20 @@ const sections = [ - - margin - 'xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge' | Margin + + + options + - Size of the margin to be applied to the component. You can pass an object with 'top', 'bottom', 'left' and - 'right' properties in order to specify different margin sizes. + Option[] +

+ being Option an object with the following properties: +

+ {optionTypeString} + An array of objects representing the selectable options. - @@ -155,6 +121,18 @@ const sections = [ 0 + + value + + number | number[] + + + The key(s) of the selected value(s). If the toggle group component doesn't allow multiple selection, it + must be one unique value. If the component allows multiple selection, value must be an array. If + undefined, the component will be uncontrolled and the value will be managed internally by the component. + + - + ), @@ -174,15 +152,13 @@ const sections = [ }, ]; -const ToggleGroupCodePage = () => { - return ( - - - - - - - ); -}; +const ToggleGroupCodePage = () => ( + + + + + + +); export default ToggleGroupCodePage; diff --git a/apps/website/screens/components/toggle-group/usage/ToggleGroupUsagePage.tsx b/apps/website/screens/components/toggle-group/overview/ToggleGroupOverviewPage.tsx similarity index 84% rename from apps/website/screens/components/toggle-group/usage/ToggleGroupUsagePage.tsx rename to apps/website/screens/components/toggle-group/overview/ToggleGroupOverviewPage.tsx index ceb684e0e3..3d88ff33fd 100644 --- a/apps/website/screens/components/toggle-group/usage/ToggleGroupUsagePage.tsx +++ b/apps/website/screens/components/toggle-group/overview/ToggleGroupOverviewPage.tsx @@ -9,7 +9,7 @@ import Code from "@/common/Code"; const sections = [ { - title: "Usage", + title: "Introduction", content: ( <> Toggles should be used in place of radio buttons whenever the options are: @@ -68,15 +68,13 @@ const sections = [ }, ]; -const ToggleGroupUsagePage = () => { - return ( - - - - - - - ); -}; +const ToggleGroupOverviewPage = () => ( + + + + + + +); -export default ToggleGroupUsagePage; +export default ToggleGroupOverviewPage; diff --git a/apps/website/screens/components/toggle-group/usage/examples/icons.tsx b/apps/website/screens/components/toggle-group/overview/examples/icons.tsx similarity index 97% rename from apps/website/screens/components/toggle-group/usage/examples/icons.tsx rename to apps/website/screens/components/toggle-group/overview/examples/icons.tsx index f4d76756f6..f425a05f5b 100644 --- a/apps/website/screens/components/toggle-group/usage/examples/icons.tsx +++ b/apps/website/screens/components/toggle-group/overview/examples/icons.tsx @@ -67,7 +67,7 @@ const code = `() => { return ( - + { return ( - + - Toggle design specifications - - ), - }, - { - title: "States", - content: ( - <> - - Different states are defined in the life cycle of the component: unselected enabled,{" "} - unselected hover, unselected focus, unselected active,{" "} - unselected disabled, selected enabled, selected hover,{" "} - selected focus, selected active and selected disabled - -
- Toggle button states -
- - ), - }, - { - title: "Anatomy", - content: ( - <> - Toggle group anatomy - - Label - Helper text - Container - Button - Button icon - Button label - - - ), - }, - { - title: "Design tokens", - subSections: [ - { - title: "Color", - content: ( - - - - Component token - Element - Core token - Value - - - - - - labelFontColor - - Label - - color-black - - #000000 - - - - disabledLabelFontColor - - Label:disabled - - color-grey-500 - - #999999 - - - - helperTextFontColor - - Helper text - - color-black - - #000000 - - - - disabledHelperTextFontColor - - Helper text:disabled - - color-grey-500 - - #999999 - - - - containerBackgroundColor - - Container - - color-grey-50 - - #fafafa - - - - containerBorderColor - - Container - - color-grey-500 - - #999999 - - - - unselectedBackgroundColor - - Button fill:enabled - - color-grey-200 - - #e6e6e6 - - - - unselectedHoverBackgroundColor - - Button fill:hover - - color-grey-300 - - #cccccc - - - - unselectedActiveBackgroundColor - - Button fill:active - - color-purple-700 - - #5f249f - - - - unselectedDisabledBackgroundColor - - Button fill:disabled - - color-grey-100 - - #f2f2f2 - - - - unselectedFontColor - - Button label - - color-black - - #000000 - - - - unselectedDisabledFontColor - - Button label:disabled - - color-grey-500 - - #999999 - - - - selectedBackgroundColor - - Button fill:enabled - - color-purple-700 - - #5f249f - - - - selectedHoverBackgroundColor - - Button fill:hover - - color-purple-800 - - #4b1c7d - - - - selectedActiveBackgroundColor - - Button fill:active - - color-purple-900 - - #321353 - - - - selectedDisabledBackgroundColor - - Button fill:disabled - - color-purple-100 - - #f2eafa - - - - selectedFontColor - - Button label - - color-white - - #ffffff - - - - selectedDisabledFontColor - - Button label:disabled - - color-purple-300 - - #cbacec - - - - focusColor - - Focus indicator - - color-blue-600 - - #0095ff - - - - ), - }, - { - title: "Typography", - content: ( - - - - Component token - Element - Core token - Value - - - - - - labelFontFamily - - Label - - font-family - - 'Open Sans', sans-serif - - - - labelFontSize - - Label - - font-scale-02 - - 0.875rem / 14px - - - - labelFontStyle - - Label - - font-style-normal - - normal - - - - labelFontWeight - - Label - - font-weight-semibold - - 600 - - - - labelLineHeight - - Label - - font-leading-loose-01 - - 1.715em - - - - helperTextFontFamily - - Helper text - - font-family - - 'Open Sans', sans-serif - - - - helperTextFontSize - - Helper text - - font-scale-01 - - 0.75rem / 12px - - - - helperTextFontStyle - - Helper text - - font-style-normal - - normal - - - - helperTextFontWeight - - Helper text - - font-weight-regular - - 400 - - - - helperTextLineHeight - - Helper text - - font-leading-normal - - 1.5em - - - - optionLabelFontFamily - - Button label - - font-family - - 'Open Sans', sans-serif - - - - optionLabelFontSize - - Button label - - font-scale-03 - - 1rem / 16px - - - - optionLabelFontStyle - - Button label - - font-style-normal - - normal - - - - optionLabelFontWeight - - Button label - - font-weight-regular - - 400 - - - - ), - }, - { - title: "Spacing", - content: ( - - - - Component token - Element - Core token - Value - - - - - - iconPaddingRight - - Icon - - spacing-8 - - 0.5rem / 8px - - - - iconPaddingLeft - - Icon - - spacing-8 - - 0.5rem / 8px - - - - labelPaddingLeft - - Label (Label + icon) - - spacing-24 - - 1.5rem / 24px - - - - labelPaddingRight - - Label (Label + icon) - - spacing-24 - - 1.5rem / 24px - - - - iconMarginRight - - Icon (Label + icon) - - spacing-8 - - 0.5rem / 8px - - - - containerMarginTop - - Container - - spacing-4 - - 0.25rem / 4px - - - - ), - }, - { - title: "Border", - content: ( - - - - Property - Element - Core token - Value - - - - - - border-width - - Button - - border-width-0 - - 0rem / 0px - - - - border-style - - Button - - border-style-none - - none - - - - border-radius - - Button - - border-radius-medium - - 0.25rem / 4px - - - - border-width - - Container - - border-width-1 - - 1px - - - - border-style - - Container - - border-style-solid - - solid - - - - border-radius - - Container - - - 0.375rem / 6px - - - - border-width - - Focus border - - border-width-2 - - 2 - - - - border-style - - Focus border - - border-style-solid - - solid - - - - border-radius - - Focus border - - border-radius-medium - - 0.25rem / 4px - - - - ), - }, - { - title: "Margin", - content: ( - <> - - - - Margin - Value - - - - - - xxsmall - - 6px - - - - xsmall - - 16px - - - - small - - 24px - - - - medium - - 36px - - - - large - - 48px - - - - xlarge - - 64px - - - - xxlarge - - 100px - - - - - And also apply different values to each side of the component: - top, bottom, left and right - - - ), - }, - ], - }, -]; - -const ToggleGroupSpecsPage = () => { - return ( - - - - - - - ); -}; - -export default ToggleGroupSpecsPage; diff --git a/apps/website/screens/components/toggle-group/specs/images/toggle_group_anatomy.png b/apps/website/screens/components/toggle-group/specs/images/toggle_group_anatomy.png deleted file mode 100644 index 3d8123ea2fcdbbfbd767a8006029ac0655e72973..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16855 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 diff --git a/apps/website/screens/components/toggle-group/specs/images/toggle_group_states.png b/apps/website/screens/components/toggle-group/specs/images/toggle_group_states.png deleted file mode 100644 index 788d80398d7d64ea435c4144753f573e96467c8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34661 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 ( <> - - - <DxcToggleGroup label="Toggle group" helperText="HelperText" options={options} /> + <Title title="Unselected" theme="light" level={3} /> + <ExampleContainer pseudoState="pseudo-hover"> + <Title title="Hover" theme="light" level={4} /> + <DxcToggleGroup label="Hover" options={oneOption} /> + </ExampleContainer> + <ExampleContainer pseudoState="pseudo-active"> + <Title title="Active" theme="light" level={4} /> + <DxcToggleGroup label="Active" options={oneOption} /> + </ExampleContainer> + <ExampleContainer pseudoState="pseudo-focus"> + <Title title="Focus" theme="light" level={4} /> + <DxcToggleGroup label="Focus" options={oneOption} /> + </ExampleContainer> + <Title title="Selected" theme="light" level={3} /> + <ExampleContainer pseudoState="pseudo-hover"> + <Title title="Hover" theme="light" level={4} /> + <DxcToggleGroup label="Hover" options={oneOption} defaultValue={1} /> + </ExampleContainer> + <ExampleContainer pseudoState="pseudo-active"> + <Title title="Active" theme="light" level={4} /> + <DxcToggleGroup label="Active" options={oneOption} defaultValue={1} /> + </ExampleContainer> + <ExampleContainer pseudoState="pseudo-focus"> + <Title title="Focus" theme="light" level={4} /> + <DxcToggleGroup label="Focus" options={oneOption} defaultValue={1} /> </ExampleContainer> <ExampleContainer> - <Title title="Selected" theme="light" level={4} /> - <DxcToggleGroup label="Selected" helperText="HelperText" defaultValue={2} options={options} /> + <Title title="Labelled options" theme="light" level={4} /> + <DxcToggleGroup label="Toggle group" helperText="HelperText" options={options} /> </ExampleContainer> <ExampleContainer> - <Title title="Icons toggle group" theme="light" level={4} /> + <Title title="Icons only" theme="light" level={4} /> <DxcToggleGroup label="Icons group" options={optionsWithIcon} /> </ExampleContainer> <ExampleContainer> - <Title title="Icons & label toggle group" theme="light" level={4} /> + <Title title="Icons & label" theme="light" level={4} /> <DxcToggleGroup label="Icons & label" options={optionsWithIconAndLabel} /> </ExampleContainer> <ExampleContainer> - <Title title="Disabled" theme="light" level={4} /> - <DxcToggleGroup label="Disabled" defaultValue={2} options={options} disabled /> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-hover"> - <Title title="Hovered" theme="light" level={4} /> - <DxcToggleGroup label="Hovered" options={twoOptions} defaultValue={2} /> + <Title title="Disabled option" theme="light" level={4} /> + <DxcToggleGroup label="Disabled" defaultValue={2} options={disabledOptions} /> </ExampleContainer> <ExampleContainer> - <Title title="Multiple toggleGroup" theme="light" level={4} /> + <Title title="Multiple options selected" theme="light" level={4} /> <DxcToggleGroup label="Toggle group" helperText="Please select one or more" @@ -168,63 +185,11 @@ const ToggleGroup = () => ( <Title title="xxLarge" theme="light" level={4} /> <DxcToggleGroup label="xxLarge margin" options={options} margin="xxlarge" /> </ExampleContainer> - <Title title="Opinionated theme" theme="light" level={2} /> - <ExampleContainer> - <HalstackProvider theme={opinionatedTheme}> - <Title title="Selected" theme="light" level={4} /> - <DxcToggleGroup label="Selected" helperText="HelperText" defaultValue={2} options={options} /> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer> - <HalstackProvider theme={opinionatedTheme}> - <Title title="Icons & label toggle group" theme="light" level={4} /> - <DxcToggleGroup label="Icons & label" options={optionsWithIconAndLabel} /> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer> - <HalstackProvider theme={opinionatedTheme}> - <Title title="Disabled" theme="light" level={4} /> - <DxcToggleGroup label="Disabled" defaultValue={2} options={options} disabled /> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-hover"> - <Title title="Hovered" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcToggleGroup label="Hovered" options={twoOptions} defaultValue={2} /> - </HalstackProvider> - </ExampleContainer> - <ExampleContainer pseudoState="pseudo-active"> - <Title title="Actived" theme="light" level={4} /> - <HalstackProvider theme={opinionatedTheme}> - <DxcToggleGroup label="Actived" options={twoOptions} defaultValue={2} /> - </HalstackProvider> - </ExampleContainer> </> ); -const OptionSelected = () => <DxcToggleGroup label="Toggle group" helperText="HelperText" options={options} />; - type Story = StoryObj<typeof DxcToggleGroup>; export const Chromatic: Story = { render: ToggleGroup, }; - -export const ToggleGroupSelectedActived: Story = { - render: OptionSelected, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const option = canvas.getByText("Linkedin"); - await userEvent.click(option); - }, -}; - -export const ToggleGroupUnselectedActived: Story = { - render: OptionSelected, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const option = canvas.getByText("X"); - await userEvent.click(option); - userEvent.tab(); - }, -}; diff --git a/packages/lib/src/toggle-group/ToggleGroup.test.tsx b/packages/lib/src/toggle-group/ToggleGroup.test.tsx index eb8c3be84d..268bcc6757 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.test.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.test.tsx @@ -32,7 +32,6 @@ describe("Toggle group component tests", () => { expect(getByText("Apple")).toBeTruthy(); expect(getByText("Google")).toBeTruthy(); }); - test("Toggle group renders with correct aria-label in only-icon scenario", () => { const { getByRole } = render( <DxcToggleGroup @@ -45,7 +44,6 @@ describe("Toggle group component tests", () => { ); expect(getByRole("button").getAttribute("aria-label")).toBe("Mute"); }); - test("Uncontrolled toggle group calls correct function on change with value", () => { const onChange = jest.fn(); const { getByText } = render(<DxcToggleGroup options={options} onChange={onChange} />); @@ -53,7 +51,6 @@ describe("Toggle group component tests", () => { fireEvent.click(option); expect(onChange).toHaveBeenCalledWith(2); }); - test("Controlled toggle group calls correct function on change with value", () => { const onChange = jest.fn(); const { getByText } = render(<DxcToggleGroup options={options} onChange={onChange} value={1} />); @@ -61,15 +58,6 @@ describe("Toggle group component tests", () => { fireEvent.click(option); expect(onChange).toHaveBeenCalledWith(2); }); - - test("Function on change is not called when disable", () => { - const onChange = jest.fn(); - const { getByText } = render(<DxcToggleGroup options={options} onChange={onChange} disabled />); - const option = getByText("Ebay"); - fireEvent.click(option); - expect(onChange).toHaveBeenCalledTimes(0); - }); - test("Uncontrolled multiple toggle group calls correct function on change with value when is multiple", () => { const onChange = jest.fn(); const { getAllByRole } = render(<DxcToggleGroup options={options} onChange={onChange} multiple />); @@ -83,7 +71,6 @@ describe("Toggle group component tests", () => { expect(toggleOptions[1]?.getAttribute("aria-pressed")).toBe("true"); expect(toggleOptions[3]?.getAttribute("aria-pressed")).toBe("true"); }); - test("Controlled multiple toggle returns always same values", () => { const onChange = jest.fn(); const { getByText } = render(<DxcToggleGroup options={options} onChange={onChange} value={[1]} multiple />); @@ -94,13 +81,11 @@ describe("Toggle group component tests", () => { fireEvent.click(option2); expect(onChange).toHaveBeenNthCalledWith(2, [1, 4]); }); - test("Single selection: Renders with correct default value", () => { const { getAllByRole } = render(<DxcToggleGroup options={options} defaultValue={2} />); const toggleOptions = getAllByRole("button"); expect(toggleOptions[1]?.getAttribute("aria-pressed")).toBe("true"); }); - test("Multiple selection: Renders with correct default value", () => { const { getAllByRole } = render(<DxcToggleGroup options={options} defaultValue={[2, 4]} multiple />); const toggleOptions = getAllByRole("button"); diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index 6ef06f8b4d..d2ca2a178a 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -1,30 +1,116 @@ -import { KeyboardEvent, useContext, useId, useState } from "react"; -import styled, { ThemeProvider } from "styled-components"; +import { KeyboardEvent, useId, useState } from "react"; +import styled from "styled-components"; import { spaces } from "../common/variables"; -import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; -import HalstackContext from "../HalstackContext"; -import ToggleGroupPropsType, { OptionLabel } from "./types"; +import ToggleGroupPropsType from "./types"; +import DxcGrid from "../grid/Grid"; -const DxcToggleGroup = ({ - label, - helperText, +const ToggleGroupContainer = styled.div<{ margin: ToggleGroupPropsType["margin"] }>` + display: grid; + gap: var(--spacing-gap-xs); + margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; + margin-top: ${(props) => + props.margin && typeof props.margin === "object" && props.margin.top ? spaces[props.margin.top] : ""}; + margin-right: ${(props) => + props.margin && typeof props.margin === "object" && props.margin.right ? spaces[props.margin.right] : ""}; + margin-bottom: ${(props) => + 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] : ""}; +`; + +const Label = styled.label` + color: var(--color-fg-neutral-dark); + font-family: var(--typography-font-family); + font-size: var(--typography-label-m); + font-weight: var(--typography-label-semibold); +`; + +const HelperText = styled.span` + color: var(--color-fg-neutral-dark); + font-family: var(--typography-font-family); + font-size: var(--typography-helper-text-s); + font-weight: var(--typography-helper-text-regular); +`; + +const ToggleGroup = styled.div` + display: flex; + width: fit-content; + padding: var(--spacing-padding-xxs); + gap: var(--spacing-gap-xs); + border-radius: var(--border-radius-m); + border: var(--border-width-s) var(--border-style-default) var(--border-color-neutral-strong); +`; + +const ToggleButton = styled.button<{ + selected: boolean; + onlyIcon: boolean; +}>` + display: flex; + align-items: center; + gap: var(--spacing-gap-s); + height: var(--height-xl); + padding: ${({ onlyIcon }) => + onlyIcon ? "var(--spacing-padding-xs)" : "var(--spacing-padding-none) var(--spacing-padding-m)"}; + border: none; + border-radius: var(--border-radius-s); + background-color: ${({ selected }) => + selected ? "var(--color-bg-primary-strong);" : "var(--color-bg-neutral-medium)"}; + color: ${({ selected }) => (selected ? "var(--color-fg-neutral-bright)" : "var(--color-fg-neutral-dark)")}; + cursor: pointer; + + &:hover:enabled { + background-color: ${({ selected }) => + selected ? "var(--color-bg-neutral-medium)" : "var(--color-bg-neutral-strong)"}; + ${({ selected }) => selected && "color: var(--color-fg-neutral-dark);"} + } + &:active:enabled { + background-color: ${({ selected }) => + selected ? "var(--color-bg-primary-strongest);" : "var(--color-bg-primary-strong);"}; + color: var(--color-fg-neutral-bright); + } + &:focus:enabled { + outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); + outline-offset: -2px; + } + &:disabled { + background-color: var(--color-bg-neutral-light); + color: var(--color-fg-neutral-medium); + cursor: not-allowed; + } +`; + +const ToggleButtonLabel = styled.span` + font-family: var(--typography-font-family); + font-size: var(--typography-label-l); + font-weight: var(--typography-label-regular); +`; + +const IconContainer = styled.div` + display: flex; + font-size: var(--height-s); + svg { + width: 24px; + height: var(--height-s); + } +`; + +export default function DxcToggleGroup({ defaultValue, - value, + helperText, + label, + margin, + multiple, onChange, - disabled = false, options, - margin, - multiple = false, tabIndex = 0, -}: ToggleGroupPropsType): JSX.Element => { + value, +}: ToggleGroupPropsType) { const toggleGroupLabelId = `label-toggle-group-${useId()}`; const [selectedValue, setSelectedValue] = useState(defaultValue ?? (multiple ? [] : -1)); - const colorsTheme = useContext(HalstackContext); - - const handleToggleChange = (selectedOption: number) => { + const handleOnChange = (selectedOption: number) => { let newSelectedOptions: number[] = []; if (value == null) { @@ -58,7 +144,7 @@ const DxcToggleGroup = ({ case "Enter": case " ": event.preventDefault(); - handleToggleChange(optionValue); + handleOnChange(optionValue); break; default: break; @@ -66,171 +152,54 @@ const DxcToggleGroup = ({ }; return ( - <ThemeProvider theme={colorsTheme.toggleGroup}> - <ToggleGroup margin={margin}> - <Label id={toggleGroupLabelId} disabled={disabled}> - {label} - </Label> - <HelperText disabled={disabled}>{helperText}</HelperText> - <OptionsContainer aria-labelledby={toggleGroupLabelId}> - {options.map((option, i) => ( - <Tooltip label={option.title} key={`toggle-${i}-${option.label}`}> - <ToggleButton - aria-label={option.title} - aria-pressed={ - multiple - ? value - ? Array.isArray(value) && value.includes(option.value) - : Array.isArray(selectedValue) && selectedValue.includes(option.value) - : value - ? option.value === value - : option.value === selectedValue - } - disabled={disabled} - onClick={() => { - handleToggleChange(option.value); - }} - onKeyDown={(event) => { - handleOnKeyDown(event, option.value); - }} - tabIndex={!disabled ? tabIndex : -1} - hasIcon={option.icon} - optionLabel={option.label ?? ""} - selected={ - multiple - ? value - ? Array.isArray(value) && value.includes(option.value) - : Array.isArray(selectedValue) && selectedValue.includes(option.value) - : value - ? option.value === value - : option.value === selectedValue - } - > - <DxcFlex alignItems="center"> - {option.icon && ( - <IconContainer optionLabel={option.label ?? ""}> - {typeof option.icon === "string" ? <DxcIcon icon={option.icon} /> : option.icon} - </IconContainer> - )} - {option.label && <LabelContainer>{option.label}</LabelContainer>} - </DxcFlex> - </ToggleButton> - </Tooltip> - ))} - </OptionsContainer> + <ToggleGroupContainer margin={margin}> + <DxcGrid> + <Label id={toggleGroupLabelId}>{label}</Label> + <HelperText>{helperText}</HelperText> + </DxcGrid> + <ToggleGroup aria-labelledby={toggleGroupLabelId}> + {options.map((option, i) => ( + <Tooltip label={option.title} key={`toggle-${i}-${option.label}`}> + <ToggleButton + aria-label={option.title} + aria-pressed={ + multiple + ? value + ? Array.isArray(value) && value.includes(option.value) + : Array.isArray(selectedValue) && selectedValue.includes(option.value) + : value + ? option.value === value + : option.value === selectedValue + } + disabled={option.disabled} + onClick={() => { + handleOnChange(option.value); + }} + onKeyDown={(event) => { + handleOnKeyDown(event, option.value); + }} + onlyIcon={!option.label && !!option.icon} + selected={ + multiple + ? value + ? Array.isArray(value) && value.includes(option.value) + : Array.isArray(selectedValue) && selectedValue.includes(option.value) + : value + ? option.value === value + : option.value === selectedValue + } + tabIndex={!option.disabled ? tabIndex : -1} + > + {option.icon && ( + <IconContainer> + {typeof option.icon === "string" ? <DxcIcon icon={option.icon} /> : option.icon} + </IconContainer> + )} + {option.label && <ToggleButtonLabel>{option.label}</ToggleButtonLabel>} + </ToggleButton> + </Tooltip> + ))} </ToggleGroup> - </ThemeProvider> + </ToggleGroupContainer> ); -}; - -const ToggleGroup = styled.div<{ margin: ToggleGroupPropsType["margin"] }>` - display: inline-flex; - flex-direction: column; - margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; - margin-top: ${(props) => - props.margin && typeof props.margin === "object" && props.margin.top ? spaces[props.margin.top] : ""}; - margin-right: ${(props) => - props.margin && typeof props.margin === "object" && props.margin.right ? spaces[props.margin.right] : ""}; - margin-bottom: ${(props) => - 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] : ""}; -`; - -const Label = styled.label<{ disabled: ToggleGroupPropsType["disabled"] }>` - color: ${(props) => (props.disabled ? props.theme.disabledLabelFontColor : props.theme.labelFontColor)}; - font-family: ${(props) => props.theme.labelFontFamily}; - font-size: ${(props) => props.theme.labelFontSize}; - font-style: ${(props) => props.theme.labelFontStyle}; - font-weight: ${(props) => props.theme.labelFontWeight}; - line-height: ${(props) => props.theme.labelLineHeight}; -`; - -const HelperText = styled.span<{ disabled: ToggleGroupPropsType["disabled"] }>` - color: ${(props) => (props.disabled ? props.theme.disabledHelperTextFontColor : props.theme.helperTextFontColor)}; - font-family: ${(props) => props.theme.helperTextFontFamily}; - font-size: ${(props) => props.theme.helperTextFontSize}; - font-style: ${(props) => props.theme.helperTextFontStyle}; - font-weight: ${(props) => props.theme.helperTextFontWeight}; - line-height: ${(props) => props.theme.helperTextLineHeight}; -`; - -const OptionsContainer = styled.div` - display: flex; - gap: 0.25rem; - width: max-content; - height: calc(48px - 4px - 4px); - padding: 0.25rem; - border-width: ${(props) => props.theme.containerBorderThickness}; - border-style: ${(props) => props.theme.containerBorderStyle}; - border-radius: ${(props) => props.theme.containerBorderRadius}; - border-color: ${(props) => props.theme.containerBorderColor}; - margin-top: ${(props) => props.theme.containerMarginTop}; - background-color: ${(props) => props.theme.containerBackgroundColor}; -`; - -const ToggleButton = styled.button<{ - selected: boolean; - hasIcon: OptionLabel["icon"]; - optionLabel: OptionLabel["label"]; -}>` - display: flex; - flex-direction: column; - justify-content: center; - padding-left: ${(props) => - (props.optionLabel && props.hasIcon) || (props.optionLabel && !props.hasIcon) - ? props.theme.labelPaddingLeft - : props.theme.iconPaddingLeft}; - padding-right: ${(props) => - (props.optionLabel && props.hasIcon) || (props.optionLabel && !props.hasIcon) - ? props.theme.labelPaddingRight - : props.theme.iconPaddingRight}; - border-width: ${(props) => props.theme.optionBorderThickness}; - border-style: ${(props) => props.theme.optionBorderStyle}; - border-radius: ${(props) => props.theme.optionBorderRadius}; - background-color: ${(props) => - props.selected ? props.theme.selectedBackgroundColor : props.theme.unselectedBackgroundColor}; - color: ${(props) => (props.selected ? props.theme.selectedFontColor : props.theme.unselectedFontColor)}; - cursor: pointer; - - &:hover { - background-color: ${(props) => - props.selected ? props.theme.selectedHoverBackgroundColor : props.theme.unselectedHoverBackgroundColor}; - } - &:active { - background-color: ${(props) => - props.selected ? props.theme.selectedActiveBackgroundColor : props.theme.unselectedActiveBackgroundColor}; - color: #ffffff; - } - &:focus { - outline: none; - box-shadow: ${(props) => `0 0 0 ${props.theme.optionFocusBorderThickness} ${props.theme.focusColor}`}; - } - &:disabled { - background-color: ${(props) => - props.selected ? props.theme.selectedDisabledBackgroundColor : props.theme.unselectedDisabledBackgroundColor}; - color: ${(props) => - props.selected ? props.theme.selectedDisabledFontColor : props.theme.unselectedDisabledFontColor}; - cursor: not-allowed; - } -`; - -const LabelContainer = styled.span` - font-family: ${(props) => props.theme.optionLabelFontFamily}; - font-size: ${(props) => props.theme.optionLabelFontSize}; - font-style: ${(props) => props.theme.optionLabelFontStyle}; - font-weight: ${(props) => props.theme.optionLabelFontWeight}; -`; - -const IconContainer = styled.div<{ optionLabel: OptionLabel["label"] }>` - display: flex; - margin-right: ${(props) => props.optionLabel && props.theme.iconMarginRight}; - overflow: hidden; - font-size: 24px; - svg { - height: 24px; - width: 24px; - } -`; - -export default DxcToggleGroup; +} diff --git a/packages/lib/src/toggle-group/types.ts b/packages/lib/src/toggle-group/types.ts index 1b713bda6a..dc55ea2ca5 100644 --- a/packages/lib/src/toggle-group/types.ts +++ b/packages/lib/src/toggle-group/types.ts @@ -1,36 +1,42 @@ import { Margin, SVG, Space } from "../common/utils"; type OptionIcon = { - /** - * String with the option display value. - */ - label?: never; /** * Material Symbols icon or SVG element. Icon and label can't be used at same time. */ icon: string | SVG; + /** + * String with the option display value. + */ + label?: never; /** * Value for the HTML properties title and aria-label. * When a label is defined, this prop can not be use. */ title: string; }; -export type OptionLabel = { - /** - * String with the option display value. - */ - label: string; + +type OptionLabel = { /** * Material Symbols icon or SVG element. Icon and label can't be used at same time. */ icon?: string | SVG; + /** + * String with the option display value. + */ + label: string; /** * Value for the HTML properties title and aria-label. * When a label is defined, this prop can not be use. */ title?: never; }; + type Option = { + /** + * If true, the option will be disabled. + */ + disabled?: boolean; /** * Number with the option inner value. */ @@ -46,10 +52,6 @@ type CommonProps = { * Helper text to be placed above the component. */ helperText?: string; - /** - * If true, the component will be disabled. - */ - disabled?: boolean; /** * An array of objects representing the selectable options. */ @@ -85,6 +87,7 @@ type SingleSelectionToggleGroup = CommonProps & { */ onChange?: (optionIndex: number) => void; }; + type MultipleSelectionToggleGroup = CommonProps & { /** * If true, the toggle group will support multiple selection. In that case, value must be an array of numbers with the keys of the selected values. @@ -105,6 +108,7 @@ type MultipleSelectionToggleGroup = CommonProps & { */ onChange?: (optionIndex: number[]) => void; }; + type Props = SingleSelectionToggleGroup | MultipleSelectionToggleGroup; export default Props; From 3ceac38b9bd7d69554499b22a381e5a074c63366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Mon, 10 Mar 2025 13:28:30 +0100 Subject: [PATCH 02/15] Removing label and helper Text props --- .../toggle-group/code/ToggleGroupCodePage.tsx | 16 --- .../src/toggle-group/ToggleGroup.stories.tsx | 44 +++--- .../lib/src/toggle-group/ToggleGroup.test.tsx | 8 +- packages/lib/src/toggle-group/ToggleGroup.tsx | 135 +++++++----------- packages/lib/src/toggle-group/types.ts | 60 ++++---- 5 files changed, 97 insertions(+), 166 deletions(-) diff --git a/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx b/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx index b14c2d945d..c9c43ed3d1 100644 --- a/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx +++ b/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx @@ -40,22 +40,6 @@ const sections = [ <td>The key(s) of the initially selected value(s), only when it is uncontrolled.</td> <td>-</td> </tr> - <tr> - <td>helperText</td> - <td> - <TableCode>string</TableCode> - </td> - <td>Helper text to be placed above the component.</td> - <td>-</td> - </tr> - <tr> - <td>label</td> - <td> - <TableCode>string</TableCode> - </td> - <td>Text to be placed above the component.</td> - <td>-</td> - </tr> <tr> <td>margin</td> <td> diff --git a/packages/lib/src/toggle-group/ToggleGroup.stories.tsx b/packages/lib/src/toggle-group/ToggleGroup.stories.tsx index c593d46724..3727df0841 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.stories.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.stories.tsx @@ -107,83 +107,77 @@ const ToggleGroup = () => ( <Title title="Unselected" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-hover"> <Title title="Hover" theme="light" level={4} /> - <DxcToggleGroup label="Hover" options={oneOption} /> + <DxcToggleGroup options={oneOption} /> </ExampleContainer> <ExampleContainer pseudoState="pseudo-active"> <Title title="Active" theme="light" level={4} /> - <DxcToggleGroup label="Active" options={oneOption} /> + <DxcToggleGroup options={oneOption} /> </ExampleContainer> <ExampleContainer pseudoState="pseudo-focus"> <Title title="Focus" theme="light" level={4} /> - <DxcToggleGroup label="Focus" options={oneOption} /> + <DxcToggleGroup options={oneOption} /> </ExampleContainer> <Title title="Selected" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-hover"> <Title title="Hover" theme="light" level={4} /> - <DxcToggleGroup label="Hover" options={oneOption} defaultValue={1} /> + <DxcToggleGroup options={oneOption} defaultValue={1} /> </ExampleContainer> <ExampleContainer pseudoState="pseudo-active"> <Title title="Active" theme="light" level={4} /> - <DxcToggleGroup label="Active" options={oneOption} defaultValue={1} /> + <DxcToggleGroup options={oneOption} defaultValue={1} /> </ExampleContainer> <ExampleContainer pseudoState="pseudo-focus"> <Title title="Focus" theme="light" level={4} /> - <DxcToggleGroup label="Focus" options={oneOption} defaultValue={1} /> + <DxcToggleGroup options={oneOption} defaultValue={1} /> </ExampleContainer> <ExampleContainer> - <Title title="Labelled options" theme="light" level={4} /> - <DxcToggleGroup label="Toggle group" helperText="HelperText" options={options} /> + <Title title="Label only" theme="light" level={4} /> + <DxcToggleGroup options={options} /> </ExampleContainer> <ExampleContainer> <Title title="Icons only" theme="light" level={4} /> - <DxcToggleGroup label="Icons group" options={optionsWithIcon} /> + <DxcToggleGroup options={optionsWithIcon} /> </ExampleContainer> <ExampleContainer> <Title title="Icons & label" theme="light" level={4} /> - <DxcToggleGroup label="Icons & label" options={optionsWithIconAndLabel} /> + <DxcToggleGroup options={optionsWithIconAndLabel} /> </ExampleContainer> <ExampleContainer> <Title title="Disabled option" theme="light" level={4} /> - <DxcToggleGroup label="Disabled" defaultValue={2} options={disabledOptions} /> + <DxcToggleGroup defaultValue={2} options={disabledOptions} /> </ExampleContainer> <ExampleContainer> <Title title="Multiple options selected" theme="light" level={4} /> - <DxcToggleGroup - label="Toggle group" - helperText="Please select one or more" - options={options} - defaultValue={[1, 3]} - multiple - ></DxcToggleGroup> + <DxcToggleGroup options={options} defaultValue={[1, 3]} multiple></DxcToggleGroup> </ExampleContainer> <Title title="Margins" theme="light" level={2} /> <ExampleContainer> <Title title="xxSmall" theme="light" level={4} /> - <DxcToggleGroup label="xxSmall margin" options={options} margin="xxsmall" /> + <DxcToggleGroup options={options} margin="xxsmall" /> </ExampleContainer> <ExampleContainer> <Title title="xSmall" theme="light" level={4} /> - <DxcToggleGroup label="xSmall margin" options={options} margin="xsmall" /> + <DxcToggleGroup options={options} margin="xsmall" /> </ExampleContainer> <ExampleContainer> <Title title="Small" theme="light" level={4} /> - <DxcToggleGroup label="Small margin" options={options} margin="small" /> + <DxcToggleGroup options={options} margin="small" /> </ExampleContainer> <ExampleContainer> <Title title="Medium" theme="light" level={4} /> - <DxcToggleGroup label="Medium margin" options={options} margin="medium" /> + <DxcToggleGroup options={options} margin="medium" /> </ExampleContainer> <ExampleContainer> <Title title="Large" theme="light" level={4} /> - <DxcToggleGroup label="Large margin" options={options} margin="large" /> + <DxcToggleGroup options={options} margin="large" /> </ExampleContainer> <ExampleContainer> <Title title="xLarge" theme="light" level={4} /> - <DxcToggleGroup label="xLarge margin" options={options} margin="xlarge" /> + <DxcToggleGroup options={options} margin="xlarge" /> </ExampleContainer> <ExampleContainer> <Title title="xxLarge" theme="light" level={4} /> - <DxcToggleGroup label="xxLarge margin" options={options} margin="xxlarge" /> + <DxcToggleGroup options={options} margin="xxlarge" /> </ExampleContainer> </> ); diff --git a/packages/lib/src/toggle-group/ToggleGroup.test.tsx b/packages/lib/src/toggle-group/ToggleGroup.test.tsx index 268bcc6757..ba957b2f7c 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.test.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.test.tsx @@ -22,11 +22,7 @@ const options = [ describe("Toggle group component tests", () => { test("Toggle group renders with correct labels", () => { - const { getByText } = render( - <DxcToggleGroup label="Toggle group label" helperText="Toggle group helper text" options={options} /> - ); - expect(getByText("Toggle group label")).toBeTruthy(); - expect(getByText("Toggle group helper text")).toBeTruthy(); + const { getByText } = render(<DxcToggleGroup options={options} />); expect(getByText("Amazon")).toBeTruthy(); expect(getByText("Ebay")).toBeTruthy(); expect(getByText("Apple")).toBeTruthy(); @@ -35,8 +31,6 @@ describe("Toggle group component tests", () => { test("Toggle group renders with correct aria-label in only-icon scenario", () => { const { getByRole } = render( <DxcToggleGroup - label="Toggle group label" - helperText="Toggle group helper text" options={[ { value: 1, icon: "https://cdn.icon-icons.com/icons2/2645/PNG/512/mic_mute_icon_159965.png", title: "Mute" }, ]} diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index d2ca2a178a..6f39e2ce4a 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -4,43 +4,20 @@ import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; import ToggleGroupPropsType from "./types"; -import DxcGrid from "../grid/Grid"; -const ToggleGroupContainer = styled.div<{ margin: ToggleGroupPropsType["margin"] }>` - display: grid; - gap: var(--spacing-gap-xs); - margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; - margin-top: ${(props) => - props.margin && typeof props.margin === "object" && props.margin.top ? spaces[props.margin.top] : ""}; - margin-right: ${(props) => - props.margin && typeof props.margin === "object" && props.margin.right ? spaces[props.margin.right] : ""}; - margin-bottom: ${(props) => - 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] : ""}; -`; - -const Label = styled.label` - color: var(--color-fg-neutral-dark); - font-family: var(--typography-font-family); - font-size: var(--typography-label-m); - font-weight: var(--typography-label-semibold); -`; - -const HelperText = styled.span` - color: var(--color-fg-neutral-dark); - font-family: var(--typography-font-family); - font-size: var(--typography-helper-text-s); - font-weight: var(--typography-helper-text-regular); -`; - -const ToggleGroup = styled.div` +const ToggleGroup = styled.div<{ margin: ToggleGroupPropsType["margin"] }>` display: flex; width: fit-content; padding: var(--spacing-padding-xxs); gap: var(--spacing-gap-xs); border-radius: var(--border-radius-m); border: var(--border-width-s) var(--border-style-default) var(--border-color-neutral-strong); + margin: ${({ margin }) => (margin && typeof margin !== "object" ? spaces[margin] : "0px")}; + margin-top: ${({ margin }) => (margin && typeof margin === "object" && margin.top ? spaces[margin.top] : "")}; + margin-right: ${({ margin }) => (margin && typeof margin === "object" && margin.right ? spaces[margin.right] : "")}; + margin-bottom: ${({ margin }) => + margin && typeof margin === "object" && margin.bottom ? spaces[margin.bottom] : ""}; + margin-left: ${({ margin }) => (margin && typeof margin === "object" && margin.left ? spaces[margin.left] : "")}; `; const ToggleButton = styled.button<{ @@ -62,8 +39,7 @@ const ToggleButton = styled.button<{ &:hover:enabled { background-color: ${({ selected }) => - selected ? "var(--color-bg-neutral-medium)" : "var(--color-bg-neutral-strong)"}; - ${({ selected }) => selected && "color: var(--color-fg-neutral-dark);"} + selected ? "var(--color-bg-primary-stronger)" : "var(--color-bg-neutral-strong)"}; } &:active:enabled { background-color: ${({ selected }) => @@ -98,8 +74,6 @@ const IconContainer = styled.div` export default function DxcToggleGroup({ defaultValue, - helperText, - label, margin, multiple, onChange, @@ -107,7 +81,6 @@ export default function DxcToggleGroup({ tabIndex = 0, value, }: ToggleGroupPropsType) { - const toggleGroupLabelId = `label-toggle-group-${useId()}`; const [selectedValue, setSelectedValue] = useState(defaultValue ?? (multiple ? [] : -1)); const handleOnChange = (selectedOption: number) => { @@ -152,54 +125,48 @@ export default function DxcToggleGroup({ }; return ( - <ToggleGroupContainer margin={margin}> - <DxcGrid> - <Label id={toggleGroupLabelId}>{label}</Label> - <HelperText>{helperText}</HelperText> - </DxcGrid> - <ToggleGroup aria-labelledby={toggleGroupLabelId}> - {options.map((option, i) => ( - <Tooltip label={option.title} key={`toggle-${i}-${option.label}`}> - <ToggleButton - aria-label={option.title} - aria-pressed={ - multiple - ? value - ? Array.isArray(value) && value.includes(option.value) - : Array.isArray(selectedValue) && selectedValue.includes(option.value) - : value - ? option.value === value - : option.value === selectedValue - } - disabled={option.disabled} - onClick={() => { - handleOnChange(option.value); - }} - onKeyDown={(event) => { - handleOnKeyDown(event, option.value); - }} - onlyIcon={!option.label && !!option.icon} - selected={ - multiple - ? value - ? Array.isArray(value) && value.includes(option.value) - : Array.isArray(selectedValue) && selectedValue.includes(option.value) - : value - ? option.value === value - : option.value === selectedValue - } - tabIndex={!option.disabled ? tabIndex : -1} - > - {option.icon && ( - <IconContainer> - {typeof option.icon === "string" ? <DxcIcon icon={option.icon} /> : option.icon} - </IconContainer> - )} - {option.label && <ToggleButtonLabel>{option.label}</ToggleButtonLabel>} - </ToggleButton> - </Tooltip> - ))} - </ToggleGroup> - </ToggleGroupContainer> + <ToggleGroup margin={margin}> + {options.map((option, i) => ( + <Tooltip label={option.title} key={`toggle-${i}-${option.label}`}> + <ToggleButton + aria-label={option.title} + aria-pressed={ + multiple + ? value + ? Array.isArray(value) && value.includes(option.value) + : Array.isArray(selectedValue) && selectedValue.includes(option.value) + : value + ? option.value === value + : option.value === selectedValue + } + disabled={option.disabled} + onClick={() => { + handleOnChange(option.value); + }} + onKeyDown={(event) => { + handleOnKeyDown(event, option.value); + }} + onlyIcon={!option.label && !!option.icon} + selected={ + multiple + ? value + ? Array.isArray(value) && value.includes(option.value) + : Array.isArray(selectedValue) && selectedValue.includes(option.value) + : value + ? option.value === value + : option.value === selectedValue + } + tabIndex={!option.disabled ? tabIndex : -1} + > + {option.icon && ( + <IconContainer> + {typeof option.icon === "string" ? <DxcIcon icon={option.icon} /> : option.icon} + </IconContainer> + )} + {option.label && <ToggleButtonLabel>{option.label}</ToggleButtonLabel>} + </ToggleButton> + </Tooltip> + ))} + </ToggleGroup> ); } diff --git a/packages/lib/src/toggle-group/types.ts b/packages/lib/src/toggle-group/types.ts index dc55ea2ca5..f78f5fc362 100644 --- a/packages/lib/src/toggle-group/types.ts +++ b/packages/lib/src/toggle-group/types.ts @@ -45,70 +45,62 @@ type Option = { type CommonProps = { /** - * Text to be placed above the component. - */ - label?: string; - /** - * Helper text to be placed above the component. + * 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. */ - helperText?: string; + margin?: Space | Margin; /** * An array of objects representing the selectable options. */ options: Option[]; - /** - * 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. - */ - margin?: Space | Margin; /** * Value of the tabindex. */ tabIndex?: number; }; -type SingleSelectionToggleGroup = CommonProps & { +type MultipleSelectionToggleGroup = CommonProps & { /** - * If true, the toggle group will support multiple selection. In that case, value must be an array of numbers with the keys of the selected values. + * The array of keys with the initially selected values. */ - multiple?: false; + defaultValue?: number[]; /** - * The key of the initially selected value. + * If true, the toggle group will support multiple selection. In that case, value must be an array of numbers with the keys of the selected values. */ - defaultValue?: number; + multiple: true; /** - * The key of the selected value. If the component allows multiple selection, value must be an array. - * If undefined, the component will be uncontrolled and the value will be managed internally by the component. + * This function will be called every time the selection changes. An array with the key of + * the selected values will be passed as a parameter to this function. */ - value?: number; + onChange?: (optionIndex: number[]) => void; /** - * This function will be called every time the selection changes. The number with the key of the selected - * value will be passed as a parameter to this function. + * An array with the keys of the selected values. + * If undefined, the component will be uncontrolled and the value will be managed internally by the component. */ - onChange?: (optionIndex: number) => void; + value?: number[]; }; -type MultipleSelectionToggleGroup = CommonProps & { +type SingleSelectionToggleGroup = CommonProps & { /** - * If true, the toggle group will support multiple selection. In that case, value must be an array of numbers with the keys of the selected values. + * The key of the initially selected value. */ - multiple: true; + defaultValue?: number; /** - * The array of keys with the initially selected values. + * If true, the toggle group will support multiple selection. In that case, value must be an array of numbers with the keys of the selected values. */ - defaultValue?: number[]; + multiple?: false; /** - * An array with the keys of the selected values. - * If undefined, the component will be uncontrolled and the value will be managed internally by the component. + * This function will be called every time the selection changes. The number with the key of the selected + * value will be passed as a parameter to this function. */ - value?: number[]; + onChange?: (optionIndex: number) => void; /** - * This function will be called every time the selection changes. An array with the key of - * the selected values will be passed as a parameter to this function. + * The key of the selected value. If the component allows multiple selection, value must be an array. + * If undefined, the component will be uncontrolled and the value will be managed internally by the component. */ - onChange?: (optionIndex: number[]) => void; + value?: number; }; -type Props = SingleSelectionToggleGroup | MultipleSelectionToggleGroup; +type Props = MultipleSelectionToggleGroup | SingleSelectionToggleGroup; export default Props; From 4891487ab6e2671f2333ecdcef7fdbc1cdcb8ba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Mon, 10 Mar 2025 14:02:37 +0100 Subject: [PATCH 03/15] Accessibility updates --- .../ToggleGroup.accessibility.test.tsx | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/lib/src/toggle-group/ToggleGroup.accessibility.test.tsx b/packages/lib/src/toggle-group/ToggleGroup.accessibility.test.tsx index 19ad30c202..08a6bdb686 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.accessibility.test.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.accessibility.test.tsx @@ -45,31 +45,33 @@ const options = [ }, ]; +const disabledOption = [ + { + value: 1, + icon: wifiSVG, + title: "WiFi connection", + disabled: true, + }, + { + value: 2, + icon: ethernetSVG, + title: "Ethernet connection", + }, + { + value: 3, + icon: gMobileSVG, + title: "3G Mobile data connection", + }, +]; + describe("Toggle group component accessibility tests", () => { it("Should not have basic accessibility issues", async () => { - const { container } = render( - <DxcToggleGroup - label="Toggle group label" - helperText="Toggle group helper text" - options={options} - margin="medium" - defaultValue={[2]} - multiple - /> - ); + const { container } = render(<DxcToggleGroup options={options} margin="medium" defaultValue={[2]} multiple />); const results = await axe(container); expect(results).toHaveNoViolations(); }); it("Should not have basic accessibility issues for disabled mode", async () => { - const { container } = render( - <DxcToggleGroup - label="Toggle group label" - helperText="Toggle group helper text" - options={options} - margin="medium" - disabled - /> - ); + const { container } = render(<DxcToggleGroup options={disabledOption} margin="medium" />); const results = await axe(container); expect(results).toHaveNoViolations(); }); From a4398a2f770ea77d799991b404399394e2679411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Mon, 10 Mar 2025 14:11:00 +0100 Subject: [PATCH 04/15] Removing older props from Toggle Group --- .../components/toggle-group/code/examples/controlled.ts | 1 - .../components/toggle-group/code/examples/uncontrolled.ts | 1 - .../components/toggle-group/overview/examples/icons.tsx | 2 -- .../components/toggle-group/overview/examples/variants.ts | 4 ---- .../theme-generator/components/previews/ToggleGroup.tsx | 2 +- 5 files changed, 1 insertion(+), 9 deletions(-) diff --git a/apps/website/screens/components/toggle-group/code/examples/controlled.ts b/apps/website/screens/components/toggle-group/code/examples/controlled.ts index 744b0ba885..c8b7b9e44c 100644 --- a/apps/website/screens/components/toggle-group/code/examples/controlled.ts +++ b/apps/website/screens/components/toggle-group/code/examples/controlled.ts @@ -24,7 +24,6 @@ const code = `() => { return ( <DxcInset space="2rem"> <DxcToggleGroup - label="Choose a social network" options={options} onChange={onChange} value={value} diff --git a/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts b/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts index dfab6fd540..6eb42d5ebb 100644 --- a/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts +++ b/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts @@ -25,7 +25,6 @@ const code = `() => { return ( <DxcInset space="2rem"> <DxcToggleGroup - label="Choose a social network" defaultValue={2} options={options} onChange={onChange} diff --git a/apps/website/screens/components/toggle-group/overview/examples/icons.tsx b/apps/website/screens/components/toggle-group/overview/examples/icons.tsx index f425a05f5b..6cb1272007 100644 --- a/apps/website/screens/components/toggle-group/overview/examples/icons.tsx +++ b/apps/website/screens/components/toggle-group/overview/examples/icons.tsx @@ -69,12 +69,10 @@ const code = `() => { <DxcInset space="2rem"> <DxcFlex gap="2rem"> <DxcToggleGroup - label="Choose an Internet connection" options={options1} defaultValue={1} /> <DxcToggleGroup - label="Choose an Internet connection" options={options2} defaultValue={1} /> diff --git a/apps/website/screens/components/toggle-group/overview/examples/variants.ts b/apps/website/screens/components/toggle-group/overview/examples/variants.ts index 5b97ab55f1..ff50b05ce4 100644 --- a/apps/website/screens/components/toggle-group/overview/examples/variants.ts +++ b/apps/website/screens/components/toggle-group/overview/examples/variants.ts @@ -31,14 +31,10 @@ const code = `() => { <DxcInset space="2rem"> <DxcFlex gap="2rem"> <DxcToggleGroup - label="Single selection" - helperText="Mutually exclusive options" defaultValue={1} options={options1} /> <DxcToggleGroup - label="Multiple selection" - helperText="Mutually inclusive options" options={options2} defaultValue={[1, 2]} multiple diff --git a/apps/website/screens/theme-generator/components/previews/ToggleGroup.tsx b/apps/website/screens/theme-generator/components/previews/ToggleGroup.tsx index 90b4d74bec..293cf699ee 100644 --- a/apps/website/screens/theme-generator/components/previews/ToggleGroup.tsx +++ b/apps/website/screens/theme-generator/components/previews/ToggleGroup.tsx @@ -44,7 +44,7 @@ const ToggleGroup = () => ( <DxcToggleGroup options={options} /> </Mode> <Mode text="Disabled"> - <DxcToggleGroup disabled options={options} /> + <DxcToggleGroup options={options} /> </Mode> <Mode text="Multiple with icons"> <DxcToggleGroup options={optionsWithIcons} multiple /> From 796819cf8a624c2a34d704db4b30e9430d9c38f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Mon, 10 Mar 2025 17:13:08 +0100 Subject: [PATCH 05/15] New prop and fixes --- .../toggle-group/code/ToggleGroupCodePage.tsx | 10 ++++++ .../toggle-group/code/examples/controlled.ts | 8 ++--- .../code/examples/uncontrolled.ts | 34 +++++++++++++----- .../overview/examples/variants.ts | 2 +- .../src/toggle-group/ToggleGroup.stories.tsx | 35 +++++++++++++++---- packages/lib/src/toggle-group/ToggleGroup.tsx | 9 +++-- packages/lib/src/toggle-group/types.ts | 10 ++++-- 7 files changed, 82 insertions(+), 26 deletions(-) diff --git a/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx b/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx index c9c43ed3d1..b9ea54743a 100644 --- a/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx +++ b/apps/website/screens/components/toggle-group/code/ToggleGroupCodePage.tsx @@ -93,6 +93,16 @@ const sections = [ <td>An array of objects representing the selectable options.</td> <td>-</td> </tr> + <tr> + <td>orientation</td> + <td> + <TableCode>'horizontal' | 'vertical'</TableCode> + </td> + <td>The orientation of the toggle group.</td> + <td> + <TableCode>'horizontal'</TableCode> + </td> + </tr> <tr> <td>tabIndex</td> <td> diff --git a/apps/website/screens/components/toggle-group/code/examples/controlled.ts b/apps/website/screens/components/toggle-group/code/examples/controlled.ts index c8b7b9e44c..eff86e6d85 100644 --- a/apps/website/screens/components/toggle-group/code/examples/controlled.ts +++ b/apps/website/screens/components/toggle-group/code/examples/controlled.ts @@ -9,23 +9,23 @@ const code = `() => { const options = [ { value: 1, - label: "Facebook", + label: "Web", }, { value: 2, - label: "X", + label: "Android", }, { value: 3, - label: "Linkedin", + label: "iOS", }, ]; return ( <DxcInset space="2rem"> <DxcToggleGroup - options={options} onChange={onChange} + options={options} value={value} /> </DxcInset> diff --git a/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts b/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts index 6eb42d5ebb..6667b6fe7d 100644 --- a/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts +++ b/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts @@ -4,30 +4,46 @@ const code = `() => { const onChange = (newValue) => { console.log(newValue); }; - const options = [ + const optionsWithIcon = [ { value: 1, - label: "Facebook", - icon: "filled_thumb_up" + icon: "format_bold", + title: "Bold", }, { value: 2, - label: "X", - icon: "filled_raven" + icon: "format_italic", + title: "Italic", }, { value: 3, - label: "Linkedin", - icon: "filled_work" + icon: "format_underlined", + title: "Underlined", + }, + { + value: 4, + icon: "format_align_left", + title: "Align left", + }, + { + value: 5, + icon: "format_align_center", + title: "Align center", + }, + { + value: 6, + icon: "format_align_right", + title: "Align right", }, ]; return ( <DxcInset space="2rem"> <DxcToggleGroup - defaultValue={2} - options={options} + defaultValue={3} onChange={onChange} + options={options} + orientation="vertical" /> </DxcInset> ); diff --git a/apps/website/screens/components/toggle-group/overview/examples/variants.ts b/apps/website/screens/components/toggle-group/overview/examples/variants.ts index ff50b05ce4..efbc31a478 100644 --- a/apps/website/screens/components/toggle-group/overview/examples/variants.ts +++ b/apps/website/screens/components/toggle-group/overview/examples/variants.ts @@ -23,7 +23,7 @@ const code = `() => { }, { value: 3, - label: "Option 02", + label: "Option 03", }, ]; diff --git a/packages/lib/src/toggle-group/ToggleGroup.stories.tsx b/packages/lib/src/toggle-group/ToggleGroup.stories.tsx index 3727df0841..2b6711b0e0 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.stories.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.stories.tsx @@ -65,20 +65,35 @@ const disabledOptions = [ const optionsWithIcon = [ { value: 1, - icon: "wifi", - title: "WiFi connection", + label: "Bold", }, { value: 2, - icon: "filled_lan", - title: "Ethernet connection", + icon: "format_italic", + title: "Italic", }, { value: 3, - icon: "5g", - title: "3G Mobile data connection", + icon: "format_underlined", + title: "Underlined", + }, + { + value: 4, + icon: "format_align_left", + title: "Align left", + }, + { + value: 5, + icon: "format_align_center", + title: "Align center", + }, + { + value: 6, + icon: "format_align_right", + title: "Align right", }, ]; + const optionsWithIconAndLabel = [ { value: 1, @@ -96,12 +111,14 @@ const optionsWithIconAndLabel = [ icon: gMobileSVG, }, ]; + const oneOption = [ { value: 1, label: "Facebook", }, ]; + const ToggleGroup = () => ( <> <Title title="Unselected" theme="light" level={3} /> @@ -148,7 +165,11 @@ const ToggleGroup = () => ( </ExampleContainer> <ExampleContainer> <Title title="Multiple options selected" theme="light" level={4} /> - <DxcToggleGroup options={options} defaultValue={[1, 3]} multiple></DxcToggleGroup> + <DxcToggleGroup options={optionsWithIcon} defaultValue={[1, 3]} multiple /> + </ExampleContainer> + <ExampleContainer> + <Title title="Vertically stacked" theme="light" level={4} /> + <DxcToggleGroup defaultValue={3} options={optionsWithIcon} orientation="vertical" /> </ExampleContainer> <Title title="Margins" theme="light" level={2} /> <ExampleContainer> diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index 6f39e2ce4a..806b3aee60 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -1,4 +1,4 @@ -import { KeyboardEvent, useId, useState } from "react"; +import { KeyboardEvent, useState } from "react"; import styled from "styled-components"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; @@ -7,6 +7,9 @@ import ToggleGroupPropsType from "./types"; const ToggleGroup = styled.div<{ margin: ToggleGroupPropsType["margin"] }>` display: flex; + &[aria-orientation="vertical"] { + flex-direction: column; + } width: fit-content; padding: var(--spacing-padding-xxs); gap: var(--spacing-gap-xs); @@ -26,6 +29,7 @@ const ToggleButton = styled.button<{ }>` display: flex; align-items: center; + justify-content: center; gap: var(--spacing-gap-s); height: var(--height-xl); padding: ${({ onlyIcon }) => @@ -80,6 +84,7 @@ export default function DxcToggleGroup({ options, tabIndex = 0, value, + orientation = "horizontal", }: ToggleGroupPropsType) { const [selectedValue, setSelectedValue] = useState(defaultValue ?? (multiple ? [] : -1)); @@ -125,7 +130,7 @@ export default function DxcToggleGroup({ }; return ( - <ToggleGroup margin={margin}> + <ToggleGroup margin={margin} role="group" aria-orientation={orientation}> {options.map((option, i) => ( <Tooltip label={option.title} key={`toggle-${i}-${option.label}`}> <ToggleButton diff --git a/packages/lib/src/toggle-group/types.ts b/packages/lib/src/toggle-group/types.ts index f78f5fc362..fe055c35f4 100644 --- a/packages/lib/src/toggle-group/types.ts +++ b/packages/lib/src/toggle-group/types.ts @@ -53,13 +53,17 @@ type CommonProps = { * An array of objects representing the selectable options. */ options: Option[]; + /** + * The orientation of the toggle group. + */ + orientation?: "horizontal" | "vertical"; /** * Value of the tabindex. */ tabIndex?: number; }; -type MultipleSelectionToggleGroup = CommonProps & { +type MultipleSelectionToggleGroup = { /** * The array of keys with the initially selected values. */ @@ -80,7 +84,7 @@ type MultipleSelectionToggleGroup = CommonProps & { value?: number[]; }; -type SingleSelectionToggleGroup = CommonProps & { +type SingleSelectionToggleGroup = { /** * The key of the initially selected value. */ @@ -101,6 +105,6 @@ type SingleSelectionToggleGroup = CommonProps & { value?: number; }; -type Props = MultipleSelectionToggleGroup | SingleSelectionToggleGroup; +type Props = CommonProps & (MultipleSelectionToggleGroup | SingleSelectionToggleGroup); export default Props; From 42a86925e53c3e4e1198c51502ede6fc1ad5ff03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Tue, 11 Mar 2025 11:35:03 +0100 Subject: [PATCH 06/15] Aria fix --- packages/lib/src/toggle-group/ToggleGroup.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index 806b3aee60..f446541b61 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -130,7 +130,7 @@ export default function DxcToggleGroup({ }; return ( - <ToggleGroup margin={margin} role="group" aria-orientation={orientation}> + <ToggleGroup margin={margin} role="group"> {options.map((option, i) => ( <Tooltip label={option.title} key={`toggle-${i}-${option.label}`}> <ToggleButton From b187ed6702989fba2c7e3e07a9a133e78eaac3b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 28 Mar 2025 13:48:44 +0100 Subject: [PATCH 07/15] Toggle group orientation and code improvements --- packages/lib/src/toggle-group/ToggleGroup.tsx | 95 ++++++++----------- 1 file changed, 40 insertions(+), 55 deletions(-) diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index f446541b61..546068a176 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -24,8 +24,8 @@ const ToggleGroup = styled.div<{ margin: ToggleGroupPropsType["margin"] }>` `; const ToggleButton = styled.button<{ - selected: boolean; onlyIcon: boolean; + selected: boolean; }>` display: flex; align-items: center; @@ -61,7 +61,7 @@ const ToggleButton = styled.button<{ } `; -const ToggleButtonLabel = styled.span` +const Label = styled.span` font-family: var(--typography-font-family); font-size: var(--typography-label-l); font-weight: var(--typography-label-regular); @@ -76,6 +76,12 @@ const IconContainer = styled.div` } `; +const isToggleButtonSelected = ( + multiple: ToggleGroupPropsType["multiple"], + optionValue: number, + value: ToggleGroupPropsType["value"] +) => (multiple ? Array.isArray(value) && value.includes(optionValue) : optionValue === value); + export default function DxcToggleGroup({ defaultValue, margin, @@ -90,30 +96,22 @@ export default function DxcToggleGroup({ const handleOnChange = (selectedOption: number) => { let newSelectedOptions: number[] = []; - if (value == null) { if (multiple && Array.isArray(selectedValue)) { newSelectedOptions = selectedValue.map((singleValue) => singleValue); if (newSelectedOptions.includes(selectedOption)) { const index = newSelectedOptions.indexOf(selectedOption); newSelectedOptions.splice(index, 1); - } else { - newSelectedOptions.push(selectedOption); - } + } else newSelectedOptions.push(selectedOption); setSelectedValue(newSelectedOptions); - } else { - setSelectedValue(selectedOption === selectedValue ? -1 : selectedOption); - } + } else setSelectedValue(selectedOption === selectedValue ? -1 : selectedOption); } else if (multiple) { newSelectedOptions = Array.isArray(value) ? value.map((v) => v) : [value]; if (newSelectedOptions.includes(selectedOption)) { const index = newSelectedOptions.indexOf(selectedOption); newSelectedOptions.splice(index, 1); - } else { - newSelectedOptions.push(selectedOption); - } + } else newSelectedOptions.push(selectedOption); } - onChange?.((multiple ? newSelectedOptions : selectedOption) as number & number[]); }; @@ -130,48 +128,35 @@ export default function DxcToggleGroup({ }; return ( - <ToggleGroup margin={margin} role="group"> - {options.map((option, i) => ( - <Tooltip label={option.title} key={`toggle-${i}-${option.label}`}> - <ToggleButton - aria-label={option.title} - aria-pressed={ - multiple - ? value - ? Array.isArray(value) && value.includes(option.value) - : Array.isArray(selectedValue) && selectedValue.includes(option.value) - : value - ? option.value === value - : option.value === selectedValue - } - disabled={option.disabled} - onClick={() => { - handleOnChange(option.value); - }} - onKeyDown={(event) => { - handleOnKeyDown(event, option.value); - }} - onlyIcon={!option.label && !!option.icon} - selected={ - multiple - ? value - ? Array.isArray(value) && value.includes(option.value) - : Array.isArray(selectedValue) && selectedValue.includes(option.value) - : value - ? option.value === value - : option.value === selectedValue - } - tabIndex={!option.disabled ? tabIndex : -1} - > - {option.icon && ( - <IconContainer> - {typeof option.icon === "string" ? <DxcIcon icon={option.icon} /> : option.icon} - </IconContainer> - )} - {option.label && <ToggleButtonLabel>{option.label}</ToggleButtonLabel>} - </ToggleButton> - </Tooltip> - ))} + <ToggleGroup aria-orientation={orientation} margin={margin} role="group"> + {options.map((option, i) => { + const selected = isToggleButtonSelected(multiple, option.value, value ?? selectedValue); + return ( + <Tooltip label={option.title} key={`toggle-${i}-${option.label}`}> + <ToggleButton + aria-label={option.title} + aria-pressed={selected} + disabled={option.disabled} + onClick={() => { + handleOnChange(option.value); + }} + onKeyDown={(event) => { + handleOnKeyDown(event, option.value); + }} + onlyIcon={!option.label && !!option.icon} + selected={selected} + tabIndex={!option.disabled ? tabIndex : -1} + > + {option.icon && ( + <IconContainer> + {typeof option.icon === "string" ? <DxcIcon icon={option.icon} /> : option.icon} + </IconContainer> + )} + {option.label && <Label>{option.label}</Label>} + </ToggleButton> + </Tooltip> + ); + })} </ToggleGroup> ); } From 661f2ca1446bbb6d28a92683c9f8ee249a7c0591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 28 Mar 2025 14:02:23 +0100 Subject: [PATCH 08/15] New tests added --- packages/lib/src/toggle-group/ToggleGroup.test.tsx | 9 ++++++++- packages/lib/src/toggle-group/ToggleGroup.tsx | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/lib/src/toggle-group/ToggleGroup.test.tsx b/packages/lib/src/toggle-group/ToggleGroup.test.tsx index ba957b2f7c..1c95547373 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.test.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.test.tsx @@ -22,11 +22,13 @@ const options = [ describe("Toggle group component tests", () => { test("Toggle group renders with correct labels", () => { - const { getByText } = render(<DxcToggleGroup options={options} />); + const { getByText, getByRole } = render(<DxcToggleGroup options={options} />); + const toggleGroup = getByRole("toolbar"); expect(getByText("Amazon")).toBeTruthy(); expect(getByText("Ebay")).toBeTruthy(); expect(getByText("Apple")).toBeTruthy(); expect(getByText("Google")).toBeTruthy(); + expect(toggleGroup.getAttribute("aria-orientation")).toBe("horizontal"); }); test("Toggle group renders with correct aria-label in only-icon scenario", () => { const { getByRole } = render( @@ -86,4 +88,9 @@ describe("Toggle group component tests", () => { expect(toggleOptions[1]?.getAttribute("aria-pressed")).toBe("true"); expect(toggleOptions[3]?.getAttribute("aria-pressed")).toBe("true"); }); + test("Aria orientation is set correctly", () => { + const { getByRole } = render(<DxcToggleGroup options={options} orientation="vertical" />); + const toggleGroup = getByRole("toolbar"); + expect(toggleGroup.getAttribute("aria-orientation")).toBe("vertical"); + }); }); diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index 546068a176..fa1b25cab8 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -128,7 +128,7 @@ export default function DxcToggleGroup({ }; return ( - <ToggleGroup aria-orientation={orientation} margin={margin} role="group"> + <ToggleGroup aria-orientation={orientation} margin={margin} role="toolbar"> {options.map((option, i) => { const selected = isToggleButtonSelected(multiple, option.value, value ?? selectedValue); return ( From a55d2acb982a5e6155ae39050ed2653916adbbef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Mon, 31 Mar 2025 11:45:34 +0200 Subject: [PATCH 09/15] Adding Button styles to the toggle button --- packages/lib/src/button/utils.ts | 12 ++++- packages/lib/src/toggle-group/ToggleGroup.tsx | 47 ++++--------------- 2 files changed, 21 insertions(+), 38 deletions(-) diff --git a/packages/lib/src/button/utils.ts b/packages/lib/src/button/utils.ts index c0896e970c..13a3b1ec0c 100644 --- a/packages/lib/src/button/utils.ts +++ b/packages/lib/src/button/utils.ts @@ -3,7 +3,7 @@ import ButtonPropsType, { Mode, Semantic, Size } from "./types"; export const getButtonStyles = ( mode: Mode, - semantic: Semantic, + semantic: Semantic | "unselected" | "selected", size: Size, ) => { let enabled = ""; @@ -26,6 +26,16 @@ export const getButtonStyles = ( switch (mode) { case "primary": switch (semantic) { + case "unselected": + enabled = `background-color: var(--color-bg-neutral-medium); + color: var(--color-fg-neutral-dark);`; + hover = `background-color: var(--color-bg-neutral-strong);`; + active = `background-color: var(--color-bg-primary-strong); + color: var(--color-fg-neutral-bright);`; + disabled = `background-color: var(--color-bg-neutral-light); + color: var(--color-fg-neutral-medium);`; + break; + case "selected": case "default": enabled = `background-color: var(--color-bg-primary-strong);`; hover = `background-color: var(--color-bg-primary-stronger);`; diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index fa1b25cab8..be9bbab75d 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -4,6 +4,7 @@ import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; import ToggleGroupPropsType from "./types"; +import { getButtonStyles, getHeight } from "../button/utils"; const ToggleGroup = styled.div<{ margin: ToggleGroupPropsType["margin"] }>` display: flex; @@ -27,44 +28,16 @@ const ToggleButton = styled.button<{ onlyIcon: boolean; selected: boolean; }>` - display: flex; + display: inline-flex; align-items: center; justify-content: center; gap: var(--spacing-gap-s); - height: var(--height-xl); - padding: ${({ onlyIcon }) => - onlyIcon ? "var(--spacing-padding-xs)" : "var(--spacing-padding-none) var(--spacing-padding-m)"}; - border: none; - border-radius: var(--border-radius-s); - background-color: ${({ selected }) => - selected ? "var(--color-bg-primary-strong);" : "var(--color-bg-neutral-medium)"}; - color: ${({ selected }) => (selected ? "var(--color-fg-neutral-bright)" : "var(--color-fg-neutral-dark)")}; + height: ${getHeight("large")}; + padding: var(--spacing-padding-none) + ${({ onlyIcon }) => (onlyIcon ? "var(--spacing-padding-xs)" : "var(--spacing-padding-m)")}; cursor: pointer; - - &:hover:enabled { - background-color: ${({ selected }) => - selected ? "var(--color-bg-primary-stronger)" : "var(--color-bg-neutral-strong)"}; - } - &:active:enabled { - background-color: ${({ selected }) => - selected ? "var(--color-bg-primary-strongest);" : "var(--color-bg-primary-strong);"}; - color: var(--color-fg-neutral-bright); - } - &:focus:enabled { - outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium); - outline-offset: -2px; - } - &:disabled { - background-color: var(--color-bg-neutral-light); - color: var(--color-fg-neutral-medium); - cursor: not-allowed; - } -`; - -const Label = styled.span` - font-family: var(--typography-font-family); - font-size: var(--typography-label-l); - font-weight: var(--typography-label-regular); + ${({ selected }) => + getButtonStyles("primary", selected ? "selected" : "unselected", { height: "large", width: "fitContent" })}; `; const IconContainer = styled.div` @@ -88,9 +61,9 @@ export default function DxcToggleGroup({ multiple, onChange, options, + orientation = "horizontal", tabIndex = 0, value, - orientation = "horizontal", }: ToggleGroupPropsType) { const [selectedValue, setSelectedValue] = useState(defaultValue ?? (multiple ? [] : -1)); @@ -130,7 +103,7 @@ export default function DxcToggleGroup({ return ( <ToggleGroup aria-orientation={orientation} margin={margin} role="toolbar"> {options.map((option, i) => { - const selected = isToggleButtonSelected(multiple, option.value, value ?? selectedValue); + const selected = !option.disabled && isToggleButtonSelected(multiple, option.value, value ?? selectedValue); return ( <Tooltip label={option.title} key={`toggle-${i}-${option.label}`}> <ToggleButton @@ -152,7 +125,7 @@ export default function DxcToggleGroup({ {typeof option.icon === "string" ? <DxcIcon icon={option.icon} /> : option.icon} </IconContainer> )} - {option.label && <Label>{option.label}</Label>} + {option.label && <span>{option.label}</span>} </ToggleButton> </Tooltip> ); From 090bf7e2a87f4d07b2af13fa879c3eefbbde785c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Tue, 1 Apr 2025 13:38:46 +0200 Subject: [PATCH 10/15] Toggle group updates --- .../code/examples/uncontrolled.ts | 63 +++++++++++++++---- .../src/toggle-group/ToggleGroup.stories.tsx | 4 +- packages/lib/src/toggle-group/ToggleGroup.tsx | 6 +- 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts b/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts index 6667b6fe7d..5666d8ae54 100644 --- a/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts +++ b/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts @@ -1,10 +1,41 @@ -import { DxcToggleGroup, DxcInset } from "@dxc-technology/halstack-react"; +import { DxcContainer, DxcFlex, DxcInset, DxcToggleGroup } from "@dxc-technology/halstack-react"; +import { useRef } from "react"; const code = `() => { - const onChange = (newValue) => { - console.log(newValue); - }; - const optionsWithIcon = [ + const refText = useRef(null); + const onChange = (selectedValue) => { + if (refText.current) { + refText.current.style.fontWeight = "normal"; + refText.current.style.fontStyle = "normal"; + refText.current.style.textDecoration = "none"; + refText.current.style.textAlign = "left"; + selectedValue.forEach((textOption) => { + switch (textOption) { + case 1: + refText.current.style.fontWeight = "bold"; + break; + case 2: + refText.current.style.fontStyle = "italic"; + break; + case 3: + refText.current.style.textDecoration = "underline"; + break; + case 4: + refText.current.style.textAlign = "left"; + break; + case 5: + refText.current.style.textAlign = "center"; + break; + case 6: + refText.current.style.textAlign = "right"; + break; + default: + break; + } + }); + } + } + const options = [ { value: 1, icon: "format_bold", @@ -39,19 +70,27 @@ const code = `() => { return ( <DxcInset space="2rem"> - <DxcToggleGroup - defaultValue={3} - onChange={onChange} - options={options} - orientation="vertical" - /> + <DxcFlex gap="var(--spacing-gap-xl)"> + <DxcToggleGroup + multiple + onChange={onChange} + options={options} + orientation="vertical" + /> + <DxcContainer width="100%"> + <p ref={refText}>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> + </DxcContainer> + </DxcFlex> </DxcInset> ); }`; const scope = { - DxcToggleGroup, + DxcContainer, + DxcFlex, DxcInset, + DxcToggleGroup, + useRef }; export default { code, scope }; diff --git a/packages/lib/src/toggle-group/ToggleGroup.stories.tsx b/packages/lib/src/toggle-group/ToggleGroup.stories.tsx index 2b6711b0e0..c0916452ee 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.stories.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.stories.tsx @@ -55,6 +55,7 @@ const disabledOptions = [ { value: 2, label: "X", + icon: "raven", disabled: true, }, { @@ -65,7 +66,8 @@ const disabledOptions = [ const optionsWithIcon = [ { value: 1, - label: "Bold", + icon: "format_bold", + title: "Bold", }, { value: 2, diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index be9bbab75d..7b4727f140 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -11,11 +11,11 @@ const ToggleGroup = styled.div<{ margin: ToggleGroupPropsType["margin"] }>` &[aria-orientation="vertical"] { flex-direction: column; } - width: fit-content; - padding: var(--spacing-padding-xxs); gap: var(--spacing-gap-xs); - border-radius: var(--border-radius-m); + padding: var(--spacing-padding-xxs); + width: fit-content; border: var(--border-width-s) var(--border-style-default) var(--border-color-neutral-strong); + border-radius: var(--border-radius-m); margin: ${({ margin }) => (margin && typeof margin !== "object" ? spaces[margin] : "0px")}; margin-top: ${({ margin }) => (margin && typeof margin === "object" && margin.top ? spaces[margin.top] : "")}; margin-right: ${({ margin }) => (margin && typeof margin === "object" && margin.right ? spaces[margin.right] : "")}; From b0eba61e704bc9f949b2089eddbbf53652f2fee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Wed, 2 Apr 2025 17:19:32 +0200 Subject: [PATCH 11/15] Toggle group documentation --- .../container/code/examples/listbox.ts | 4 +- ...natomy.png => contextual_menu_anatomy.png} | Bin .../toggle-group/ToggleGroupPageLayout.tsx | 7 +- .../code/examples/uncontrolled.ts | 18 +- .../overview/ToggleGroupOverviewPage.tsx | 193 ++++++++++++++---- .../toggle-group/overview/examples/icons.tsx | 92 --------- .../overview/examples/multipleSelection.ts | 53 +++++ .../overview/examples/orientation.ts | 54 +++++ .../overview/examples/singleSelection.ts | 42 ++++ .../overview/examples/variants.ts | 53 ----- .../overview/images/toggle_group_anatomy.png | Bin 0 -> 17178 bytes 11 files changed, 323 insertions(+), 193 deletions(-) rename apps/website/screens/components/contextual-menu/overview/images/{contextual-menu_anatomy.png => contextual_menu_anatomy.png} (100%) delete mode 100644 apps/website/screens/components/toggle-group/overview/examples/icons.tsx create mode 100644 apps/website/screens/components/toggle-group/overview/examples/multipleSelection.ts create mode 100644 apps/website/screens/components/toggle-group/overview/examples/orientation.ts create mode 100644 apps/website/screens/components/toggle-group/overview/examples/singleSelection.ts delete mode 100644 apps/website/screens/components/toggle-group/overview/examples/variants.ts create mode 100644 apps/website/screens/components/toggle-group/overview/images/toggle_group_anatomy.png diff --git a/apps/website/screens/components/container/code/examples/listbox.ts b/apps/website/screens/components/container/code/examples/listbox.ts index 00953231f0..19d00ad1bc 100644 --- a/apps/website/screens/components/container/code/examples/listbox.ts +++ b/apps/website/screens/components/container/code/examples/listbox.ts @@ -9,8 +9,8 @@ const code = `() => { background={{ color: "var(--border-color-neutral-brighter)" }} border={{ color: "var(--border-color-neutral-medium)", - width: "var(--border-width-s)", - style: "var(--border-style-default)" + style: "var(--border-style-default)", + width: "var(--border-width-s)" }} borderRadius="var(--border-radius-s)" boxShadow="var(--shadow-mid-x-position) var(--shadow-mid-y-position) var(--shadow-mid-blur) var(--shadow-mid-spread) var(--shadow-light)" diff --git a/apps/website/screens/components/contextual-menu/overview/images/contextual-menu_anatomy.png b/apps/website/screens/components/contextual-menu/overview/images/contextual_menu_anatomy.png similarity index 100% rename from apps/website/screens/components/contextual-menu/overview/images/contextual-menu_anatomy.png rename to apps/website/screens/components/contextual-menu/overview/images/contextual_menu_anatomy.png diff --git a/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx b/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx index bcf66467ca..7a6858bdbe 100644 --- a/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx +++ b/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx @@ -16,10 +16,9 @@ const ToggleGroupPageHeading = ({ children }: { children: ReactNode }) => { <DxcFlex direction="column" gap="2rem"> <ComponentHeading name="Toggle group" /> <DxcParagraph> - Toggle buttons can be used to put together related options that share a common attribute modification. It - allows the user to switch from one selected option to another in the same control, having one option - selected at a time. Also, there can be another variation that allows selecting multiple options from the - current toggle group. + The toggle group component is a set of toggle buttons that function as a unified control, allowing users to + make either single or multiple selections. It is ideal for grouping related actions or options within a + compact and interactive interface. </DxcParagraph> <TabsPageHeading tabs={tabs} /> </DxcFlex> diff --git a/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts b/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts index 5666d8ae54..f0a5e8619d 100644 --- a/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts +++ b/apps/website/screens/components/toggle-group/code/examples/uncontrolled.ts @@ -3,6 +3,7 @@ import { useRef } from "react"; const code = `() => { const refText = useRef(null); + const onChange = (selectedValue) => { if (refText.current) { refText.current.style.fontWeight = "normal"; @@ -35,6 +36,7 @@ const code = `() => { }); } } + const options = [ { value: 1, @@ -77,8 +79,20 @@ const code = `() => { options={options} orientation="vertical" /> - <DxcContainer width="100%"> - <p ref={refText}>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> + <DxcContainer + width="100%" + background={{ color: "var(--border-color-neutral-brighter)" }} + border={{ + color: "var(--border-color-neutral-strong)", + style: "var(--border-style-default)", + width: "var(--border-width-s)" + }} + borderRadius="var(--border-radius-m)" + padding="var(--spacing-padding-m)" + > + <p ref={refText} style={{ margin: 0 }}> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + </p> </DxcContainer> </DxcFlex> </DxcInset> diff --git a/apps/website/screens/components/toggle-group/overview/ToggleGroupOverviewPage.tsx b/apps/website/screens/components/toggle-group/overview/ToggleGroupOverviewPage.tsx index 3d88ff33fd..caabd6ae29 100644 --- a/apps/website/screens/components/toggle-group/overview/ToggleGroupOverviewPage.tsx +++ b/apps/website/screens/components/toggle-group/overview/ToggleGroupOverviewPage.tsx @@ -3,69 +3,182 @@ import QuickNavContainer from "@/common/QuickNavContainer"; import DocFooter from "@/common/DocFooter"; import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; import Example from "@/common/example/Example"; -import variants from "./examples/variants"; -import icons from "./examples/icons"; import Code from "@/common/Code"; +import Image from "@/common/Image"; +import anatomy from "./images/toggle_group_anatomy.png"; +import singleSelection from "./examples/singleSelection"; +import multipleSelection from "./examples/multipleSelection"; +import orientation from "./examples/orientation"; const sections = [ { title: "Introduction", content: ( - <> - <DxcParagraph>Toggles should be used in place of radio buttons whenever the options are:</DxcParagraph> - <DxcBulletedList> - <DxcBulletedList.Item> - Minimal in number, i.e. 3 or 4 maximum choices where only one selection is required. - </DxcBulletedList.Item> - <DxcBulletedList.Item>Opposites of each other.</DxcBulletedList.Item> - </DxcBulletedList> - </> - ), - }, - { - title: "Variants", - content: ( - <> - <Example example={variants} /> - <DxcParagraph> - The selection of the toggle group can be mutually exclusive (single variant) or mutually inclusive (multiple - variant). - </DxcParagraph> - </> + <DxcParagraph> + The toggle group component provides a flexible way to present related options or actions within a single + interface. It consists of multiple toggle buttons, allowing users to make either single or multiple selections + depending on the configuration. This component is particularly useful for settings, filtering options, or mode + switching, where users need to quickly toggle between states. By grouping these actions together, it enhances + usability and keeps the interface organized, ensuring a seamless interaction experience. + </DxcParagraph> ), }, { - title: "Icon usage", + title: "Anatomy", content: ( <> - <DxcParagraph> - Icons can be used to add information and clarify the action performed by each button in the toggle group. Do - not use icons primarily for visual interest. - </DxcParagraph> - <DxcBulletedList> + <Image src={anatomy} alt="Button's anatomy" /> + <DxcBulletedList type="number"> <DxcBulletedList.Item> - The size of the icons is 24 by 24 pixels. They must be aligned vertically and horizontally with respect to - the corresponding toggle button box. + <strong>Container:</strong> the structural wrapper that holds all toggle buttons together. </DxcBulletedList.Item> <DxcBulletedList.Item> - A group of icon-only toggle buttons is a valid use case and allowed in the design system. In such a - situation and in order to preserve the accessibility of the component, the use of the title prop is - mandatory. + <strong>Icon:</strong> an optional visual element within a toggle button that helps users quickly identify + its function or meaning. </DxcBulletedList.Item> <DxcBulletedList.Item> - The <Code>title</Code> prop offers a contextual bubble with missing information necessary to clarify the - action performed by each toggle button. It also provides an accessible gateway when no regular label can be - specified. + <strong>Label:</strong> the textual representation inside each toggle button, describing its function or + selection state. </DxcBulletedList.Item> <DxcBulletedList.Item> - Try to limit the use of icon-only toggle groups. Whenever possible, the icon should be accompanied by a - label. + <strong>Toggle button</strong> <em>(selected)</em>: a button in an active state, indicating that the user + has chosen this option. + </DxcBulletedList.Item> + <DxcBulletedList.Item> + <strong>Toggle button</strong> <em>(unselected)</em>: a button in its default or inactive state. It remains + visually subdued compared to the selected button but is still clearly visible and interactive, allowing + users to switch selections easily. </DxcBulletedList.Item> </DxcBulletedList> - <Example example={icons} /> </> ), }, + { + title: "Variants", + content: ( + <DxcParagraph> + Depending on the number of actions or options the user can select on a toggle group, there are two different + variants of the component. + </DxcParagraph> + ), + subSections: [ + { + title: "Single selection", + content: ( + <DxcParagraph> + The <strong>single selection</strong> variant allows users to select{" "} + <strong>only one option at a time</strong>. When a new option is selected, the previous one is automatically + deselected. This variant is ideal for scenarios where users need to toggle between mutually exclusive + options, ensuring clarity and preventing conflicting selections. + </DxcParagraph> + ), + subSections: [ + { + title: "Use cases", + content: ( + <> + <DxcBulletedList> + <DxcBulletedList.Item> + <strong>View selection:</strong> allowing users to switch between different data presentation + formats, such as <strong>grid view vs. list view</strong> in a product catalog. + </DxcBulletedList.Item> + <DxcBulletedList.Item> + <strong>Mode switching:</strong> Enabling users to toggle between modes like{" "} + <strong>light mode vs. dark mode</strong> in an interface. + </DxcBulletedList.Item> + <DxcBulletedList.Item> + <strong>Filter selection:</strong> Helping users refine content by choosing a{" "} + <strong>single category filter</strong> in dashboards or reports. + </DxcBulletedList.Item> + </DxcBulletedList> + <Example example={singleSelection} /> + </> + ), + }, + ], + }, + { + title: "Multiple selection", + content: ( + <DxcParagraph> + The <strong>multiple selection</strong> variant of the toggle group component allows users to select + multiple options at the same time. Unlike the single selection variant, this version enables users to + activate or deactivate multiple toggles independently, making it useful for scenarios where multiple choices + can be applied simultaneously. + </DxcParagraph> + ), + subSections: [ + { + title: "Use cases", + content: ( + <> + <DxcBulletedList> + <DxcBulletedList.Item> + <strong>Formatting options:</strong> enabling users to apply <strong>bold</strong>,{" "} + <strong>italic</strong>, and <strong>underline</strong> text styles simultaneously in a text editor. + </DxcBulletedList.Item> + <DxcBulletedList.Item> + <strong>Risk assessment options:</strong> underwriters can enable multiple risk factors when + evaluating a client, such as <strong>pre-existing conditions</strong>, <strong>vehicle age</strong>, + and <strong>past claim history</strong>. + </DxcBulletedList.Item> + <DxcBulletedList.Item> + <strong>Filtering:</strong> allowing users to refine searches by toggling filters like "in + progress", "ready to review" or "done". + </DxcBulletedList.Item> + </DxcBulletedList> + <Example example={multipleSelection} /> + </> + ), + }, + ], + }, + { + title: "Orientation", + content: ( + <> + <DxcParagraph> + Although not technically a variant, the toggle group can also be{" "} + <strong>stacked in two different ways: horizontally and vertically</strong>. Users can choose the option + that better fits their needs according to layout constraints. + </DxcParagraph> + <Example example={orientation} /> + </> + ), + }, + ], + }, + { + title: "Best practices", + content: ( + <DxcBulletedList type="number"> + <DxcBulletedList.Item> + <strong>Choose the right selection mode:</strong> use <strong>single selection</strong> when only one option + can be active at a time (e.g., selecting a payment method). Use <strong>multiple selection</strong> when users + can activate several options simultaneously (e.g., selecting policy add-ons). + </DxcBulletedList.Item> + <DxcBulletedList.Item> + <strong>Group related actions logically:</strong> ensure that the toggle buttons represent actions or choices + that are related to each other, helping users make clear and informed selections. + </DxcBulletedList.Item> + <DxcBulletedList.Item> + <strong>Use appropriate labels and icons:</strong> labels should be concise and self-explanatory. If using + icons, ensure they clearly represent their respective actions or choices. + </DxcBulletedList.Item> + <DxcBulletedList.Item> + <strong>Stack options based on content needs:</strong> use a <strong>horizontal layout</strong> when there are + only a few options and space allows, making comparisons easier. Use a <strong>vertical layout</strong> when + dealing with multiple choices or longer text labels to enhance readability. + </DxcBulletedList.Item> + <DxcBulletedList.Item> + <strong>Be mindful of where you place the toggle group:</strong> this component is not meant to replace{" "} + <strong>radio buttons</strong>, <strong>checkboxes</strong>, or <strong>switches</strong>, as it does not + register selections as form inputs. If the user needs to provide structured answers, use the appropriate form + elements instead. + </DxcBulletedList.Item> + </DxcBulletedList> + ), + }, ]; const ToggleGroupOverviewPage = () => ( diff --git a/apps/website/screens/components/toggle-group/overview/examples/icons.tsx b/apps/website/screens/components/toggle-group/overview/examples/icons.tsx deleted file mode 100644 index 6cb1272007..0000000000 --- a/apps/website/screens/components/toggle-group/overview/examples/icons.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { DxcToggleGroup, DxcInset, DxcFlex } from "@dxc-technology/halstack-react"; -import { useState } from "react"; - -const icons = { - ethernet: ( - <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor"> - <path d="M0 0h24v24H0V0z" fill="none" /> - <path d="M7.77 6.76L6.23 5.48.82 12l5.41 6.52 1.54-1.28L3.42 12l4.35-5.24zM7 13h2v-2H7v2zm10-2h-2v2h2v-2zm-6 2h2v-2h-2v2zm6.77-7.52l-1.54 1.28L20.58 12l-4.35 5.24 1.54 1.28L23.18 12l-5.41-6.52z" /> - </svg> - ), - gMobile: ( - <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor"> - <g> - <path d="M0,0h24v24H0V0z" fill="none" /> - </g> - <g> - <g> - <path d="M3,7v2h5v2H4v2h4v2H3v2h5c1.1,0,2-0.9,2-2v-1.5c0-0.83-0.67-1.5-1.5-1.5c0.83,0,1.5-0.67,1.5-1.5V9c0-1.1-0.9-2-2-2H3z M21,11v4c0,1.1-0.9,2-2,2h-5c-1.1,0-2-0.9-2-2V9c0-1.1,0.9-2,2-2h5c1.1,0,2,0.9,2,2h-7v6h5v-2h-2.5v-2H21z" /> - </g> - </g> - </svg> - ), - wifi: ( - <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor"> - <path d="M0 0h24v24H0V0zm0 0h24v24H0V0z" fill="none" /> - <path d="M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.08 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z" /> - </svg> - ), -}; - -const code = `() => { - const options1 = [ - { - value: 1, - label: "Wi-fi", - icon: icons.wifi, - }, - { - value: 2, - label: "Ethernet", - icon: icons.ethernet, - }, - { - value: 3, - label: "3G Mobile", - icon: icons.gMobile, - }, - ]; - - const options2 = [ - { - value: 1, - icon: icons.wifi, - title: "Wi-fi connection", - }, - { - value: 2, - icon: icons.ethernet, - title: "Ethernet connection" - }, - { - value: 3, - icon: icons.gMobile, - title: "3G Mobile data connection" - }, - ]; - - return ( - <DxcInset space="2rem"> - <DxcFlex gap="2rem"> - <DxcToggleGroup - options={options1} - defaultValue={1} - /> - <DxcToggleGroup - options={options2} - defaultValue={1} - /> - </DxcFlex> - </DxcInset> - ); -}`; - -const scope = { - DxcToggleGroup, - DxcInset, - DxcFlex, - useState, - icons, -}; - -export default { code, scope }; diff --git a/apps/website/screens/components/toggle-group/overview/examples/multipleSelection.ts b/apps/website/screens/components/toggle-group/overview/examples/multipleSelection.ts new file mode 100644 index 0000000000..6503e67e65 --- /dev/null +++ b/apps/website/screens/components/toggle-group/overview/examples/multipleSelection.ts @@ -0,0 +1,53 @@ +import { DxcInset, DxcToggleGroup } from "@dxc-technology/halstack-react"; + +const code = `() => { + const options = [ + { + value: 1, + icon: "format_bold", + title: "Bold", + }, + { + value: 2, + icon: "format_italic", + title: "Italic", + }, + { + value: 3, + icon: "format_underlined", + title: "Underlined", + }, + { + value: 4, + icon: "format_align_left", + title: "Align left", + }, + { + value: 5, + icon: "format_align_center", + title: "Align center", + }, + { + value: 6, + icon: "format_align_right", + title: "Align right", + }, + ]; + + return ( + <DxcInset space="2rem"> + <DxcToggleGroup + defaultValue={[3, 4]} + multiple + options={options} + /> + </DxcInset> + ); +}`; + +const scope = { + DxcInset, + DxcToggleGroup, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/toggle-group/overview/examples/orientation.ts b/apps/website/screens/components/toggle-group/overview/examples/orientation.ts new file mode 100644 index 0000000000..1987dbd702 --- /dev/null +++ b/apps/website/screens/components/toggle-group/overview/examples/orientation.ts @@ -0,0 +1,54 @@ +import { DxcInset, DxcToggleGroup } from "@dxc-technology/halstack-react"; + +const code = `() => { + const options = [ + { + value: 1, + icon: "format_bold", + title: "Bold", + }, + { + value: 2, + icon: "format_italic", + title: "Italic", + }, + { + value: 3, + icon: "format_underlined", + title: "Underlined", + }, + { + value: 4, + icon: "format_align_left", + title: "Align left", + }, + { + value: 5, + icon: "format_align_center", + title: "Align center", + }, + { + value: 6, + icon: "format_align_right", + title: "Align right", + }, + ]; + + return ( + <DxcInset space="2rem"> + <DxcToggleGroup + defaultValue={[3, 4]} + multiple + options={options} + orientation="vertical" + /> + </DxcInset> + ); +}`; + +const scope = { + DxcInset, + DxcToggleGroup, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/toggle-group/overview/examples/singleSelection.ts b/apps/website/screens/components/toggle-group/overview/examples/singleSelection.ts new file mode 100644 index 0000000000..3d72c99d05 --- /dev/null +++ b/apps/website/screens/components/toggle-group/overview/examples/singleSelection.ts @@ -0,0 +1,42 @@ +import { DxcInset, DxcToggleGroup } from "@dxc-technology/halstack-react"; + +const code = `() => { + const options = [ + { + value: 1, + label: "Not started", + }, + { + value: 2, + label: "In progress", + }, + { + value: 3, + label: "Ready to review", + }, + { + value: 4, + label: "Completed", + }, + { + value: 5, + label: "Awaiting for approval", + }, + ]; + + return ( + <DxcInset space="2rem"> + <DxcToggleGroup + defaultValue={1} + options={options} + /> + </DxcInset> + ); +}`; + +const scope = { + DxcInset, + DxcToggleGroup, +}; + +export default { code, scope }; diff --git a/apps/website/screens/components/toggle-group/overview/examples/variants.ts b/apps/website/screens/components/toggle-group/overview/examples/variants.ts deleted file mode 100644 index efbc31a478..0000000000 --- a/apps/website/screens/components/toggle-group/overview/examples/variants.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { DxcToggleGroup, DxcInset, DxcFlex } from "@dxc-technology/halstack-react"; - -const code = `() => { - const options1 = [ - { - value: 1, - label: "Option 01", - }, - { - value: 2, - label: "Option 02", - }, - ]; - - const options2 = [ - { - value: 1, - label: "Option 01", - }, - { - value: 2, - label: "Option 02", - }, - { - value: 3, - label: "Option 03", - }, - ]; - - return ( - <DxcInset space="2rem"> - <DxcFlex gap="2rem"> - <DxcToggleGroup - defaultValue={1} - options={options1} - /> - <DxcToggleGroup - options={options2} - defaultValue={[1, 2]} - multiple - /> - </DxcFlex> - </DxcInset> - ); -}`; - -const scope = { - DxcToggleGroup, - DxcInset, - DxcFlex, -}; - -export default { code, scope }; diff --git a/apps/website/screens/components/toggle-group/overview/images/toggle_group_anatomy.png b/apps/website/screens/components/toggle-group/overview/images/toggle_group_anatomy.png new file mode 100644 index 0000000000000000000000000000000000000000..a5411c91dc6361da8a941cdb762ce579feef8e17 GIT binary patch literal 17178 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 literal 0 HcmV?d00001 From 8d5bfec7d710f583e98388e1c9544996ee91bb79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Wed, 2 Apr 2025 17:34:50 +0200 Subject: [PATCH 12/15] Toggle group examples and updates --- .../overview/examples/orientation.ts | 47 ++++++++----------- packages/lib/src/toggle-group/ToggleGroup.tsx | 1 + 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/apps/website/screens/components/toggle-group/overview/examples/orientation.ts b/apps/website/screens/components/toggle-group/overview/examples/orientation.ts index 1987dbd702..f7b04854d5 100644 --- a/apps/website/screens/components/toggle-group/overview/examples/orientation.ts +++ b/apps/website/screens/components/toggle-group/overview/examples/orientation.ts @@ -1,52 +1,43 @@ -import { DxcInset, DxcToggleGroup } from "@dxc-technology/halstack-react"; +import { DxcFlex, DxcInset, DxcToggleGroup } from "@dxc-technology/halstack-react"; const code = `() => { const options = [ { value: 1, - icon: "format_bold", - title: "Bold", + icon: "wifi", + label: "Wifi", }, { value: 2, - icon: "format_italic", - title: "Italic", + label: "Ethernet", }, { value: 3, - icon: "format_underlined", - title: "Underlined", - }, - { - value: 4, - icon: "format_align_left", - title: "Align left", - }, - { - value: 5, - icon: "format_align_center", - title: "Align center", - }, - { - value: 6, - icon: "format_align_right", - title: "Align right", + icon: "5g", + label: "5G", }, ]; return ( <DxcInset space="2rem"> - <DxcToggleGroup - defaultValue={[3, 4]} - multiple - options={options} - orientation="vertical" - /> + <DxcFlex gap="var(--spacing-gap-l)"> + <DxcToggleGroup + defaultValue={2} + options={options} + orientation="vertical" + /> + <DxcToggleGroup + defaultValue={2} + options={options} + orientation="horizontal" + /> + </DxcFlex> </DxcInset> ); }`; const scope = { + DxcFlex, DxcInset, DxcToggleGroup, }; diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index 7b4727f140..e4cb3623c4 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -13,6 +13,7 @@ const ToggleGroup = styled.div<{ margin: ToggleGroupPropsType["margin"] }>` } gap: var(--spacing-gap-xs); padding: var(--spacing-padding-xxs); + height: fit-content; width: fit-content; border: var(--border-width-s) var(--border-style-default) var(--border-color-neutral-strong); border-radius: var(--border-radius-m); From 7c70d926f72794db6508d3ed61a1490ab20e53c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Thu, 3 Apr 2025 09:53:47 +0200 Subject: [PATCH 13/15] Contextual menu image name fix --- .../contextual-menu/overview/ContextualMenuOverviewPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/website/screens/components/contextual-menu/overview/ContextualMenuOverviewPage.tsx b/apps/website/screens/components/contextual-menu/overview/ContextualMenuOverviewPage.tsx index b2e4195fbf..dafe0021a7 100644 --- a/apps/website/screens/components/contextual-menu/overview/ContextualMenuOverviewPage.tsx +++ b/apps/website/screens/components/contextual-menu/overview/ContextualMenuOverviewPage.tsx @@ -3,7 +3,7 @@ import DocFooter from "@/common/DocFooter"; import QuickNavContainer from "@/common/QuickNavContainer"; import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; import Image from "@/common/Image"; -import anatomy from "./images/contextual-menu_anatomy.png"; +import anatomy from "./images/contextual_menu_anatomy.png"; const sections = [ { From c5c3c8c05791dd21ea93d4a56b74c63772a10603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 4 Apr 2025 12:18:48 +0200 Subject: [PATCH 14/15] Toggle group theme generator update --- .../components/previews/ToggleGroup.tsx | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/apps/website/screens/theme-generator/components/previews/ToggleGroup.tsx b/apps/website/screens/theme-generator/components/previews/ToggleGroup.tsx index 293cf699ee..f2a20aa047 100644 --- a/apps/website/screens/theme-generator/components/previews/ToggleGroup.tsx +++ b/apps/website/screens/theme-generator/components/previews/ToggleGroup.tsx @@ -1,8 +1,5 @@ import { DxcToggleGroup } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; -import facebookIcon from "../../images/FacebookIcon"; -import linkedinIcon from "../../images/LinkedinIcon"; -import xIcon from "../../images/XIcon"; import PreviewContainer from "./PreviewContainer"; const options = [ @@ -20,21 +17,37 @@ const options = [ }, ]; +const disabledOptions = [ + { + value: 1, + label: "Wi-fi", + }, + { + value: 2, + label: "Ethernet", + disabled: true, + }, + { + value: 3, + label: "5G", + }, +]; + const optionsWithIcons = [ { value: 1, - label: "Facebook", - icon: facebookIcon, + icon: "format_bold", + title: "Bold", }, { value: 2, - label: "Linkedin", - icon: linkedinIcon, + icon: "format_italic", + title: "Italic", }, { value: 3, - label: "X", - icon: xIcon, + icon: "format_underlined", + title: "Underlined", }, ]; @@ -43,11 +56,11 @@ const ToggleGroup = () => ( <Mode text="Default"> <DxcToggleGroup options={options} /> </Mode> - <Mode text="Disabled"> - <DxcToggleGroup options={options} /> + <Mode text="Disabled option"> + <DxcToggleGroup options={disabledOptions} /> </Mode> - <Mode text="Multiple with icons"> - <DxcToggleGroup options={optionsWithIcons} multiple /> + <Mode text="With icons"> + <DxcToggleGroup defaultValue={[1, 3]} options={optionsWithIcons} multiple /> </Mode> </PreviewContainer> ); From 10bca0c1b0a506196cd1071062080ff857cf0ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 4 Apr 2025 13:30:17 +0200 Subject: [PATCH 15/15] Updates based on feedback --- .../overview/ToggleGroupOverviewPage.tsx | 15 ++++++++------- .../overview/examples/singleSelection.ts | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/website/screens/components/toggle-group/overview/ToggleGroupOverviewPage.tsx b/apps/website/screens/components/toggle-group/overview/ToggleGroupOverviewPage.tsx index caabd6ae29..ee6c7994db 100644 --- a/apps/website/screens/components/toggle-group/overview/ToggleGroupOverviewPage.tsx +++ b/apps/website/screens/components/toggle-group/overview/ToggleGroupOverviewPage.tsx @@ -15,11 +15,12 @@ const sections = [ title: "Introduction", content: ( <DxcParagraph> - The toggle group component provides a flexible way to present related options or actions within a single - interface. It consists of multiple toggle buttons, allowing users to make either single or multiple selections - depending on the configuration. This component is particularly useful for settings, filtering options, or mode - switching, where users need to quickly toggle between states. By grouping these actions together, it enhances - usability and keeps the interface organized, ensuring a seamless interaction experience. + The toggle group component provides a flexible way to present <strong>related options or actions</strong> within + a single interface. It consists of multiple toggle buttons, allowing users to make either single or multiple + selections depending on the configuration. This component is particularly useful for settings, filtering + options, or mode switching, where users need to quickly <strong>toggle between states</strong>. By grouping + these actions together, it enhances usability and keeps the interface organized, ensuring a seamless interaction + experience. </DxcParagraph> ), }, @@ -27,7 +28,7 @@ const sections = [ title: "Anatomy", content: ( <> - <Image src={anatomy} alt="Button's anatomy" /> + <Image src={anatomy} alt="Toggle group anatomy" /> <DxcBulletedList type="number"> <DxcBulletedList.Item> <strong>Container:</strong> the structural wrapper that holds all toggle buttons together. @@ -151,7 +152,7 @@ const sections = [ { title: "Best practices", content: ( - <DxcBulletedList type="number"> + <DxcBulletedList> <DxcBulletedList.Item> <strong>Choose the right selection mode:</strong> use <strong>single selection</strong> when only one option can be active at a time (e.g., selecting a payment method). Use <strong>multiple selection</strong> when users diff --git a/apps/website/screens/components/toggle-group/overview/examples/singleSelection.ts b/apps/website/screens/components/toggle-group/overview/examples/singleSelection.ts index 3d72c99d05..c427c0e1f3 100644 --- a/apps/website/screens/components/toggle-group/overview/examples/singleSelection.ts +++ b/apps/website/screens/components/toggle-group/overview/examples/singleSelection.ts @@ -20,7 +20,7 @@ const code = `() => { }, { value: 5, - label: "Awaiting for approval", + label: "Awaiting approval", }, ];