refactor: switch component using radix-ui primitives

This commit is contained in:
Nicolas Meienberger 2023-04-07 13:12:41 +02:00
parent f398958a8f
commit ccb8b1f31a
4 changed files with 46 additions and 29 deletions

View file

@ -0,0 +1,10 @@
.root[data-state='checked'] {
background-color: var(--tblr-primary);
background-position: right center;
--tblr-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23ffffff'/%3e%3c/svg%3e");
&:focus,
&:hover {
--tblr-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23ffffff'/%3e%3c/svg%3e");
}
}

View file

@ -1,9 +1,7 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { Switch } from './Switch';
import { fireEvent, render } from '../../../../../tests/test-utils';
import { fireEvent, render, screen } from '../../../../../tests/test-utils';
describe('Switch', () => {
it('renders the label', () => {
@ -22,16 +20,16 @@ describe('Switch', () => {
});
it('renders the checked state', () => {
const { container } = render(<Switch checked onChange={jest.fn} />);
const checkbox = container.querySelector('input[type="checkbox"]');
render(<Switch checked onChange={jest.fn} />);
const checkbox = screen.getByRole('switch');
expect(checkbox).toBeChecked();
});
it('triggers onChange event when clicked', () => {
const onChange = jest.fn();
const { container } = render(<Switch onChange={onChange} />);
const checkbox = container.querySelector('input[type="checkbox"]') as Element;
render(<Switch onCheckedChange={onChange} />);
const checkbox = screen.getByRole('switch');
fireEvent.click(checkbox);
@ -40,8 +38,8 @@ describe('Switch', () => {
it('triggers onBlur event when blurred', () => {
const onBlur = jest.fn();
const { container } = render(<Switch onBlur={onBlur} />);
const checkbox = container.querySelector('input[type="checkbox"]') as Element;
render(<Switch onBlur={onBlur} />);
const checkbox = screen.getByRole('switch');
fireEvent.blur(checkbox);
@ -49,8 +47,8 @@ describe('Switch', () => {
});
it('should change the checked state when clicked', () => {
const { container } = render(<Switch onChange={jest.fn} />);
const checkbox = container.querySelector('input[type="checkbox"]') as Element;
render(<Switch onChange={jest.fn} />);
const checkbox = screen.getByRole('switch');
fireEvent.click(checkbox);

View file

@ -1,19 +1,22 @@
import React from 'react';
'use client';
interface IProps {
label?: string;
className?: string;
checked?: boolean;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
name?: string;
onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
}
import * as React from 'react';
import * as SwitchPrimitives from '@radix-ui/react-switch';
import clsx from 'clsx';
import classes from './Switch.module.scss';
export const Switch = React.forwardRef<HTMLInputElement, IProps>(({ onChange, onBlur, name, label, checked, className }, ref) => (
<div className={className}>
<label htmlFor={name} aria-labelledby={name} className="form-check form-switch">
<input id={name} name={name} ref={ref} onChange={onChange} onBlur={onBlur} className="form-check-input" type="checkbox" checked={checked} />
<span className="form-check-label">{label}</span>
</label>
</div>
type RootProps = typeof SwitchPrimitives.Root;
const Switch = React.forwardRef<React.ElementRef<RootProps>, React.ComponentPropsWithoutRef<RootProps> & { label?: string }>(({ className, ...props }, ref) => (
<label htmlFor={props.name} aria-labelledby={props.name} className={clsx('form-check form-switch form-check-sigle', className)}>
<SwitchPrimitives.Root name={props.name} className={clsx('form-check-input', classes.root)} {...props} ref={ref}>
<SwitchPrimitives.Thumb />
</SwitchPrimitives.Root>
<span id={props.name} className="form-check-label text-muted">
{props.label}
</span>
</label>
));
Switch.displayName = SwitchPrimitives.Root.displayName;
export { Switch };

View file

@ -1,5 +1,5 @@
import React, { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { Controller, useForm } from 'react-hook-form';
import { Button } from '../../../../components/ui/Button';
import { Switch } from '../../../../components/ui/Switch';
@ -32,6 +32,7 @@ export const InstallForm: React.FC<IProps> = ({ formFields, onSubmit, initalValu
setValue,
watch,
setError,
control,
} = useForm<FormValues>({});
const watchExposed = watch('exposed', false);
@ -57,7 +58,12 @@ export const InstallForm: React.FC<IProps> = ({ formFields, onSubmit, initalValu
const renderExposeForm = () => (
<>
<Switch className="mb-3" {...register('exposed')} label="Expose app" />
<Controller
control={control}
name="exposed"
defaultValue={false}
render={({ field: { onChange, value, ref, ...props } }) => <Switch className="mb-3" ref={ref} checked={value} onCheckedChange={onChange} {...props} label="Expose app" />}
/>
{watchExposed && (
<div className="mb-3">
<Input {...register('domain')} label="Domain name" error={errors.domain?.message} disabled={loading} placeholder="Domain name" />