From bfde11c161b317e7298121798412105634e5597d Mon Sep 17 00:00:00 2001 From: Jaap-Hein Wester Date: Fri, 3 Jan 2025 09:57:56 +0100 Subject: [PATCH] test(components-react): add visual baseline to all formfield components (#347) # Contents Add visual tests baseline to all formfield components Co-authored-by: Jaap-Hein Wester Co-authored-by: Remy Parzinski --- .../FormFieldCheckbox.scss | 2 +- .../form-field-checkbox/FormFieldCheckbox.tsx | 40 ++++++--- .../test/form-field-checkbox.spec.tsx | 50 +++++++++++ .../FormFieldRadioGroup.tsx | 6 +- .../src/form-field-select/FormFieldSelect.tsx | 19 ++++- .../form-field-textbox/FormFieldTextbox.tsx | 10 +-- packages/components-react/src/index.ts | 6 +- .../components-react/src/select/Select.tsx | 7 +- packages/components-react/src/utils/object.ts | 17 ++++ .../form-field-checkbox.stories.tsx | 25 ++++++ .../form-field-checkbox/visual/States.tsx | 23 +++++ .../form-field-description.mdx | 5 ++ .../form-field-description.stories.tsx | 64 +++++++++++++- .../form-field-description/visual/States.tsx | 14 ++++ .../form-field-error-message.stories.tsx | 19 ++++- .../visual/States.tsx | 7 ++ .../form-field-label.stories.tsx | 19 ++++- .../form-field-label/visual/States.tsx | 11 +++ .../form-field-radio-group.stories.tsx | 15 ++++ .../form-field-radio-group/visual/States.tsx | 44 ++++++++++ .../form-field-radio-option.stories.tsx | 15 ++++ .../form-field-radio-option/visual/States.tsx | 17 ++++ .../form-field-select/form-field-select.mdx | 26 +++++- .../form-field-select.stories.tsx | 70 +++++++++++++++- .../form-field-select/visual/States.tsx | 27 ++++++ .../form-field-textbox.stories.tsx | 17 ++++ .../form-field-textbox/visual/States.tsx | 21 +++++ .../form-field/form-field.stories.tsx | 48 +++++++++++ .../form-field/visual/FormFieldTypes.tsx | 84 +++++++++++++++++++ 29 files changed, 690 insertions(+), 38 deletions(-) create mode 100644 packages/components-react/src/form-field-checkbox/test/form-field-checkbox.spec.tsx create mode 100644 packages/components-react/src/utils/object.ts create mode 100644 packages/storybook/src/react-components/form-field-checkbox/visual/States.tsx create mode 100644 packages/storybook/src/react-components/form-field-description/visual/States.tsx create mode 100644 packages/storybook/src/react-components/form-field-error-message/visual/States.tsx create mode 100644 packages/storybook/src/react-components/form-field-label/visual/States.tsx create mode 100644 packages/storybook/src/react-components/form-field-radio-group/visual/States.tsx create mode 100644 packages/storybook/src/react-components/form-field-radio-option/visual/States.tsx create mode 100644 packages/storybook/src/react-components/form-field-select/visual/States.tsx create mode 100644 packages/storybook/src/react-components/form-field-textbox/visual/States.tsx create mode 100644 packages/storybook/src/react-components/form-field/visual/FormFieldTypes.tsx diff --git a/packages/components-react/src/form-field-checkbox/FormFieldCheckbox.scss b/packages/components-react/src/form-field-checkbox/FormFieldCheckbox.scss index bc6ac373..50c83a1a 100644 --- a/packages/components-react/src/form-field-checkbox/FormFieldCheckbox.scss +++ b/packages/components-react/src/form-field-checkbox/FormFieldCheckbox.scss @@ -82,7 +82,7 @@ outline: none; } - &:not(#{&}--disabled):has(.utrecht-form-field__input .utrecht-checkbox:hover)::before { + &--with-target:not(#{&}--disabled):has(.utrecht-form-field__input .utrecht-checkbox:hover)::before { border-color: var(--lux-form-field-checkbox-hover-inner-border-color); background-color: var(--lux-form-field-checkbox-hover-inner-background-color); color: var(--lux-form-field-checkbox-hover-inner-color); diff --git a/packages/components-react/src/form-field-checkbox/FormFieldCheckbox.tsx b/packages/components-react/src/form-field-checkbox/FormFieldCheckbox.tsx index 7c7fc442..7d130930 100644 --- a/packages/components-react/src/form-field-checkbox/FormFieldCheckbox.tsx +++ b/packages/components-react/src/form-field-checkbox/FormFieldCheckbox.tsx @@ -1,7 +1,7 @@ import clsx from 'clsx'; import { useId } from 'react'; -import { LuxCheckbox } from '../checkbox/Checkbox'; -import { LuxFormField, LuxFormFieldProps } from '../form-field/FormField'; +import { LuxCheckbox, type LuxCheckboxProps } from '../checkbox/Checkbox'; +import { LuxFormField, type LuxFormFieldProps } from '../form-field/FormField'; import { LuxFormFieldDescription, type LuxFormFieldDescriptionAppearance, @@ -9,14 +9,16 @@ import { import { LuxFormFieldErrorMessage } from '../form-field-error-message/FormFieldErrorMessage'; import { LuxFormFieldLabel } from '../form-field-label/FormFieldLabel'; import './FormFieldCheckbox.scss'; +import { pick } from '../utils/object'; -export type LuxFormFieldCheckboxProps = LuxFormFieldProps & { - checked?: boolean; - disabled?: boolean; - appearance?: LuxFormFieldDescriptionAppearance; - withTarget?: boolean; - distanced?: boolean; -}; +export type LuxFormFieldCheckboxProps = LuxFormFieldProps & + LuxCheckboxProps & { + checked?: boolean; + disabled?: boolean; + appearance?: LuxFormFieldDescriptionAppearance; + withTarget?: boolean; + distanced?: boolean; + }; export const LuxFormFieldCheckbox = ({ label, @@ -73,6 +75,17 @@ export const LuxFormFieldCheckbox = ({ errorMessage ); + const checkBoxAttrs = pick(restProps, [ + 'required', + 'inputRequired', + 'value', + 'defaultValue', + 'onFocus', + 'onBlur', + 'onInput', + 'onChange', + ]); + return ( + } className={clsx('lux-form-field-checkbox', { 'lux-form-field-checkbox--invalid': invalid, diff --git a/packages/components-react/src/form-field-checkbox/test/form-field-checkbox.spec.tsx b/packages/components-react/src/form-field-checkbox/test/form-field-checkbox.spec.tsx new file mode 100644 index 00000000..34c14f16 --- /dev/null +++ b/packages/components-react/src/form-field-checkbox/test/form-field-checkbox.spec.tsx @@ -0,0 +1,50 @@ +import { describe, expect, it } from '@jest/globals'; +import { render, screen } from '@testing-library/react'; +import { LuxFormFieldCheckbox } from '../FormFieldCheckbox'; + +describe('Form Field Checkbox', () => { + it('renders a basic form field checkbox with label and input', () => { + render(); + + expect(screen.getByText('Name')).toBeInTheDocument(); + expect(screen.getByRole('checkbox')).toBeInTheDocument(); + }); + + it('applies the base class', () => { + render(); + + const formField = screen.getByText('Name').closest('.utrecht-form-field'); + expect(formField).toHaveClass('utrecht-form-field'); + }); + + it('can have an additional class name', () => { + render(); + + const formField = screen.getByText('Name').closest('.utrecht-form-field'); + expect(formField).toHaveClass('utrecht-form-field'); + expect(formField).toHaveClass('custom-class'); + }); + + it('renders description when provided', () => { + render(); + + expect(screen.getByText('Enter your full name')).toBeInTheDocument(); + }); + + it('renders error message when invalid and error message provided', () => { + render(); + + expect(screen.getByText('Name is required')).toBeInTheDocument(); + }); + + it('adds the correct attributes to the Checkbox', () => { + render(); + + const checkbox = screen.getByRole('checkbox'); + + expect(checkbox).toBeInTheDocument(); + expect(checkbox).toBeChecked(); + expect(checkbox).toHaveAttribute('aria-required', 'true'); + expect(checkbox).not.toHaveAttribute('required'); + }); +}); diff --git a/packages/components-react/src/form-field-radio-group/FormFieldRadioGroup.tsx b/packages/components-react/src/form-field-radio-group/FormFieldRadioGroup.tsx index 0d90bfdc..615bc520 100644 --- a/packages/components-react/src/form-field-radio-group/FormFieldRadioGroup.tsx +++ b/packages/components-react/src/form-field-radio-group/FormFieldRadioGroup.tsx @@ -3,8 +3,10 @@ import React, { ForwardedRef, forwardRef } from 'react'; import './FormFieldRadioGroup.css'; import { LuxFormFieldRadioOption } from '../form-field-radio-option/FormFieldRadioOption'; +type RadioOptionValue = string | number; + interface RadioOption { - value: string; + value: RadioOptionValue; label: string; disabled?: boolean; description?: React.ReactNode; @@ -16,7 +18,7 @@ export interface LuxFormFieldRadioGroupProps { description?: string; errorMessage?: string; options: RadioOption[]; - value?: string; + value?: RadioOptionValue; invalid?: boolean; required?: boolean; className?: string; diff --git a/packages/components-react/src/form-field-select/FormFieldSelect.tsx b/packages/components-react/src/form-field-select/FormFieldSelect.tsx index 3c123bed..e65085f8 100644 --- a/packages/components-react/src/form-field-select/FormFieldSelect.tsx +++ b/packages/components-react/src/form-field-select/FormFieldSelect.tsx @@ -8,7 +8,11 @@ import { } from '../form-field-description/FormFieldDescription'; import { LuxFormFieldErrorMessage } from '../form-field-error-message/FormFieldErrorMessage'; import { LuxFormFieldLabel } from '../form-field-label/FormFieldLabel'; -import { LuxSelect, type LuxSelectProps } from '../select/Select'; +import { LuxSelect, LuxSelectOption, type LuxSelectOptionProps, type LuxSelectProps } from '../select/Select'; + +export type LuxFormFieldSelectOptionsProps = LuxSelectOptionProps & { + label?: string | number; +}; export interface LuxFormFieldSelectProps extends Omit, @@ -27,6 +31,7 @@ export interface LuxFormFieldSelectProps | 'size' | 'value' > { + options?: LuxFormFieldSelectOptionsProps[]; appearance?: LuxFormFieldDescriptionAppearance; distanced?: boolean; inputRef?: Ref; @@ -34,6 +39,7 @@ export interface LuxFormFieldSelectProps export const LuxFormFieldSelect = ({ label, + options, description, errorMessage, disabled, @@ -114,10 +120,17 @@ export const LuxFormFieldSelect = ({ }) || undefined } {...selectAttrs} - /> + > + {options + ? options.map((option) => ( + + {option.label} + + )) + : formFieldAttrs['children']} + } className={className} - {...formFieldAttrs} /> ); }; diff --git a/packages/components-react/src/form-field-textbox/FormFieldTextbox.tsx b/packages/components-react/src/form-field-textbox/FormFieldTextbox.tsx index b7e55d39..5f5526d7 100644 --- a/packages/components-react/src/form-field-textbox/FormFieldTextbox.tsx +++ b/packages/components-react/src/form-field-textbox/FormFieldTextbox.tsx @@ -10,6 +10,7 @@ import { import { LuxFormFieldErrorMessage } from '../form-field-error-message/FormFieldErrorMessage'; import { LuxFormFieldLabel } from '../form-field-label/FormFieldLabel'; import { type Direction, LuxTextbox } from '../textbox/Textbox'; +import { pick } from '../utils/object'; export type LuxFormFieldTextboxProps = UtrechtFormFieldTextboxProps & { appearance?: LuxFormFieldDescriptionAppearance; @@ -60,15 +61,6 @@ export const LuxFormFieldTextbox = ({ errorMessage ); - // TODO: naar utils - function pick(obj: T, paths: Array): Pick { - const ret = {} as Pick; - for (const k of paths) { - ret[k] = obj[k]; - } - return ret; - } - const textBoxAttrs = pick(restProps, [ 'autoComplete', 'min', diff --git a/packages/components-react/src/index.ts b/packages/components-react/src/index.ts index edc57531..cca02bbc 100644 --- a/packages/components-react/src/index.ts +++ b/packages/components-react/src/index.ts @@ -25,7 +25,11 @@ export { type LuxFormFieldErrorMessageProps, } from './form-field-error-message/FormFieldErrorMessage'; export { LuxFormFieldLabel, type LuxFormFieldLabelProps } from './form-field-label/FormFieldLabel'; -export { LuxFormFieldSelect, type LuxFormFieldSelectProps } from './form-field-select/FormFieldSelect'; +export { + LuxFormFieldSelect, + type LuxFormFieldSelectProps, + type LuxFormFieldSelectOptionsProps, +} from './form-field-select/FormFieldSelect'; export { LuxFormFieldTextbox, type LuxFormFieldTextboxProps } from './form-field-textbox/FormFieldTextbox'; export { LuxParagraph, type LuxParagraphProps } from './paragraph/Paragraph'; export { diff --git a/packages/components-react/src/select/Select.tsx b/packages/components-react/src/select/Select.tsx index e1f906dd..43bd5344 100644 --- a/packages/components-react/src/select/Select.tsx +++ b/packages/components-react/src/select/Select.tsx @@ -6,14 +6,15 @@ import { } from '@utrecht/component-library-react/dist/css-module'; import './Select.css'; import clsx from 'clsx'; -import { forwardRef } from 'react'; +import { ForwardedRef, forwardRef } from 'react'; + export type LuxSelectProps = UtrechtSelectProps; export type LuxSelectOptionProps = UtrechtSelectOptionProps; -export const LuxSelect = forwardRef((props: LuxSelectProps) => { +export const LuxSelect = forwardRef((props: LuxSelectProps, ref: ForwardedRef) => { const { className, ...restProps } = props; - return ; + return ; }); export const LuxSelectOption = SelectOption; diff --git a/packages/components-react/src/utils/object.ts b/packages/components-react/src/utils/object.ts new file mode 100644 index 00000000..4f57d08b --- /dev/null +++ b/packages/components-react/src/utils/object.ts @@ -0,0 +1,17 @@ +/** + * Pick only certain keys form object. + * + * @export + * @template {object} T + * @template {keyof T} U + * @param {T} obj Object to pick from + * @param {Array} keys Keys to pick + * @returns {Pick} Object containing only picked keys + */ +export function pick(obj: T, keys: Array): Pick { + const ret = {} as Pick; + for (const k of keys) { + ret[k] = obj[k]; + } + return ret; +} diff --git a/packages/storybook/src/react-components/form-field-checkbox/form-field-checkbox.stories.tsx b/packages/storybook/src/react-components/form-field-checkbox/form-field-checkbox.stories.tsx index b60e856c..eaf55845 100644 --- a/packages/storybook/src/react-components/form-field-checkbox/form-field-checkbox.stories.tsx +++ b/packages/storybook/src/react-components/form-field-checkbox/form-field-checkbox.stories.tsx @@ -1,7 +1,9 @@ import { LuxFormFieldCheckbox, type LuxFormFieldCheckboxProps } from '@lux-design-system/components-react'; import tokens from '@lux-design-system/design-tokens/dist/index.json'; import type { Meta, StoryObj } from '@storybook/react'; +import { VisualStates } from './visual/States'; import { BADGES } from '../../../config/preview'; +import { createDesignTokensStory, createVisualRegressionStory, VisualRegressionWrapper } from '../../utils'; import CheckboxMeta from '../checkbox/checkbox.stories'; import FormFieldDescriptionMeta from '../form-field-description/form-field-description.stories'; import FormFieldErrorMessageMeta from '../form-field-error-message/form-field-error-message.stories'; @@ -91,6 +93,14 @@ export const WithTarget: Story = { ...Playground.args, withTarget: true, }, + parameters: { + docs: { + description: { + story: + 'Met `withTarget` wordt het hele component (behalve de foutmelding) een klikdoel. _Let op:_ dit kan voor gebruikers onverwacht zijn.', + }, + }, + }, }; export const withLongTexts: Story = { @@ -105,3 +115,18 @@ export const withLongTexts: Story = { withTarget: true, }, }; + +export const DesignTokens = createDesignTokensStory(meta); + +export const Visual = createVisualRegressionStory(() => ( + <> +

Light

+ + + +

Dark

+ + + + +)); diff --git a/packages/storybook/src/react-components/form-field-checkbox/visual/States.tsx b/packages/storybook/src/react-components/form-field-checkbox/visual/States.tsx new file mode 100644 index 00000000..69d4b505 --- /dev/null +++ b/packages/storybook/src/react-components/form-field-checkbox/visual/States.tsx @@ -0,0 +1,23 @@ +import { LuxFormFieldCheckbox } from '@lux-design-system/components-react'; + +export const VisualStates = () => ( + <> + + + +
Hover & Focus
+
+ +
+
+ +
+
Invalid
+ + +
Disabled
+ + + + +); diff --git a/packages/storybook/src/react-components/form-field-description/form-field-description.mdx b/packages/storybook/src/react-components/form-field-description/form-field-description.mdx index 124ea418..8243a5b2 100644 --- a/packages/storybook/src/react-components/form-field-description/form-field-description.mdx +++ b/packages/storybook/src/react-components/form-field-description/form-field-description.mdx @@ -43,6 +43,11 @@ import { CitationDocumentation } from "../../utils/CitationDocumentation"; +### Appearance vormgegeven met tokens + + + + ### Lange Beschrijving diff --git a/packages/storybook/src/react-components/form-field-description/form-field-description.stories.tsx b/packages/storybook/src/react-components/form-field-description/form-field-description.stories.tsx index d9b9747c..f912623d 100644 --- a/packages/storybook/src/react-components/form-field-description/form-field-description.stories.tsx +++ b/packages/storybook/src/react-components/form-field-description/form-field-description.stories.tsx @@ -1,7 +1,20 @@ import { LuxFormFieldDescription } from '@lux-design-system/components-react'; import tokens from '@lux-design-system/design-tokens/dist/index.json'; import type { Meta, StoryObj } from '@storybook/react'; +import { VisualStates } from './visual/States'; import { BADGES } from '../../../config/preview'; +import { createDesignTokensStory, createVisualRegressionStory, VisualRegressionWrapper } from '../../utils'; + +const CustomStyleBlock = () => ( + +); const meta = { title: 'React Components/Form Field/Form Field Description', @@ -10,7 +23,7 @@ const meta = { parameters: { badges: [BADGES.WIP, BADGES.LATEST], tokens, - tokensPrefix: 'react-form-field-description', + tokensPrefix: 'utrecht-form-field-description', docs: { description: { component: 'A description component for form fields that provides additional context or validation feedback.', @@ -94,6 +107,35 @@ export const Invalid: Story = { }, }; +export const CustomAppearance: Story = { + args: {}, + render: () => ( + <> + + + Valid Form Field Description + + + Invalid Form Field Description + + + ), + parameters: { + controls: { + disable: true, + }, + docs: { + source: { + code: null, + }, + description: { + story: + 'Om de `valid` en `invalid` _appearance_ een styling te geven kan dit met de `...valid.color`- en `...invalid.color`-tokens.', + }, + }, + }, +}; + export const LongDescription: Story = { args: { children: @@ -107,3 +149,23 @@ export const LongDescription: Story = { }, }, }; + +export const DesignTokens = createDesignTokensStory(meta); + +export const Visual = createVisualRegressionStory(() => ( + <> +

