Skip to content

Commit cfdf705

Browse files
authored
demo: A2UI label, sidebar auto-open, flex modes + e2e fixes (#448)
* feat(demo): A2UI label, sidebar auto-open, flex modes + e2e fallout fixes User-facing: - Gen UI dropdown label drops the 'v1-compatible' suffix → just 'A2UI'. - Sidebar mode auto-opens the chat panel on entry (was: 'click the launcher first'). Hint text updated for the new flow. - All three modes (embed, popup, sidebar) switch from `:host { height: 100% }` to `:host { display: block; flex: 1; min-height: 0 }` so the chat surface sits below the toolbar via flex column, not overlapping it. E2E fallout from PR #444's toolbar tighten: - test-helpers' toolbarSelect/selectToolbarOption now identifies fields via `[data-field="<label>"]` (added to demo-shell.component.html); the prior `filter({ hasText })` broke when the per-field labels were removed. - lifecycle 'new conversation clears local thread' test updated to use the sidenav's 'New chat' (the toolbar 'New conversation' was removed in #444). Asserts a fresh thread id is created server-side. - debug-devtools 'sidebar launcher remains reachable' now closes the auto-opened panel first so the launcher is the affordance under test. - keyboard-accessibility 'Escape closes ... sidebar' no longer clicks the launcher (hidden when panel is auto-open); asserts the panel is already open, then Escape closes it. - control-palette's Gen UI assertion follows the new 'A2UI' label. * fix(e2e): adapt mode-routing/debug-devtools/model-picker for auto-open + tighten PR #444 (toolbar tighten) and the auto-open sidebar landed several e2e test fallouts that surfaced on CI: - model-picker queried .demo-shell__field via hasText filter; updated to use the data-field attribute introduced for the toolbar. - mode-routing 'landmarks' uses Escape to dismiss the auto-opened sidebar panel (close-button click was hanging against the overlapping debug-devtools panel in CI). - mode-routing 'cross-mode persistence' no longer clicks the launcher before asserting the conversation in /sidebar — panel is open. - debug-devtools 'sidebar surface remains reachable' updated to verify the auto-opened panel + its close button stay visible while chat-debug is open (was: click the now-hidden launcher). - demo-shell toolbar adds flex-wrap so small viewports (480px) don't overflow horizontally — the previous overflow-x:auto would clip the chat-select popover menus, so wrapping is preferred.
1 parent 73df2ba commit cfdf705

10 files changed

Lines changed: 48 additions & 35 deletions

examples/chat/angular/e2e/control-palette.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ test('control palette: toolbar renders defaults and persists selected controls',
2323
// chat-select trigger displays the option's label as text, not the value.
2424
await expect(toolbarSelect(page, 'Model')).toHaveText(/gpt-5-mini/);
2525
await expect(toolbarSelect(page, 'Effort')).toHaveText(/minimal \(fast\)/);
26-
await expect(toolbarSelect(page, 'Gen UI')).toHaveText(/A2UI v1-compatible/);
26+
await expect(toolbarSelect(page, 'Gen UI')).toHaveText(/A2UI/);
2727
await expect(toolbarSelect(page, 'Theme')).toHaveText(/Default dark/);
2828

2929
await selectToolbarOption(page, 'Model', 'gpt-5-nano');

examples/chat/angular/e2e/debug-devtools.spec.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ test('chat-debug devtools: opens from the sidenav with accessible controls and c
2121
});
2222

2323
test.describe('chat-debug × chat-sidebar coexistence', () => {
24-
test('sidebar launcher remains reachable while chat-debug is open', async ({
24+
test('sidebar surface remains reachable while chat-debug is open', async ({
2525
page,
2626
}) => {
2727
await openDemo(page, '/sidebar');
2828
await expect(page.locator('chat-sidebar')).toBeAttached();
2929

30-
// Open chat-debug from the sidenav footer.
30+
// Sidebar mode auto-opens its panel on entry. With the panel open the
31+
// launcher is hidden by design (its close button on the panel handles
32+
// dismissal). Verify the open chat-sidebar surface stays reachable
33+
// (its close button is visible) while chat-debug is open.
3134
await openChatDevtools(page);
3235

3336
// Debug auto-picks bottom dock because <chat-sidebar> is present.
@@ -40,22 +43,14 @@ test.describe('chat-debug × chat-sidebar coexistence', () => {
4043
'bottom'
4144
);
4245

43-
// Sidebar launcher remains visible (the bottom dock did not cover it).
44-
// Click the actual <button> inside <chat-launcher-button> rather than the
45-
// wrapping div — avoids any hit-test ambiguity between the wrapper and
46-
// the higher-z-index debug panel.
47-
const sidebarLauncherButton = page.locator(
48-
'.chat-sidebar__launcher button.chat-launcher-button'
49-
);
50-
await expect(sidebarLauncherButton).toBeVisible();
51-
await sidebarLauncherButton.click();
52-
53-
// Sidebar panel slides in — the click was not intercepted by the debug
54-
// panel, which is the user-visible bug this design fixes.
46+
// The chat-sidebar panel is auto-opened on entry — verify it remains
47+
// visible (the bottom-docked debug panel did not cover or unmount it)
48+
// and the close button stays reachable.
5549
const sidebarPanel = page.locator('.chat-sidebar__panel[data-open="true"]');
5650
await expect(sidebarPanel).toBeVisible();
51+
await expect(sidebarPanel.locator('.chat-sidebar__close')).toBeVisible();
5752

58-
// Once the sidebar is open, the edge-claim attribute reflects it too.
53+
// The edge-claim attribute reflects the open sidebar.
5954
await expect(page.locator('html')).toHaveAttribute(
6055
'data-ngaf-chat-sidebar',
6156
'open'

examples/chat/angular/e2e/keyboard-accessibility.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ test('keyboard: Escape closes popup and sidebar panels', async ({ page }) => {
5252

5353
await page.goto('/sidebar');
5454
await closeChatDevtools(page);
55-
await page.locator('.chat-sidebar__launcher button.chat-launcher-button').click();
55+
// Sidebar mode auto-opens the panel on entry (the launcher would be
56+
// hidden in that state). Verify the panel is already open, then
57+
// Escape closes it.
5658
await expect(page.getByRole('complementary')).toHaveAttribute('aria-hidden', 'false');
5759
await page.keyboard.press('Escape');
5860
await expect(page.locator('.chat-sidebar__panel')).toHaveAttribute('aria-hidden', 'true');

examples/chat/angular/e2e/lifecycle.spec.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,38 @@ test('lifecycle: reload reconnects to the active conversation', async ({
2424
).toContainText(/hi/i);
2525
});
2626

27-
test('lifecycle: new conversation clears local thread and restores welcome state', async ({
27+
test('lifecycle: New chat (sidenav) starts a fresh thread and restores welcome state', async ({
2828
page,
2929
}) => {
3030
await openDemo(page, '/embed');
3131
await messageInput(page).fill('say hi briefly');
3232
await sendButton(page).click();
3333
await waitForFinalAssistant(page);
3434

35-
await page.getByRole('button', { name: 'New conversation' }).click();
35+
const threadIdBefore = await page.evaluate(() => {
36+
const raw = localStorage.getItem('ngaf-chat-demo:palette');
37+
return raw ? (JSON.parse(raw) as { threadId?: string | null }).threadId ?? null : null;
38+
});
39+
40+
// The toolbar "New conversation" button was removed; the sidenav's
41+
// "New chat" pill is now the only affordance for starting a fresh
42+
// thread. It creates a new thread server-side (rather than clearing
43+
// local state) and routes the UI back to the welcome surface.
44+
await page.getByRole('button', { name: 'New chat' }).first().click();
3645

3746
await expect(
3847
page.getByRole('heading', { name: 'How can I help?' })
3948
).toBeVisible();
4049
await expect(page.locator('chat-message')).toHaveCount(0);
41-
const threadId = await page.evaluate(() => {
50+
51+
const threadIdAfter = await page.evaluate(() => {
4252
const raw = localStorage.getItem('ngaf-chat-demo:palette');
43-
return raw
44-
? (JSON.parse(raw) as { threadId?: string | null }).threadId
45-
: undefined;
53+
return raw ? (JSON.parse(raw) as { threadId?: string | null }).threadId ?? null : null;
4654
});
47-
expect(threadId ?? null).toBeNull();
55+
// A fresh thread id was persisted, and it's different from the one we
56+
// had before clicking New chat.
57+
expect(threadIdAfter).toBeTruthy();
58+
expect(threadIdAfter).not.toBe(threadIdBefore);
4859
});
4960

5061
test('lifecycle: selecting a welcome suggestion submits and clears welcome state', async ({

examples/chat/angular/e2e/mode-routing.spec.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ test('mode routing: embed, popup, and sidebar expose the expected landmarks', as
2525
await page.goto('/sidebar');
2626
await expect(page.locator('sidebar-mode chat-sidebar')).toBeVisible();
2727
await closeChatDevtools(page);
28-
await page.locator('.chat-sidebar__launcher button.chat-launcher-button').click();
28+
// Sidebar mode auto-opens the panel on entry — no launcher click needed.
2929
const sidebar = page.getByRole('complementary');
3030
await expect(sidebar).toBeVisible();
3131
await expect(sidebar).toHaveAttribute('aria-hidden', 'false');
32-
await page.locator('.chat-sidebar__close').click();
32+
// Escape dismisses the panel (closeOnEscape default true on chat-sidebar).
33+
// Avoids click-action races against any overlapping debug/sidenav chrome.
34+
await page.keyboard.press('Escape');
3335
await expect(page).toHaveURL(/\/sidebar$/);
3436
await expect(page.locator('.chat-sidebar__panel')).toHaveAttribute('aria-hidden', 'true');
3537
});
@@ -51,7 +53,8 @@ test('cross-mode persistence: conversation follows embed, popup, and sidebar', a
5153

5254
await page.goto('/sidebar');
5355
await closeChatDevtools(page);
54-
await page.locator('.chat-sidebar__launcher button.chat-launcher-button').click();
56+
// Sidebar mode auto-opens the panel; assert the existing conversation
57+
// is visible without a launcher click.
5558
await expect(
5659
page.getByRole('complementary').locator('chat-message[data-role="assistant"]'),
5760
).toContainText(/hi/i, { timeout: 30_000 });

examples/chat/angular/src/app/modes/embed-mode.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { WelcomeSuggestionsComponent } from './welcome-suggestions.component';
2424
</chat>
2525
`,
2626
styles: [`
27-
:host { display: block; height: 100%; }
27+
:host { display: block; flex: 1; min-height: 0; }
2828
`],
2929
})
3030
export class EmbedMode {

examples/chat/angular/src/app/modes/popup-mode.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { WelcomeSuggestionsComponent } from './welcome-suggestions.component';
2929
</chat-popup>
3030
`,
3131
styles: [`
32-
:host { display: block; height: 100%; }
32+
:host { display: block; flex: 1; min-height: 0; }
3333
.popup-mode__background {
3434
display: grid;
3535
place-items: center;

examples/chat/angular/src/app/modes/sidebar-mode.component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ import { WelcomeSuggestionsComponent } from './welcome-suggestions.component';
1313
template: `
1414
<div class="sidebar-mode__background">
1515
<p class="sidebar-mode__hint">
16-
Click the launcher button (right edge) to slide in the chat panel.
16+
Use the launcher (right edge) to dismiss or re-open the chat panel.
1717
</p>
1818
</div>
1919
<chat-sidebar
2020
[agent]="agent"
2121
[views]="catalog"
2222
[modelOptions]="shell.modelOptions()"
2323
[selectedModel]="shell.model()"
24+
[open]="true"
2425
(selectedModelChange)="shell.onModelChange($event)"
2526
(replayRequested)="shell.onTimelineReplay($event)"
2627
(forkRequested)="shell.onTimelineFork($event)"
@@ -29,7 +30,7 @@ import { WelcomeSuggestionsComponent } from './welcome-suggestions.component';
2930
</chat-sidebar>
3031
`,
3132
styles: [`
32-
:host { display: block; height: 100%; }
33+
:host { display: block; flex: 1; min-height: 0; }
3334
.sidebar-mode__background {
3435
display: grid;
3536
place-items: center;

examples/chat/angular/src/app/shell/demo-shell.component.css

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
min-height: 48px;
5757
display: flex;
5858
align-items: center;
59+
flex-wrap: wrap;
60+
row-gap: 6px;
5961
gap: 10px;
6062
padding: 8px 14px;
6163
border-bottom: 1px solid var(--ngaf-chat-separator);
@@ -69,9 +71,8 @@
6971
* chat content's scroll/transform stacking contexts below. */
7072
position: relative;
7173
z-index: 50;
72-
/* overflow-x: auto would clip absolutely-positioned dropdown menus.
73-
* Skip it — the toolbar fits on reasonable viewports and the per-field
74-
* dropdowns hold the long labels via the chat-select popover. */
74+
/* No overflow-x: that would clip absolutely-positioned dropdown menus.
75+
* flex-wrap handles narrow viewports by wrapping fields to a new row. */
7576
}
7677

7778
/* Push every field after Mode to the right of the toolbar. */

examples/chat/angular/src/app/shell/demo-shell.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ export class DemoShell {
250250
]);
251251

252252
protected readonly genUiOptions = signal<readonly { value: string; label: string }[]>([
253-
{ value: 'a2ui', label: 'A2UI v1-compatible' },
253+
{ value: 'a2ui', label: 'A2UI' },
254254
{ value: 'json-render', label: 'json-render' },
255255
]);
256256

0 commit comments

Comments
 (0)