diff --git a/src/components/Switch/Switch.stories.tsx b/src/components/Switch/Switch.stories.tsx index 6b2a2f68..8be012a3 100644 --- a/src/components/Switch/Switch.stories.tsx +++ b/src/components/Switch/Switch.stories.tsx @@ -167,6 +167,15 @@ export const HiddenLabel: Story = { render: (args: SwitchProps) => , }; +export const HiddenLabelWithoutDescription: Story = { + args: { + description: undefined, + isLabelHidden: true, + label: 'Enable notifications', + }, + render: (args: SwitchProps) => , +}; + export const LabelIcon: Story = { args: { label: 'Security alerts', diff --git a/src/components/Switch/Switch.test.tsx b/src/components/Switch/Switch.test.tsx index 154153c6..8c723d2b 100644 --- a/src/components/Switch/Switch.test.tsx +++ b/src/components/Switch/Switch.test.tsx @@ -175,6 +175,45 @@ describe('Switch', () => { ).toBeNull(); }); + it.each(['default', 'spread'] as const)( + 'does not reserve label space with %s spacing when the label is hidden', + labelSpacing => { + render( + {}} + />, + ); + + expect( + screen.getByRole('switch', {name: 'Notifications'}), + ).toBeInTheDocument(); + const control = getControl('notifications'); + /* eslint-disable testing-library/no-node-access -- the regression is the + row's direct-child layout: only the control may remain in flex flow. */ + const row = control.parentElement; + const field = row?.parentElement; + const visuallyHiddenClass = css({position: 'absolute'}); + const hiddenLabel = screen + .getByText('Notifications') + .closest(`[class~="${visuallyHiddenClass}"]`); + + expect(hiddenLabel?.parentElement).toBe(row); + expect( + [...(row?.children ?? [])].filter( + child => !child.classList.contains(visuallyHiddenClass), + ), + ).toEqual([control]); + expect(field).toHaveClass(css({w: 'fit-content'})); + expect(row).not.toHaveClass(css({w: 'full'})); + /* eslint-enable testing-library/no-node-access */ + }, + ); + it('renders the label before the switch when labelPosition is start', () => { render( (null); @@ -166,7 +166,7 @@ export function Switch({ !effectiveDisabled && (isReadOnly || fieldset?.isReadOnly === true); const classes = switchRecipe({ size, - labelSpacing, + labelSpacing: hasVisibleLabelContent ? labelSpacing : 'default', isSelected, isDisabled: effectiveDisabled, isReadOnly: effectiveReadOnly, @@ -262,21 +262,28 @@ export function Switch({ ) : null} ); - const labelNode = ( -
+ const labelElement = isLabelHidden ? ( + - {isNonEmptyReactNode(description) ? ( + + ) : ( + + ); + const labelNode = hasVisibleLabelContent ? ( +
+ {labelElement} + {hasDescription ? ( {description} ) : null}
+ ) : ( + labelElement ); return (