Light

+ + + +

Dark

+ + + +

Custom

+ + + + + +)); diff --git a/packages/storybook/src/react-components/form-field-description/visual/States.tsx b/packages/storybook/src/react-components/form-field-description/visual/States.tsx new file mode 100644 index 00000000..1add8ec1 --- /dev/null +++ b/packages/storybook/src/react-components/form-field-description/visual/States.tsx @@ -0,0 +1,14 @@ +import { LuxFormFieldDescription } from '@lux-design-system/components-react'; + +export const VisualStates = () => ( + <> + Form Field Description + Valid Form Field Description + Invalid Form Field Description + + Long Form Field Description. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Dolor ante id varius, + aenean eu faucibus vitae malesuada. Viverra malesuada aliquam et placerat justo porta ipsum parturient. Cursus + nostra varius efficitur lobortis aliquam lectus bibendum. + + +); diff --git a/packages/storybook/src/react-components/form-field-error-message/form-field-error-message.stories.tsx b/packages/storybook/src/react-components/form-field-error-message/form-field-error-message.stories.tsx index 9fae1ed6..eb0c9d53 100644 --- a/packages/storybook/src/react-components/form-field-error-message/form-field-error-message.stories.tsx +++ b/packages/storybook/src/react-components/form-field-error-message/form-field-error-message.stories.tsx @@ -2,7 +2,9 @@ import { LuxFormFieldErrorMessage, type LuxFormFieldErrorMessageProps } from '@l import tokens from '@lux-design-system/design-tokens/dist/index.json'; import type { Meta, StoryObj } from '@storybook/react'; import { forwardRef, PropsWithChildren } from 'react'; +import { VisualStates } from './visual/States'; import { BADGES } from '../../../config/preview'; +import { createDesignTokensStory, createVisualRegressionStory, VisualRegressionWrapper } from '../../utils'; const WrappedLuxFormFieldErrorMessage = forwardRef< HTMLParagraphElement, @@ -18,7 +20,7 @@ const meta = { parameters: { badges: [BADGES.WIP, BADGES.LATEST], tokens, - tokensPrefix: 'react-form-field-error-message', + tokensPrefix: 'utrecht-form-field-error-message', }, argTypes: { children: { @@ -72,3 +74,18 @@ export const Distanced: Story = { distanced: true, }, }; + +export const DesignTokens = createDesignTokensStory(meta); + +export const Visual = createVisualRegressionStory(() => ( + <> +

