refactor: switch component using radix-ui primitives
This commit is contained in:
parent
f398958a8f
commit
98c931ed4c
4 changed files with 46 additions and 29 deletions
10
src/client/components/ui/Switch/Switch.module.scss
Normal file
10
src/client/components/ui/Switch/Switch.module.scss
Normal 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");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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" />
|
||||
|
|
Loading…
Add table
Reference in a new issue