Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/Checkbox/Checkbox.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
<script>
/**
* @generics {T = any} T
* @restProps {div}
* @event {boolean} check
*/

/**
* Specify the value of the checkbox.
* @type {any}
* @type {T}
*/
export let value = "";
export let value = /** @type {T} */ ("");

/** Specify whether the checkbox is checked */
export let checked = false;

/**
* Specify the bound group.
* @type {ReadonlyArray<any>}
* @type {ReadonlyArray<T> | undefined}
*/
export let group = undefined;

Expand Down
3 changes: 2 additions & 1 deletion src/DataTable/ToolbarBatchActions.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script>
/**
* @generics {Id = any} Id
* @event {null} cancel
*/

Expand All @@ -19,7 +20,7 @@
/**
* Specify the selected IDs for standalone usage.
* This is unnecessary if using this component with `DataTable`.
* @type {ReadonlyArray<any>}
* @type {ReadonlyArray<Id>}
*/
export let selectedIds = [];

Expand Down
9 changes: 5 additions & 4 deletions src/LocalStorage/LocalStorage.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script>
/**
* @generics {T = any} T
* @event {null} save
* @event update - Fires when the stored value changes, either from a bound value update or when localStorage is modified from another tab/window.
* @property {any} prevValue
* @property {any} value
* @property {T} prevValue
* @property {T} value
*/

/**
Expand All @@ -13,9 +14,9 @@

/**
* Provide a value to persist.
* @type {any}
* @type {T}
*/
export let value = "";
export let value = /** @type {T} */ ("");

/**
* Remove the persisted key value from the browser's local storage
Expand Down
5 changes: 3 additions & 2 deletions src/Search/Search.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<script>
/**
* @generics {T = any} T
* @event {null} expand
* @event {null} collapse
* @restProps {input}
*/

/**
* Specify the value of the search input.
* @type {any}
* @type {T}
*/
export let value = "";
export let value = /** @type {T} */ ("");

/**
* Specify the size of the search input.
Expand Down
9 changes: 5 additions & 4 deletions src/SessionStorage/SessionStorage.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script>
/**
* @generics {T = any} T
* @event {null} save
* @event update - Fires when the stored value changes, either from a bound value update or when sessionStorage is modified from another tab/window.
* @property {any} prevValue
* @property {any} value
* @property {T} prevValue
* @property {T} value
*/

/**
Expand All @@ -13,9 +14,9 @@

/**
* Provide a value to persist.
* @type {any}
* @type {T}
*/
export let value = "";
export let value = /** @type {T} */ ("");

/**
* Remove the persisted key value from the browser's session storage
Expand Down
5 changes: 3 additions & 2 deletions src/Theme/Theme.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

/**
* @generics {Tokens = Record<string, any>} Tokens
* @typedef {"white" | "g10" | "g80" | "g90" | "g100"} CarbonTheme
* @event update
* @type {object}
Expand All @@ -32,9 +33,9 @@
/**
* Customize a theme with your own tokens.
* @see https://carbondesignsystem.com/guidelines/themes/overview#customizing-a-theme
* @type {{ [token: string]: any; }}
* @type {Tokens}
*/
export let tokens = {};
export let tokens = /** @type {Tokens} */ ({});

/** Set to `true` to persist the theme using window.localStorage */
export let persist = false;
Expand Down
75 changes: 75 additions & 0 deletions tests/Checkbox/Checkbox.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { render, screen } from "@testing-library/svelte";
import type CheckboxComponent from "carbon-components-svelte/Checkbox/Checkbox.svelte";
import type { ComponentProps } from "svelte";
import { tick } from "svelte";
import { user } from "../setup-tests";
import CheckboxGroup from "./Checkbox.group.test.svelte";
Expand Down Expand Up @@ -503,4 +505,77 @@ describe("Checkbox", () => {
const helperElement = screen.queryByText("Helper text message");
expect(helperElement).not.toBeInTheDocument();
});

