FN-6985: pack embedded automations panes
Keep the embedded Automations pane list and detail content top-packed instead of spreading vertically. - Add grid row and alignment rules so narrow embedded Automations content stays content-sized.\n- Cover populated embedded list/detail rendering and CSS contracts for mobile and wide layouts.\n- Add a patch changeset for the published Fusion package.\n\nFiles changed:\n .changeset/fn-6985-automations-spacing.md | 7 +++\n packages/dashboard/app/components/ScriptsModal.css | 14 +++++-\n .../__tests__/ScheduledTasksModal.test.tsx | 52 ++++++++++++++++++++++\n 3 files changed, 72 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-6985 Fusion-Task-Lineage: ba2b117a-4dc5-4bc9-9ecb-7bc008cd79cd
This commit is contained in:
7
.changeset/fn-6985-automations-spacing.md
Normal file
7
.changeset/fn-6985-automations-spacing.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Fix excessive spacing in the embedded Automations pane.
|
||||||
|
category: fix
|
||||||
|
dev: Top-pack embedded Automations grid rows and add regression coverage for the list/detail layout.
|
||||||
@@ -1278,15 +1278,27 @@ With the header now edge-to-edge and dividerless, the first body row still needs
|
|||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Two-pane body: single column by default (narrow); two columns when the container is wide enough. */
|
/*
|
||||||
|
FNXC:Automations 2026-06-24-23:30:
|
||||||
|
The embedded single-column Automations list/detail grid must not stretch implicit rows to fill the pane; stretch pushes the selected detail card far below the list row on tall/narrow surfaces. Keep rows content-sized and top-aligned so toolbar, list, and detail stay packed while the wide breakpoint only changes the column template.
|
||||||
|
*/
|
||||||
.automations-two-pane {
|
.automations-two-pane {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
grid-auto-rows: max-content;
|
||||||
|
align-content: start;
|
||||||
|
align-items: start;
|
||||||
gap: var(--space-lg);
|
gap: var(--space-lg);
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.automations-two-pane {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.automations-single-pane {
|
.automations-single-pane {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@@ -392,6 +392,58 @@ describe("ScheduledTasksModal", () => {
|
|||||||
expect(screen.queryByRole("button", { name: "Close" })).toBeNull();
|
expect(screen.queryByRole("button", { name: "Close" })).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("packs populated list and selected detail panes in the embedded list/detail structure", async () => {
|
||||||
|
mockFetchRoutines.mockResolvedValue([
|
||||||
|
makeRoutine({ id: "routine-backup", name: "Database Backup", command: "fn backup --create" }),
|
||||||
|
makeRoutine({ id: "routine-disabled", name: "Disabled Import", command: "fn import", enabled: false }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { container } = render(
|
||||||
|
<ScheduledTasksModal onClose={onClose} addToast={addToast} presentation="embedded" />,
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole("option", { name: /database backup/i })).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
const twoPane = container.querySelector(".automations-two-pane");
|
||||||
|
const listPane = container.querySelector(".automations-list-pane");
|
||||||
|
const detailPane = container.querySelector(".automations-detail-pane");
|
||||||
|
expect(twoPane).not.toBeNull();
|
||||||
|
expect(listPane).not.toBeNull();
|
||||||
|
expect(detailPane).not.toBeNull();
|
||||||
|
expect(twoPane?.children[0]).toBe(listPane);
|
||||||
|
expect(twoPane?.children[1]).toBe(detailPane);
|
||||||
|
expect(screen.getByText("Select an automation")).toBeDefined();
|
||||||
|
expect(screen.getByText("Disabled")).toBeDefined();
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole("option", { name: /database backup/i }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(detailPane?.querySelector(".routine-card .routine-card-name")?.textContent).toBe("Database Backup");
|
||||||
|
});
|
||||||
|
expect(screen.getByText("fn backup --create")).toBeDefined();
|
||||||
|
expect(container.querySelector(".automations-single-pane")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps embedded automation grid rows top-packed while preserving wide two-pane rules", () => {
|
||||||
|
const source = readFileSync(resolve(__dirname, "../ScriptsModal.css"), "utf8");
|
||||||
|
const baseRule = source.match(/\.automations-two-pane\s*\{[^}]*\}/)?.[0] ?? "";
|
||||||
|
const containerRule = source.match(/@container \(min-width: 900px\)\s*\{\s*\.automations-two-pane\s*\{[^}]*\}/)?.[0] ?? "";
|
||||||
|
const mediaRule = source.match(/@media \(min-width: 900px\)\s*\{\s*\.automations-two-pane\s*\{[^}]*\}/)?.[0] ?? "";
|
||||||
|
const mobileRule = source.match(/@media \(max-width: 768px\)\s*\{[\s\S]*?\.automations-two-pane\s*\{[^}]*\}/)?.[0] ?? "";
|
||||||
|
|
||||||
|
expect(baseRule).toContain("grid-template-columns: 1fr;");
|
||||||
|
expect(baseRule).toContain("align-content: start;");
|
||||||
|
expect(baseRule).toContain("align-items: start;");
|
||||||
|
expect(baseRule).toContain("grid-auto-rows: max-content;");
|
||||||
|
expect(containerRule).toContain("grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);");
|
||||||
|
expect(containerRule).toContain("align-items: start;");
|
||||||
|
expect(mediaRule).toContain("grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);");
|
||||||
|
expect(mediaRule).toContain("align-items: start;");
|
||||||
|
expect(mobileRule).toContain("grid-template-columns: 1fr;");
|
||||||
|
});
|
||||||
|
|
||||||
it("does not dismiss on Escape in embedded mode", async () => {
|
it("does not dismiss on Escape in embedded mode", async () => {
|
||||||
render(<ScheduledTasksModal onClose={onClose} addToast={addToast} presentation="embedded" />);
|
render(<ScheduledTasksModal onClose={onClose} addToast={addToast} presentation="embedded" />);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user