Skip to content

Commit 22f1d24

Browse files
committed
feat(ui-shell): icon is generic
1 parent a8bb771 commit 22f1d24

File tree

9 files changed

+205
-20
lines changed

9 files changed

+205
-20
lines changed

src/UIShell/HamburgerMenu.svelte

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
<script>
2+
/**
3+
* @generics {Icon = any} Icon
4+
*/
5+
26
/**
37
* Specify the ARIA label for the button.
48
* @type {string}
@@ -10,15 +14,15 @@
1014
1115
/**
1216
* Specify the icon to render for the closed state.
13-
* @type {any}
17+
* @type {Icon}
1418
*/
15-
export let iconMenu = Menu;
19+
export let iconMenu = /** @type {Icon} */ (Menu);
1620
1721
/**
1822
* Specify the icon to render for the opened state.
19-
* @type {any}
23+
* @type {Icon}
2024
*/
21-
export let iconClose = Close;
25+
export let iconClose = /** @type {Icon} */ (Close);
2226
2327
/** Obtain a reference to the HTML button element */
2428
export let ref = null;

src/UIShell/Header.svelte

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
<script>
2+
/**
3+
* @generics {Icon = any} Icon
4+
*/
5+
26
/** Set to `false` to hide the side nav by default */
37
export let expandedByDefault = true;
48
@@ -62,15 +66,15 @@
6266
6367
/**
6468
* Specify the icon to render for the closed state.
65-
* @type {any}
69+
* @type {Icon}
6670
*/
67-
export let iconMenu = Menu;
71+
export let iconMenu = /** @type {Icon} */ (Menu);
6872
6973
/**
7074
* Specify the icon to render for the opened state.
71-
* @type {any}
75+
* @type {Icon}
7276
*/
73-
export let iconClose = Close;
77+
export let iconClose = /** @type {Icon} */ (Close);
7478
7579
/**
7680
* Specify the ARIA label for the hamburger menu.

src/UIShell/HeaderAction.svelte

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script>
22
/**
3+
* @generics {Icon = any} Icon
34
* @event {null} open
45
* @event {null} close
56
*/
@@ -9,15 +10,15 @@
910
1011
/**
1112
* Specify the icon to render when the action panel is closed.
12-
* @type {any}
13+
* @type {Icon}
1314
*/
14-
export let icon = Switcher;
15+
export let icon = /** @type {Icon} */ (Switcher);
1516
1617
/**
1718
* Specify the icon to render when the action panel is open.
18-
* @type {any}
19+
* @type {Icon}
1920
*/
20-
export let closeIcon = Close;
21+
export let closeIcon = /** @type {Icon} */ (Close);
2122
2223
/**
2324
* Specify the text displayed next to the icon.

src/UIShell/HeaderActionLink.svelte

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
<script>
2+
/**
3+
* @generics {Icon = any} Icon
4+
*/
5+
26
/** Set to `true` to use the active state */
37
export let linkIsActive = false;
48
@@ -10,9 +14,9 @@
1014
1115
/**
1216
* Specify the icon to render.
13-
* @type {any}
17+
* @type {Icon}
1418
*/
15-
export let icon = undefined;
19+
export let icon = /** @type {Icon} */ (undefined);
1620
1721
/** Obtain a reference to the HTML anchor element */
1822
export let ref = null;

src/UIShell/HeaderGlobalAction.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script>
22
/**
3+
* @generics {Icon = any} Icon
34
* @extends {"../Button/Button.svelte"} ButtonProps
45
*/
56
@@ -8,9 +9,9 @@
89
910
/**
1011
* Specify the icon to render.
11-
* @type {any}
12+
* @type {Icon}
1213
*/
13-
export let icon = undefined;
14+
export let icon = /** @type {Icon} */ (undefined);
1415
1516
/** Obtain a reference to the HTML button element.
1617
* @type {HTMLButtonElement}

src/UIShell/SideNavLink.svelte

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
<script>
2+
/**
3+
* @generics {Icon = any} Icon
4+
*/
5+
26
/** Set to `true` to select the current link */
37
export let isSelected = false;
48
@@ -16,9 +20,9 @@
1620
1721
/**
1822
* Specify the icon to render.
19-
* @type {any}
23+
* @type {Icon}
2024
*/
21-
export let icon = undefined;
25+
export let icon = /** @type {Icon} */ (undefined);
2226
2327
/** Obtain a reference to the HTML anchor element */
2428
export let ref = null;