Light

+ + + +

Dark

+ + + + +)); diff --git a/packages/storybook/src/react-components/form-field-error-message/visual/States.tsx b/packages/storybook/src/react-components/form-field-error-message/visual/States.tsx new file mode 100644 index 00000000..d945b517 --- /dev/null +++ b/packages/storybook/src/react-components/form-field-error-message/visual/States.tsx @@ -0,0 +1,7 @@ +import { LuxFormFieldErrorMessage } from '@lux-design-system/components-react'; + +export const VisualStates = () => ( + <> + Error Message + +); diff --git a/packages/storybook/src/react-components/form-field-label/form-field-label.stories.tsx b/packages/storybook/src/react-components/form-field-label/form-field-label.stories.tsx index 87ad9aad..3eaca33f 100644 --- a/packages/storybook/src/react-components/form-field-label/form-field-label.stories.tsx +++ b/packages/storybook/src/react-components/form-field-label/form-field-label.stories.tsx @@ -2,7 +2,9 @@ import { LuxFormFieldLabel as FormFieldLabel, LuxFormFieldLabelProps } from '@lu import tokens from '@lux-design-system/design-tokens/dist/index.json'; import type { Meta, StoryObj } from '@storybook/react'; import { type LabelHTMLAttributes, type PropsWithChildren } from 'react'; +import { VisualStates } from './visual/States'; import { BADGES } from '../../../config/preview'; +import { createDesignTokensStory, createVisualRegressionStory, VisualRegressionWrapper } from '../../utils'; const LuxFormFieldLabel = ( props: PropsWithChildren & LabelHTMLAttributes, @@ -18,7 +20,7 @@ const meta = { parameters: { badges: [BADGES.WIP, BADGES.LATEST], tokens, - tokensPrefix: 'react-form-label', + tokensPrefix: 'utrecht-form-label', }, argTypes: { children: { @@ -110,3 +112,18 @@ export const CheckedLabel: Story = { type: 'checkbox', }, }; + +export const DesignTokens = createDesignTokensStory(meta); + +export const Visual = createVisualRegressionStory(() => ( +
+

