Implements the controlled/uncontrolled state primitive with full TDD coverage (5 tests passing). Delegates to an internal signal when uncontrolled, defers to the external value accessor when controlled.
68 lines
2.0 KiB
TypeScript
68 lines
2.0 KiB
TypeScript
import { createRoot, createSignal } from "solid-js";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { createControllableSignal } from "../../src/primitives/create-controllable-signal";
|
|
|
|
describe("createControllableSignal", () => {
|
|
it("uses defaultValue when value accessor returns undefined (uncontrolled)", () => {
|
|
createRoot((dispose) => {
|
|
const [get] = createControllableSignal({
|
|
value: () => undefined,
|
|
defaultValue: () => false,
|
|
});
|
|
expect(get()).toBe(false);
|
|
dispose();
|
|
});
|
|
});
|
|
|
|
it("uses value accessor when provided (controlled)", () => {
|
|
createRoot((dispose) => {
|
|
const [get] = createControllableSignal({
|
|
value: () => true,
|
|
defaultValue: () => false,
|
|
});
|
|
expect(get()).toBe(true);
|
|
dispose();
|
|
});
|
|
});
|
|
|
|
it("calls onChange when setter is called in uncontrolled mode", () => {
|
|
createRoot((dispose) => {
|
|
const onChange = vi.fn();
|
|
const [, set] = createControllableSignal({
|
|
value: () => undefined,
|
|
defaultValue: () => false,
|
|
onChange,
|
|
});
|
|
set(true);
|
|
expect(onChange).toHaveBeenCalledWith(true);
|
|
dispose();
|
|
});
|
|
});
|
|
|
|
it("updates internal signal when setter called in uncontrolled mode", () => {
|
|
createRoot((dispose) => {
|
|
const [get, set] = createControllableSignal({
|
|
value: () => undefined,
|
|
defaultValue: () => "initial",
|
|
});
|
|
set("updated");
|
|
expect(get()).toBe("updated");
|
|
dispose();
|
|
});
|
|
});
|
|
|
|
it("does not update internal signal in controlled mode — external value is source of truth", () => {
|
|
createRoot((dispose) => {
|
|
const [externalValue] = createSignal<string>("controlled");
|
|
const [get, set] = createControllableSignal({
|
|
value: externalValue,
|
|
defaultValue: () => "default",
|
|
});
|
|
set("ignored");
|
|
// Still reflects external value since we're controlled
|
|
expect(get()).toBe("controlled");
|
|
dispose();
|
|
});
|
|
});
|
|
});
|