From be663f4e0ccac755ad45336ffb8d832e6e5332ae Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Tue, 22 Sep 2026 14:56:14 -0700 Subject: [PATCH] improvement(ui): compose horizontal and credential fields in EMCN --- .claude/rules/emcn-components.md | 4 +- .cursor/rules/emcn-components.mdc | 4 +- .../[executionId]/resume-page-client.tsx | 3 +- .../client-credential-account-modal.tsx | 3 +- .../mcp-server-form-modal.tsx | 3 +- .../no-organization-view.tsx | 22 +++--- .../lock-settings-modal.tsx | 2 +- .../components/chip-input/chip-input.test.tsx | 2 + .../components/chip-modal/chip-modal.test.tsx | 63 +++++++++++++++- .../src/components/chip-modal/chip-modal.tsx | 74 +++++++++++++++---- .../chip-textarea/chip-textarea.tsx | 16 +++- packages/emcn/src/components/index.ts | 1 + 12 files changed, 162 insertions(+), 35 deletions(-) diff --git a/.claude/rules/emcn-components.md b/.claude/rules/emcn-components.md index 8c8b468d246..0342cadb8cb 100644 --- a/.claude/rules/emcn-components.md +++ b/.claude/rules/emcn-components.md @@ -24,10 +24,10 @@ The menu surface intentionally diverges from the pill: `dropdown-menu.tsx` items - **`Chip` / `ChipLink`** — the pill button (`} + orientation={orientation} + value='Review' + onChange={() => {}} + required + error='Enter a valid name' + hint='Hidden while invalid' + /> + ) + const input = container!.querySelector('input')! + expect(input.labels?.[0].textContent).toBe('Name*') + expect(input.getAttribute('aria-required')).toBe('true') + expect(input.getAttribute('aria-invalid')).toBe('true') + const error = document.getElementById(input.getAttribute('aria-describedby')!)! + expect(error.textContent).toBe('Enter a valid name') + expect(error.getAttribute('role')).toBe('alert') + expect(container!.textContent).not.toContain('Hidden while invalid') + expect(input.labels?.[0].contains(buttonByText('Help'))).toBe(false) + act(() => input.focus()) + expect(document.activeElement).toBe(input) + } + ) + + it('forwards monospace and height settings to a labeled textarea', () => { + mount( + {}} + mono + minHeight={120} + rows={4} + hint='Enter JSON' + /> + ) + const textarea = container!.querySelector('textarea')! + expect(textarea.labels?.[0].textContent).toBe('JSON') + expect(textarea.className).toContain('font-mono') + expect(textarea.style.minHeight).toBe('120px') + expect(textarea.rows).toBe(4) + expect(document.getElementById(textarea.getAttribute('aria-describedby')!)?.textContent).toBe( + 'Enter JSON' + ) + }) +}) + describe('ChipModalField file actions', () => { it('names each upload action with its field title', () => { mount( @@ -501,7 +556,13 @@ describe('ChipModal default actions', () => { {}} srTitle='Visible action'> {}}>Visible action - {}} /> + {}} + />