FN-6249: theme Compound Engineering textareas
Compound Engineering plugin textareas now use dashboard theme input styling. - Apply dashboard surface, text, border, radius, font, focus, and placeholder tokens to CE free-text and guidance textareas. - Add CSS token coverage for standard, degraded fallback, guidance, focus, and placeholder textarea states. Files changed: .../src/dashboard/CompoundEngineeringView.css | 19 ++++++ .../src/dashboard/__tests__/theme-tokens.test.ts | 71 ++++++++++++++++++++++ 2 files changed, 90 insertions(+) Fusion-Task-Id: FN-6249 Fusion-Task-Lineage: d3c4dd30-1a94-4af2-a672-a2b814eb2c0d
This commit is contained in:
@@ -277,10 +277,29 @@
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
.ce-flow-text textarea,
|
||||
.ce-flow-guidance-row textarea {
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
font-family: var(--font-primary);
|
||||
outline: none;
|
||||
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
|
||||
}
|
||||
.ce-flow-text textarea {
|
||||
width: 100%;
|
||||
resize: vertical;
|
||||
}
|
||||
.ce-flow-text textarea:focus,
|
||||
.ce-flow-guidance-row textarea:focus {
|
||||
border-color: var(--todo);
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
.ce-flow-text textarea::placeholder,
|
||||
.ce-flow-guidance-row textarea::placeholder {
|
||||
color: var(--text-dim);
|
||||
}
|
||||
.ce-flow-confirm {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
|
||||
@@ -13,6 +13,31 @@ function selectorBlocks(selector: string): string[] {
|
||||
return css.match(pattern) ?? [];
|
||||
}
|
||||
|
||||
function selectorGroupBlocks(selector: string): string[] {
|
||||
const blocks: string[] = [];
|
||||
const rulePattern = /([^{}]+)\{([^}]*)\}/g;
|
||||
for (const match of css.matchAll(rulePattern)) {
|
||||
const selectors = match[1]
|
||||
.split(",")
|
||||
.map((candidate) => candidate.trim())
|
||||
.filter(Boolean);
|
||||
if (selectors.includes(selector)) {
|
||||
blocks.push(match[0]);
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
function expectTextareaThemeTokens(selector: string, surfaceName: string) {
|
||||
const blocks = selectorGroupBlocks(selector);
|
||||
expect(blocks, `expected themed textarea block for ${surfaceName} (${selector})`).not.toHaveLength(0);
|
||||
const block = blocks.join("\n");
|
||||
expect(block, `expected ${surfaceName} to set theme surface background`).toMatch(/background:\s*var\(--surface\)\s*;/);
|
||||
expect(block, `expected ${surfaceName} to set theme text color`).toMatch(/color:\s*var\(--text\)\s*;/);
|
||||
expect(block, `expected ${surfaceName} to set theme border`).toMatch(/border:\s*1px\s+solid\s+var\(--border\)\s*;/);
|
||||
expect(block, `expected ${surfaceName} not to rely on a transparent background`).not.toMatch(/background:\s*transparent\s*;/);
|
||||
}
|
||||
|
||||
describe("CompoundEngineeringView theme tokens", () => {
|
||||
it("does not use hardcoded legacy color fallbacks", () => {
|
||||
const forbiddenPatterns = [
|
||||
@@ -63,6 +88,52 @@ describe("CompoundEngineeringView theme tokens", () => {
|
||||
expect(viewBlock).toMatch(/color:\s*var\(--text\)\s*;/);
|
||||
});
|
||||
|
||||
it("themes every CE free-text textarea with dashboard input tokens", () => {
|
||||
const textareaSurfaces = [
|
||||
{
|
||||
name: 'standard question/answer textarea (data-testid="ce-flow-text-input")',
|
||||
selector: ".ce-flow-text textarea",
|
||||
},
|
||||
{
|
||||
name: 'degraded chat fallback textarea (data-testid="ce-flow-degraded-input")',
|
||||
selector: ".ce-flow-text textarea",
|
||||
},
|
||||
{
|
||||
name: 'guidance textarea (data-testid="ce-flow-guidance-input")',
|
||||
selector: ".ce-flow-guidance-row textarea",
|
||||
},
|
||||
];
|
||||
|
||||
for (const surface of textareaSurfaces) {
|
||||
expectTextareaThemeTokens(surface.selector, surface.name);
|
||||
}
|
||||
});
|
||||
|
||||
it("themes CE textarea focus and placeholder states", () => {
|
||||
const textFocusBlocks = selectorGroupBlocks(".ce-flow-text textarea:focus");
|
||||
const guidanceFocusBlocks = selectorGroupBlocks(".ce-flow-guidance-row textarea:focus");
|
||||
const textPlaceholderBlocks = selectorGroupBlocks(".ce-flow-text textarea::placeholder");
|
||||
const guidancePlaceholderBlocks = selectorGroupBlocks(".ce-flow-guidance-row textarea::placeholder");
|
||||
|
||||
for (const [selector, blocks] of [
|
||||
[".ce-flow-text textarea:focus", textFocusBlocks],
|
||||
[".ce-flow-guidance-row textarea:focus", guidanceFocusBlocks],
|
||||
] as const) {
|
||||
expect(blocks, `expected focus block for ${selector}`).not.toHaveLength(0);
|
||||
const block = blocks.join("\n");
|
||||
expect(block, `expected ${selector} to use themed focus border`).toMatch(/border-color:\s*var\(--todo\)\s*;/);
|
||||
expect(block, `expected ${selector} to use themed focus ring`).toMatch(/box-shadow:\s*var\(--focus-ring\)\s*;/);
|
||||
}
|
||||
|
||||
for (const [selector, blocks] of [
|
||||
[".ce-flow-text textarea::placeholder", textPlaceholderBlocks],
|
||||
[".ce-flow-guidance-row textarea::placeholder", guidancePlaceholderBlocks],
|
||||
] as const) {
|
||||
expect(blocks, `expected placeholder block for ${selector}`).not.toHaveLength(0);
|
||||
expect(blocks.join("\n"), `expected ${selector} to use dim text token`).toMatch(/color:\s*var\(--text-dim\)\s*;/);
|
||||
}
|
||||
});
|
||||
|
||||
it("does not use opacity to dim text selectors", () => {
|
||||
const textDimmingSelectors = [
|
||||
".ce-view-summary",
|
||||
|
||||
Reference in New Issue
Block a user