src/UIShell/SideNavMenu.svelte

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
<script>
2+
/**
3+
* @generics {Icon = any} Icon
4+
*/
5+
26
/** Set to `true` to toggle the expanded state */
37
export let expanded = false;
48
@@ -10,9 +14,9 @@
1014
1115
/**
1216
* Specify the icon to render.
13-
* @type {any}
17+
* @type {Icon}
1418
*/
15-
export let icon = undefined;
19+
export let icon = /** @type {Icon} */ (undefined);
1620
1721
/** Obtain a reference to the HTML button element */
1822
export let ref = null;

tests/UIShell/HeaderAction.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { render, screen } from "@testing-library/svelte";
2+
import type HeaderActionComponent from "carbon-components-svelte/UIShell/HeaderAction.svelte";
3+
import type { ComponentProps } from "svelte";
24
import HeaderActionSlot from "./HeaderAction.slot.test.svelte";
35

46
describe("HeaderAction", () => {
@@ -19,4 +21,28 @@ describe("HeaderAction", () => {
1921
const customIcon = screen.getByTestId("custom-icon");
2022
expect(customIcon).toBeInTheDocument();
2123
});
24+
25+
describe("Generics", () => {
26+
it("should support custom Icon types with generics", () => {
27+
type CustomIcon = new (...args: unknown[]) => unknown;
28+
29+
type ComponentType = HeaderActionComponent<CustomIcon>;
30+
type Props = ComponentProps<ComponentType>;
31+
32+
expectTypeOf<Props["icon"]>().toEqualTypeOf<CustomIcon | undefined>();
33+
expectTypeOf<Props["closeIcon"]>().toEqualTypeOf<
34+
CustomIcon | undefined
35+
>();
36+
});
37+
38+
it("should default to any type when generic is not specified", () => {
39+
type ComponentType = HeaderActionComponent;
40+
type Props = ComponentProps<ComponentType>;
41+
42+
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
43+
expectTypeOf<Props["icon"]>().toEqualTypeOf<any>();
44+
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
45+
expectTypeOf<Props["closeIcon"]>().toEqualTypeOf<any>();
46+
});
47+
});
2248
});

