Ensure checked files persist after FileExplorer rerenders (#6691)

* add tree_updated flag

* add changeset

* add changeset

* test

* generate notebooks

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
This commit is contained in:
Hannah 2023-12-08 19:27:25 +01:00 committed by GitHub
parent 053bec98be
commit 128ab5d65b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 33 additions and 8 deletions

View File

@ -0,0 +1,6 @@
---
"@gradio/fileexplorer": patch
"gradio": patch
---
fix:Ensure checked files persist after FileExplorer rerenders

View File

@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: file_explorer_component_events"]}, {"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": ["# Downloading files from the demo repo\n", "import os\n", "os.mkdir('dir1')\n", "!wget -q -O dir1/bar.txt https://github.com/gradio-app/gradio/raw/main/demo/file_explorer_component_events/dir1/bar.txt\n", "!wget -q -O dir1/foo.txt https://github.com/gradio-app/gradio/raw/main/demo/file_explorer_component_events/dir1/foo.txt\n", "os.mkdir('dir2')\n", "!wget -q -O dir2/baz.png https://github.com/gradio-app/gradio/raw/main/demo/file_explorer_component_events/dir2/baz.png\n", "!wget -q -O dir2/foo.png https://github.com/gradio-app/gradio/raw/main/demo/file_explorer_component_events/dir2/foo.png"]}, {"cell_type": "code", "execution_count": null, "id": "44380577570523278879349135829904343037", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "from pathlib import Path\n", "\n", "base_root = Path(__file__).parent.resolve()\n", "\n", "with gr.Blocks() as demo:\n", " dd = gr.Dropdown(label=\"Select File Explorer Root\",\n", " value=str(base_root / \"dir1\"),\n", " choices=[str(base_root / \"dir1\"), str(base_root / \"dir2\")])\n", " fe = gr.FileExplorer(root=str(base_root / \"dir1\"), interactive=True)\n", " dd.select(lambda s: gr.FileExplorer(root=s), inputs=[dd], outputs=[fe])\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: file_explorer_component_events"]}, {"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": ["# Downloading files from the demo repo\n", "import os\n", "os.mkdir('dir1')\n", "!wget -q -O dir1/bar.txt https://github.com/gradio-app/gradio/raw/main/demo/file_explorer_component_events/dir1/bar.txt\n", "!wget -q -O dir1/foo.txt https://github.com/gradio-app/gradio/raw/main/demo/file_explorer_component_events/dir1/foo.txt\n", "os.mkdir('dir2')\n", "!wget -q -O dir2/baz.png https://github.com/gradio-app/gradio/raw/main/demo/file_explorer_component_events/dir2/baz.png\n", "!wget -q -O dir2/foo.png https://github.com/gradio-app/gradio/raw/main/demo/file_explorer_component_events/dir2/foo.png"]}, {"cell_type": "code", "execution_count": null, "id": "44380577570523278879349135829904343037", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "from pathlib import Path\n", "\n", "base_root = Path(__file__).parent.resolve()\n", "\n", "with gr.Blocks() as demo:\n", " dd = gr.Dropdown(\n", " label=\"Select File Explorer Root\",\n", " value=str(base_root / \"dir1\"),\n", " choices=[str(base_root / \"dir1\"), str(base_root / \"dir2\")],\n", " )\n", " fe = gr.FileExplorer(root=str(base_root / \"dir1\"), interactive=True)\n", " dd.select(lambda s: gr.FileExplorer(root=s), inputs=[dd], outputs=[fe])\n", "\n", " with gr.Row():\n", " a = gr.Textbox(elem_id=\"input-box\")\n", " a.change(lambda x: x, inputs=[a])\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -4,12 +4,18 @@ from pathlib import Path
base_root = Path(__file__).parent.resolve()
with gr.Blocks() as demo:
dd = gr.Dropdown(label="Select File Explorer Root",
value=str(base_root / "dir1"),
choices=[str(base_root / "dir1"), str(base_root / "dir2")])
dd = gr.Dropdown(
label="Select File Explorer Root",
value=str(base_root / "dir1"),
choices=[str(base_root / "dir1"), str(base_root / "dir2")],
)
fe = gr.FileExplorer(root=str(base_root / "dir1"), interactive=True)
dd.select(lambda s: gr.FileExplorer(root=s), inputs=[dd], outputs=[fe])
with gr.Row():
a = gr.Textbox(elem_id="input-box")
a.change(lambda x: x, inputs=[a])
if __name__ == "__main__":
demo.launch()

View File

@ -1,4 +1,4 @@
import { test } from "@gradio/tootils";
import { test, expect } from "@gradio/tootils";
test("File Explorer is interactive and re-runs the server_fn when root is updated", async ({
page
@ -26,4 +26,14 @@ test("File Explorer is interactive and re-runs the server_fn when root is update
.filter({ hasText: "foo.png" })
.getByRole("checkbox")
.check();
await page.locator("#input-box").getByTestId("textbox").fill("test");
await expect(
page.locator("span").filter({ hasText: "baz.png" }).getByRole("checkbox")
).toBeChecked();
await expect(
page.locator("span").filter({ hasText: "foo.png" }).getByRole("checkbox")
).toBeChecked();
});

View File

@ -30,7 +30,7 @@ interface FSStore {
}
export const make_fs_store = (): FSStore => {
const { subscribe, set, update } = writable<Node[] | null>(null);
const { subscribe, set } = writable<Node[] | null>(null);
let root: Node = {
type: "folder",
path: "",
@ -38,16 +38,18 @@ export const make_fs_store = (): FSStore => {
children_visible: false,
parent: null
};
let tree_updated = false;
function create_fs_graph(serialised_node: SerialisedNode[]): void {
root.children = process_tree(serialised_node);
tree_updated = true;
set(root.children);
}
let old_checked_paths: string[][] = [];
function set_checked_from_paths(checked_paths: string[][]): string[][] {
if (dequal(checked_paths, old_checked_paths)) {
if (dequal(checked_paths, old_checked_paths) && !tree_updated) {
return checked_paths;
}
old_checked_paths = checked_paths;
@ -88,6 +90,8 @@ export const make_fs_store = (): FSStore => {
set(root.children!);
tree_updated = false;
return new_checked_paths;
}
@ -235,7 +239,6 @@ function check_node_and_children(
checked: boolean,
checked_nodes: Node[]
): Node[] {
// console.log(node, checked);
if (node === null || node === undefined) return checked_nodes;
for (let i = 0; i < node.length; i++) {
node[i].checked = checked;