Skip to content

Commit

Permalink
fix switch accessibility
Browse files Browse the repository at this point in the history
  • Loading branch information
Riley Pearce committed Feb 8, 2024
1 parent 0cd20b4 commit 58de0b3
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 49 deletions.
4 changes: 2 additions & 2 deletions packages/@mantine-tests/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import userEvent from '@testing-library/user-event';
import { axe } from './axe';
import { itConnectsLabelAndInput } from './inputs/it-connects-label-and-input';
import { itHandlesCheckboxState } from './inputs/it-handles-checkbox-state';
import { itHandlesSwitchCheckboxState } from './inputs/it-handles-switch-checkbox-state';
import { itSupportsInputAsterisk } from './inputs/it-supports-input-asterisk';
import { itSupportsInputContainer } from './inputs/it-supports-input-container';
import { itSupportsInputProps } from './inputs/it-supports-input-props';
Expand Down Expand Up @@ -73,7 +73,7 @@ export const tests = {
itSupportsInputAsterisk,
itSupportsInputWrapperOrder,
itSupportsInputWrapperElements,
itHandlesCheckboxState,
itHandlesSwitchCheckboxState,
itConnectsLabelAndInput,

itSupportsSystemProps,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { render } from '../render';

interface Options<Props = any> {
component: React.ComponentType<Props>;
props: Props;
}

export function itHandlesSwitchCheckboxState<Props>(
options: Options<Props>,
name = 'handles switch checkbox state'
) {
describe(name, () => {
it('correctly handles controlled switch checkbox state', async () => {
const spy = jest.fn();
render(<options.component {...options.props} checked={false} onChange={spy} />);
expect(screen.getByRole('switch')).not.toBeChecked();
await userEvent.click(screen.getByRole('switch'));
expect(spy).toHaveBeenCalledTimes(1);
expect(screen.getByRole('switch')).not.toBeChecked();
});

it('correctly handles uncontrolled switch checkbox state', async () => {
render(<options.component {...options.props} defaultChecked={false} />);
expect(screen.getByRole('switch')).not.toBeChecked();
await userEvent.click(screen.getByRole('switch'));
expect(screen.getByRole('switch')).toBeChecked();
});
});
}
21 changes: 17 additions & 4 deletions packages/@mantine/core/src/components/InlineInput/InlineInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export interface InlineInputProps
error: React.ReactNode;
size: MantineSize | (string & {}) | undefined;
labelPosition?: 'left' | 'right';
bodyElement?: any;
labelElement?: any;
}

export type InlineInputFactory = Factory<{
Expand All @@ -60,6 +62,8 @@ export const InlineInput = forwardRef<HTMLDivElement, InlineInputProps>(
error,
size,
labelPosition = 'left',
bodyElement = 'div',
labelElement = 'label',
variant,
style,
vars,
Expand Down Expand Up @@ -92,14 +96,23 @@ export const InlineInput = forwardRef<HTMLDivElement, InlineInputProps>(
size={size}
{...others}
>
<div {...getStyles('body')}>
<Box
component={bodyElement}
htmlFor={bodyElement === 'label' ? id : undefined}
{...getStyles('body')}
>
{children}

<div {...getStyles('labelWrapper')} data-disabled={disabled || undefined}>
{label && (
<label {...getStyles('label')} data-disabled={disabled || undefined} htmlFor={id}>
<Box
component={labelElement}
htmlFor={labelElement === 'label' ? id : undefined}
{...getStyles('label')}
data-disabled={disabled || undefined}
>
{label}
</label>
</Box>
)}

{description && (
Expand All @@ -114,7 +127,7 @@ export const InlineInput = forwardRef<HTMLDivElement, InlineInputProps>(
</Input.Error>
)}
</div>
</div>
</Box>
</Box>
);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/@mantine/core/src/components/Switch/Switch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const defaultProps: SwitchProps = {
describe('@mantine/core/Switch', () => {
tests.axe([<Switch label="test-label" />, <Switch aria-label="test-label" />]);
tests.itSupportsFocusEvents({ component: Switch, props: defaultProps, selector: 'input' });
tests.itHandlesCheckboxState({ component: Switch, props: defaultProps });
tests.itHandlesSwitchCheckboxState({ component: Switch, props: defaultProps });
tests.itConnectsLabelAndInput({ component: Switch, props: defaultProps });
tests.itSupportsSystemProps<SwitchProps, SwitchStylesNames>({
component: Switch,
Expand Down Expand Up @@ -59,7 +59,7 @@ describe('@mantine/core/Switch', () => {

it('sets disabled attribute on input based on disabled prop', () => {
render(<Switch disabled />);
expect(screen.getByRole('checkbox')).toBeDisabled();
expect(screen.getByRole('switch')).toBeDisabled();
});

it('exposes SwitchGroup component', () => {
Expand Down
6 changes: 4 additions & 2 deletions packages/@mantine/core/src/components/Switch/Switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ export const Switch = factory<SwitchFactory>((_props, ref) => {
description={description}
error={error}
disabled={disabled}
bodyElement="label"
labelElement="span"
classNames={classNames}
styles={styles}
unstyled={unstyled}
Expand All @@ -208,12 +210,12 @@ export const Switch = factory<SwitchFactory>((_props, ref) => {
id={uuid}
ref={ref}
type="checkbox"
role="switch"
{...getStyles('input')}
/>

<Box
component="label"
htmlFor={uuid}
aria-hidden="true"
mod={{ error, 'label-position': labelPosition }}
{...getStyles('track')}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,18 @@ describe('@mantine/core/SwitchGroup', () => {

it('supports uncontrolled state', async () => {
render(<SwitchGroup {...defaultProps} defaultValue={['test-value-1']} />);
expect(screen.getAllByRole('checkbox')[0]).toBeChecked();
await userEvent.click(screen.getAllByRole('checkbox')[1]);
expect(screen.getAllByRole('checkbox')[1]).toBeChecked();
expect(screen.getAllByRole('switch')[0]).toBeChecked();
await userEvent.click(screen.getAllByRole('switch')[1]);
expect(screen.getAllByRole('switch')[1]).toBeChecked();
});

it('supports controlled state', async () => {
const spy = jest.fn();
render(<SwitchGroup {...defaultProps} value={['test-value-2']} onChange={spy} />);
expect(screen.getAllByRole('checkbox')[1]).toBeChecked();
await userEvent.click(screen.getAllByRole('checkbox')[0]);
expect(screen.getAllByRole('checkbox')[1]).toBeChecked();
expect(screen.getAllByRole('checkbox')[0]).not.toBeChecked();
expect(screen.getAllByRole('switch')[1]).toBeChecked();
await userEvent.click(screen.getAllByRole('switch')[0]);
expect(screen.getAllByRole('switch')[1]).toBeChecked();
expect(screen.getAllByRole('switch')[0]).not.toBeChecked();
expect(spy).toHaveBeenCalledWith(['test-value-2', 'test-value-1']);
});
});

0 comments on commit 58de0b3

Please sign in to comment.