Light

+ + + +

Dark

+ + + +
+)); diff --git a/packages/storybook/src/react-components/form-field-label/visual/States.tsx b/packages/storybook/src/react-components/form-field-label/visual/States.tsx new file mode 100644 index 00000000..bb2852d5 --- /dev/null +++ b/packages/storybook/src/react-components/form-field-label/visual/States.tsx @@ -0,0 +1,11 @@ +import { LuxFormFieldLabel } from '@lux-design-system/components-react'; + +export const VisualStates = () => ( + <> + Label + Checkbox Label + Radio Label + Checked Label + Disabled Label + +); diff --git a/packages/storybook/src/react-components/form-field-radio-group/form-field-radio-group.stories.tsx b/packages/storybook/src/react-components/form-field-radio-group/form-field-radio-group.stories.tsx index 4d27bcea..e1cfdbf8 100644 --- a/packages/storybook/src/react-components/form-field-radio-group/form-field-radio-group.stories.tsx +++ b/packages/storybook/src/react-components/form-field-radio-group/form-field-radio-group.stories.tsx @@ -1,6 +1,8 @@ import { LuxFormFieldRadioGroup } from '@lux-design-system/components-react'; import { Meta, StoryObj } from '@storybook/react'; +import { VisualStates } from './visual/States'; import { BADGES } from '../../../config/preview'; +import { createVisualRegressionStory, VisualRegressionWrapper } from '../../utils'; type Story = StoryObj; @@ -208,3 +210,16 @@ export const WithLongLabels: Story = { }, }, }; + +export const Visual = createVisualRegressionStory(() => ( +
+

Light

+ + + +

Dark

+ + + +
+)); diff --git a/packages/storybook/src/react-components/form-field-radio-group/visual/States.tsx b/packages/storybook/src/react-components/form-field-radio-group/visual/States.tsx new file mode 100644 index 00000000..5faff29e --- /dev/null +++ b/packages/storybook/src/react-components/form-field-radio-group/visual/States.tsx @@ -0,0 +1,44 @@ +import { LuxFormFieldRadioGroup } from '@lux-design-system/components-react'; + +const options = [ + { value: '1', label: 'Radio Option 1' }, + { value: '2', label: 'Radio Option 2' }, + { value: '3', label: 'Radio Option 3' }, +]; + +export const VisualStates = ({ mode }: { mode: 'light' | 'dark' }) => ( + <> + + + Object.assign({}, { ...option }, { description: `Option Description ${option.value}` }), + )} + /> +
+ +
+
+ +
+ Object.assign({}, { ...option }, { checked: option.value === '2' }))} + /> + Object.assign({}, { ...option }, { disabled: option.value === '2' }))} + /> + + +); diff --git a/packages/storybook/src/react-components/form-field-radio-option/form-field-radio-option.stories.tsx b/packages/storybook/src/react-components/form-field-radio-option/form-field-radio-option.stories.tsx index f416fda0..3252834a 100644 --- a/packages/storybook/src/react-components/form-field-radio-option/form-field-radio-option.stories.tsx +++ b/packages/storybook/src/react-components/form-field-radio-option/form-field-radio-option.stories.tsx @@ -1,7 +1,9 @@ import { LuxFormFieldRadioOption } from '@lux-design-system/components-react'; import tokens from '@lux-design-system/design-tokens/dist/index.json'; import type { Meta, StoryObj } from '@storybook/react'; +import { VisualStates } from './visual/States'; import { BADGES } from '../../../config/preview'; +import { createVisualRegressionStory, VisualRegressionWrapper } from '../../utils'; type Story = StoryObj; @@ -150,3 +152,16 @@ export const FocusVisible: Story = { pseudo: { focus: true, focusVisible: true }, }, }; + +export const Visual = createVisualRegressionStory(() => ( +
+

Light

+ + + +

Dark

+ + + +
+)); diff --git a/packages/storybook/src/react-components/form-field-radio-option/visual/States.tsx b/packages/storybook/src/react-components/form-field-radio-option/visual/States.tsx new file mode 100644 index 00000000..8e935324 --- /dev/null +++ b/packages/storybook/src/react-components/form-field-radio-option/visual/States.tsx @@ -0,0 +1,17 @@ +import { LuxFormFieldRadioOption } from '@lux-design-system/components-react'; + +export const VisualStates = ({ mode }: { mode: 'light' | 'dark' }) => ( + <> + + +
+ +
+
+ +
+ + + + +); diff --git a/packages/storybook/src/react-components/form-field-select/form-field-select.mdx b/packages/storybook/src/react-components/form-field-select/form-field-select.mdx index 811cbd24..e319df63 100644 --- a/packages/storybook/src/react-components/form-field-select/form-field-select.mdx +++ b/packages/storybook/src/react-components/form-field-select/form-field-select.mdx @@ -1,11 +1,33 @@ -import { Canvas, Controls, Meta } from "@storybook/blocks"; +import { Canvas, Controls, Description, Meta } from "@storybook/blocks"; import * as FormFieldSelectStories from "./form-field-select.stories"; -# Lux Form Field Select +# Form Field Select ## Playground + +## States + +### Disabled + + + + +### Invalid + + + + +### Hover + + + + +### Focus + + + diff --git a/packages/storybook/src/react-components/form-field-select/form-field-select.stories.tsx b/packages/storybook/src/react-components/form-field-select/form-field-select.stories.tsx index f5f2e73b..a232523f 100644 --- a/packages/storybook/src/react-components/form-field-select/form-field-select.stories.tsx +++ b/packages/storybook/src/react-components/form-field-select/form-field-select.stories.tsx @@ -1,11 +1,23 @@ -import { LuxFormFieldSelect, type LuxFormFieldSelectProps } from '@lux-design-system/components-react'; +import { + LuxFormFieldSelect, + type LuxFormFieldSelectOptionsProps, + type LuxFormFieldSelectProps, +} from '@lux-design-system/components-react'; import tokens from '@lux-design-system/design-tokens/dist/index.json'; import type { Meta, StoryObj } from '@storybook/react'; +import { VisualStates } from './visual/States'; import { BADGES } from '../../../config/preview'; +import { createDesignTokensStory, createVisualRegressionStory, VisualRegressionWrapper } from '../../utils'; import FormFieldDescriptionMeta from '../form-field-description/form-field-description.stories'; import FormFieldErrorMessageMeta from '../form-field-error-message/form-field-error-message.stories'; import SelectMeta from '../select/select.stories'; +const options: LuxFormFieldSelectOptionsProps[] = [ + { value: '1', label: 'Option 1', disabled: true }, + { value: '2', label: 'Option 2', selected: true }, + { value: '3', label: 'Option 3' }, +]; + const meta = { title: 'React Components/Form Field/Form Field Select', id: 'react-components-form-field-form-field-select', @@ -41,9 +53,10 @@ type Story = StoryObj; export const Playground: Story = { name: 'Playground', args: { - label: 'Form Field Select', - description: 'Select in een FormField', - errorMessage: 'Zo kan het ook een ErrorMessage hebben', + label: 'Label', + options, + description: 'Description', + errorMessage: 'Error Message', invalid: false, appearance: undefined, }, @@ -54,3 +67,52 @@ export const Playground: Story = { }, tags: ['!autodocs'], }; + +export const Disabled: Story = { + ...Playground, + name: 'Disabled', + args: { + ...Playground.args, + disabled: true, + }, +}; + +export const Invalid: Story = { + ...Playground, + name: 'Invalid', + args: { + ...Playground.args, + invalid: true, + }, +}; + +export const Hover: Story = { + ...Playground, + name: 'Hover', + parameters: { + pseudo: { hover: true }, + }, +}; + +export const Focus: Story = { + ...Playground, + name: 'Focus', + parameters: { + pseudo: { focus: true, focusVisible: true }, + }, +}; + +export const DesignTokens = createDesignTokensStory(meta); + +export const Visual = createVisualRegressionStory(() => ( + <> +

