Files
wehub-resource-sync adf0d17497
publish / version_or_publish (push) Has been cancelled
storybook-build / changes (push) Has been cancelled
storybook-build / :storybook-build (push) Has been cancelled
Sync Gradio Skills to Hugging Face / sync-skills (push) Has been cancelled
functional / changes (push) Has been cancelled
functional / build-frontend (push) Has been cancelled
functional / functional-test-SSR=false (push) Has been cancelled
functional / functional-reload (push) Has been cancelled
js / changes (push) Has been cancelled
js / js-test (push) Has been cancelled
docs-build / changes (push) Has been cancelled
docs-build / docs-build (push) Has been cancelled
docs-build / website-build (push) Has been cancelled
functional / functional-test-SSR=true (push) Has been cancelled
hygiene / hygiene-test (push) Has been cancelled
python / changes (push) Has been cancelled
python / build (push) Has been cancelled
python / test-ubuntu-latest-flaky (push) Has been cancelled
python / test-ubuntu-latest-not-flaky (push) Has been cancelled
python / test-windows-latest-flaky (push) Has been cancelled
python / test-windows-latest-not-flaky (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:17:32 +08:00

965 lines
22 KiB
Svelte

<script module lang="ts">
// @ts-nocheck
import { defineMeta } from "@storybook/addon-svelte-csf";
import Table from "./shared/Table.svelte";
import { within } from "@testing-library/dom";
import { userEvent } from "storybook/test";
import { get } from "svelte/store";
import { format } from "svelte-i18n";
import Image from "@gradio/image";
const { Story } = defineMeta({
title: "Components/DataFrame",
component: Table,
parameters: {
test: {
dangerouslyIgnoreUnhandledErrors: true // ignore fullscreen permission error
}
},
argTypes: {
editable: {
control: [true, false],
description: "Whether the DataFrame is editable",
name: "interactive",
value: true
},
wrap: {
control: "boolean",
description: "Whether text should wrap or truncate with ellipsis",
defaultValue: false
},
column_widths: {
control: "object",
description: "Width of each column (px, %, or auto)",
defaultValue: ["200px", "200px", "200px"]
}
}
});
</script>
<Story
name="Interactive dataframe"
args={{
values: [
["Cat", 5],
["Horse", 3],
["Snake", 1]
],
headers: ["Animal", "Votes"],
label: "Animals",
col_count: [2, "dynamic"],
row_count: [3, "dynamic"]
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Interactive dataframe with label"
args={{
values: [
["Cat", 5, true],
["Horse", 3, false],
["Snake", 1, false]
],
headers: ["Animal", "Votes", "Is Pet"],
datatype: ["str", "number", "bool"],
label: "Animals",
show_label: true,
col_count: [2, "dynamic"],
row_count: [3, "dynamic"]
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Interactive dataframe no label"
args={{
values: [
["Cat", 5],
["Horse", 3],
["Snake", 1]
],
headers: ["Animal", "Votes"],
metadata: null,
label: "Animals",
show_label: false,
col_count: [2, "dynamic"],
row_count: [3, "dynamic"]
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Static dataframe"
args={{
values: [
["Cat", 5, true],
["Horse", 3, false],
["Snake", 1, false]
],
headers: ["Animal", "Votes", "Is Pet"],
datatype: ["str", "number", "bool"],
label: "Animals",
col_count: [3, "dynamic"],
row_count: [3, "dynamic"],
editable: false
}}
play={async ({ canvasElement }) => {
// tests that the cell is not editable
const canvas = within(canvasElement);
const cells = canvas.getAllByRole("cell");
const initial_value = cells[0].textContent;
await userEvent.click(cells[0]);
await userEvent.keyboard("new value");
const final_value = cells[0].textContent;
if (initial_value?.trim() !== final_value?.trim()) {
throw new Error("Cell content changed when it should be non-editable");
}
const inputs = canvas.queryAllByRole("textbox");
if (inputs.length > 0) {
throw new Error("Input field appeared when table should be non-editable");
}
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with different precisions"
args={{
values: [
[1.24, 1.24, 1.24],
[1.21, 1.21, 1.21]
],
headers: ["Precision=0", "Precision=1", "Precision=2"],
display_value: [
["1", "1.2", "1.24"],
["1", "1.2", "1.21"]
],
label: "Numbers",
col_count: [3, "dynamic"],
row_count: [2, "dynamic"],
editable: false
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with markdown and math"
args={{
values: [
["Linear", "$y=x$", "Has a *maximum* of 1 root"],
["Quadratic", "$y=x^2$", "Has a *maximum* of 2 roots"],
["Cubic", "$y=x^3$", "Has a *maximum* of 3 roots"]
],
headers: ["Type", "Example", "Roots"],
datatype: ["str", "markdown", "markdown"],
latex_delimiters: [{ left: "$", right: "$", display: false }],
label: "Math",
col_count: [3, "dynamic"],
row_count: [3, "dynamic"],
editable: false
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe without a label"
args={{
values: [
[800, 100, 800],
[200, 800, 700]
],
headers: ["Math", "Reading", "Writing"],
show_label: false,
col_count: [3, "dynamic"],
row_count: [2, "dynamic"],
editable: false
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with different colors"
args={{
values: [
[800, 100, 800],
[200, 800, 700]
],
headers: ["Math", "Reading", "Writing"],
styling: [
[
"background-color:teal; color: white",
"1.2",
"background-color:teal; color: white"
],
["1", "background-color:teal; color: white", "1.21"]
],
label: "Test scores",
col_count: [3, "dynamic"],
row_count: [2, "dynamic"],
editable: false
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with column widths"
args={{
values: [
[800, 100, 800],
[200, 800, 700]
],
headers: ["Narrow", "Wide", "Half"],
label: "Test scores",
col_count: [3, "dynamic"],
row_count: [2, "dynamic"],
column_widths: ["20%", "30%", "50%"],
editable: false
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with zero row count"
args={{
values: [],
headers: ["Narrow", "Wide", "Half"],
label: "Test scores",
col_count: [0, "dynamic"],
row_count: [0, "dynamic"],
editable: false
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Interactive dataframe with zero row count"
args={{
values: [],
headers: ["Narrow", "Wide", "Half"],
label: "Test scores",
col_count: [0, "dynamic"],
row_count: [0, "dynamic"],
editable: true
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with link"
args={{
values: [['<a href="https://www.google.com/">google</a>']],
headers: ["link"],
datatype: ["markdown"],
editable: false,
col_count: [1, "dynamic"],
row_count: [1, "dynamic"]
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with dialog interactions"
args={{
values: [
[800, 100, 400],
[200, 800, 700]
],
col_count: [3, "dynamic"],
row_count: [2, "dynamic"],
headers: ["Math", "Reading", "Writing"],
editable: true
}}
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);
const cell_400 = canvas.getAllByRole("cell")[5];
await userEvent.click(cell_400);
const open_dialog_btn =
await within(cell_400).findByLabelText("Open cell menu");
await userEvent.click(open_dialog_btn);
const add_row_btn = canvas.getByText("Add row above");
await userEvent.click(add_row_btn);
const new_cell = canvas.getAllByRole("cell")[9];
userEvent.click(new_cell);
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with fullscreen button and label and search"
args={{
col_count: [3, "dynamic"],
row_count: [2, "dynamic"],
headers: ["Math", "Reading", "Writing"],
values: [
[800, 100, 400],
[200, 800, 700]
],
show_fullscreen_button: true,
show_label: true,
show_copy_button: true,
show_search: "search",
label: "Test scores"
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with multiple selection interactions"
args={{
values: [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
],
col_count: [4, "dynamic"],
row_count: [4, "dynamic"],
headers: ["A", "B", "C", "D"],
editable: true
}}
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);
const cells = canvas.getAllByRole("cell");
const user = userEvent.setup();
// cmd+click to select non-contiguous cells
await user.keyboard("[MetaLeft>]");
await user.click(cells[4]);
await user.click(cells[6]);
await user.click(cells[2]);
await user.keyboard("[/MetaLeft]");
// shift+click to select a range
await user.keyboard("[ShiftLeft>]");
await user.click(cells[7]);
await user.click(cells[6]);
await user.keyboard("[/ShiftLeft]");
// clear selected cells
await user.keyboard("{Delete}");
// verify cells were cleared by clicking one
await user.click(cells[2]);
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe toolbar interactions"
args={{
col_count: [3, "dynamic"],
row_count: [2, "dynamic"],
headers: ["Math", "Reading", "Writing"],
values: [
[800, 100, 400],
[200, 800, 700]
],
show_fullscreen_button: true,
show_copy_button: true
}}
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);
const copy_button = canvas.getByRole("button", {
name: "Copy table data"
});
await userEvent.click(copy_button);
const fullscreen_button = canvas.getByRole("button", {
name: "Fullscreen"
});
await userEvent.click(fullscreen_button);
await userEvent.click(fullscreen_button);
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with row numbers"
args={{
values: [
[95, 92, 88],
[89, 90, 85],
[92, 88, 91],
[87, 85, 89],
[91, 93, 90]
],
headers: ["Model A", "Model B", "Model C"],
label: "Model Performance",
col_count: [3, "dynamic"],
row_count: [5, "dynamic"],
show_row_numbers: true,
editable: false
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with truncated text"
args={{
values: [
[
"This is a very long text that should be truncated",
"Short text",
"Another very long text that needs truncation"
],
[
"Short",
"This text is also quite long and should be truncated as well",
"Medium length text here"
],
[
"Medium text",
"Brief",
"This is the longest text in the entire table and it should definitely be truncated"
]
],
headers: ["Column A", "Column B", "Column C"],
label: "Truncated Text Example",
max_chars: 20,
col_count: [3, "dynamic"],
row_count: [3, "dynamic"]
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with multiline headers"
args={{
values: [
[95, 92, 88],
[89, 90, 85],
[92, 88, 91]
],
headers: [
"Dataset A\nAccuracy",
"Dataset B\nPrecision",
"Dataset C\nRecall"
],
label: "Model Metrics",
col_count: [3, "dynamic"],
row_count: [3, "dynamic"],
editable: false
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with row and column selection"
args={{
values: [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
],
col_count: [4, "dynamic"],
row_count: [4, "dynamic"],
headers: ["A", "B", "C", "D"],
editable: true
}}
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);
const user = userEvent.setup();
const grid = canvas.getByRole("grid");
await user.click(grid);
const cells = canvas.getAllByRole("cell");
await user.click(cells[5]); // Click cell with value 6
const row_button = await canvas.findAllByRole("button", {
name: "Select row"
})[0];
await user.click(row_button);
await user.click(cells[6]);
const col_button = await canvas.findAllByRole("button", {
name: "Select column"
})[0];
await user.click(col_button);
await user.keyboard("{Delete}");
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with lots of values"
args={{
values: [
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
],
col_count: [10, "dynamic"],
row_count: [10, "dynamic"],
max_height: 700
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with search and filter"
args={{
values: [
["Cat", 5, "Pet"],
["Horse", 3, "Farm"],
["Snake", 1, "Pet"],
["Cow", 4, "Farm"],
["Dog", 6, "Pet"]
],
headers: ["Animal", "Count", "Type"],
col_count: [3, "dynamic"],
row_count: [5, "dynamic"],
show_search: "filter",
editable: false
}}
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);
const user = userEvent.setup();
const search_input = canvas.getByPlaceholderText("Filter...");
await user.type(search_input, "Pet");
await user.click(canvas.getByText("Cat"));
await new Promise((resolve) => setTimeout(resolve, 100));
const filter_button = canvas.getByLabelText(
"Apply filter and update dataframe values"
);
await user.click(filter_button);
await new Promise((resolve) => setTimeout(resolve, 100));
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with pinned columns"
args={{
values: [
["ID", "Name", "Age", "City", "Country", "Score"],
["1", "John", "25", "New York", "USA", "95"],
["2", "Emma", "30", "London", "UK", "88"],
["3", "Luis", "28", "Madrid", "Spain", "92"],
["4", "Anna", "35", "Paris", "France", "90"],
["5", "Chen", "27", "Beijing", "China", "94"]
],
headers: ["ID", "Name", "Age", "City", "Country", "Score"],
label: "User Data",
col_count: [6, "dynamic"],
row_count: [6, "dynamic"],
pinned_columns: 2,
show_row_numbers: true,
editable: false
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with pixel and percentage column widths set"
args={{
values: [
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10]
],
headers: ["10%", "50%", "40%", "100px", "100px"],
col_count: [5, "dynamic"],
row_count: [2, "dynamic"],
column_widths: ["10%", "50%", "40%", "100px", "100px"]
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with drag selection"
args={{
values: [
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
[21, 22, 23, 24, 25]
],
headers: ["A", "B", "C", "D", "E"],
label: "Drag Selection Demo",
col_count: [5, "dynamic"],
row_count: [5, "dynamic"],
editable: true
}}
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);
const user = userEvent.setup();
await new Promise((resolve) => setTimeout(resolve, 300));
const table = canvas.getByRole("grid");
const cells = canvas.getAllByRole("cell");
const startCell = cells[6];
const startRect = startCell.getBoundingClientRect();
const startX = startRect.left + startRect.width / 2;
const startY = startRect.top + startRect.height / 2;
const endCell = cells[18];
const endRect = endCell.getBoundingClientRect();
const endX = endRect.left + endRect.width / 2;
const endY = endRect.top + endRect.height / 2;
await user.pointer({
keys: "[MouseLeft>]",
target: startCell,
coords: { clientX: startX, clientY: startY }
});
await new Promise((resolve) => setTimeout(resolve, 50));
const midX1 = startX + (endX - startX) / 3;
const midY1 = startY + (endY - startY) / 3;
await user.pointer({
target: table,
coords: { clientX: midX1, clientY: midY1 }
});
await new Promise((resolve) => setTimeout(resolve, 50));
const midX2 = startX + ((endX - startX) * 2) / 3;
const midY2 = startY + ((endY - startY) * 2) / 3;
await user.pointer({
target: table,
coords: { clientX: midX2, clientY: midY2 }
});
await new Promise((resolve) => setTimeout(resolve, 50));
await user.pointer({
target: endCell,
coords: { clientX: endX, clientY: endY }
});
await new Promise((resolve) => setTimeout(resolve, 50));
await user.pointer({
keys: "[/MouseLeft]"
});
await new Promise((resolve) => setTimeout(resolve, 500));
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Non-interactive dataframe with sorting by multiple columns"
args={{
values: [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
],
headers: ["A", "B", "C"],
col_count: [3, "dynamic"],
row_count: [3, "dynamic"],
editable: false,
sort_columns: [
{ col: 0, direction: "asc" },
{ col: 1, direction: "desc" }
],
sort_state: {
sort_columns: [
{ col: 0, direction: "asc" },
{ col: 1, direction: "desc" }
],
row_order: [0, 1, 2]
}
}}
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);
const user = userEvent.setup();
const header_1 = canvas.getAllByText("A")[1];
await userEvent.click(header_1);
const cell_menu_button = canvas.getAllByLabelText("Open cell menu")[0];
await userEvent.click(cell_menu_button);
const sort_ascending_button = canvas.getByRole("menuitem", {
name: "Sort ascending"
});
await userEvent.click(sort_ascending_button);
const header_2 = canvas.getAllByText("B")[1];
await userEvent.click(header_2);
const cell_menu_button_2 = canvas.getAllByLabelText("Open cell menu")[1];
await userEvent.click(cell_menu_button_2);
const sort_descending_button = canvas.getByRole("menuitem", {
name: "Sort descending"
});
await userEvent.click(sort_descending_button);
const header_3 = canvas.getAllByText("C")[1];
await userEvent.click(header_3);
const cell_menu_button_3 = canvas.getAllByLabelText("Open cell menu")[2];
await userEvent.click(cell_menu_button_3);
const sort_ascending_button_3 = canvas.getByRole("menuitem", {
name: "Sort ascending"
});
await userEvent.click(sort_ascending_button_3);
await userEvent.click(header_3);
await userEvent.click(cell_menu_button_3);
await userEvent.click(canvas.getByText("Clear sort"));
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with display values"
args={{
values: [
[95, 92, 88],
[89, 90, 85],
[92, 88, 91],
[87, 85, 89],
[91, 93, 90],
[82, 81, 83]
],
headers: ["Model A", "Model B", "Empty Values"],
display_value: [
["🥇 95", "92", ""],
["🥈 89", "90", ""],
["🥉 92", "88", ""],
["87", "85", ""],
["91", "93", ""],
["82", "81", ""]
],
label: "Model Performance with Medal Indicators",
col_count: [3, "dynamic"],
row_count: [6, "dynamic"],
show_row_numbers: true,
editable: false
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe with text wrapping, no max chars"
args={{
values: [
[
"This is a very long text that should wrap to multiple lines when wrap is enabled",
"Short text",
"Another very long text that demonstrates wrapping behavior in the table cells"
],
[
"Short",
"This text is also quite long and should demonstrate the wrapping behavior when enabled",
"Medium length text here"
],
[
"Medium text",
"Brief",
"When wrap is disabled, this text should be truncated with an ellipsis instead of wrapping to multiple lines"
]
],
headers: ["Column A", "Column B", "Column C"],
label: "Text Wrapping Example",
col_count: [3, "dynamic"],
row_count: [3, "dynamic"],
wrap: true,
column_widths: ["33%", "33%", "33%"]
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe text truncation and wrapping"
args={{
values: [
[
"This is a very long text that should be truncated with an ellipsis when wrap is false",
"Short text",
"Another very long text that should also be truncated with an ellipsis"
],
[
"Short",
"This text is also quite long and should be truncated with an ellipsis",
"Medium length text here"
],
[
"Medium text",
"Brief",
"When wrap is false this text should definitely be truncated with an ellipsis"
]
],
headers: ["Column A", "Column B", "Column C"],
label: "Text Truncation Example",
col_count: [3, "dynamic"],
row_count: [3, "dynamic"],
wrap: true,
max_chars: 50,
editable: false,
column_widths: ["200px", "200px", "200px"]
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>
<Story
name="Dataframe column addition test"
args={{
values: [
["Alice", 25],
["Bob", 30],
["Carol", 22]
],
headers: ["Name", "Age"],
label: "Column Addition Test",
col_count: [2, "dynamic"],
row_count: [3, "dynamic"],
editable: true
}}
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);
const user = userEvent.setup();
const header_menu_button = canvas.getAllByLabelText("Open cell menu")[0];
await user.click(header_menu_button);
const add_column_button = canvas.getByText("Add column to the right");
await user.click(add_column_button);
const new_header = canvas.getAllByText("Header 3")[0];
await user.keyboard("{Enter}");
const input = canvas.getByRole("textbox");
await user.clear(input);
await user.type(input, "New Column");
await user.keyboard("{Enter}");
}}
>
{#snippet template(args)}
<Table {...args} i18n={get(format)} />
{/snippet}
</Story>