ensure css loads before mounting app (#3573)

* ensure css loads before mounting app

* changelog

* fix tests

* change?

* changelog
This commit is contained in:
pngwn 2023-03-27 15:52:07 +01:00 committed by GitHub
parent cecd5a2526
commit 0d9a08bf64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 73 additions and 38 deletions

View File

@ -13,6 +13,7 @@
- Use Gradio API server to send telemetry using `huggingface_hub` [@dawoodkhan82](https://github.com/dawoodkhan82) in [PR 3488](https://github.com/gradio-app/gradio/pull/3488)
- Fixes an an issue where if the Blocks scope was not exited, then State could be shared across sessions, by [@abidlabs](https://github.com/abidlabs) in [PR 3600](https://github.com/gradio-app/gradio/pull/3600)
- Fixed bug where "or" was not being localized in file upload text by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3599](https://github.com/gradio-app/gradio/pull/3599)
- Ensure CSS has fully loaded before rendering the application, by [@pngwn](https://github.com/pngwn) in [PR 3573](https://github.com/gradio-app/gradio/pull/3573)
## Documentation Changes:

View File

@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: code"]}, {"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", "!wget -q https://github.com/gradio-app/gradio/raw/main/demo/code/file.css"]}, {"cell_type": "code", "execution_count": null, "id": 44380577570523278879349135829904343037, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "import os\n", "from time import sleep\n", "\n", "\n", "css_file = os.path.join(os.path.abspath(''), \"file.css\")\n", "\n", "\n", "def set_lang(language):\n", " print(language)\n", " return gr.Code.update(language=language)\n", "\n", "\n", "def set_lang_from_path():\n", " sleep(1)\n", " return gr.Code.update((css_file, ), language=\"css\")\n", "\n", "\n", "def code(language, code):\n", " return gr.Code.update(code, language=language)\n", "\n", "\n", "io = gr.Interface(lambda x: x, \"code\", \"code\")\n", "\n", "with gr.Blocks() as demo:\n", " lang = gr.Dropdown(value=\"python\", choices=gr.Code.languages)\n", " with gr.Row():\n", " code_in = gr.Code(language=\"python\", label=\"Input\")\n", " code_out = gr.Code(label=\"Ouput\")\n", " btn = gr.Button(\"Run\")\n", " btn_two = gr.Button(\"Load File\")\n", "\n", " lang.change(set_lang, inputs=lang, outputs=code_in)\n", " btn.click(code, inputs=[lang, code_in], outputs=code_out)\n", " btn_two.click(set_lang_from_path, inputs=None, outputs=code_out)\n", " io.render()\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: code"]}, {"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", "!wget -q https://github.com/gradio-app/gradio/raw/main/demo/code/file.css"]}, {"cell_type": "code", "execution_count": null, "id": 44380577570523278879349135829904343037, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "import os\n", "from time import sleep\n", "\n", "\n", "css_file = os.path.join(os.path.abspath(''), \"file.css\")\n", "\n", "\n", "def set_lang(language):\n", " print(language)\n", " return gr.Code.update(language=language)\n", "\n", "\n", "def set_lang_from_path():\n", " sleep(1)\n", " return gr.Code.update((css_file,), language=\"css\")\n", "\n", "\n", "def code(language, code):\n", " return gr.Code.update(code, language=language)\n", "\n", "\n", "io = gr.Interface(lambda x: x, \"code\", \"code\")\n", "\n", "with gr.Blocks() as demo:\n", " lang = gr.Dropdown(value=\"python\", choices=gr.Code.languages)\n", " with gr.Row():\n", " code_in = gr.Code(\n", " language=\"python\",\n", " label=\"Input\",\n", " value='def all_odd_elements(sequence):\\n \"\"\"Returns every odd element of the sequence.\"\"\"',\n", " )\n", " code_out = gr.Code(label=\"Ouput\")\n", " btn = gr.Button(\"Run\")\n", " btn_two = gr.Button(\"Load File\")\n", "\n", " lang.change(set_lang, inputs=lang, outputs=code_in)\n", " btn.click(code, inputs=[lang, code_in], outputs=code_out)\n", " btn_two.click(set_lang_from_path, inputs=None, outputs=code_out)\n", " io.render()\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -13,7 +13,7 @@ def set_lang(language):
def set_lang_from_path():
sleep(1)
return gr.Code.update((css_file, ), language="css")
return gr.Code.update((css_file,), language="css")
def code(language, code):
@ -25,7 +25,11 @@ io = gr.Interface(lambda x: x, "code", "code")
with gr.Blocks() as demo:
lang = gr.Dropdown(value="python", choices=gr.Code.languages)
with gr.Row():
code_in = gr.Code(language="python", label="Input")
code_in = gr.Code(
language="python",
label="Input",
value='def all_odd_elements(sequence):\n """Returns every odd element of the sequence."""',
)
code_out = gr.Code(label="Ouput")
btn = gr.Button("Run")
btn_two = gr.Button("Load File")

View File

@ -157,4 +157,4 @@ Nothing
## Variable
Nothing
Nothing

View File

@ -90,22 +90,28 @@
let config: Config;
let loading_text: string = "Loading...";
function mount_custom_css(target: HTMLElement, css_string: string | null) {
async function mount_custom_css(
target: HTMLElement,
css_string: string | null
) {
if (css_string) {
let style = document.createElement("style");
style.innerHTML = css_string;
target.appendChild(style);
}
mount_css(config.root + "/theme.css", document.head);
await mount_css(config.root + "/theme.css", document.head);
if (!config.stylesheets) return;
for (let stylesheet of config.stylesheets) {
let absolute_link =
stylesheet.startsWith("http:") || stylesheet.startsWith("https:");
mount_css(
absolute_link ? stylesheet : config.root + "/" + stylesheet,
document.head
);
}
await Promise.all(
config.stylesheets.map((stylesheet) => {
let absolute_link =
stylesheet.startsWith("http:") || stylesheet.startsWith("https:");
return mount_css(
absolute_link ? stylesheet : config.root + "/" + stylesheet,
document.head
);
})
);
}
async function reload_check(root: string) {
@ -178,6 +184,7 @@
};
let app: Awaited<ReturnType<typeof client>>;
let css_ready = false;
function handle_status(_status: SpaceStatus) {
status = _status;
}
@ -201,7 +208,8 @@
detail: "RUNNING"
};
mount_custom_css(wrapper, config.css);
await mount_custom_css(wrapper, config.css);
css_ready = true;
window.__is_colab__ = config.is_colab;
if (config.dev_mode) {
@ -314,7 +322,7 @@
is_space={config.is_space}
{app_mode}
/>
{:else if config && Blocks}
{:else if config && Blocks && css_ready}
<Blocks
{app}
{...config}

View File

@ -1,4 +1,5 @@
import { test, expect, Page } from "@playwright/test";
import { mock_theme, wait_for_page } from "./utils";
function mock_demo(page: Page, demo: string) {
return page.route("**/config", (route) => {
@ -28,7 +29,8 @@ function mock_api(page: Page, body: Array<unknown>) {
test("renders the correct elements", async ({ page }) => {
await mock_demo(page, "blocks_inputs");
await mock_api(page, [["hi dawood"]]);
await page.goto("http://localhost:9876");
await mock_theme(page);
await wait_for_page(page);
const textboxes = await page.getByLabel("Input");

View File

@ -1,4 +1,5 @@
import { test, expect, Page } from "@playwright/test";
import { mock_theme, wait_for_page } from "./utils";
function mock_demo(page: Page, demo: string) {
return page.route("**/config", (route) => {
@ -28,7 +29,8 @@ function mock_api(page: Page, body: Array<unknown>) {
test("renders the correct elements", async ({ page }) => {
await mock_demo(page, "blocks_kinematics");
await mock_api(page, [[25, 45]]);
await page.goto("http://localhost:9876");
await mock_theme(page);
await wait_for_page(page);
await Promise.all([
page.click("button:has-text('Run')"),

View File

@ -1,4 +1,5 @@
import { test, expect, Page } from "@playwright/test";
import { mock_theme, wait_for_page } from "./utils";
function mock_demo(page: Page, demo: string) {
return page.route("**/config", (route) => {
@ -28,7 +29,8 @@ function mock_api(page: Page, body: Array<unknown>) {
test("renders the correct elements", async ({ page }) => {
await mock_demo(page, "blocks_page_load");
await mock_api(page, [["Welcome! This page has loaded for Frank"]]);
await page.goto("http://localhost:9876");
await mock_theme(page);
await wait_for_page(page);
const textbox = await page.getByLabel("Name");

View File

@ -1,4 +1,5 @@
import { test, expect, Page } from "@playwright/test";
import { mock_theme, wait_for_page } from "./utils";
function mock_demo(page: Page, demo: string) {
return page.route("**/config", (route) => {
@ -27,7 +28,8 @@ function mock_api(page: Page, body: Array<unknown>) {
test("renders the correct elements", async ({ page }) => {
await mock_demo(page, "blocks_xray");
await page.goto("http://localhost:9876");
await mock_theme(page);
await wait_for_page(page);
const description = await page.getByTestId("markdown");
await expect(description).toContainText("Detect Disease From Scan");
@ -56,7 +58,8 @@ test("can run an api request and display the data", async ({ page }) => {
]
]);
await page.goto("http://localhost:9876");
await mock_theme(page);
await wait_for_page(page);
await page.getByLabel("Covid").check();
await page.getByLabel("Lung Cancer").check();

View File

@ -1,4 +1,5 @@
import { test, expect, Page } from "@playwright/test";
import { mock_theme, wait_for_page } from "./utils";
function mock_demo(page: Page, demo: string) {
return page.route("**/config", (route) => {
@ -28,7 +29,8 @@ function mock_api(page: Page, body: Array<unknown>) {
test("a component acts as both input and output", async ({ page }) => {
await mock_demo(page, "input_output");
await mock_api(page, [["tset"]]);
await page.goto("http://localhost:9876");
await mock_theme(page);
await wait_for_page(page);
const textbox = await page.getByLabel("Input-Output");

View File

@ -1,5 +1,6 @@
import { test, expect, Page } from "@playwright/test";
import { BASE64_IMAGE, BASE64_AUDIO } from "./media_data";
import { mock_theme, wait_for_page } from "./utils";
function mock_demo(page: Page, demo: string) {
return page.route("**/config", (route) => {
@ -28,7 +29,8 @@ function mock_api(page: Page, body: Array<unknown>) {
test("test inputs", async ({ page }) => {
await mock_demo(page, "kitchen_sink");
await page.goto("http://localhost:9876");
await mock_theme(page);
await wait_for_page(page);
const textbox = await page.getByLabel("Textbox").nth(0);
await expect(textbox).toHaveValue("Lorem ipsum");
@ -209,7 +211,8 @@ test("test outputs", async ({ page }) => {
]
]);
await page.goto("http://localhost:9876");
await mock_theme(page);
await wait_for_page(page);
const submit_button = await page.locator("button", { hasText: /Submit/ });

View File

@ -1,5 +1,6 @@
import { test, expect, Page } from "@playwright/test";
import { BASE64_PLOT_IMG } from "./media_data";
import { mock_theme, wait_for_page } from "./utils";
function mock_demo(page: Page, demo: string) {
return page.route("**/config", (route) => {
@ -12,17 +13,6 @@ function mock_demo(page: Page, demo: string) {
});
}
function mock_theme(page: Page) {
return page.route("**/theme.css", (route) => {
return route.fulfill({
headers: {
"Access-Control-Allow-Origin": "*"
},
path: `./test/mocks/theme.css`
});
});
}
function mock_api(page: Page, body: Array<unknown>) {
return page.route("**/run/predict", (route) => {
const id = JSON.parse(route.request().postData()!).fn_index;
@ -41,7 +31,7 @@ test("matplotlib", async ({ page }) => {
await mock_demo(page, "outbreak_forecast");
await mock_api(page, [[{ type: "matplotlib", plot: BASE64_PLOT_IMG }]]);
await mock_theme(page);
await page.goto("http://localhost:9876");
await wait_for_page(page);
await page.getByLabel("Plot Type").click();
await page.getByRole("button", { name: "Matplotlib" }).click();

View File

@ -1,4 +1,5 @@
import { test, expect, Page, Locator } from "@playwright/test";
import { mock_theme, wait_for_page } from "./utils";
//taken from: https://github.com/microsoft/playwright/issues/20032
async function changeSlider(
@ -59,8 +60,8 @@ function mock_api(page: Page) {
test("slider release", async ({ page }) => {
await mock_demo(page, "slider_release");
await mock_api(page);
await page.goto("http://localhost:9876");
await mock_theme(page);
await wait_for_page(page);
const slider = page.getByLabel("Slider");
await changeSlider(page, slider, slider, 0.7);

View File

@ -0,0 +1,17 @@
import type { Page } from "@playwright/test";
export function mock_theme(page: Page) {
return page.route("**/theme.css", (route) => {
return route.fulfill({
headers: {
"Access-Control-Allow-Origin": "*"
},
path: `./test/mocks/theme.css`
});
});
}
export async function wait_for_page(page: Page) {
await page.goto("http://localhost:9876");
await page.waitForResponse("**/theme.css");
}