tests/UIShell/UIShell.test.ts

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
import { render, screen } from "@testing-library/svelte";
2+
import type HamburgerMenuComponent from "carbon-components-svelte/UIShell/HamburgerMenu.svelte";
3+
import type HeaderComponent from "carbon-components-svelte/UIShell/Header.svelte";
4+
import type HeaderActionLinkComponent from "carbon-components-svelte/UIShell/HeaderActionLink.svelte";
5+
import type HeaderGlobalActionComponent from "carbon-components-svelte/UIShell/HeaderGlobalAction.svelte";
6+
import type SideNavLinkComponent from "carbon-components-svelte/UIShell/SideNavLink.svelte";
7+
import type SideNavMenuComponent from "carbon-components-svelte/UIShell/SideNavMenu.svelte";
8+
import type { ComponentProps } from "svelte";
29
import { user } from "../setup-tests";
310
import HeaderSlot from "./Header.slot.test.svelte";
411
import UiShell from "./UIShell.test.svelte";
@@ -674,4 +681,134 @@ describe("UIShell", () => {
674681
});
675682
});
676683
});
684+
685+
describe("Generics", () => {
686+
describe("HamburgerMenu", () => {
687+
it("should support custom Icon types with generics", () => {
688+
type CustomIcon = new (...args: unknown[]) => unknown;
689+
690+
type ComponentType = HamburgerMenuComponent<CustomIcon>;
691+
type Props = ComponentProps<ComponentType>;
692+
693+
expectTypeOf<Props["iconMenu"]>().toEqualTypeOf<
694+
CustomIcon | undefined
695+
>();
696+
expectTypeOf<Props["iconClose"]>().toEqualTypeOf<
697+
CustomIcon | undefined
698+
>();
699+
});
700+
701+
it("should default to any type when generic is not specified", () => {
702+
type ComponentType = HamburgerMenuComponent;
703+
type Props = ComponentProps<ComponentType>;
704+
705+
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
706+
expectTypeOf<Props["iconMenu"]>().toEqualTypeOf<any>();
707+
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
708+
expectTypeOf<Props["iconClose"]>().toEqualTypeOf<any>();
709+
});
710+
});
711+
712+
describe("Header", () => {
713+
it("should support custom Icon types with generics", () => {
714+
type CustomIcon = new (...args: unknown[]) => unknown;
715+
716+
type ComponentType = HeaderComponent<CustomIcon>;
717+
type Props = ComponentProps<ComponentType>;
718+
719+
expectTypeOf<Props["iconMenu"]>().toEqualTypeOf<
720+
CustomIcon | undefined
721+
>();
722+
expectTypeOf<Props["iconClose"]>().toEqualTypeOf<
723+
CustomIcon | undefined
724+
>();
725+
});
726+
727+
it("should default to any type when generic is not specified", () => {
728+
type ComponentType = HeaderComponent;
729+
type Props = ComponentProps<ComponentType>;
730+
731+
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
732+
expectTypeOf<Props["iconMenu"]>().toEqualTypeOf<any>();
733+
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
734+
expectTypeOf<Props["iconClose"]>().toEqualTypeOf<any>();
735+
});
736+
});
737+
738+
describe("HeaderActionLink", () => {
739+
it("should support custom Icon types with generics", () => {
740+
type CustomIcon = new (...args: unknown[]) => unknown;
741+
742+
type ComponentType = HeaderActionLinkComponent<CustomIcon>;
743+
type Props = ComponentProps<ComponentType>;
744+
745+
expectTypeOf<Props["icon"]>().toEqualTypeOf<CustomIcon | undefined>();
746+
});
747+
748+
it("should default to any type when generic is not specified", () => {
749+
type ComponentType = HeaderActionLinkComponent;
750+
type Props = ComponentProps<ComponentType>;
751+
752+
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
753+
expectTypeOf<Props["icon"]>().toEqualTypeOf<any>();
754+
});
755+
});
756+
757+
describe("HeaderGlobalAction", () => {
758+
it("should support custom Icon types with generics", () => {
759+
type CustomIcon = new (...args: unknown[]) => unknown;
760+
761+
type ComponentType = HeaderGlobalActionComponent<CustomIcon>;
762+
type Props = ComponentProps<ComponentType>;
763+
764+
expectTypeOf<Props["icon"]>().toExtend<CustomIcon | undefined>();
765+
});
766+
767+
it("should default to any type when generic is not specified", () => {
768+
type ComponentType = HeaderGlobalActionComponent;
769+
type Props = ComponentProps<ComponentType>;
770+
771+
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
772+
expectTypeOf<Props["icon"]>().toEqualTypeOf<any>();
773+
});
774+
});
775+
776+
describe("SideNavLink", () => {
777+
it("should support custom Icon types with generics", () => {
778+
type CustomIcon = new (...args: unknown[]) => unknown;
779+
780+
type ComponentType = SideNavLinkComponent<CustomIcon>;
781+
type Props = ComponentProps<ComponentType>;
782+
783+
expectTypeOf<Props["icon"]>().toEqualTypeOf<CustomIcon | undefined>();
784+
});
785+
786+
it("should default to any type when generic is not specified", () => {
787+
type ComponentType = SideNavLinkComponent;
788+
type Props = ComponentProps<ComponentType>;
789+
790+
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
791+
expectTypeOf<Props["icon"]>().toEqualTypeOf<any>();
792+
});
793+
});
794+
795+
describe("SideNavMenu", () => {
796+
it("should support custom Icon types with generics", () => {
797+
type CustomIcon = new (...args: unknown[]) => unknown;
798+
799+
type ComponentType = SideNavMenuComponent<CustomIcon>;
800+
type Props = ComponentProps<ComponentType>;
801+
802+
expectTypeOf<Props["icon"]>().toEqualTypeOf<CustomIcon | undefined>();
803+
});
804+
805+
it("should default to any type when generic is not specified", () => {
806+
type ComponentType = SideNavMenuComponent;
807+
type Props = ComponentProps<ComponentType>;
808+
809+
// biome-ignore lint/suspicious/noExplicitAny: Testing default any type
810+
expectTypeOf<Props["icon"]>().toEqualTypeOf<any>();
811+
});
812+
});
813+
});
677814
});

0 commit comments

Comments
 (0)