feat: add bulk download and delete flows (#7849)

* update input to have h-fit

* Update McpServerTab text size

* Update Tools Component

* Update header text sizes

* Update list component to match design

* Update home page paddings

* Update home page to use ListComponent in both views

* Delete Grid

* Update skeleton to match design

* Remove old grid reference

* Implemented different border on checkbox

* Added selected flows

* Added selected flows action buttons

* Added flow selection on list component

* Added get download flows

* Added download and delete functions

* change download flows to download one flow directly

* implement shift selection

* Fix ctrl and meta behavior on selection

* remove selected flows if they dont exist

* added control just if its not mac

* Updated deletion modal

* Fixed delete confirmation modal taking up space in grid

* Fixed data-testids and success messages

* Added bulk actions test and fixed actionsMainPage

* added max width to home page

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Lucas Oliveira 2025-05-13 15:58:02 -03:00 committed by GitHub
commit 9ee4df696e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 493 additions and 315 deletions

View file

@ -26,7 +26,7 @@ test(
});
// click on the delete button
await page.getByText("Delete").last().click();
await page.getByText("Note: This action is irreversible.").isVisible({
await page.getByText("This can't be undone.").isVisible({
timeout: 1000,
});

View file

@ -0,0 +1,128 @@
import { expect, test } from "@playwright/test";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
test(
"user should be able to select flows with different methods and perform bulk actions",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await awaitBootstrapTest(page);
// Add some flows to test with
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await adjustScreenView(page);
// Go back to main page
await page.waitForSelector('[data-testid="icon-ChevronLeft"]', {
timeout: 100000,
});
await page.getByTestId("icon-ChevronLeft").first().click();
await page.getByText("Projects").first().isVisible();
await page.getByText("New Flow", { exact: true }).click();
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Document Q&A" }).click();
await page.waitForSelector('[data-testid="icon-ChevronLeft"]', {
timeout: 100000,
});
await page.getByTestId("icon-ChevronLeft").first().click();
await page.getByText("Projects").first().isVisible();
await page.getByText("New Flow", { exact: true }).click();
await page.getByTestId("side_nav_options_all-templates").click();
await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page.waitForSelector('[data-testid="icon-ChevronLeft"]', {
timeout: 100000,
});
await page.getByTestId("icon-ChevronLeft").first().click();
await page.getByText("Projects").first().isVisible();
await page.waitForSelector('[data-testid="home-dropdown-menu"]', {
timeout: 100000,
});
await page.getByTestId("list-card").first().isVisible({ timeout: 3000 });
await page.waitForTimeout(500);
// Test shift selection
await page.keyboard.down("Shift");
await page.getByTestId("list-card").first().click();
await page.getByTestId("list-card").nth(2).click();
await page.keyboard.up("Shift");
// Verify both flows are selected
const firstCheckbox = await page.getByTestId(/^checkbox-/).first();
const secondCheckbox = await page.getByTestId(/^checkbox-/).nth(1);
const thirdCheckbox = await page.getByTestId(/^checkbox-/).nth(2);
await expect(firstCheckbox).toBeChecked();
await expect(secondCheckbox).toBeChecked();
await expect(thirdCheckbox).toBeChecked();
// Test bulk download
await page.getByTestId("home-dropdown-menu").first().click();
await page.getByTestId("btn-download-json").last().click();
await expect(page.getByText(/.*exported successfully/)).toBeVisible({
timeout: 10000,
});
// Deselect all
await page.keyboard.down("Shift");
await page.getByTestId("list-card").first().click();
await page.keyboard.up("Shift");
// Verify both flows are deselected
await expect(firstCheckbox).not.toBeChecked();
await expect(secondCheckbox).not.toBeChecked();
await expect(thirdCheckbox).not.toBeChecked();
// Test Ctrl/Cmd selection
await page.keyboard.down("ControlOrMeta");
await page.getByTestId("list-card").first().click();
await page.getByTestId("list-card").nth(2).click();
await page.keyboard.up("ControlOrMeta");
// Verify both flows are selected again
await expect(firstCheckbox).toBeChecked();
await expect(secondCheckbox).not.toBeChecked();
await expect(thirdCheckbox).toBeChecked();
const firstFlowName =
(await page
.locator("[data-testid='flow-name-div']")
.first()
.locator("span")
.textContent()) ?? "";
const secondFlowName =
(await page
.locator("[data-testid='flow-name-div']")
.nth(1)
.locator("span")
.textContent()) ?? "";
const thirdFlowName =
(await page
.locator("[data-testid='flow-name-div']")
.nth(2)
.locator("span")
.textContent()) ?? "";
// Test bulk delete
await page.getByTestId("delete-bulk-btn").first().click();
await page.getByText("This can't be undone.").isVisible({
timeout: 1000,
});
await page.getByText("Delete").last().click();
// Verify deletion success message
await expect(page.getByText("Flows deleted successfully")).toBeVisible({
timeout: 10000,
});
// Verify flows are deleted
await expect(
page.getByText(firstFlowName, { exact: true }),
).not.toBeVisible();
await expect(page.getByText(secondFlowName, { exact: true })).toBeVisible();
await expect(
page.getByText(thirdFlowName, { exact: true }),
).not.toBeVisible();
},
);