Light

+ + + +

Dark

+ + + + +)); diff --git a/packages/storybook/src/react-components/form-field-select/visual/States.tsx b/packages/storybook/src/react-components/form-field-select/visual/States.tsx new file mode 100644 index 00000000..2ca70f0e --- /dev/null +++ b/packages/storybook/src/react-components/form-field-select/visual/States.tsx @@ -0,0 +1,27 @@ +import { LuxFormFieldSelect, type LuxFormFieldSelectProps } from '@lux-design-system/components-react'; + +export const VisualStates = ({ options }: Pick) => ( + <> + + +
Hover & Focus
+
+ +
+
+ +
+
Invalid
+ + +
Disabled
+ + + +); diff --git a/packages/storybook/src/react-components/form-field-textbox/form-field-textbox.stories.tsx b/packages/storybook/src/react-components/form-field-textbox/form-field-textbox.stories.tsx index 8a770803..e4878322 100644 --- a/packages/storybook/src/react-components/form-field-textbox/form-field-textbox.stories.tsx +++ b/packages/storybook/src/react-components/form-field-textbox/form-field-textbox.stories.tsx @@ -1,7 +1,9 @@ import { LuxFormFieldTextbox, type LuxFormFieldTextboxProps } from '@lux-design-system/components-react'; import tokens from '@lux-design-system/design-tokens/dist/index.json'; import type { Meta, StoryObj } from '@storybook/react'; +import { VisualStates } from './visual/States'; import { BADGES } from '../../../config/preview'; +import { createDesignTokensStory, createVisualRegressionStory, VisualRegressionWrapper } from '../../utils'; import FormFieldDescriptionMeta from '../form-field-description/form-field-description.stories'; import FormFieldErrorMessageMeta from '../form-field-error-message/form-field-error-message.stories'; import TextboxMeta from '../textbox/textbox.stories'; @@ -54,3 +56,18 @@ export const Playground: Story = { }, tags: ['!autodocs'], }; + +export const DesignTokens = createDesignTokensStory(meta); + +export const Visual = createVisualRegressionStory(() => ( + <> +

Light

+ + + +

Dark

+ + + + +)); diff --git a/packages/storybook/src/react-components/form-field-textbox/visual/States.tsx b/packages/storybook/src/react-components/form-field-textbox/visual/States.tsx new file mode 100644 index 00000000..9c5f9301 --- /dev/null +++ b/packages/storybook/src/react-components/form-field-textbox/visual/States.tsx @@ -0,0 +1,21 @@ +import { LuxFormFieldTextbox } from '@lux-design-system/components-react'; + +export const VisualStates = () => ( + <> + + +
Hover & Focus
+
+ +
+
+ +
+
Invalid
+ + +
Disabled
+ + + +); diff --git a/packages/storybook/src/react-components/form-field/form-field.stories.tsx b/packages/storybook/src/react-components/form-field/form-field.stories.tsx index 08d1b03e..58cf776d 100644 --- a/packages/storybook/src/react-components/form-field/form-field.stories.tsx +++ b/packages/storybook/src/react-components/form-field/form-field.stories.tsx @@ -8,7 +8,9 @@ import { } from '@lux-design-system/components-react'; import tokens from '@lux-design-system/design-tokens/dist/index.json'; import type { Meta, StoryObj } from '@storybook/react'; +import { FormFieldType } from './visual/FormFieldTypes'; import { BADGES } from '../../../config/preview'; +import { createDesignTokensStory, createVisualRegressionStory, VisualRegressionWrapper } from '../../utils'; const meta = { title: 'React Components/Form Field', @@ -141,3 +143,49 @@ export const FormFieldError: Story = { invalid: true, }, }; + +export const DesignTokens = createDesignTokensStory(meta); + +export const Visual = createVisualRegressionStory(() => ( + <> + + Note: Dit is alleen een test voor het FormField, de verschillende inputs hebben hun eigen tests. + +

Light

+ +
Type: text
+ + + + +
Type: checkbox
+ + + + +
Type: radio
+ + + + +
+

Dark

+ +
Type: text
+ + + + +
Type: checkbox
+ + + + +
Type: radio
+ + + + +
+ +)); diff --git a/packages/storybook/src/react-components/form-field/visual/FormFieldTypes.tsx b/packages/storybook/src/react-components/form-field/visual/FormFieldTypes.tsx new file mode 100644 index 00000000..401fab6a --- /dev/null +++ b/packages/storybook/src/react-components/form-field/visual/FormFieldTypes.tsx @@ -0,0 +1,84 @@ +import { + LuxFormField, + LuxFormFieldDescription, + LuxFormFieldErrorMessage, + LuxFormFieldLabel, + type LuxFormFieldLabelProps, + LuxParagraph, +} from '@lux-design-system/components-react'; + +type FormFieldTypeProps = { + type?: 'text' | 'checkbox' | 'radio'; + invalid?: boolean; + show?: { + description?: boolean; + extra?: boolean; + }; +}; + +export const FormFieldType = ({ + type = 'text', + invalid = false, + show = { description: true, extra: false }, +}: FormFieldTypeProps) => { + const Input = ({ type }: any) => { + switch (type) { + case 'checkbox': + return ( + + ); + + case 'radio': + return ( + + ); + + default: + return ( + + ); + } + }; + + return ( + {`Label`}} + description={ + show.description ? ( + + {`Description`} + + ) : null + } + errorMessage={invalid ? {`Error Message`} : null} + input={} + invalid={invalid} + > + {show.extra ? {`Extra content`} : null} + + ); +};