Toggle component
This commit is contained in:
parent
230133415b
commit
d522090872
2
packages/core/src/components/toggle/index.ts
Normal file
2
packages/core/src/components/toggle/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { Toggle } from "./toggle";
|
||||||
|
export type { ToggleProps } from "./toggle";
|
||||||
57
packages/core/src/components/toggle/toggle.tsx
Normal file
57
packages/core/src/components/toggle/toggle.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import type { JSX } from "solid-js";
|
||||||
|
import { splitProps } from "solid-js";
|
||||||
|
import { createControllableSignal } from "../../primitives/create-controllable-signal";
|
||||||
|
|
||||||
|
export interface ToggleProps extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||||
|
/** Controlled pressed state. */
|
||||||
|
pressed?: boolean;
|
||||||
|
/** Default pressed state (uncontrolled). @default false */
|
||||||
|
defaultPressed?: boolean;
|
||||||
|
/** Called when pressed state changes. */
|
||||||
|
onPressedChange?: (pressed: boolean) => void;
|
||||||
|
children?: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A two-state button that can be toggled on and off.
|
||||||
|
* Uses aria-pressed to communicate state to assistive technology.
|
||||||
|
*/
|
||||||
|
export function Toggle(props: ToggleProps): JSX.Element {
|
||||||
|
const [local, rest] = splitProps(props, [
|
||||||
|
"pressed",
|
||||||
|
"defaultPressed",
|
||||||
|
"onPressedChange",
|
||||||
|
"disabled",
|
||||||
|
"children",
|
||||||
|
"onClick",
|
||||||
|
]);
|
||||||
|
|
||||||
|
const options: Parameters<typeof createControllableSignal<boolean>>[0] = {
|
||||||
|
value: () => local.pressed,
|
||||||
|
defaultValue: () => local.defaultPressed ?? false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (local.onPressedChange) {
|
||||||
|
options.onChange = local.onPressedChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [isPressed, setPressed] = createControllableSignal<boolean>(options);
|
||||||
|
|
||||||
|
const handleClick: JSX.EventHandler<HTMLButtonElement, MouseEvent> = (e) => {
|
||||||
|
if (typeof local.onClick === "function") local.onClick(e);
|
||||||
|
if (!local.disabled) setPressed(!isPressed());
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-pressed={isPressed()}
|
||||||
|
data-state={isPressed() ? "on" : "off"}
|
||||||
|
disabled={local.disabled}
|
||||||
|
onClick={handleClick}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{local.children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
40
packages/core/tests/components/toggle/toggle.test.tsx
Normal file
40
packages/core/tests/components/toggle/toggle.test.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { fireEvent, render, screen } from "@solidjs/testing-library";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { Toggle } from "../../../src/components/toggle/index";
|
||||||
|
|
||||||
|
describe("Toggle", () => {
|
||||||
|
it("is off by default", () => {
|
||||||
|
render(() => <Toggle>Bold</Toggle>);
|
||||||
|
expect(screen.getByRole("button").getAttribute("aria-pressed")).toBe("false");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toggles on click", () => {
|
||||||
|
render(() => <Toggle>Bold</Toggle>);
|
||||||
|
fireEvent.click(screen.getByRole("button"));
|
||||||
|
expect(screen.getByRole("button").getAttribute("aria-pressed")).toBe("true");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("respects defaultPressed=true", () => {
|
||||||
|
render(() => <Toggle defaultPressed>Bold</Toggle>);
|
||||||
|
expect(screen.getByRole("button").getAttribute("aria-pressed")).toBe("true");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("controlled: stays at given value", () => {
|
||||||
|
render(() => <Toggle pressed={false} onPressedChange={() => {}}>Bold</Toggle>);
|
||||||
|
fireEvent.click(screen.getByRole("button"));
|
||||||
|
expect(screen.getByRole("button").getAttribute("aria-pressed")).toBe("false");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("data-state reflects pressed state", () => {
|
||||||
|
render(() => <Toggle>Bold</Toggle>);
|
||||||
|
expect(screen.getByRole("button").getAttribute("data-state")).toBe("off");
|
||||||
|
fireEvent.click(screen.getByRole("button"));
|
||||||
|
expect(screen.getByRole("button").getAttribute("data-state")).toBe("on");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not toggle when disabled", () => {
|
||||||
|
render(() => <Toggle disabled>Bold</Toggle>);
|
||||||
|
fireEvent.click(screen.getByRole("button"));
|
||||||
|
expect(screen.getByRole("button").getAttribute("aria-pressed")).toBe("false");
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user