From 2877761e0b9016812146ecbaa6585bb6b5567aee Mon Sep 17 00:00:00 2001 From: Hunter Johnston Date: Fri, 31 May 2024 19:56:05 -0400 Subject: [PATCH] toolbar tests --- .../src/lib/bits/toolbar/toolbar.svelte.ts | 111 ++++++++++-------- .../bits-ui/src/lib/bits/toolbar/types.ts | 7 +- .../bits-ui/src/tests/toolbar/Toolbar.spec.ts | 30 ++--- .../src/tests/toolbar/ToolbarTest.svelte | 36 +++--- 4 files changed, 95 insertions(+), 89 deletions(-) diff --git a/packages/bits-ui/src/lib/bits/toolbar/toolbar.svelte.ts b/packages/bits-ui/src/lib/bits/toolbar/toolbar.svelte.ts index a6240f220..fd14bdc51 100644 --- a/packages/bits-ui/src/lib/bits/toolbar/toolbar.svelte.ts +++ b/packages/bits-ui/src/lib/bits/toolbar/toolbar.svelte.ts @@ -28,11 +28,11 @@ type ToolbarRootStateProps = ReadableBoxedValues<{ }>; class ToolbarRootState { - #id = undefined as unknown as ToolbarRootStateProps["id"]; - orientation = undefined as unknown as ToolbarRootStateProps["orientation"]; - #loop = undefined as unknown as ToolbarRootStateProps["loop"]; + #id: ToolbarRootStateProps["id"]; + orientation: ToolbarRootStateProps["orientation"]; + #loop: ToolbarRootStateProps["loop"]; #node = box(null); - rovingFocusGroup = undefined as unknown as UseRovingFocusReturn; + rovingFocusGroup: UseRovingFocusReturn; constructor(props: ToolbarRootStateProps) { this.#id = props.id; @@ -64,12 +64,15 @@ class ToolbarRootState { return new ToolbarButtonState(props, this); } - props = $derived({ - id: this.#id.value, - role: "toolbar", - "data-orientation": this.orientation.value, - [ROOT_ATTR]: "", - } as const); + props = $derived.by( + () => + ({ + id: this.#id.value, + role: "toolbar", + "data-orientation": this.orientation.value, + [ROOT_ATTR]: "", + }) as const + ); } type ToolbarGroupBaseStateProps = ReadableBoxedValues<{ @@ -78,10 +81,10 @@ type ToolbarGroupBaseStateProps = ReadableBoxedValues<{ }>; class ToolbarGroupBaseState { - id = undefined as unknown as ToolbarGroupBaseStateProps["id"]; + id: ToolbarGroupBaseStateProps["id"]; node = box(null); - disabled = undefined as unknown as ToolbarGroupBaseStateProps["disabled"]; - root = undefined as unknown as ToolbarRootState; + disabled: ToolbarGroupBaseStateProps["disabled"]; + root: ToolbarRootState; constructor(props: ToolbarGroupBaseStateProps, root: ToolbarRootState) { this.id = props.id; @@ -90,13 +93,16 @@ class ToolbarGroupBaseState { this.root = root; } - props = $derived({ - id: this.id.value, - [GROUP_ATTR]: "", - role: "group", - "data-orientation": getDataOrientation(this.root.orientation.value), - "data-disabled": getDataDisabled(this.disabled.value), - } as const); + props = $derived.by( + () => + ({ + id: this.id.value, + [GROUP_ATTR]: "", + role: "group", + "data-orientation": getDataOrientation(this.root.orientation.value), + "data-disabled": getDataDisabled(this.disabled.value), + }) as const + ); } // @@ -109,9 +115,9 @@ type ToolbarGroupSingleStateProps = ToolbarGroupBaseStateProps & }>; class ToolbarGroupSingleState extends ToolbarGroupBaseState { - #value = undefined as unknown as ToolbarGroupSingleStateProps["value"]; + #value: ToolbarGroupSingleStateProps["value"]; isMulti = false; - anyPressed = $derived(this.#value.value !== ""); + anyPressed = $derived.by(() => this.#value.value !== ""); constructor(props: ToolbarGroupSingleStateProps, root: ToolbarRootState) { super(props, root); @@ -145,9 +151,9 @@ type ToolbarGroupMultipleStateProps = ToolbarGroupBaseStateProps & }>; class ToolbarGroupMultipleState extends ToolbarGroupBaseState { - #value = undefined as unknown as ToolbarGroupMultipleStateProps["value"]; + #value: ToolbarGroupMultipleStateProps["value"]; isMulti = true; - anyPressed = $derived(this.#value.value.length > 0); + anyPressed = $derived.by(() => this.#value.value.length > 0); constructor(props: ToolbarGroupMultipleStateProps, root: ToolbarRootState) { super(props, root); @@ -184,13 +190,13 @@ type ToolbarGroupItemStateProps = ReadableBoxedValues<{ }>; class ToolbarGroupItemState { - #id = undefined as unknown as ToolbarGroupItemStateProps["id"]; - #group = undefined as unknown as ToolbarGroupState; - #root = undefined as unknown as ToolbarRootState; - #value = undefined as unknown as ToolbarGroupItemStateProps["value"]; + #id: ToolbarGroupItemStateProps["id"]; + #group: ToolbarGroupState; + #root: ToolbarRootState; + #value: ToolbarGroupItemStateProps["value"]; #node = box(null); - #disabled = undefined as unknown as ToolbarGroupItemStateProps["disabled"]; - #isDisabled = $derived(this.#disabled.value || this.#group.disabled.value); + #disabled: ToolbarGroupItemStateProps["disabled"]; + #isDisabled = $derived.by(() => this.#disabled.value || this.#group.disabled.value); constructor( props: ToolbarGroupItemStateProps, @@ -225,35 +231,38 @@ class ToolbarGroupItemState { this.#root.rovingFocusGroup.handleKeydown(this.#node.value, e); }; - #isPressed = $derived(this.#group.includesItem(this.#value.value)); + #isPressed = $derived.by(() => this.#group.includesItem(this.#value.value)); #ariaChecked = $derived.by(() => { return this.#group.isMulti ? undefined : getAriaChecked(this.#isPressed); }); #ariaPressed = $derived.by(() => { - return this.#group.isMulti ? undefined : getAriaPressed(this.#isPressed); + return this.#group.isMulti ? getAriaPressed(this.#isPressed) : undefined; }); - #tabIndex = $derived(this.#root.rovingFocusGroup.getTabIndex(this.#node.value).value); - - props = $derived({ - id: this.#id.value, - role: this.#group.isMulti ? undefined : "radio", - tabindex: this.#tabIndex, - "data-orientation": getDataOrientation(this.#root.orientation.value), - "data-disabled": getDataDisabled(this.#isDisabled), - "data-state": getToggleItemDataState(this.#isPressed), - "data-value": this.#value.value, - "aria-pressed": this.#ariaPressed, - "aria-checked": this.#ariaChecked, - [ITEM_ATTR]: "", - [GROUP_ITEM_ATTR]: "", - disabled: getDisabledAttr(this.#isDisabled), - // - onclick: this.#onclick, - onkeydown: this.#onkeydown, - }); + #tabIndex = $derived.by(() => this.#root.rovingFocusGroup.getTabIndex(this.#node.value).value); + + props = $derived.by( + () => + ({ + id: this.#id.value, + role: this.#group.isMulti ? undefined : "radio", + tabindex: this.#tabIndex, + "data-orientation": getDataOrientation(this.#root.orientation.value), + "data-disabled": getDataDisabled(this.#isDisabled), + "data-state": getToggleItemDataState(this.#isPressed), + "data-value": this.#value.value, + "aria-pressed": this.#ariaPressed, + "aria-checked": this.#ariaChecked, + [ITEM_ATTR]: "", + [GROUP_ITEM_ATTR]: "", + disabled: getDisabledAttr(this.#isDisabled), + // + onclick: this.#onclick, + onkeydown: this.#onkeydown, + }) as const + ); } type ToolbarLinkStateProps = ReadableBoxedValues<{ diff --git a/packages/bits-ui/src/lib/bits/toolbar/types.ts b/packages/bits-ui/src/lib/bits/toolbar/types.ts index 8f360d169..ef85839f5 100644 --- a/packages/bits-ui/src/lib/bits/toolbar/types.ts +++ b/packages/bits-ui/src/lib/bits/toolbar/types.ts @@ -9,6 +9,7 @@ import type { PrimitiveButtonAttributes, PrimitiveDivAttributes, WithAsChild, + Without, } from "$lib/internal/types.js"; import type { EventCallback } from "$lib/internal/events.js"; @@ -25,7 +26,7 @@ export type ToolbarGroupPropsWithoutHTML = Omit< >; export type ToolbarGroupProps = ToolbarGroupPropsWithoutHTML & - Omit; + Without; export type ToolbarGroupItemPropsWithoutHTML = ToggleGroupItemPropsWithoutHTML; @@ -37,11 +38,11 @@ export type ToolbarButtonPropsWithoutHTML = WithAsChild<{ }>; export type ToolbarButtonProps = ToolbarButtonPropsWithoutHTML & - Omit; + Without; export type ToolbarLinkPropsWithoutHTML = WithAsChild<{ onkeydown?: EventCallback; }>; export type ToolbarLinkProps = ToolbarLinkPropsWithoutHTML & - Omit; + Without; diff --git a/packages/bits-ui/src/tests/toolbar/Toolbar.spec.ts b/packages/bits-ui/src/tests/toolbar/Toolbar.spec.ts index 26403abfe..ec7f032c4 100644 --- a/packages/bits-ui/src/tests/toolbar/Toolbar.spec.ts +++ b/packages/bits-ui/src/tests/toolbar/Toolbar.spec.ts @@ -1,19 +1,14 @@ -import { render } from "@testing-library/svelte"; +import { render } from "@testing-library/svelte/svelte5"; import { userEvent } from "@testing-library/user-event"; import { axe } from "jest-axe"; import { describe, it } from "vitest"; import { getTestKbd } from "../utils.js"; import ToolbarTest from "./ToolbarTest.svelte"; -import type { Toolbar } from "$lib/index.js"; +import type { ToolbarTestProps } from "./ToolbarTest.svelte"; const kbd = getTestKbd(); -function setup( - props: Toolbar.Props & { - multipleProps?: Toolbar.GroupProps<"multiple">; - singleProps?: Toolbar.GroupProps<"single">; - } = {} -) { +function setup(props: Partial = {}) { const user = userEvent.setup(); const returned = render(ToolbarTest, { ...props }); const root = returned.getByTestId("root"); @@ -114,7 +109,7 @@ describe("toolbar", () => { expect(groupMultipleItemBold).toHaveFocus(); }); - it("respects the loop prop", async () => { + it("respects the loop: false prop", async () => { const { user, groupMultipleItemBold, button } = setup({ loop: false, }); @@ -130,7 +125,7 @@ describe("toolbar", () => { it("toolbar toogle group, type `'single'`, toggles when clicked", async () => { const { user, groupSingleItemLeft, groupSingleItemCenter, alignBinding } = setup(); - expect(alignBinding).toHaveTextContent("undefined"); + expect(alignBinding).toHaveTextContent(""); await user.click(groupSingleItemLeft); expect(alignBinding).toHaveTextContent("left"); await user.click(groupSingleItemCenter); @@ -141,7 +136,7 @@ describe("toolbar", () => { "toolbar toogle group, type `'single'`, toggles when the %s key is pressed", async (key) => { const { user, groupSingleItemLeft, groupSingleItemCenter, alignBinding } = setup(); - expect(alignBinding).toHaveTextContent("undefined"); + expect(alignBinding).toHaveTextContent(""); groupSingleItemLeft.focus(); await user.keyboard(key); expect(alignBinding).toHaveTextContent("left"); @@ -180,7 +175,10 @@ describe("toolbar", () => { groupSingleItemLeft, groupSingleItemCenter, groupSingleItemRight, - } = setup({ multipleProps: { disabled: true }, singleProps: { disabled: true } }); + } = setup({ + multipleProps: { disabled: true }, + singleProps: { disabled: true }, + }); expect(groupMultipleItemBold).toBeDisabled(); expect(groupMultipleItemItalic).toBeDisabled(); expect(groupMultipleItemStrikethrough).toBeDisabled(); @@ -216,7 +214,7 @@ describe("toolbar", () => { const { user, groupMultipleItemItalic, groupSingleItemCenter, styleBinding, alignBinding } = setup(); expect(styleBinding).toHaveTextContent("bold"); - expect(alignBinding).toHaveTextContent("undefined"); + expect(alignBinding).toHaveTextContent(""); expect(groupMultipleItemItalic).toHaveAttribute("data-state", "off"); expect(groupMultipleItemItalic).toHaveAttribute("aria-pressed", "false"); expect(groupSingleItemCenter).toHaveAttribute("data-state", "off"); @@ -236,7 +234,7 @@ describe("toolbar", () => { it.each(["link", "button"])("toolbar %s forwards click event", async (kind) => { const { user, clickedBinding, [kind as keyof ReturnType]: el } = setup(); - expect(clickedBinding).toHaveTextContent("undefined"); + expect(clickedBinding).toHaveTextContent(""); await user.click(el as Element); expect(clickedBinding).toHaveTextContent(kind); }); @@ -247,11 +245,9 @@ describe("toolbar", () => { const { user, button, clickedBinding } = setup(); button.focus(); - expect(clickedBinding).toHaveTextContent("undefined"); + expect(clickedBinding).toHaveTextContent(""); await user.keyboard(key); expect(clickedBinding).toHaveTextContent("button"); } ); - - it.todo("`asChild` behavior"); }); diff --git a/packages/bits-ui/src/tests/toolbar/ToolbarTest.svelte b/packages/bits-ui/src/tests/toolbar/ToolbarTest.svelte index 789894e9c..f8e9aba25 100644 --- a/packages/bits-ui/src/tests/toolbar/ToolbarTest.svelte +++ b/packages/bits-ui/src/tests/toolbar/ToolbarTest.svelte @@ -1,34 +1,34 @@ - - export let multipleProps: $$Props["multipleProps"] = undefined; - export let singleProps: $$Props["singleProps"] = undefined; +
- - - + - + - (clicked = "link")} + (clicked = "link")} >Edited 2 hours ago - (clicked = "button")} + (clicked = "button")} >Save