From 51098469f3bf0b22e9d2f8be248ec1670941ac80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Grabowski?= Date: Wed, 24 Sep 2025 16:59:26 +0200 Subject: [PATCH] IBX-10698: Toggle Field --- packages/components/assets/translations.xliff | 8 +-- .../ToggleButtonField.stories.tsx | 40 +++++++++++ .../ToggleButtonField.test.stories.tsx | 68 +++++++++++++++++++ .../ToggleButtonField/ToggleButtonField.tsx | 47 +++++++++++++ .../ToggleButtonField.types.ts | 23 +++++++ .../ToggleButton/ToggleButtonField/index.ts | 2 + .../ToggleButtonInput/ToggleButtonInput.tsx | 4 +- .../ToggleButtonInput.types.ts | 1 - 8 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.stories.tsx create mode 100644 packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.test.stories.tsx create mode 100644 packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.tsx create mode 100644 packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.types.ts create mode 100644 packages/components/src/components/ToggleButton/ToggleButtonField/index.ts diff --git a/packages/components/assets/translations.xliff b/packages/components/assets/translations.xliff index d3660b0..b408143 100644 --- a/packages/components/assets/translations.xliff +++ b/packages/components/assets/translations.xliff @@ -6,12 +6,12 @@ Clear - Yes - Yes + On + On - No - No + Off + Off Autosave is off, draft not created diff --git a/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.stories.tsx b/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.stories.tsx new file mode 100644 index 0000000..83d210e --- /dev/null +++ b/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.stories.tsx @@ -0,0 +1,40 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { action } from 'storybook/actions'; + +import { ToggleButtonFieldSize, ToggleButtonFieldStateful } from '.'; + +const meta: Meta = { + component: ToggleButtonFieldStateful, + tags: ['autodocs', 'foundation'], + args: { + id: 'default-input', + name: 'default-input', + helperText: 'This is a helper text', + label: 'Input Label', + onChange: action('on-change'), + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + name: 'Default', +}; + +export const Required: Story = { + name: 'Required', + args: { + required: true, + }, +}; + +export const Small: Story = { + name: 'Small', + args: { + input: { + size: ToggleButtonFieldSize.Small, + }, + }, +}; diff --git a/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.test.stories.tsx b/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.test.stories.tsx new file mode 100644 index 0000000..8f72ea0 --- /dev/null +++ b/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.test.stories.tsx @@ -0,0 +1,68 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { expect, fn, userEvent, within } from 'storybook/test'; + +import { ToggleButtonFieldStateful } from '.'; + +const meta: Meta = { + component: ToggleButtonFieldStateful, + tags: ['!dev'], + args: { + id: 'default-input', + label: 'Checkbox Label', + name: 'default-input', + onChange: fn(), + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + name: 'Default', + play: async ({ canvasElement, step, args }) => { + const canvas = within(canvasElement); + const toggler = canvas.getByRole('button'); + const checkStepState = async (numberOfClicks: number, currValue: boolean, expectAdditionalParam = false) => { + if (expectAdditionalParam) { + await expect(args.onChange).toHaveBeenNthCalledWith(numberOfClicks, currValue, expect.anything()); + } else { + await expect(args.onChange).toHaveBeenNthCalledWith(numberOfClicks, currValue); + } + + if (currValue) { + await expect(toggler.parentNode).toHaveClass('ids-toggle--checked'); + } else { + await expect(toggler.parentNode).not.toHaveClass('ids-toggle--checked'); + } + }; + + await step('Click toggle to check it', async () => { + await userEvent.click(toggler); + + await checkStepState(1, true); // eslint-disable-line no-magic-numbers + }); + + await step('Click toggle to uncheck it', async () => { + await userEvent.click(toggler); + + await checkStepState(2, false); // eslint-disable-line no-magic-numbers + }); + + await step('Click toggle label to check it', async () => { + const togglerLabel = canvas.getByText('Off'); + + await userEvent.click(togglerLabel); + + await checkStepState(3, true, true); // eslint-disable-line no-magic-numbers + }); + + await step('Click toggle label to uncheck it', async () => { + const togglerLabel = canvas.getByText('On'); + + await userEvent.click(togglerLabel); + + await checkStepState(4, false, true); // eslint-disable-line no-magic-numbers + }); + }, +}; diff --git a/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.tsx b/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.tsx new file mode 100644 index 0000000..a2e3215 --- /dev/null +++ b/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.tsx @@ -0,0 +1,47 @@ +import React from 'react'; + +import { BaseField } from '@ids-partials/BaseField'; +import { HelperTextType } from '@ids-components/HelperText'; +import { ToggleButtonInput } from '../ToggleButtonInput'; +import withStateChecked from '@ids-hoc/withStateChecked'; + +import { ToggleButtonFieldProps } from './ToggleButtonField.types'; + +export const ToggleButtonField = ({ + checked = false, + helperText, + helperTextExtra = {}, + id, + input = {}, + label, + labelExtra = {}, + name, + onChange = () => undefined, + required = false, +}: ToggleButtonFieldProps) => { + const helperTextProps = { + children: helperText, + type: HelperTextType.Default, + ...helperTextExtra, + }; + const labelProps = { + children: label, + required, + ...labelExtra, + }; + const inputProps = { + ...input, + checked, + id, + name, + onChange, + }; + + return ( + + + + ); +}; + +export const ToggleButtonFieldStateful = withStateChecked(ToggleButtonField); diff --git a/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.types.ts b/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.types.ts new file mode 100644 index 0000000..38eb95d --- /dev/null +++ b/packages/components/src/components/ToggleButton/ToggleButtonField/ToggleButtonField.types.ts @@ -0,0 +1,23 @@ +import { BaseComponentAttributes } from '@ids-types/general'; + +import { + ToggleButtonInputProps as BasicToggleButtonProps, + ToggleButtonInputSize as ToggleButtonFieldSize, +} from '../ToggleButtonInput/ToggleButtonInput.types'; +import { HelperTextProps } from '@ids-components/HelperText/HelperText.types'; +import { LabelProps } from '@ids-components/Label/Label.types'; + +export { ToggleButtonFieldSize }; + +export interface ToggleButtonFieldProps extends BaseComponentAttributes { + id: string; + name: BasicToggleButtonProps['name']; + checked?: boolean; + input?: Omit; + helperText?: HelperTextProps['children']; + helperTextExtra?: Omit; + label?: LabelProps['children']; + labelExtra?: Omit; + onChange?: BasicToggleButtonProps['onChange']; + required?: boolean; +} diff --git a/packages/components/src/components/ToggleButton/ToggleButtonField/index.ts b/packages/components/src/components/ToggleButton/ToggleButtonField/index.ts new file mode 100644 index 0000000..c14d5b0 --- /dev/null +++ b/packages/components/src/components/ToggleButton/ToggleButtonField/index.ts @@ -0,0 +1,2 @@ +export * from './ToggleButtonField'; +export * from './ToggleButtonField.types'; diff --git a/packages/components/src/components/ToggleButton/ToggleButtonInput/ToggleButtonInput.tsx b/packages/components/src/components/ToggleButton/ToggleButtonInput/ToggleButtonInput.tsx index 3b9724c..40e6893 100644 --- a/packages/components/src/components/ToggleButton/ToggleButtonInput/ToggleButtonInput.tsx +++ b/packages/components/src/components/ToggleButton/ToggleButtonInput/ToggleButtonInput.tsx @@ -53,8 +53,8 @@ export const ToggleButtonInput = ({ onBlur(event); }; const getLabel = () => { - const defaultEnabledLabel = Translator.trans(/*@Desc("Yes")*/ 'ids.toggle.label.enabled'); - const defaultDisabledLabel = Translator.trans(/*@Desc("No")*/ 'ids.toggle.label.disabled'); + const defaultEnabledLabel = Translator.trans(/*@Desc("On")*/ 'ids.toggle.label.enabled'); + const defaultDisabledLabel = Translator.trans(/*@Desc("Off")*/ 'ids.toggle.label.disabled'); if (checked) { return enabledLabel ?? defaultEnabledLabel; diff --git a/packages/components/src/components/ToggleButton/ToggleButtonInput/ToggleButtonInput.types.ts b/packages/components/src/components/ToggleButton/ToggleButtonInput/ToggleButtonInput.types.ts index 37be398..601ccb0 100644 --- a/packages/components/src/components/ToggleButton/ToggleButtonInput/ToggleButtonInput.types.ts +++ b/packages/components/src/components/ToggleButton/ToggleButtonInput/ToggleButtonInput.types.ts @@ -10,5 +10,4 @@ export type ToggleButtonInputProps = Omit void; size?: ToggleButtonInputSize; - value: string; };