Skip to content

Commit

Permalink
Ensures tabs with visible set to false are not visible. (#9653)
Browse files Browse the repository at this point in the history
* * fix tab visibility
* add story

* add changeset

* stuff

* fix

* more fix

* fix undefined tab labels

* fix tabs again

* add changeset

* format

* format

* fix type

* add changeset

* fix all things

* format

* add changeset

* notebooks

* visible tabs

---------

Co-authored-by: gradio-pr-bot <[email protected]>
Co-authored-by: Abubakar Abid <[email protected]>
Co-authored-by: pngwn <[email protected]>
Co-authored-by: freddyaboulton <[email protected]>
  • Loading branch information
5 people authored Oct 21, 2024
1 parent cfd60b0 commit 61cd768
Show file tree
Hide file tree
Showing 19 changed files with 318 additions and 126 deletions.
12 changes: 12 additions & 0 deletions .changeset/open-geckos-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@gradio/chatbot": patch
"@gradio/core": patch
"@gradio/sanitize": patch
"@gradio/tabitem": patch
"@gradio/tabs": patch
"@self/app": patch
"gradio": patch
"website": patch
---

fix:Ensures tabs with visible set to false are not visible.
2 changes: 1 addition & 1 deletion .config/playwright.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const base = defineConfig({
}
},
expect: { timeout: 10000 },
timeout: 10000,
timeout: 30000,
testMatch: /.*\.spec\.ts/,
testDir: "..",
workers: process.env.CI ? 1 : undefined,
Expand Down
2 changes: 1 addition & 1 deletion demo/custom_css/run.ipynb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: custom_css"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "css = \"\"\"\n", "/* CSSKeyframesRule for animation */\n", "@keyframes animation {\n", " from {background-color: red;}\n", " to {background-color: blue;}\n", "}\n", "\n", ".cool-col {\n", " animation-name: animation;\n", " animation-duration: 4s;\n", " animation-iteration-count: infinite;\n", " border-radius: 10px;\n", " padding: 20px;\n", "}\n", "\n", "/* CSSStyleRule */\n", ".markdown {\n", " background-color: lightblue;\n", " padding: 20px;\n", "}\n", "\n", ".markdown p {\n", " color: royalblue;\n", "}\n", "\n", "/* CSSMediaRule */\n", "@media screen and (max-width: 600px) {\n", " .markdown {\n", " background: blue;\n", " }\n", " .markdown p {\n", " color: lightblue;\n", " }\n", "}\n", "\n", ".dark .markdown {\n", " background: pink;\n", "}\n", "\n", ".darktest h3 {\n", " color: black;\n", "}\n", "\n", ".dark .darktest h3 {\n", " color: yellow;\n", "}\n", "\n", "/* CSSFontFaceRule */\n", "@font-face {\n", " font-family: \"test-font\";\n", " src: url(\"https://mdn.github.io/css-examples/web-fonts/VeraSeBd.ttf\") format(\"truetype\");\n", "}\n", "\n", ".cool-col {\n", " font-family: \"test-font\";\n", "}\n", "\n", "/* CSSImportRule */\n", "@import url(\"https://fonts.googleapis.com/css2?family=Protest+Riot&display=swap\");\n", "\n", ".markdown {\n", " font-family: \"Protest Riot\", sans-serif;\n", "}\n", "\"\"\"\n", "\n", "with gr.Blocks(css=css) as demo:\n", " with gr.Column(elem_classes=\"cool-col\"):\n", " gr.Markdown(\"### Gradio Demo with Custom CSS\", elem_classes=\"darktest\")\n", " gr.Markdown(elem_classes=\"markdown\", value=\"Resize the browser window to see the CSS media query in action.\")\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: custom_css"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "css = \"\"\"\n", "/* CSSKeyframesRule for animation */\n", "@keyframes animation {\n", " from {background-color: red;}\n", " to {background-color: blue;}\n", "}\n", "\n", ".cool-col {\n", " background-color: red;\n", " animation-name: animation;\n", " animation-duration: 4s;\n", " animation-delay: 2s;\n", " animation-iteration-count: infinite;\n", " border-radius: 10px;\n", " padding: 20px;\n", "}\n", "\n", "/* CSSStyleRule */\n", ".markdown {\n", " background-color: lightblue;\n", " padding: 20px;\n", "}\n", "\n", ".markdown p {\n", " color: royalblue;\n", "}\n", "\n", "/* CSSMediaRule */\n", "@media screen and (max-width: 600px) {\n", " .markdown {\n", " background: blue;\n", " }\n", " .markdown p {\n", " color: lightblue;\n", " }\n", "}\n", "\n", ".dark .markdown {\n", " background: pink;\n", "}\n", "\n", ".darktest h3 {\n", " color: black;\n", "}\n", "\n", ".dark .darktest h3 {\n", " color: yellow;\n", "}\n", "\n", "/* CSSFontFaceRule */\n", "@font-face {\n", " font-family: \"test-font\";\n", " src: url(\"https://mdn.github.io/css-examples/web-fonts/VeraSeBd.ttf\") format(\"truetype\");\n", "}\n", "\n", ".cool-col {\n", " font-family: \"test-font\";\n", "}\n", "\n", "/* CSSImportRule */\n", "@import url(\"https://fonts.googleapis.com/css2?family=Protest+Riot&display=swap\");\n", "\n", ".markdown {\n", " font-family: \"Protest Riot\", sans-serif;\n", "}\n", "\"\"\"\n", "\n", "with gr.Blocks(css=css) as demo:\n", " with gr.Column(elem_classes=\"cool-col\"):\n", " gr.Markdown(\"### Gradio Demo with Custom CSS\", elem_classes=\"darktest\")\n", " gr.Markdown(\n", " elem_classes=\"markdown\",\n", " value=\"Resize the browser window to see the CSS media query in action.\",\n", " )\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
7 changes: 6 additions & 1 deletion demo/custom_css/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
}
.cool-col {
background-color: red;
animation-name: animation;
animation-duration: 4s;
animation-delay: 2s;
animation-iteration-count: infinite;
border-radius: 10px;
padding: 20px;
Expand Down Expand Up @@ -68,7 +70,10 @@
with gr.Blocks(css=css) as demo:
with gr.Column(elem_classes="cool-col"):
gr.Markdown("### Gradio Demo with Custom CSS", elem_classes="darktest")
gr.Markdown(elem_classes="markdown", value="Resize the browser window to see the CSS media query in action.")
gr.Markdown(
elem_classes="markdown",
value="Resize the browser window to see the CSS media query in action.",
)

if __name__ == "__main__":
demo.launch()
1 change: 1 addition & 0 deletions demo/tabs_visibility/run.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: tabs_visibility"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Tab(\"abc\"):\n", " gr.Textbox(label=\"abc\")\n", " with gr.Tab(\"def\", visible=False) as t:\n", " gr.Textbox(label=\"def\")\n", " with gr.Tab(\"ghi\"):\n", " gr.Textbox(label=\"ghi\")\n", " with gr.Tab(\"jkl\", visible=False) as t2:\n", " gr.Textbox(label=\"jkl\")\n", " with gr.Tab(\"mno\"):\n", " gr.Textbox(label=\"mno\")\n", " with gr.Tab(\"pqr\", visible=False) as t3:\n", " gr.Textbox(label=\"pqr\")\n", " with gr.Tab(\"stu\"):\n", " gr.Textbox(label=\"stu\")\n", " with gr.Tab(\"vwx\", visible=False) as t4:\n", " gr.Textbox(label=\"vwx\")\n", " with gr.Tab(\"yz\"):\n", " gr.Textbox(label=\"yz\")\n", " b = gr.Button(\"Make visible\")\n", "\n", " b.click(\n", " lambda: [\n", " gr.Tab(visible=True),\n", " gr.Tab(visible=True),\n", " gr.Tab(visible=True),\n", " gr.Tab(visible=True),\n", " ],\n", " inputs=None,\n", " outputs=[t, t2, t3, t4],\n", " )\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
36 changes: 36 additions & 0 deletions demo/tabs_visibility/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import gradio as gr

with gr.Blocks() as demo:
with gr.Tab("abc"):
gr.Textbox(label="abc")
with gr.Tab("def", visible=False) as t:
gr.Textbox(label="def")
with gr.Tab("ghi"):
gr.Textbox(label="ghi")
with gr.Tab("jkl", visible=False) as t2:
gr.Textbox(label="jkl")
with gr.Tab("mno"):
gr.Textbox(label="mno")
with gr.Tab("pqr", visible=False) as t3:
gr.Textbox(label="pqr")
with gr.Tab("stu"):
gr.Textbox(label="stu")
with gr.Tab("vwx", visible=False) as t4:
gr.Textbox(label="vwx")
with gr.Tab("yz"):
gr.Textbox(label="yz")
b = gr.Button("Make visible")

b.click(
lambda: [
gr.Tab(visible=True),
gr.Tab(visible=True),
gr.Tab(visible=True),
gr.Tab(visible=True),
],
inputs=None,
outputs=[t, t2, t3, t4],
)

if __name__ == "__main__":
demo.launch()
2 changes: 1 addition & 1 deletion js/_website/src/lib/components/DemosLite.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@
class="mt-1 flex-1 flex flex-col relative overflow-scroll code-scroll"
>
<Tabs
inital_tabs={TABS}
initial_tabs={TABS}
selected={selected_tab}
elem_classes={["editor-tabs"]}
>
Expand Down
57 changes: 54 additions & 3 deletions js/app/src/routes/[...catchall]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,59 @@
export let container: boolean;
let stream: EventSource;
function handle_theme_mode(target: HTMLElement): "light" | "dark" {
let new_theme_mode: ThemeMode;
const url = new URL(window.location.toString());
const url_color_mode: ThemeMode | null = url.searchParams.get(
"__theme"
) as ThemeMode | null;
new_theme_mode = theme_mode || url_color_mode || "system";
if (new_theme_mode === "dark" || new_theme_mode === "light") {
apply_theme(target, new_theme_mode);
} else {
new_theme_mode = sync_system_theme(target);
}
return new_theme_mode;
}
function sync_system_theme(target: HTMLElement): "light" | "dark" {
const theme = update_scheme();
window
?.matchMedia("(prefers-color-scheme: dark)")
?.addEventListener("change", update_scheme);
function update_scheme(): "light" | "dark" {
let _theme: "light" | "dark" = window?.matchMedia?.(
"(prefers-color-scheme: dark)"
).matches
? "dark"
: "light";
apply_theme(target, _theme);
return _theme;
}
return theme;
}
function apply_theme(target: HTMLElement, theme: "dark" | "light"): void {
const dark_class_element = is_embed ? target.parentElement! : document.body;
const bg_element = is_embed ? target : target.parentElement!;
bg_element.style.background = "var(--body-background-fill)";
if (theme === "dark") {
dark_class_element.classList.add("dark");
} else {
dark_class_element.classList.remove("dark");
}
}
let active_theme_mode: ThemeMode;
if (browser) {
active_theme_mode = handle_theme_mode(document.body);
}
// These utilities are exported to be injectable for the Wasm version.
// export let Client: typeof ClientType;
Expand All @@ -112,7 +165,7 @@
let render_complete = false;
$: config = data.config;
let loading_text = $_("common.loading") + "...";
let active_theme_mode: ThemeMode;
let intersecting: ReturnType<typeof create_intersection_store> = {
register: () => {},
subscribe: writable({}).subscribe
Expand All @@ -139,8 +192,6 @@
let gradio_dev_mode = "";
onMount(async () => {
// active_theme_mode = handle_theme_mode(wrapper);
//@ts-ignore
config = data.config;
window.gradio_config = config;
Expand Down
2 changes: 1 addition & 1 deletion js/app/src/routes/[...catchall]/+page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export async function load({
throw new Error("No config found");
}

const { create_layout, layout } = create_components();
const { create_layout, layout } = create_components(undefined);

await create_layout({
app,
Expand Down
4 changes: 3 additions & 1 deletion js/chatbot/shared/ChatBot.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
let _components: Record<string, ComponentType<SvelteComponent>> = {};
const is_browser = typeof window !== "undefined";
async function update_components(): Promise<void> {
_components = await load_components(
get_components_from_messages(value),
Expand Down Expand Up @@ -323,7 +325,7 @@
show_undo={_undoable && is_last_bot_message(messages, value)}
{show_copy_button}
handle_action={(selected) => handle_like(i, messages[0], selected)}
{scroll}
scroll={is_browser ? scroll : () => {}}
/>
{/each}
{#if pending_message}
Expand Down
33 changes: 21 additions & 12 deletions js/core/src/init.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { writable, type Writable, get } from "svelte/store";

import type {
ComponentMeta,
Dependency,
Expand Down Expand Up @@ -27,6 +28,7 @@ const raf = is_browser
* Create a store with the layout and a map of targets
* @returns A store with the layout and a map of targets
*/
let has_run = new Set<number>();
export function create_components(initial_layout: ComponentMeta | undefined): {
layout: Writable<ComponentMeta>;
targets: Writable<TargetMap>;
Expand Down Expand Up @@ -290,19 +292,26 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
);
}

if (instance.type === "tabs") {
instance.children =
instance?.children?.map((c) => ({
...c,
props: {
...c.props,
id: c.props.id || c.id
}
})) || [];
const child_tab_items = instance.children?.filter(
if (instance.type === "tabs" && !instance.props.initial_tabs) {
const tab_items_props =
node.children?.map((c) => {
const instance = instance_map[c.id];
// console.log("tabs", JSON.stringify(instance.props, null, 2));
instance.props.id ??= c.id;
return {
type: instance.type,
props: {
...(instance.props as any),
id: instance.props.id
}
};
}) || [];

const child_tab_items = tab_items_props.filter(
(child) => child.type === "tabitem"
);
instance.props.inital_tabs = child_tab_items?.map((child) => ({

instance.props.initial_tabs = child_tab_items?.map((child) => ({
label: child.props.label,
id: child.props.id,
visible: child.props.visible,
Expand Down Expand Up @@ -337,7 +346,7 @@ export function create_components(initial_layout: ComponentMeta | undefined): {
else if (update.value instanceof Set)
new_value = new Set(update.value);
else if (Array.isArray(update.value)) new_value = [...update.value];
else if (update.value === null) new_value = null;
else if (update.value == null) new_value = null;
else if (typeof update.value === "object")
new_value = { ...update.value };
else new_value = update.value;
Expand Down
3 changes: 2 additions & 1 deletion js/sanitize/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"development": "./server.ts",
"default": "./dist/server.js"
}
}
},
"./package.json": "./package.json"
},
"scripts": {
"package": "svelte-package --input=. --cwd=../../.config/"
Expand Down
2 changes: 1 addition & 1 deletion js/spa/test/audio_debugger.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ test("recording audio", async ({ page }) => {
permissions: ["microphone"]
});

await page.getByText("Interface").click();
await page.getByRole("tab", { name: "Interface" }).click();
await page.getByLabel("Record audio").click();

context.grantPermissions(["microphone"]);
Expand Down
3 changes: 2 additions & 1 deletion js/spa/test/blocks_xray.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ test("renders the correct elements", async ({ page }) => {
const checkboxes = await page.getByTestId("checkbox-group");
await expect(checkboxes).toContainText("Covid Malaria Lung Cancer");

const tabs = await page.locator("button", { hasText: /X-ray|CT Scan/ });
// const tabs = await page.locator("button", { hasText: /X-ray|CT Scan/ });
const tabs = await page.getByRole("tab", { name: /X-ray|CT Scan/ });
await expect(tabs).toHaveCount(2);
});

Expand Down
12 changes: 7 additions & 5 deletions js/tabitem/Index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@
export let elem_classes: string[] = [];
export let label: string;
export let id: string | number;
export let gradio: Gradio<{
select: SelectData;
}>;
export let gradio:
| Gradio<{
select: SelectData;
}>
| undefined;
export let visible = true;
export let interactive = true;
</script>

<TabItem
{elem_id}
{elem_classes}
name={label}
{label}
{visible}
{interactive}
{id}
on:select={({ detail }) => gradio.dispatch("select", detail)}
on:select={({ detail }) => gradio?.dispatch("select", detail)}
>
<slot />
</TabItem>
8 changes: 4 additions & 4 deletions js/tabitem/shared/TabItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
export let elem_id = "";
export let elem_classes: string[] = [];
export let name: string;
export let label: string;
export let id: string | number | object = {};
export let visible: boolean;
export let interactive: boolean;
Expand All @@ -18,14 +18,14 @@
let tab_index: number;
$: tab_index = register_tab({ name, id, elem_id, visible, interactive });
$: tab_index = register_tab({ label, id, elem_id, visible, interactive });
onMount(() => {
return (): void => unregister_tab({ name, id, elem_id });
return (): void => unregister_tab({ label, id, elem_id });
});
$: $selected_tab_index === tab_index &&
tick().then(() => dispatch("select", { value: name, index: tab_index }));
tick().then(() => dispatch("select", { value: label, index: tab_index }));
</script>

<div
Expand Down
18 changes: 10 additions & 8 deletions js/tabs/Index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
export let elem_id = "";
export let elem_classes: string[] = [];
export let selected: number | string;
export let inital_tabs: Tab[] = [];
export let gradio: Gradio<{
change: never;
select: SelectData;
}>;
export let initial_tabs: Tab[] = [];
export let gradio:
| Gradio<{
change: never;
select: SelectData;
}>
| undefined;
$: dispatch("prop_change", { selected });
</script>
Expand All @@ -27,9 +29,9 @@
{elem_id}
{elem_classes}
bind:selected
on:change={() => gradio.dispatch("change")}
on:select={(e) => gradio.dispatch("select", e.detail)}
{inital_tabs}
on:change={() => gradio?.dispatch("change")}
on:select={(e) => gradio?.dispatch("select", e.detail)}
{initial_tabs}
>
<slot />
</Tabs>
Loading

0 comments on commit 61cd768

Please sign in to comment.