Skip to content

Commit

Permalink
test: checkboxes tests
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Aug 18, 2024
1 parent e104020 commit a28a071
Show file tree
Hide file tree
Showing 4 changed files with 385 additions and 23 deletions.
129 changes: 119 additions & 10 deletions packages/core/src/useCheckbox/useCheckbox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const InputBase: string = `
<input v-bind="inputProps" />
<label v-bind="labelProps">{{ label }}</label>
<span v-bind="errorMessageProps">{{ errorMessage }}</span>
<span data-testid="value">{{ fieldValue }}</span>
</div>
`;

Expand All @@ -18,19 +19,19 @@ const CustomBase: string = `
<div v-bind="inputProps"></div>
<div v-bind="labelProps" >{{ label }}</div>
<span v-bind="errorMessageProps">{{ errorMessage }}</span>
<span data-testid="value">{{ fieldValue }}</span>
</div>
`;

const createCheckbox = (template = InputBase): Component => {
const createCheckbox = (props: CheckboxProps, template = InputBase): Component => {
return defineComponent({
template,
inheritAttrs: false,
setup(props: CheckboxProps, { attrs }) {
const box = useCheckbox({ ...props, ...attrs });
setup() {
const box = useCheckbox(props);

return {
...props,
...attrs,
...box,
};
},
Expand All @@ -39,13 +40,13 @@ const createCheckbox = (template = InputBase): Component => {

describe('has no a11y violations', () => {
test('with input as base element', async () => {
const Checkbox = createCheckbox();
const Checkbox = createCheckbox({ label: 'First' });

await render({
components: { Checkbox },
template: `
<div data-testid="fixture">
<Checkbox label="First" value="1" />
<Checkbox value="1" />
</div>
`,
});
Expand All @@ -56,13 +57,13 @@ describe('has no a11y violations', () => {
});

test('with custom elements as base', async () => {
const Checkbox = createCheckbox(CustomBase);
const Checkbox = createCheckbox({ label: 'First' }, CustomBase);

await render({
components: { Checkbox },
template: `
<div data-testid="fixture">
<Checkbox label="First" value="1" />
<Checkbox value="1" />
</div>
`,
});
Expand All @@ -73,15 +74,123 @@ describe('has no a11y violations', () => {
});
});

describe('value toggling on click', () => {
test('with input as base element', async () => {
const Checkbox = createCheckbox({ label: 'First' });

await render({
components: { Checkbox },
template: `
<Checkbox label="First" />
`,
});

expect(screen.getByTestId('value')).toHaveTextContent('');
await fireEvent.click(screen.getByLabelText('First'));
expect(screen.getByTestId('value')).toHaveTextContent('true');
await fireEvent.click(screen.getByLabelText('First'));
expect(screen.getByTestId('value')).toHaveTextContent('false');
});

test('with custom elements as base', async () => {
const Checkbox = createCheckbox({ label: 'First' }, CustomBase);

await render({
components: { Checkbox },
template: `
<Checkbox label="First" />
`,
});

expect(screen.getByTestId('value')).toHaveTextContent('');
await fireEvent.click(screen.getByLabelText('First'));
expect(screen.getByTestId('value')).toHaveTextContent('true');
await fireEvent.click(screen.getByLabelText('First'));
expect(screen.getByTestId('value')).toHaveTextContent('false');
});
});

describe('value toggling on space key', () => {
test('with input as base element', async () => {
const Checkbox = createCheckbox({ label: 'First' });

await render({
components: { Checkbox },
template: `
<Checkbox label="First" />
`,
});

expect(screen.getByTestId('value')).toHaveTextContent('');
await fireEvent.keyDown(screen.getByLabelText('First'), { key: 'Space' });
expect(screen.getByTestId('value')).toHaveTextContent('true');
await fireEvent.keyDown(screen.getByLabelText('First'), { key: 'Space' });
expect(screen.getByTestId('value')).toHaveTextContent('false');
});

test('with custom elements as base', async () => {
const Checkbox = createCheckbox({ label: 'First' }, CustomBase);

await render({
components: { Checkbox },
template: `
<Checkbox label="First" />
`,
});

expect(screen.getByTestId('value')).toHaveTextContent('');
await fireEvent.click(screen.getByLabelText('First'));
expect(screen.getByTestId('value')).toHaveTextContent('true');
await fireEvent.click(screen.getByLabelText('First'));
expect(screen.getByTestId('value')).toHaveTextContent('false');
});
});

describe('value toggling with custom true and false values', () => {
test('with input as base element', async () => {
const Checkbox = createCheckbox({ label: 'First', trueValue: '1', falseValue: '2' });

await render({
components: { Checkbox },
template: `
<Checkbox label="First" />
`,
});

expect(screen.getByTestId('value')).toHaveTextContent('');
await fireEvent.click(screen.getByLabelText('First'));
expect(screen.getByTestId('value')).toHaveTextContent('1');
await fireEvent.click(screen.getByLabelText('First'));
expect(screen.getByTestId('value')).toHaveTextContent('2');
});

test('with custom elements as base', async () => {
const Checkbox = createCheckbox({ label: 'First', trueValue: '1', falseValue: '2' }, CustomBase);

await render({
components: { Checkbox },
template: `
<Checkbox label="First" />
`,
});

expect(screen.getByTestId('value')).toHaveTextContent('');
await fireEvent.click(screen.getByLabelText('First'));
expect(screen.getByTestId('value')).toHaveTextContent('1');
await fireEvent.click(screen.getByLabelText('First'));
expect(screen.getByTestId('value')).toHaveTextContent('2');
});
});

describe('validation', () => {
test('picks up native error messages', async () => {
const Checkbox = createCheckbox();
const Checkbox = createCheckbox({ label: 'First', required: true });

await render({
components: { Checkbox },
template: `
<div data-testid="fixture">
<Checkbox label="First" value="1" :required="true" />
<Checkbox label="First" value="1" />
</div>
`,
});
Expand Down
33 changes: 22 additions & 11 deletions packages/core/src/useCheckbox/useCheckbox.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Ref, computed, inject, nextTick, ref, toValue } from 'vue';
import { computed, inject, nextTick, Ref, ref, toValue } from 'vue';
import {
createAccessibleErrorMessageProps,
isEqual,
Expand All @@ -9,22 +9,24 @@ import {
} from '../utils/common';
import {
AriaLabelableProps,
Reactivify,
InputBaseAttributes,
NormalizedProps,
Reactivify,
RovingTabIndex,
TypedSchema,
NormalizedProps,
} from '../types';
import { useLabel } from '../a11y/useLabel';
import { CheckboxGroupContext, CheckboxGroupKey } from './useCheckboxGroup';
import { useFormField } from '../useFormField';
import { FieldTypePrefixes } from '../constants';
import { useInputValidity } from '@core/validation';
import { useInputValidity } from '../validation';

export interface CheckboxProps<TValue = string> {
name?: string;
label?: string;
modelValue?: TValue;

value?: TValue;
trueValue?: TValue;
falseValue?: TValue;

Expand Down Expand Up @@ -57,7 +59,7 @@ export function useCheckbox<TValue = string>(
) {
const props = normalizeProps(_props, ['schema']);
const inputId = useUniqId(FieldTypePrefixes.Checkbox);
const getTrueValue = () => (toValue(props.trueValue) as TValue) ?? (true as TValue);
const getTrueValue = createTrueValueGetter(props);
const getFalseValue = () => (toValue(props.falseValue) as TValue) ?? (false as TValue);
const group: CheckboxGroupContext<TValue> | null = inject(CheckboxGroupKey, null);
const inputRef = elementRef || ref<HTMLElement>();
Expand Down Expand Up @@ -108,7 +110,7 @@ export function useCheckbox<TValue = string>(
return;
}

if (e.code === 'Space') {
if (e.key === 'Space') {
e.preventDefault();
toggleValue();
setTouched(true);
Expand Down Expand Up @@ -196,7 +198,13 @@ export function useCheckbox<TValue = string>(
return;
}

group?.toggleValue(getTrueValue(), force);
if (group) {
group?.toggleValue(getTrueValue(), force);

return;
}

setValue(force ? getTrueValue() : getFalseValue());
}

function toggleValue(force?: boolean) {
Expand Down Expand Up @@ -228,20 +236,19 @@ function useCheckboxField<TValue = string>(
props: NormalizedProps<Reactivify<CheckboxProps<TValue>, 'schema'>, 'schema'>,
) {
const group: CheckboxGroupContext<TValue> | null = inject(CheckboxGroupKey, null);
const getTrueValue = () => (toValue(props.trueValue) as TValue) ?? (true as TValue);

if (group) {
const getTrueValue = createTrueValueGetter(props);

return createGroupField(group, getTrueValue);
}

const field = useFormField<TValue>({
return useFormField<TValue>({
path: props.name,
initialValue: toValue(props.modelValue) as TValue,
disabled: props.disabled,
schema: props.schema,
});

return field;
}

function createGroupField<TValue = unknown>(group: CheckboxGroupContext<TValue>, getTrueValue: () => TValue) {
Expand All @@ -254,3 +261,7 @@ function createGroupField<TValue = unknown>(group: CheckboxGroupContext<TValue>,
setValue,
};
}

function createTrueValueGetter<TValue>(props: NormalizedProps<Reactivify<CheckboxProps<TValue>, 'schema'>, 'schema'>) {
return () => (toValue(props.trueValue) as TValue) ?? (toValue(props.value) as TValue) ?? (true as TValue);
}
Loading

0 comments on commit a28a071

Please sign in to comment.