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
5 changes: 4 additions & 1 deletion frontend/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,9 @@
"taskClaimedVerb": "claimed"
},
"claimRun": {
"label": "{{count}} claims · {{first}} to {{last}}"
"label": "{{count}} claims · {{first}} to {{last}}",
"count": "{{count}} claims",
"range": "{{first}} to {{last}}"
},
"starts": {
"label": "Ways to start",
Expand Down Expand Up @@ -764,6 +766,7 @@
"startThread": "Thread",
"replyInThread": "Reply in thread",
"replyFromExpandedThread": "Reply from expanded thread",
"groupLabel": "Thread, {{count}} {{unit}}",
"replyingInThread": "Replying in thread ·",
"last": "last",
"collapse": "Collapse",
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,9 @@
"taskClaimedVerb": "认领了"
},
"claimRun": {
"label": "{{count}}次认领 · {{first}} 至 {{last}}"
"label": "{{count}}次认领 · {{first}} 至 {{last}}",
"count": "{{count}}次认领",
"range": "{{first}} 至 {{last}}"
},
"starts": {
"label": "开始方式",
Expand Down Expand Up @@ -763,6 +765,7 @@
"startThread": "话题",
"replyInThread": "在话题中回复",
"replyFromExpandedThread": "在已展开的话题中回复",
"groupLabel": "话题,{{count}} {{unit}}",
"replyingInThread": "正在话题中回复:",
"last": "最近",
"collapse": "收起",
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/v2/V2App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ import '@fontsource/ibm-plex-mono/500.css';
import './v2.css';
// D223 shared primitives (TASK-730): the keys and segment chat and the sidebar use.
import './primitives/primitives.css';
// Chat surface CSS lifted out of v2.css (D223, S3). Imported here, after
// v2.css, so its rules still win the order ties they won inside v2.css (a
// component-level import would load before v2.css: V2Layout is imported above).
// No chat rule ties a primitive on specificity, so following primitives.css
// changes nothing.
import './chat/chat-message.css';
import './chat/chat-pane.css';
import './chat/chat-composer.css';
import './chat/chat-inspector.css';
import './chat/chat-states.css';
import { applyV2Theme, getV2Theme } from './utils/theme';

// TASK-145 — inside the shell, only the surfaces the composer path needs stay
Expand Down
18 changes: 18 additions & 0 deletions frontend/src/v2/__testkit__/v2Css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import fs from 'fs';
import path from 'path';

// The v2 stylesheet as the app loads it: v2.css, then each surface file lifted
// out of it (D223), in V2App.tsx's import order. Tests that read "the v2 CSS"
// read this, so a rule keeps its pin when its lift moves it to its own file.
export const V2_CSS_FILES = [
'v2.css',
'chat/chat-message.css',
'chat/chat-pane.css',
'chat/chat-composer.css',
'chat/chat-inspector.css',
'chat/chat-states.css',
];

export const readV2Css = (): string => V2_CSS_FILES
.map((file) => fs.readFileSync(path.join(__dirname, '..', file), 'utf8'))
.join('\n');
14 changes: 14 additions & 0 deletions frontend/src/v2/__tests__/V2ClaimRun.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,20 @@ describe('claim run summary control (TASK-692)', () => {
expect(claimRowTaskIds(container)).toEqual([]);
});

test('the bar draws the D215 summary: board glyph, faces, bold count, range, time, chevron', () => {
const { container } = renderClaims();
const toggle = screen.getByRole('button', { name: '4 claims · TASK-674 to TASK-678' });
// One claimer here, so one face; the faces, label and time are aria-hidden
// because the button's accessible name already says the run in one line.
expect(toggle.querySelectorAll('.v2-claim-run__faces .v2-avatar')).toHaveLength(1);
expect(toggle.querySelector('.v2-claim-run__label b')).toHaveTextContent('4 claims');
expect(toggle.querySelector('.v2-claim-run__label')).toHaveTextContent('4 claims · TASK-674 to TASK-678');
expect(toggle.querySelector('.v2-claim-run__time')).not.toBeNull();
expect(toggle.querySelector('.v2-claim-run__chev svg')).not.toBeNull();
act(() => toggle.click());
expect(container.querySelectorAll('.v2-claim-run__list .v2-system-action-line')).toHaveLength(4);
});

test('the expanded rows sit inside the summary group, below the toggle', () => {
const { container } = renderClaims();
act(() => screen.getByRole('button', { name: /4 claims/ }).click());
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/v2/__tests__/V2Composer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,12 @@ describe('V2Composer send button', () => {
});
});

test('there is no Send until the draft has text', () => {
// S3c (D215): Send is drawn at rest, and disabled until the draft has text.
test('Send is disabled, keeping its name, until the draft has text', () => {
renderChat(makeDetail());
expect(screen.queryByRole('button', { name: /send message/i })).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: /send message/i })).toBeDisabled();
fireEvent.change(composerInput(), { target: { value: 'hello' } });
expect(screen.getByRole('button', { name: /send message/i })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /send message/i })).toBeEnabled();
});

test('shows send failures by the composer and keeps the reply draft intact', async () => {
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/v2/__tests__/V2ComposerMenu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ const renderComposer = (overrides = {}) => {
describe('V2Composer (direction C)', () => {
beforeAll(async () => { await i18nReady; await act(async () => { await i18n.changeLanguage('en'); }); });

test('one row: a 24px plus, a one-line field, no identity line, and no Send until there is text', () => {
test('one row: a 24px plus, a one-line field, no identity line, and a disabled Send until there is text', () => {
const { container } = renderComposer();
expect(container.querySelector('.v2-composer__row')).toBeTruthy();
expect(screen.getByRole('textbox')).toHaveAttribute('rows', '1');
expect(screen.queryByText(/posts as/)).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Send message' })).not.toBeInTheDocument();
// S3c (D215): Send is drawn at rest and disabled with an empty draft.
expect(screen.getByRole('button', { name: 'Send message' })).toBeDisabled();
expect(screen.getByRole('button', { name: 'Add' })).toHaveAttribute('aria-haspopup', 'menu');
});

Expand Down
7 changes: 5 additions & 2 deletions frontend/src/v2/__tests__/v2-copy-budget.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { fireEvent, render, screen } from '@testing-library/react';
import fs from 'fs';
import path from 'path';
import V2PageHead from '../components/V2PageHead';
import { readV2Css } from '../__testkit__/v2Css';

const source = (name: string) => fs.readFileSync(path.resolve(__dirname, '..', name), 'utf8');

Expand Down Expand Up @@ -51,8 +52,10 @@ describe('page head copy budget', () => {
});

test('phone disclosure keeps its trigger in the title row and uses the shared focus ring', () => {
const css = source('v2.css');
expect(css).toContain('.v2-page-head__identity .v2-hint--open,\n .v2-thread__title-line .v2-hint--open { display: contents; }');
const css = readV2Css();
// The chat half of this rule moved to chat/chat-pane.css (D223, S3b).
expect(css).toContain('.v2-page-head__identity .v2-hint--open { display: contents; }');
expect(css).toContain('.v2-thread__title-line .v2-hint--open { display: contents; }');
expect(css).toContain('.v2-thread__title-line { display: flex; flex-wrap: wrap; align-items: center; }');
expect(css).toContain('.v2-thread__title-line .v2-hint__content { width: 100%; flex-basis: 100%; order: 1; }');
expect(css).toMatch(/\.v2-pod-header \.v2-thread__header-row \{[^}]*padding: 10px 0;[^}]*align-items: flex-start;/);
Expand Down
Loading
Loading