@gradio/tabitem 0.6.6 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # @gradio/tabitem
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Features
6
+
7
+ - [#13509](https://github.com/gradio-app/gradio/pull/13509) [`dcd072c`](https://github.com/gradio-app/gradio/commit/dcd072cd57f09ffd2dc5f97ae6afc505894824a6) - Migrate Chatbot, Tabs, TabItem to Svelte 5. Thanks @dawoodkhan82!
8
+
9
+ ### Dependency updates
10
+
11
+ - @gradio/tabs@0.7.0
12
+
13
+ ## 0.7.0
14
+
15
+ ### Features
16
+
17
+ - [#13467](https://github.com/gradio-app/gradio/pull/13467) [`feba2e1`](https://github.com/gradio-app/gradio/commit/feba2e1e41c68f4bbdacf4c963d8d9689d4b3346) - `Tab`, `TabItem`, `Plot` Unit tests. Thanks @dawoodkhan82!
18
+
19
+ ### Dependency updates
20
+
21
+ - @gradio/tabs@0.6.0
22
+
23
+ ## 0.6.6
24
+
25
+ ### Dependency updates
26
+
27
+ - @gradio/tabs@0.5.10
28
+
29
+ ## 0.6.6
30
+
31
+ ### Dependency updates
32
+
33
+ - @gradio/column@0.3.3
34
+
35
+ ## 0.6.6
36
+
37
+ ### Dependency updates
38
+
39
+ - @gradio/utils@0.12.2
40
+ - @gradio/tabs@0.5.9
41
+
42
+ ## 0.6.6
43
+
44
+ ### Dependency updates
45
+
46
+ - @gradio/utils@0.12.1
47
+ - @gradio/column@0.3.2
48
+
3
49
  ## 0.6.6
4
50
 
5
51
  ### Dependency updates
package/Index.svelte CHANGED
@@ -22,7 +22,7 @@
22
22
  order={gradio.props.order}
23
23
  scale={gradio.props.scale}
24
24
  component_id={gradio.props.component_id}
25
- on:select={({ detail }) => gradio.dispatch("select", detail)}
25
+ onselect={(data) => gradio.dispatch("select", data)}
26
26
  >
27
27
  <slot />
28
28
  </TabItem>
@@ -0,0 +1,123 @@
1
+ import { test, describe, afterEach, expect, vi } from "vitest";
2
+ import { cleanup, render, waitFor } from "@self/tootils/render";
3
+
4
+ import TabItemHarness from "./TabItemHarness.svelte";
5
+
6
+ describe("TabItem", () => {
7
+ afterEach(() => cleanup());
8
+
9
+ test("renders its slot content inside a tabpanel", async () => {
10
+ const { getByRole, getByTestId } = await render(TabItemHarness, {
11
+ tab_selected: "t1"
12
+ });
13
+
14
+ expect(getByRole("tabpanel", { hidden: true })).toBeInTheDocument();
15
+ expect(getByTestId("tab-content")).toHaveTextContent("tab panel content");
16
+ });
17
+
18
+ test("content is visible when this tab is the selected tab", async () => {
19
+ const { getByTestId } = await render(TabItemHarness, {
20
+ tab_selected: "t1"
21
+ });
22
+
23
+ expect(getByTestId("tab-content")).toBeVisible();
24
+ });
25
+
26
+ test("content is hidden when a different tab is selected", async () => {
27
+ const { getByTestId } = await render(TabItemHarness, {
28
+ tab_selected: "t2"
29
+ });
30
+
31
+ expect(getByTestId("tab-content")).not.toBeVisible();
32
+ });
33
+
34
+ test("content stays hidden when visible is false even if selected", async () => {
35
+ const { getByTestId } = await render(TabItemHarness, {
36
+ tab_selected: "t1",
37
+ tab_visible: false
38
+ });
39
+
40
+ expect(getByTestId("tab-content")).not.toBeVisible();
41
+ });
42
+
43
+ test("uses a stable internal id when no explicit id is provided", async () => {
44
+ const on_register = vi.fn();
45
+ const { getByTestId } = await render(TabItemHarness, {
46
+ omit_id: true,
47
+ tab_selected: 1,
48
+ on_register
49
+ });
50
+
51
+ expect(getByTestId("tab-content")).toBeVisible();
52
+ await waitFor(() => {
53
+ expect(on_register).toHaveBeenCalledWith(
54
+ expect.objectContaining({ id: 1 })
55
+ );
56
+ });
57
+ });
58
+
59
+ test("keeps 0 as an explicit id", async () => {
60
+ const on_register = vi.fn();
61
+ const { getByTestId } = await render(TabItemHarness, {
62
+ tab_id: 0,
63
+ tab_selected: 0,
64
+ on_register
65
+ });
66
+
67
+ expect(getByTestId("tab-content")).toBeVisible();
68
+ await waitFor(() => {
69
+ expect(on_register).toHaveBeenCalledWith(
70
+ expect.objectContaining({ id: 0 })
71
+ );
72
+ });
73
+ });
74
+
75
+ test("keeps an empty string as an explicit id", async () => {
76
+ const on_register = vi.fn();
77
+ const { getByTestId } = await render(TabItemHarness, {
78
+ tab_id: "",
79
+ tab_selected: "",
80
+ on_register
81
+ });
82
+
83
+ expect(getByTestId("tab-content")).toBeVisible();
84
+ await waitFor(() => {
85
+ expect(on_register).toHaveBeenCalledWith(
86
+ expect.objectContaining({ id: "" })
87
+ );
88
+ });
89
+ });
90
+ });
91
+
92
+ describe("Events: select", () => {
93
+ afterEach(() => cleanup());
94
+
95
+ test("fires when this tab is the active index on mount", async () => {
96
+ const on_tab_select = vi.fn();
97
+
98
+ await render(TabItemHarness, {
99
+ tab_selected: "t1",
100
+ tab_selected_index: 0,
101
+ on_tab_select
102
+ });
103
+
104
+ await waitFor(() => {
105
+ expect(on_tab_select).toHaveBeenCalledWith({
106
+ value: "First Tab",
107
+ index: 0
108
+ });
109
+ });
110
+ });
111
+
112
+ test("does not fire when this tab is not the active index", async () => {
113
+ const on_tab_select = vi.fn();
114
+
115
+ await render(TabItemHarness, {
116
+ tab_selected: "t2",
117
+ tab_selected_index: 1,
118
+ on_tab_select
119
+ });
120
+
121
+ expect(on_tab_select).not.toHaveBeenCalled();
122
+ });
123
+ });
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ import { setContext } from "svelte";
3
+ import { writable } from "svelte/store";
4
+ import { TABS } from "@gradio/tabs";
5
+ import { BaseTabItem } from "./Index.svelte";
6
+
7
+ let all = $props();
8
+ const cfg = (all as any).props ?? {};
9
+
10
+ const selected_tab = writable<string | number | false>(
11
+ cfg.tab_selected ?? "t1"
12
+ );
13
+ const selected_tab_index = writable<number>(cfg.tab_selected_index ?? 0);
14
+
15
+ setContext(TABS, {
16
+ register_tab: (tab: any, order: number) => {
17
+ cfg.on_register?.(tab);
18
+ return order;
19
+ },
20
+ unregister_tab: () => {},
21
+ selected_tab,
22
+ selected_tab_index
23
+ });
24
+ </script>
25
+
26
+ <BaseTabItem
27
+ label="First Tab"
28
+ id={cfg.omit_id ? undefined : (cfg.tab_id ?? "t1")}
29
+ order={0}
30
+ visible={cfg.tab_visible ?? true}
31
+ interactive={true}
32
+ scale={0}
33
+ component_id={1}
34
+ onselect={(data) => cfg.on_tab_select?.(data)}
35
+ >
36
+ <div data-testid="tab-content">tab panel content</div>
37
+ </BaseTabItem>
package/dist/Index.svelte CHANGED
@@ -22,7 +22,7 @@
22
22
  order={gradio.props.order}
23
23
  scale={gradio.props.scale}
24
24
  component_id={gradio.props.component_id}
25
- on:select={({ detail }) => gradio.dispatch("select", detail)}
25
+ onselect={(data) => gradio.dispatch("select", data)}
26
26
  >
27
27
  <slot />
28
28
  </TabItem>
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ import { setContext } from "svelte";
3
+ import { writable } from "svelte/store";
4
+ import { TABS } from "@gradio/tabs";
5
+ import { BaseTabItem } from "./Index.svelte";
6
+
7
+ let all = $props();
8
+ const cfg = (all as any).props ?? {};
9
+
10
+ const selected_tab = writable<string | number | false>(
11
+ cfg.tab_selected ?? "t1"
12
+ );
13
+ const selected_tab_index = writable<number>(cfg.tab_selected_index ?? 0);
14
+
15
+ setContext(TABS, {
16
+ register_tab: (tab: any, order: number) => {
17
+ cfg.on_register?.(tab);
18
+ return order;
19
+ },
20
+ unregister_tab: () => {},
21
+ selected_tab,
22
+ selected_tab_index
23
+ });
24
+ </script>
25
+
26
+ <BaseTabItem
27
+ label="First Tab"
28
+ id={cfg.omit_id ? undefined : (cfg.tab_id ?? "t1")}
29
+ order={0}
30
+ visible={cfg.tab_visible ?? true}
31
+ interactive={true}
32
+ scale={0}
33
+ component_id={1}
34
+ onselect={(data) => cfg.on_tab_select?.(data)}
35
+ >
36
+ <div data-testid="tab-content">tab panel content</div>
37
+ </BaseTabItem>
@@ -0,0 +1,3 @@
1
+ declare const TabItemHarness: import("svelte").Component<$$ComponentProps, {}, "">;
2
+ type TabItemHarness = ReturnType<typeof TabItemHarness>;
3
+ export default TabItemHarness;
@@ -1,56 +1,81 @@
1
1
  <script lang="ts">
2
- import { getContext, onMount, createEventDispatcher, tick } from "svelte";
2
+ import { getContext, tick, untrack } from "svelte";
3
3
  import { TABS } from "@gradio/tabs";
4
4
  import { BaseColumn } from "@gradio/column";
5
5
  import type { SelectData } from "@gradio/utils";
6
6
 
7
- export let elem_id = "";
8
- export let elem_classes: string[] = [];
9
- export let label: string;
10
- export let id: string | number | object = {};
11
- export let visible: boolean | "hidden";
12
- export let interactive: boolean;
13
- export let order: number;
14
- export let scale: number;
15
- export let component_id: number;
16
-
17
- const dispatch = createEventDispatcher<{ select: SelectData }>();
7
+ let {
8
+ elem_id = "",
9
+ elem_classes = [],
10
+ label,
11
+ id,
12
+ visible,
13
+ interactive,
14
+ order,
15
+ scale,
16
+ component_id,
17
+ onselect
18
+ }: {
19
+ elem_id?: string;
20
+ elem_classes?: string[];
21
+ label: string;
22
+ id?: string | number | null;
23
+ visible: boolean | "hidden";
24
+ interactive: boolean;
25
+ order: number;
26
+ scale: number;
27
+ component_id: number;
28
+ onselect?: (data: SelectData) => void;
29
+ } = $props();
18
30
 
19
31
  const { register_tab, unregister_tab, selected_tab, selected_tab_index } =
20
32
  getContext(TABS) as any;
21
33
 
22
- let tab_index: number;
34
+ let tab_index = $state<number | undefined>(undefined);
23
35
 
24
36
  function _register_tab(obj: string, order: number): number {
25
37
  obj = JSON.parse(obj);
26
38
  return register_tab(obj, order);
27
39
  }
28
40
 
29
- $: props_json = JSON.stringify({
30
- label,
31
- id,
32
- elem_id,
33
- visible,
34
- interactive,
35
- scale,
36
- component_id
37
- });
41
+ let tab_id = $derived(id ?? component_id);
38
42
 
39
- $: tab_index = _register_tab(props_json, order);
43
+ let props_json = $derived(
44
+ JSON.stringify({
45
+ label,
46
+ id: tab_id,
47
+ elem_id,
48
+ visible,
49
+ interactive,
50
+ scale,
51
+ component_id
52
+ })
53
+ );
40
54
 
41
- onMount(() => {
42
- return (): void => unregister_tab({ label, id, elem_id }, order);
55
+ let is_visible = $derived(visible !== false && visible !== "hidden");
56
+
57
+ $effect(() => {
58
+ const tab_props = props_json;
59
+ tab_index = untrack(() => _register_tab(tab_props, order));
60
+ });
61
+
62
+ $effect(() => {
63
+ return (): void => unregister_tab({ label, id: tab_id, elem_id }, order);
43
64
  });
44
65
 
45
- $: $selected_tab_index === tab_index &&
46
- tick().then(() => dispatch("select", { value: label, index: tab_index }));
66
+ $effect(() => {
67
+ const index = tab_index;
68
+ if (index !== undefined && $selected_tab_index === index) {
69
+ tick().then(() => onselect?.({ value: label, index }));
70
+ }
71
+ });
47
72
  </script>
48
73
 
49
74
  <div
50
75
  id={elem_id}
51
76
  class="tabitem {elem_classes.join(' ')}"
52
77
  class:grow-children={scale >= 1}
53
- style:display={$selected_tab === id && visible !== false ? "flex" : "none"}
78
+ style:display={$selected_tab === tab_id && is_visible ? "flex" : "none"}
54
79
  style:flex-grow={scale}
55
80
  role="tabpanel"
56
81
  >
@@ -1,4 +1,16 @@
1
1
  import type { SelectData } from "@gradio/utils";
2
+ type $$ComponentProps = {
3
+ elem_id?: string;
4
+ elem_classes?: string[];
5
+ label: string;
6
+ id?: string | number | null;
7
+ visible: boolean | "hidden";
8
+ interactive: boolean;
9
+ order: number;
10
+ scale: number;
11
+ component_id: number;
12
+ onselect?: (data: SelectData) => void;
13
+ };
2
14
  interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
15
  new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
16
  $$bindings?: Bindings;
@@ -17,24 +29,12 @@ type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
17
29
  } ? Props extends Record<string, never> ? any : {
18
30
  children?: any;
19
31
  } : {});
20
- declare const TabItem: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
21
- elem_id?: string;
22
- elem_classes?: string[];
23
- label: string;
24
- id?: string | number | object;
25
- visible: boolean | "hidden";
26
- interactive: boolean;
27
- order: number;
28
- scale: number;
29
- component_id: number;
30
- }, {
32
+ declare const TabItem: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<$$ComponentProps, {
31
33
  default: {};
32
34
  }>, {
33
- select: CustomEvent<SelectData>;
34
- } & {
35
35
  [evt: string]: CustomEvent<any>;
36
36
  }, {
37
37
  default: {};
38
- }, {}, string>;
38
+ }, {}, "">;
39
39
  type TabItem = InstanceType<typeof TabItem>;
40
40
  export default TabItem;
package/dist/types.d.ts CHANGED
@@ -3,7 +3,7 @@ export interface TabItemProps {
3
3
  elem_id: string;
4
4
  elem_classes: string[];
5
5
  label: string;
6
- id: string | number;
6
+ id?: string | number | null;
7
7
  visible: boolean | "hidden";
8
8
  interactive: boolean;
9
9
  order: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/tabitem",
3
- "version": "0.6.6",
3
+ "version": "0.8.0",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -16,12 +16,12 @@
16
16
  "./package.json": "./package.json"
17
17
  },
18
18
  "dependencies": {
19
- "@gradio/column": "^0.3.2",
20
- "@gradio/tabs": "^0.5.8",
21
- "@gradio/utils": "^0.12.0"
19
+ "@gradio/column": "^0.3.3",
20
+ "@gradio/utils": "^0.12.2",
21
+ "@gradio/tabs": "^0.7.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@gradio/preview": "^0.16.0"
24
+ "@gradio/preview": "^0.16.2"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "svelte": "^5.48.0"
@@ -1,56 +1,81 @@
1
1
  <script lang="ts">
2
- import { getContext, onMount, createEventDispatcher, tick } from "svelte";
2
+ import { getContext, tick, untrack } from "svelte";
3
3
  import { TABS } from "@gradio/tabs";
4
4
  import { BaseColumn } from "@gradio/column";
5
5
  import type { SelectData } from "@gradio/utils";
6
6
 
7
- export let elem_id = "";
8
- export let elem_classes: string[] = [];
9
- export let label: string;
10
- export let id: string | number | object = {};
11
- export let visible: boolean | "hidden";
12
- export let interactive: boolean;
13
- export let order: number;
14
- export let scale: number;
15
- export let component_id: number;
16
-
17
- const dispatch = createEventDispatcher<{ select: SelectData }>();
7
+ let {
8
+ elem_id = "",
9
+ elem_classes = [],
10
+ label,
11
+ id,
12
+ visible,
13
+ interactive,
14
+ order,
15
+ scale,
16
+ component_id,
17
+ onselect
18
+ }: {
19
+ elem_id?: string;
20
+ elem_classes?: string[];
21
+ label: string;
22
+ id?: string | number | null;
23
+ visible: boolean | "hidden";
24
+ interactive: boolean;
25
+ order: number;
26
+ scale: number;
27
+ component_id: number;
28
+ onselect?: (data: SelectData) => void;
29
+ } = $props();
18
30
 
19
31
  const { register_tab, unregister_tab, selected_tab, selected_tab_index } =
20
32
  getContext(TABS) as any;
21
33
 
22
- let tab_index: number;
34
+ let tab_index = $state<number | undefined>(undefined);
23
35
 
24
36
  function _register_tab(obj: string, order: number): number {
25
37
  obj = JSON.parse(obj);
26
38
  return register_tab(obj, order);
27
39
  }
28
40
 
29
- $: props_json = JSON.stringify({
30
- label,
31
- id,
32
- elem_id,
33
- visible,
34
- interactive,
35
- scale,
36
- component_id
37
- });
41
+ let tab_id = $derived(id ?? component_id);
38
42
 
39
- $: tab_index = _register_tab(props_json, order);
43
+ let props_json = $derived(
44
+ JSON.stringify({
45
+ label,
46
+ id: tab_id,
47
+ elem_id,
48
+ visible,
49
+ interactive,
50
+ scale,
51
+ component_id
52
+ })
53
+ );
40
54
 
41
- onMount(() => {
42
- return (): void => unregister_tab({ label, id, elem_id }, order);
55
+ let is_visible = $derived(visible !== false && visible !== "hidden");
56
+
57
+ $effect(() => {
58
+ const tab_props = props_json;
59
+ tab_index = untrack(() => _register_tab(tab_props, order));
60
+ });
61
+
62
+ $effect(() => {
63
+ return (): void => unregister_tab({ label, id: tab_id, elem_id }, order);
43
64
  });
44
65
 
45
- $: $selected_tab_index === tab_index &&
46
- tick().then(() => dispatch("select", { value: label, index: tab_index }));
66
+ $effect(() => {
67
+ const index = tab_index;
68
+ if (index !== undefined && $selected_tab_index === index) {
69
+ tick().then(() => onselect?.({ value: label, index }));
70
+ }
71
+ });
47
72
  </script>
48
73
 
49
74
  <div
50
75
  id={elem_id}
51
76
  class="tabitem {elem_classes.join(' ')}"
52
77
  class:grow-children={scale >= 1}
53
- style:display={$selected_tab === id && visible !== false ? "flex" : "none"}
78
+ style:display={$selected_tab === tab_id && is_visible ? "flex" : "none"}
54
79
  style:flex-grow={scale}
55
80
  role="tabpanel"
56
81
  >
package/types.ts CHANGED
@@ -4,7 +4,7 @@ export interface TabItemProps {
4
4
  elem_id: string;
5
5
  elem_classes: string[];
6
6
  label: string;
7
- id: string | number;
7
+ id?: string | number | null;
8
8
  visible: boolean | "hidden";
9
9
  interactive: boolean;
10
10
  order: number;