feat(FN-3631): replicate task creation across mesh nodes and fix modal scrolling
- Add mesh task replication plumbing in core store/types with node-aware task creation metadata - Expose dashboard and mesh API route coverage for replicated task creation and sync behavior - Restore independent scrolling in Git Manager changes list and prevent Scripts modal scroll regressions - Expand tests across core, dashboard routes, API, and CLI extension insights for the new behavior Fusion-Task-Id: FN-3631
This commit is contained in:
@@ -1117,7 +1117,7 @@ function ChangesPanel({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="gm-file-list">
|
<div className="gm-file-list gm-file-list-unstaged" data-testid="gm-file-list-unstaged">
|
||||||
{unstagedFiles.length === 0 ? (
|
{unstagedFiles.length === 0 ? (
|
||||||
<div className="gm-empty">No unstaged changes</div>
|
<div className="gm-empty">No unstaged changes</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -1189,7 +1189,7 @@ function ChangesPanel({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="gm-file-list">
|
<div className="gm-file-list gm-file-list-staged" data-testid="gm-file-list-staged">
|
||||||
{stagedFiles.length === 0 ? (
|
{stagedFiles.length === 0 ? (
|
||||||
<div className="gm-empty">No staged changes</div>
|
<div className="gm-empty">No staged changes</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -2058,6 +2058,7 @@
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
align-self: start;
|
align-self: start;
|
||||||
|
min-height: 0;
|
||||||
/* Cap the sticky column so its internal lists never push past the viewport
|
/* Cap the sticky column so its internal lists never push past the viewport
|
||||||
* — without this, two long lists at max-height 200px each + headers can
|
* — without this, two long lists at max-height 200px each + headers can
|
||||||
* exceed the available scroll-port height and the column visually clips. */
|
* exceed the available scroll-port height and the column visually clips. */
|
||||||
@@ -2118,6 +2119,9 @@
|
|||||||
/* ── File Section ── */
|
/* ── File Section ── */
|
||||||
|
|
||||||
.gm-file-section {
|
.gm-file-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -2149,8 +2153,11 @@
|
|||||||
/* ── File List ── */
|
/* ── File List ── */
|
||||||
|
|
||||||
.gm-file-list {
|
.gm-file-list {
|
||||||
max-height: 200px;
|
min-height: 0;
|
||||||
|
max-height: calc(var(--space-2xl) * 6 + var(--space-md));
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gm-file-item {
|
.gm-file-item {
|
||||||
|
|||||||
@@ -420,6 +420,35 @@ describe("GitManagerModal", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps separate scroll containers for unstaged and staged file lists", async () => {
|
||||||
|
(fetchFileChanges as any).mockResolvedValue([
|
||||||
|
...Array.from({ length: 30 }, (_, index) => ({
|
||||||
|
file: `src/unstaged-${index}.ts`,
|
||||||
|
status: "modified",
|
||||||
|
staged: false,
|
||||||
|
})),
|
||||||
|
{ file: "src/staged.ts", status: "added", staged: true },
|
||||||
|
]);
|
||||||
|
|
||||||
|
render(
|
||||||
|
<GitManagerModal isOpen={true} onClose={vi.fn()} tasks={mockTasks} addToast={mockAddToast} />
|
||||||
|
);
|
||||||
|
fireEvent.click(screen.getByRole("tab", { name: /changes/i }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText("Unstaged Changes (30)")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("Staged Changes (1)")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
const unstagedList = screen.getByTestId("gm-file-list-unstaged");
|
||||||
|
const stagedList = screen.getByTestId("gm-file-list-staged");
|
||||||
|
|
||||||
|
expect(unstagedList).toHaveClass("gm-file-list", "gm-file-list-unstaged");
|
||||||
|
expect(stagedList).toHaveClass("gm-file-list", "gm-file-list-staged");
|
||||||
|
expect(within(unstagedList).getAllByRole("button").length).toBeGreaterThan(10);
|
||||||
|
expect(within(stagedList).getAllByRole("button").length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
it("stages all files when Stage All is clicked", async () => {
|
it("stages all files when Stage All is clicked", async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
render(
|
render(
|
||||||
|
|||||||
@@ -193,6 +193,22 @@ describe("core modals mobile css coverage", () => {
|
|||||||
expect(fileSectionRule![0]).toContain("max-width: 100%");
|
expect(fileSectionRule![0]).toContain("max-width: 100%");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("GitManagerModal: file sections and file lists keep independent scrolling constraints", () => {
|
||||||
|
const css = loadAllAppCss();
|
||||||
|
|
||||||
|
const fileSectionRule = css.match(/\.gm-file-section\s*\{[^}]+\}/s);
|
||||||
|
expect(fileSectionRule).not.toBeNull();
|
||||||
|
expect(fileSectionRule![0]).toContain("display: flex");
|
||||||
|
expect(fileSectionRule![0]).toContain("flex-direction: column");
|
||||||
|
expect(fileSectionRule![0]).toContain("min-height: 0");
|
||||||
|
|
||||||
|
const fileListRule = css.match(/\.gm-file-list\s*\{[^}]+\}/s);
|
||||||
|
expect(fileListRule).not.toBeNull();
|
||||||
|
expect(fileListRule![0]).toContain("overflow-y: auto");
|
||||||
|
expect(fileListRule![0]).toContain("overscroll-behavior: contain");
|
||||||
|
expect(fileListRule![0]).toContain("-webkit-overflow-scrolling: touch");
|
||||||
|
});
|
||||||
|
|
||||||
it("GitManagerModal: modal uses full-screen viewport sizing on mobile (641-768px range)", () => {
|
it("GitManagerModal: modal uses full-screen viewport sizing on mobile (641-768px range)", () => {
|
||||||
const css = loadAllAppCss();
|
const css = loadAllAppCss();
|
||||||
const mobileBlock = getMainMobileBlock(css);
|
const mobileBlock = getMainMobileBlock(css);
|
||||||
|
|||||||
Reference in New Issue
Block a user