Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions frontend/src/v2/__tests__/v2Primitives.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,25 @@ describe('D223 primitives', () => {
expect(screen.getByLabelText('Drawer example')).toBeInTheDocument();
expect(screen.getByRole('heading', { name: 'Drawer title' })).toBeInTheDocument();
});

test('the page renders the columns, empty state, tiles and popover', () => {
render(<V2PrimitivesPage />);
expect(screen.getByText('Todo')).toBeInTheDocument();
expect(screen.getByText('Doing')).toBeInTheDocument();
expect(screen.getByText('Nothing in progress')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Settings' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Add' })).toBeInTheDocument();
expect(screen.getByLabelText('Popover example')).toBeInTheDocument();
expect(screen.getByText('Popover title')).toBeInTheDocument();
});

test('the dashed Add tile keeps a visible keyboard focus ring', () => {
expect(css).toMatch(/\.v2p-tile\.add:focus-visible\s*\{[^}]*box-shadow:[^;]*--v2-accent/);
});

test('the showcase pane scrolls, so the long page is not clipped', () => {
expect(css).toMatch(/\.v2p-pane--scroll\s*\{[^}]*overflow-y:\s*auto/);
render(<V2PrimitivesPage />);
expect(screen.getByLabelText('Primitives')).toHaveClass('v2p-pane--scroll');
});
});
38 changes: 37 additions & 1 deletion frontend/src/v2/primitives/V2PrimitivesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const V2PrimitivesPage: React.FC = () => (
<div className="v2p-root" data-testid="v2p-root">
<div className="v2p-sh v2p-sh--page" style={{ height: '100vh' }}>
<div className="v2p-rail" aria-hidden="true" />
<section className="v2p-pane" aria-label="Primitives">
<section className="v2p-pane v2p-pane--scroll" aria-label="Primitives">
<header className="v2p-ph">
<h1>Primitives</h1>
<span className="meta">D223 · TASK-730</span>
Expand Down Expand Up @@ -59,6 +59,42 @@ const V2PrimitivesPage: React.FC = () => (
<div className="top1"><span>task-730</span><span className="sp" /><span>P0</span></div>
<div className="tt">Build the shared primitives from the artboard frame</div>
</div>

<div className="v2p-gtitle">columns, empty state</div>
<div className="v2p-cols">
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
<div className="v2p-col">
<div className="v2p-chd">Todo <span className="mo">1</span><span className="sp" /></div>
<div className="v2p-cbody">
<div className="v2p-tc">
<div className="top1"><span>task-731</span><span className="sp" /><span>P1</span></div>
<div className="tt">Empty state and popover from the artboard</div>
</div>
</div>
</div>
<div className="v2p-col">
<div className="v2p-chd">Doing <span className="mo">0</span><span className="sp" /></div>
<div className="v2p-cbody">
<div className="v2p-empty">
<strong>Nothing in progress</strong>
<span>A claimed task shows up here.</span>
</div>
</div>
</div>
</div>

<div className="v2p-gtitle">tiles, popover</div>
<div className="v2p-tiles">
<button type="button" className="v2p-tile">
<span className="tn">Settings</span>
</button>
<button type="button" className="v2p-tile add">
<span className="tn">Add</span>
</button>
</div>
<div className="v2p-pop" role="note" aria-label="Popover example">
<div className="v2p-pop-h">Popover title</div>
<p>Shown inline here. On the live pages it floats over its anchor.</p>
</div>
</div>

<aside className="v2p-drawer v2p-drawer--inline" aria-label="Drawer example">
Expand Down
84 changes: 84 additions & 0 deletions frontend/src/v2/primitives/primitives.css
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@
flex-direction: column;
position: relative;
}
/* The showcase is longer than the viewport, so its pane scrolls at every width. */
.v2p-pane--scroll { overflow-y: auto; }
.v2p-ph {
flex: none;
height: 64px;
Expand Down Expand Up @@ -241,6 +243,86 @@
.v2p-drawer .db { padding: 18px 20px; display: grid; gap: 14px; align-content: start; }
.v2p-drawer h2 { font: 700 19px/1.25 var(--v2-font-display); letter-spacing: -0.02em; margin: 0; }

/* ---- columns (board lanes) ---- */
.v2p-cols { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; align-items: start; }
.v2p-col {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
border-radius: var(--v2-r-12);
background: var(--v2-bg-subtle);
box-shadow: inset 0 0 0 1px var(--v2-border-soft);
overflow: hidden;
}
.v2p-chd {
flex: none;
display: flex;
align-items: center;
gap: 8px;
height: 40px;
padding: 0 12px;
font: 600 13px/1 var(--v2-font);
color: var(--v2-text-primary);
}
.v2p-chd .mo { font: 500 11.5px/1 var(--v2-font-mono); color: var(--v2-text-muted); }
.v2p-chd .sp { flex: 1; }
.v2p-cbody { padding: 0 8px 8px; display: grid; gap: 6px; align-content: start; overflow: hidden; }

/* ---- empty state (no artboard class; built from the column and card tokens) ---- */
.v2p-empty {
display: grid;
justify-items: center;
gap: 6px;
padding: 24px 16px;
border-radius: var(--v2-r-10);
box-shadow: inset 0 0 0 1px var(--v2-border-soft);
background: var(--v2-surface);
text-align: center;
font-size: 13px;
color: var(--v2-text-muted);
}
.v2p-empty strong { font-weight: 600; color: var(--v2-text-secondary); }

/* ---- popover ---- */
.v2p-pop {
position: relative;
width: 300px;
max-width: 100%;
padding: 14px;
border-radius: var(--v2-r-12);
background: var(--v2-surface);
box-shadow: var(--v2-shadow-pop);
display: grid;
gap: 10px;
}
.v2p-pop-h { display: flex; align-items: center; gap: 10px; font: 600 14px/1.2 var(--v2-font); color: var(--v2-text-primary); }
.v2p-pop p { margin: 0; font-size: 13px; line-height: 1.5; color: var(--v2-text-secondary); }

/* ---- tile (the app grid) ---- */
.v2p-tile {
all: unset;
box-sizing: border-box;
cursor: pointer;
position: relative;
display: grid;
justify-items: center;
align-content: center;
gap: 10px;
height: 128px;
border-radius: var(--v2-r-14);
background: linear-gradient(180deg, var(--v2-key-a), var(--v2-key-b));
box-shadow: 0 0 0 1px var(--v2-key-edge), var(--v2-key-inset), 0 6px 14px -10px rgba(16, 24, 40, 0.2);
transition: box-shadow 140ms, transform 120ms;
}
.v2p-tile:hover { box-shadow: 0 0 0 1px var(--v2-border), var(--v2-key-inset), 0 12px 22px -12px rgba(16, 24, 40, 0.28); }
.v2p-tile:focus-visible { box-shadow: 0 0 0 2px var(--v2-accent); }
.v2p-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.v2p-tile .tn { font: 600 13px/1.2 var(--v2-font); color: var(--v2-ink-2); }
.v2p-tile.add { background: transparent; box-shadow: none; border: 1.5px dashed var(--v2-border); color: var(--v2-text-muted); }
.v2p-tile.add:focus-visible { box-shadow: 0 0 0 2px var(--v2-accent); }
.v2p-tile.add .tn { color: var(--v2-text-muted); font-weight: 500; }

/* ---- 390 (the artboard's phone frame): the rail goes, rows stack, the drawer
becomes a full sheet, and the header wraps instead of overflowing ---- */
@media (max-width: 480px) {
Expand All @@ -252,4 +334,6 @@
.v2p-body { padding: 16px 14px; }
.v2p-lrow { grid-template-columns: minmax(0, 1fr); min-height: 48px; padding: 8px 12px; }
.v2p-drawer:not(.v2p-drawer--inline) { width: 100%; border-radius: 0; }
.v2p-cols { grid-template-columns: minmax(0, 1fr); }
.v2p-tile { height: 104px; }
}
Loading