Skip to content

Commit

Permalink
Added basic Field and Fieldset implementation with disabled context (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
sureshjoshi authored Dec 5, 2024
1 parent 888a039 commit faaca6c
Show file tree
Hide file tree
Showing 9 changed files with 371 additions and 20 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/web.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ on:
- ".github/workflows/web.yaml"
pull_request:
branches: ["main"]
paths:
- ".github/workflows/web.yaml"
workflow_dispatch:

env:
Expand Down Expand Up @@ -54,7 +52,7 @@ jobs:
# ${{ runner.os }}-pnpm-store-

- name: Install
run: pnpm install
run: pnpm install --frozen-lockfile

# - name: Lint
# run: pnpm lint
Expand All @@ -64,3 +62,6 @@ jobs:

- name: Test
run: pnpm test:unit

- name: Build
run: pnpm build
6 changes: 3 additions & 3 deletions src/lib/button/Button.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@
{@render children?.({ active, autofocus, disabled, focus, hover })}
</svelte:element>
{:else}
{@const Component = as}
<Component {...theirProps} {...ourProps} {...dataAttributes}>
{@const AsComponent = as}
<AsComponent {...theirProps} {...ourProps} {...dataAttributes}>
{@render children?.(snippetProps)}
</Component>
</AsComponent>
{/if}
6 changes: 3 additions & 3 deletions src/lib/checkbox/Checkbox.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@
{@render children?.(snippetProps)}
</svelte:element>
{:else}
{@const Component = as}
<Component {...theirProps} {...ourProps} {...dataAttributes}>
{@const AsComponent = as}
<AsComponent {...theirProps} {...ourProps} {...dataAttributes}>
{@render children?.(snippetProps)}
</Component>
</AsComponent>
{/if}
57 changes: 57 additions & 0 deletions src/lib/field/Field.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
import type { Component, Snippet } from "svelte";
import { useId } from "../../hooks/use-id";
import { getAllContexts, getContext } from "svelte";
const contexts = getAllContexts();
console.log(contexts);
type Props = {
/** The element or component the checkbox should render as. */
as?: string | Component;
/** Whether or not the field is disabled. */
disabled?: boolean;
children?: Snippet<[SnippetProps]>;
};
type SnippetProps = {
/** Whether or not the field is disabled. */
disabled?: boolean;
};
let providedDisabled = getContext<boolean>("headlessui-disabled-context");
let {
id = `headlessui-control-${useId()}`,
as = "div",
disabled = providedDisabled || false,
children,
...theirProps
}: Props & Record<string, any> = $props();
let ourProps = {
disabled,
"aria-disabled": disabled,
};
let snippetProps: SnippetProps = {
disabled,
};
// TODO: Utility function to create this
let dataAttributes: DataAttributes<SnippetProps> = {
"data-disabled": disabled,
};
</script>

{#if typeof as === "string"}
<svelte:element this={as} {...theirProps} {...ourProps} {...dataAttributes}>
{@render children?.(snippetProps)}
</svelte:element>
{:else}
{@const AsComponent = as}
<AsComponent {...theirProps} {...ourProps} {...dataAttributes}>
{@render children?.(snippetProps)}
</AsComponent>
{/if}
86 changes: 86 additions & 0 deletions src/lib/field/field.dom.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { render, screen } from "@testing-library/svelte";
import type { SvelteComponent } from "svelte";

function sveltify(input: string): Promise<typeof SvelteComponent> {
throw new Error("TODO");
}

describe("Rendering", () => {
it("should render a `Field` component", async () => {
const component = await sveltify(`
<script>
import Field from "$lib/field/Field.svelte";
</script>
<Field>
<input />
</Field>
`);
const { container } = render(component);

expect(container.firstElementChild).not.toHaveAttribute("aria-disabled", "true");
});

it.skip("should render a `Field` component with a render prop", async () => {
const component = await sveltify(`
<script>
import Field from "$lib/field/Field.svelte";
</script>
<Field>
<input />
</Field>
`);
const { container } = render(component);

// let { container } = render(
// <Field>
// {(slot) => {
// return (
// <div data-slot={JSON.stringify(slot)}>
// <input />
// </div>
// )
// }}
// </Field>
// )

expect(container.querySelector("[data-slot]")?.getAttribute("data-slot")).toEqual(
JSON.stringify({ disabled: false }),
);
expect(container.firstChild).not.toHaveAttribute("aria-disabled", "true");
});

it("should add `aria-disabled` when a `Field` is disabled", async () => {
const component = await sveltify(`
<script>
import Field from "$lib/field/Field.svelte";
</script>
<Field disabled>
<div ></div>
</Field>
`);
const { container } = render(component);

expect(container.firstElementChild).toHaveAttribute("aria-disabled", "true");
});

it("should inherit the `disabled` state from a parent `Fieldset`", async () => {
const component = await sveltify(`
<script>
import Field from "$lib/field/Field.svelte";
import Fieldset from "$lib/fieldset/Fieldset.svelte";
</script>
<Fieldset disabled>
<Field>
<input />
</Field>
</Fieldset>
`);
const { container } = render(component);

let fieldset = container.firstElementChild;
let field = fieldset?.firstElementChild;

expect(fieldset).toHaveAttribute("disabled");
expect(field).toHaveAttribute("aria-disabled", "true");
});
});
57 changes: 57 additions & 0 deletions src/lib/fieldset/Fieldset.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
import { type Component, getContext, setContext, type Snippet } from "svelte";
type Props = {
/** The element or component the fieldset should render as. */
as?: string | Component;
/** Use this to disable all form controls in the fieldset. */
disabled?: boolean;
children?: Snippet<[SnippetProps]>;
};
type SnippetProps = {
/** Whether or not the fieldset is disabled. */
disabled?: boolean;
};
let providedDisabled = getContext<boolean>("headlessui-disabled-context");
let {
as = "fieldset",
disabled = providedDisabled || false,
children,
...theirProps
}: Props & Record<string, any> = $props();
let ourProps = as === "fieldset"
? {
disabled,
// 'aria-labelledby': labelledBy
}
: {
role: "group",
"aria-disabled": disabled,
// 'aria-labelledby': labelledBy
};
let snippetProps: SnippetProps = {
disabled,
};
// TODO: Utility function to create this
let dataAttributes: DataAttributes<SnippetProps> = {
"data-disabled": disabled,
};
setContext("headlessui-disabled-context", disabled);
</script>

{#if typeof as === "string"}
<svelte:element this={as} {...theirProps} {...ourProps} {...dataAttributes}>
{@render children?.(snippetProps)}
</svelte:element>
{:else}
{@const AsComponent = as}
<AsComponent {...theirProps} {...ourProps} {...dataAttributes}>
{@render children?.(snippetProps)}
</AsComponent>
{/if}
Loading

0 comments on commit faaca6c

Please sign in to comment.