mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-07-30 13:48:07 +02:00
test(web): Switch (and add label back in)
This commit is contained in:
parent
f70fb12c3d
commit
5eaf8a5448
@ -1,7 +1,7 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
import { useCallback, useState } from 'preact/hooks';
|
import { useCallback, useState } from 'preact/hooks';
|
||||||
|
|
||||||
export default function Switch({ checked, id, onChange }) {
|
export default function Switch({ checked, id, onChange, label, labelPosition = 'before' }) {
|
||||||
const [isFocused, setFocused] = useState(false);
|
const [isFocused, setFocused] = useState(false);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
@ -24,15 +24,21 @@ export default function Switch({ checked, id, onChange }) {
|
|||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
className={`flex items-center justify-center ${onChange ? 'cursor-pointer' : 'cursor-not-allowed'}`}
|
className={`flex items-center space-x-4 w-full ${onChange ? 'cursor-pointer' : 'cursor-not-allowed'}`}
|
||||||
>
|
>
|
||||||
|
{label && labelPosition === 'before' ? (
|
||||||
|
<div data-testid={`${id}-label`} className="inline-flex flex-grow">
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<div
|
<div
|
||||||
onMouseOver={handleFocus}
|
onMouseOver={handleFocus}
|
||||||
onMouseOut={handleBlur}
|
onMouseOut={handleBlur}
|
||||||
className={`w-8 h-5 relative ${!onChange ? 'opacity-60' : ''}`}
|
className={`self-end w-8 h-5 relative ${!onChange ? 'opacity-60' : ''}`}
|
||||||
>
|
>
|
||||||
<div className="relative overflow-hidden">
|
<div className="relative overflow-hidden">
|
||||||
<input
|
<input
|
||||||
|
data-testid={`${id}-input`}
|
||||||
className="absolute left-48"
|
className="absolute left-48"
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
@ -55,6 +61,11 @@ export default function Switch({ checked, id, onChange }) {
|
|||||||
style={checked ? 'transform: translateX(100%);' : 'transform: translateX(0%);'}
|
style={checked ? 'transform: translateX(100%);' : 'transform: translateX(0%);'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{label && labelPosition !== 'before' ? (
|
||||||
|
<div data-testid={`${id}-label`} class="inline-flex flex-grow">
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
47
web/src/components/__tests__/Switch.test.jsx
Normal file
47
web/src/components/__tests__/Switch.test.jsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { h } from 'preact';
|
||||||
|
import Switch from '../Switch';
|
||||||
|
import { fireEvent, render, screen } from '@testing-library/preact';
|
||||||
|
|
||||||
|
describe('Switch', () => {
|
||||||
|
test('renders a hidden checkbox', async () => {
|
||||||
|
render(
|
||||||
|
<div>
|
||||||
|
<Switch id="unchecked-switch" />
|
||||||
|
<Switch id="checked-switch" checked={true} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const unchecked = screen.queryByTestId('unchecked-switch-input');
|
||||||
|
expect(unchecked).toHaveAttribute('type', 'checkbox');
|
||||||
|
expect(unchecked).not.toBeChecked();
|
||||||
|
|
||||||
|
const checked = screen.queryByTestId('checked-switch-input');
|
||||||
|
expect(checked).toHaveAttribute('type', 'checkbox');
|
||||||
|
expect(checked).toBeChecked();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('calls onChange callback when checked/unchecked', async () => {
|
||||||
|
const handleChange = jest.fn();
|
||||||
|
const { rerender } = render(<Switch id="check" onChange={handleChange} />);
|
||||||
|
fireEvent.change(screen.queryByTestId('check-input'), { checked: true });
|
||||||
|
expect(handleChange).toHaveBeenCalledWith('check', true);
|
||||||
|
|
||||||
|
rerender(<Switch id="check" onChange={handleChange} checked />);
|
||||||
|
fireEvent.change(screen.queryByTestId('check-input'), { checked: false });
|
||||||
|
expect(handleChange).toHaveBeenCalledWith('check', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a label before', async () => {
|
||||||
|
render(<Switch id="check" label="This is the label" />);
|
||||||
|
const items = screen.queryAllByTestId(/check-.+/);
|
||||||
|
expect(items[0]).toHaveTextContent('This is the label');
|
||||||
|
expect(items[1]).toHaveAttribute('data-testid', 'check-input');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renders a label after', async () => {
|
||||||
|
render(<Switch id="check" label="This is the label" labelPosition="after" />);
|
||||||
|
const items = screen.queryAllByTestId(/check-.+/);
|
||||||
|
expect(items[0]).toHaveAttribute('data-testid', 'check-input');
|
||||||
|
expect(items[1]).toHaveTextContent('This is the label');
|
||||||
|
});
|
||||||
|
});
|
@ -45,30 +45,36 @@ export default function Camera({ camera }) {
|
|||||||
|
|
||||||
const optionContent = showSettings ? (
|
const optionContent = showSettings ? (
|
||||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
||||||
<div className="flex space-x-3">
|
<Switch
|
||||||
<Switch checked={options['bbox']} id="bbox" onChange={handleSetOption} />
|
checked={options['bbox']}
|
||||||
<span className="inline-flex">Bounding box</span>
|
id="bbox"
|
||||||
</div>
|
onChange={handleSetOption}
|
||||||
<div className="flex space-x-3">
|
label="Bounding box"
|
||||||
<Switch checked={options['timestamp']} id="timestamp" onChange={handleSetOption} />
|
labelPosition="after"
|
||||||
<span className="inline-flex">Timestamp</span>
|
/>
|
||||||
</div>
|
<Switch
|
||||||
<div className="flex space-x-3">
|
checked={options['timestamp']}
|
||||||
<Switch checked={options['zones']} id="zones" onChange={handleSetOption} />
|
id="timestamp"
|
||||||
<span className="inline-flex">Zones</span>
|
onChange={handleSetOption}
|
||||||
</div>
|
label="Timestamp"
|
||||||
<div className="flex space-x-3">
|
labelPosition="after"
|
||||||
<Switch checked={options['mask']} id="mask" onChange={handleSetOption} />
|
/>
|
||||||
<span className="inline-flex">Masks</span>
|
<Switch checked={options['zones']} id="zones" onChange={handleSetOption} label="Zones" labelPosition="after" />
|
||||||
</div>
|
<Switch checked={options['mask']} id="mask" onChange={handleSetOption} label="Masks" labelPosition="after" />
|
||||||
<div className="flex space-x-3">
|
<Switch
|
||||||
<Switch checked={options['motion']} id="motion" onChange={handleSetOption} />
|
checked={options['motion']}
|
||||||
<span className="inline-flex">Motion boxes</span>
|
id="motion"
|
||||||
</div>
|
onChange={handleSetOption}
|
||||||
<div className="flex space-x-3">
|
label="Motion boxes"
|
||||||
<Switch checked={options['regions']} id="regions" onChange={handleSetOption} />
|
labelPosition="after"
|
||||||
<span className="inline-flex">Regions</span>
|
/>
|
||||||
</div>
|
<Switch
|
||||||
|
checked={options['regions']}
|
||||||
|
id="regions"
|
||||||
|
onChange={handleSetOption}
|
||||||
|
label="Regions"
|
||||||
|
labelPosition="after"
|
||||||
|
/>
|
||||||
<Link href={`/cameras/${camera}/editor`}>Mask & Zone creator</Link>
|
<Link href={`/cameras/${camera}/editor`}>Mask & Zone creator</Link>
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
@ -222,8 +222,8 @@ ${Object.keys(objectMaskPoints)
|
|||||||
height={height}
|
height={height}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex space-x-4">
|
<div className="max-w-xs">
|
||||||
<span>Snap to edges</span> <Switch checked={snap} onChange={handleChangeSnap} />
|
<Switch checked={snap} label="Snap to edges" labelPosition="after" onChange={handleChangeSnap} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import TextField from '../components/TextField';
|
|||||||
import { useCallback, useState } from 'preact/hooks';
|
import { useCallback, useState } from 'preact/hooks';
|
||||||
|
|
||||||
export default function StyleGuide() {
|
export default function StyleGuide() {
|
||||||
const [switches, setSwitches] = useState({ 0: false, 1: true });
|
const [switches, setSwitches] = useState({ 0: false, 1: true, 2: false, 3: false });
|
||||||
|
|
||||||
const handleSwitch = useCallback(
|
const handleSwitch = useCallback(
|
||||||
(id, checked) => {
|
(id, checked) => {
|
||||||
@ -53,23 +53,26 @@ export default function StyleGuide() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Heading size="md">Switch</Heading>
|
<Heading size="md">Switch</Heading>
|
||||||
<div className="flex">
|
<div className="flex-col space-y-4 max-w-4xl">
|
||||||
<div>
|
<Switch label="Disabled, off" labelPosition="after" />
|
||||||
<p>Disabled, off</p>
|
<Switch label="Disabled, on" labelPosition="after" checked />
|
||||||
<Switch />
|
<Switch
|
||||||
</div>
|
label="Enabled, (off initial)"
|
||||||
<div>
|
labelPosition="after"
|
||||||
<p>Disabled, on</p>
|
checked={switches[0]}
|
||||||
<Switch checked />
|
id={0}
|
||||||
</div>
|
onChange={handleSwitch}
|
||||||
<div>
|
/>
|
||||||
<p>Enabled, (off initial)</p>
|
<Switch
|
||||||
<Switch checked={switches[0]} id={0} onChange={handleSwitch} label="Default" />
|
label="Enabled, (on initial)"
|
||||||
</div>
|
labelPosition="after"
|
||||||
<div>
|
checked={switches[1]}
|
||||||
<p>Enabled, (on initial)</p>
|
id={1}
|
||||||
<Switch checked={switches[1]} id={1} onChange={handleSwitch} label="Default" />
|
onChange={handleSwitch}
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
|
<Switch checked={switches[2]} id={2} label="Label before" onChange={handleSwitch} />
|
||||||
|
<Switch checked={switches[3]} id={3} label="Label after" labelPosition="after" onChange={handleSwitch} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Heading size="md">Select</Heading>
|
<Heading size="md">Select</Heading>
|
||||||
|
Loading…
Reference in New Issue
Block a user