describe("Generics", () => {
it("should support custom types with generics", () => {
type CustomValue = "option1" | "option2" | "option3";

type ComponentType = CheckboxComponent<CustomValue>;
type Props = ComponentProps<ComponentType>;

expectTypeOf<Props["value"]>().toEqualTypeOf<CustomValue | undefined>();
expectTypeOf<Props["group"]>().toEqualTypeOf<
ReadonlyArray<CustomValue> | undefined
>();
});

it("should default to any type when generic is not specified", () => {
type ComponentType = CheckboxComponent;
type Props = ComponentProps<ComponentType>;

// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
expectTypeOf<Props["value"]>().toEqualTypeOf<any>();
expectTypeOf<Props["group"]>().toEqualTypeOf<
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
ReadonlyArray<any> | undefined
>();
});

it("should provide type-safe access when using group binding", () => {
type Status = "pending" | "approved" | "rejected";

const handleCheck = (checked: boolean) => {
expectTypeOf(checked).toEqualTypeOf<boolean>();
};

type ComponentType = CheckboxComponent<Status>;
type Props = ComponentProps<ComponentType>;

expectTypeOf<Props["value"]>().toEqualTypeOf<Status | undefined>();
expectTypeOf<Props["group"]>().toEqualTypeOf<
ReadonlyArray<Status> | undefined
>();
expectTypeOf(handleCheck).parameter(0).toEqualTypeOf<boolean>();
});

it("should work with object types", () => {
type Option = { id: string; label: string };

type ComponentType = CheckboxComponent<Option>;
type Props = ComponentProps<ComponentType>;

expectTypeOf<Props["value"]>().toEqualTypeOf<Option | undefined>();
expectTypeOf<Props["group"]>().toEqualTypeOf<
ReadonlyArray<Option> | undefined
>();
});

it("should work with 'as const' for type inference", () => {
const options = ["light", "dark", "auto"] as const;
type InferredType = (typeof options)[number];

expectTypeOf<typeof options>().toEqualTypeOf<
readonly ["light", "dark", "auto"]
>();
expectTypeOf<InferredType>().toEqualTypeOf<"light" | "dark" | "auto">();

type ComponentType = CheckboxComponent<InferredType>;
type Props = ComponentProps<ComponentType>;

expectTypeOf<Props["value"]>().toEqualTypeOf<InferredType | undefined>();
expectTypeOf<Props["group"]>().toEqualTypeOf<
ReadonlyArray<InferredType> | undefined
>();
});
});
});
69 changes: 69 additions & 0 deletions tests/DataTable/Toolbar.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { render, screen } from "@testing-library/svelte";
import type ToolbarBatchActionsComponent from "carbon-components-svelte/DataTable/ToolbarBatchActions.svelte";
import type { ComponentProps } from "svelte";
import Toolbar from "./Toolbar.test.svelte";

describe("DataTable Toolbar", () => {
Expand Down Expand Up @@ -191,5 +193,72 @@ describe("DataTable Toolbar", () => {
cancelButton.click();
expect(cancelFired).toBe(true);
});

describe("Generics", () => {
it("should support custom Id types with generics", () => {
type CustomId = "row-1" | "row-2" | "row-3";

type ComponentType = ToolbarBatchActionsComponent<CustomId>;
type Props = ComponentProps<ComponentType>;

expectTypeOf<Props["selectedIds"]>().toEqualTypeOf<
ReadonlyArray<CustomId> | undefined
>();
});

it("should work with number Id types", () => {
type ComponentType = ToolbarBatchActionsComponent<number>;
type Props = ComponentProps<ComponentType>;

expectTypeOf<Props["selectedIds"]>().toEqualTypeOf<
ReadonlyArray<number> | undefined
>();
});

it("should provide type-safe selectedIds for handlers", () => {
type RowId = "a" | "b" | "c";

const handleSelected = (ids: ReadonlyArray<RowId>) => {
expectTypeOf(ids).toEqualTypeOf<ReadonlyArray<RowId>>();
};

type ComponentType = ToolbarBatchActionsComponent<RowId>;
type Props = ComponentProps<ComponentType>;

expectTypeOf<Props["selectedIds"]>().toEqualTypeOf<
ReadonlyArray<RowId> | undefined
>();
expectTypeOf<Parameters<typeof handleSelected>[0]>().toEqualTypeOf<
ReadonlyArray<RowId>
>();
});

it("should default to any type when generic is not specified", () => {
type ComponentType = ToolbarBatchActionsComponent;
type Props = ComponentProps<ComponentType>;

expectTypeOf<Props["selectedIds"]>().toEqualTypeOf<
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
ReadonlyArray<any> | undefined
>();
});

it("should work with 'as const' for type inference", () => {
const ids = ["id-a", "id-b", "id-c"] as const;
type InferredId = (typeof ids)[number];

expectTypeOf<typeof ids>().toEqualTypeOf<
readonly ["id-a", "id-b", "id-c"]
>();
expectTypeOf<InferredId>().toEqualTypeOf<"id-a" | "id-b" | "id-c">();

type ComponentType = ToolbarBatchActionsComponent<InferredId>;
type Props = ComponentProps<ComponentType>;

expectTypeOf<Props["selectedIds"]>().toEqualTypeOf<
ReadonlyArray<InferredId> | undefined
>();
});
});
});
});
Loading
Loading