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