fix: layout fixes

This commit is contained in:
2026-02-26 12:09:27 +01:00
parent 121aa6a9f7
commit a3c571f7cd
8 changed files with 481 additions and 46 deletions

View File

@@ -66,7 +66,7 @@ Instead of asking the LLM to produce A2UI JSON as free text (unreliable), we add
| Tool | Purpose | A2UI Output | | Tool | Purpose | A2UI Output |
|------|---------|-------------| |------|---------|-------------|
| `render_chart` | Show bar/line/pie chart | `updateComponents` with chart component | | `render_chart` | Show bar/stacked-bar/line/pie chart | `updateComponents` with chart component |
| `render_table` | Show data table | `updateComponents` with table rows | | `render_table` | Show data table | `updateComponents` with table rows |
| `render_form` | Show input form | `updateComponents` with form fields | | `render_form` | Show input form | `updateComponents` with form fields |
| `render_card` | Show info card | `updateComponents` with card component | | `render_card` | Show info card | `updateComponents` with card component |
@@ -268,7 +268,7 @@ Individual component renderers, refactored from `AssistantPanelControls`:
1. Create `src/main/a2ui/generator.ts` — converts tool args to A2UI messages 1. Create `src/main/a2ui/generator.ts` — converts tool args to A2UI messages
2. Create `src/main/a2ui/catalog.ts` — defines our component catalog 2. Create `src/main/a2ui/catalog.ts` — defines our component catalog
3. Add UI-rendering tools to `OpenCodeManager.getToolDefinitions()`: 3. Add UI-rendering tools to `OpenCodeManager.getToolDefinitions()`:
- `render_chart({ chartType, title, series })` - `render_chart({ chartType, title, series })` — chartType includes `bar`, `stacked-bar`, `line`, `pie`
- `render_table({ title, columns, rows })` - `render_table({ title, columns, rows })`
- `render_form({ title, fields, submitAction })` - `render_form({ title, fields, submitAction })`
- `render_card({ title, body, subtitle, actions })` - `render_card({ title, body, subtitle, actions })`

View File

@@ -56,9 +56,9 @@ function createSurfaceMessages(
// ---- Tool argument interfaces ---- // ---- Tool argument interfaces ----
export interface RenderChartArgs { export interface RenderChartArgs {
chartType: 'bar' | 'line' | 'pie'; chartType: 'bar' | 'stacked-bar' | 'line' | 'pie';
title?: string; title?: string;
series: Array<{ label: string; value: number }>; series: Array<{ label: string; value: number; segments?: Array<{ label: string; value: number }> }>;
} }
export interface RenderTableArgs { export interface RenderTableArgs {

View File

@@ -323,7 +323,7 @@ Available Data Tools:
- get_media_posts: Get posts that use a specific media file. - get_media_posts: Get posts that use a specific media file.
Available UI Render Tools (use these to show rich interactive elements): Available UI Render Tools (use these to show rich interactive elements):
- render_chart: Show data as a bar, line, or pie chart. Use when presenting statistics or comparisons. - render_chart: Show data as a bar, stacked-bar, line, or pie chart. Use when presenting statistics or comparisons. Use stacked-bar when each bar has multiple segments (e.g., published vs draft posts per year).
- render_table: Show data in a structured table. Use for tabular comparisons and listings. - render_table: Show data in a structured table. Use for tabular comparisons and listings.
- render_form: Show an interactive form to collect user input (e.g., metadata edits, settings). - render_form: Show an interactive form to collect user input (e.g., metadata edits, settings).
- render_card: Show an information card with title, body, and action buttons. - render_card: Show an information card with title, body, and action buttons.

View File

@@ -924,7 +924,7 @@ export class OpenCodeManager {
input_schema: { input_schema: {
type: 'object', type: 'object',
properties: { properties: {
chartType: { type: 'string', enum: ['bar', 'line', 'pie'], description: 'The type of chart to render' }, chartType: { type: 'string', enum: ['bar', 'stacked-bar', 'line', 'pie'], description: 'The type of chart to render. Use stacked-bar when each bar has multiple segments (categories).' },
title: { type: 'string', description: 'Optional chart title' }, title: { type: 'string', description: 'Optional chart title' },
series: { series: {
type: 'array', type: 'array',
@@ -932,11 +932,23 @@ export class OpenCodeManager {
type: 'object', type: 'object',
properties: { properties: {
label: { type: 'string', description: 'Data point label' }, label: { type: 'string', description: 'Data point label' },
value: { type: 'number', description: 'Data point value' }, value: { type: 'number', description: 'Data point value (total for stacked bars)' },
segments: {
type: 'array',
items: {
type: 'object',
properties: {
label: { type: 'string', description: 'Segment category label' },
value: { type: 'number', description: 'Segment value' },
},
required: ['label', 'value'],
},
description: 'Segments for stacked-bar charts. Each segment is a category within the bar.',
},
}, },
required: ['label', 'value'], required: ['label', 'value'],
}, },
description: 'Array of data points with label and value', description: 'Array of data points with label and value. For stacked-bar charts, include segments.',
}, },
}, },
required: ['chartType', 'series'], required: ['chartType', 'series'],

View File

@@ -9,28 +9,117 @@ interface A2UIComponentProps {
renderChildren?: (children: A2UIResolvedComponent[]) => React.ReactNode; renderChildren?: (children: A2UIResolvedComponent[]) => React.ReactNode;
} }
interface SegmentEntry {
label: string;
value: number;
}
interface SeriesEntry { interface SeriesEntry {
label: string; label: string;
value: number; value: number;
segments?: SegmentEntry[];
}
const SEGMENT_COLORS = [
'var(--vscode-charts-blue, #75beff)',
'var(--vscode-charts-green, #89d185)',
'var(--vscode-charts-orange, #d18616)',
'var(--vscode-charts-red, #f14c4c)',
'var(--vscode-charts-purple, #b180d7)',
'var(--vscode-charts-yellow, #e2e210)',
];
function getSegmentColor(index: number): string {
return SEGMENT_COLORS[index % SEGMENT_COLORS.length];
}
/** Collect unique segment labels across all series entries, preserving order. */
function collectSegmentLabels(series: SeriesEntry[]): string[] {
const seen = new Set<string>();
const labels: string[] = [];
for (const entry of series) {
if (entry.segments) {
for (const seg of entry.segments) {
if (!seen.has(seg.label)) {
seen.add(seg.label);
labels.push(seg.label);
}
}
}
}
return labels;
} }
export const A2UIChart: React.FC<A2UIComponentProps> = ({ component }) => { export const A2UIChart: React.FC<A2UIComponentProps> = ({ component }) => {
const chartType = String(component.properties.chartType ?? 'bar'); const chartType = String(component.properties.chartType ?? 'bar');
const title = component.properties.title as string | undefined; const title = component.properties.title as string | undefined;
const series = (component.boundValue as SeriesEntry[]) ?? (component.properties.series as SeriesEntry[]) ?? []; const series = (component.boundValue as SeriesEntry[]) ?? (component.properties.series as SeriesEntry[]) ?? [];
const maxValue = Math.max(...series.map((entry) => entry.value), 0); const isStacked = chartType === 'stacked-bar';
const maxValue = Math.max(
...series.map((entry) => {
if (isStacked && entry.segments) {
return entry.segments.reduce((sum, s) => sum + s.value, 0);
}
return entry.value;
}),
0,
);
const segmentLabels = isStacked ? collectSegmentLabels(series) : [];
return ( return (
<div className="assistant-panel-chart"> <div className="assistant-panel-chart">
{title && <p className="assistant-panel-chart-title">{title}</p>} {title && <p className="assistant-panel-chart-title">{title}</p>}
<div className="assistant-panel-chart-type">{chartType}</div> <div className="assistant-panel-chart-type">{chartType}</div>
{series.map((entry, index) => ( {series.map((entry, index) => {
<div key={`${component.id}-series-${index}`} className="assistant-panel-chart-item"> const totalValue = isStacked && entry.segments
<span>{entry.label}</span> ? entry.segments.reduce((sum, s) => sum + s.value, 0)
<progress value={entry.value} max={maxValue || 1} /> : entry.value;
<span>{entry.value}</span>
return (
<div key={`${component.id}-series-${index}`} className="assistant-panel-chart-item">
<span className="assistant-panel-chart-label">{entry.label}</span>
<div className="assistant-panel-chart-bar-track">
{isStacked && entry.segments ? (
entry.segments.map((seg, si) => {
const segWidth = maxValue > 0 ? (seg.value / maxValue) * 100 : 0;
return (
<div
key={`${component.id}-seg-${index}-${si}`}
className="assistant-panel-chart-bar-segment"
style={{
width: `${segWidth}%`,
backgroundColor: getSegmentColor(segmentLabels.indexOf(seg.label)),
}}
title={`${seg.label}: ${seg.value}`}
/>
);
})
) : (
<div
className="assistant-panel-chart-bar-fill"
style={{ width: `${maxValue > 0 ? (entry.value / maxValue) * 100 : 0}%` }}
/>
)}
</div>
<span className="assistant-panel-chart-value">{totalValue}</span>
</div>
);
})}
{isStacked && segmentLabels.length > 0 && (
<div className="assistant-panel-chart-legend">
{segmentLabels.map((label, i) => (
<span key={label} className="assistant-panel-chart-legend-item">
<span
className="assistant-panel-chart-legend-swatch"
style={{ backgroundColor: getSegmentColor(i) }}
/>
{label}
</span>
))}
</div> </div>
))} )}
</div> </div>
); );
}; };

View File

@@ -74,7 +74,7 @@
padding-top: 10px; padding-top: 10px;
} }
.assistant-sidebar-metric { .assistant-panel-metric {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: baseline; align-items: baseline;
@@ -83,22 +83,22 @@
border-radius: 6px; border-radius: 6px;
} }
.assistant-sidebar-metric-label { .assistant-panel-metric-label {
font-size: 12px; font-size: 12px;
opacity: 0.85; opacity: 0.85;
} }
.assistant-sidebar-metric-value { .assistant-panel-metric-value {
font-size: 14px; font-size: 14px;
} }
.assistant-sidebar-table { .assistant-panel-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
} }
.assistant-sidebar-table th, .assistant-panel-table th,
.assistant-sidebar-table td { .assistant-panel-table td {
border: 1px solid var(--vscode-panel-border); border: 1px solid var(--vscode-panel-border);
padding: 6px; padding: 6px;
font-size: 12px; font-size: 12px;
@@ -112,30 +112,30 @@
white-space: pre-wrap; white-space: pre-wrap;
} }
.assistant-sidebar-widget-block { .assistant-panel-widget-block {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 6px; gap: 6px;
} }
.assistant-sidebar-widget-label { .assistant-panel-widget-label {
font-size: 12px; font-size: 12px;
opacity: 0.9; opacity: 0.9;
} }
.assistant-sidebar-widget-input { .assistant-panel-widget-input {
width: 100%; width: 100%;
padding: 8px; padding: 8px;
} }
.assistant-sidebar-checkbox { .assistant-panel-checkbox {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
font-size: 12px; font-size: 12px;
} }
.assistant-sidebar-chart { .assistant-panel-chart {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 6px; gap: 6px;
@@ -144,30 +144,99 @@
padding: 8px; padding: 8px;
} }
.assistant-sidebar-chart-title { .assistant-panel-chart-title {
margin: 0; margin: 0;
font-weight: 600; font-weight: 600;
} }
.assistant-sidebar-chart-type { .assistant-panel-chart-type {
font-size: 11px; font-size: 11px;
text-transform: uppercase; text-transform: uppercase;
opacity: 0.7; opacity: 0.7;
} }
.assistant-sidebar-chart-item { .assistant-panel-chart-item {
display: grid; display: grid;
grid-template-columns: minmax(48px, auto) 1fr auto; grid-template-columns: auto 1fr auto;
gap: 8px; gap: 8px;
align-items: center; align-items: center;
font-size: 12px; font-size: 12px;
} }
.assistant-sidebar-chart-item progress { .assistant-panel-chart-label {
width: 100%; justify-self: end;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 140px;
font-variant-numeric: tabular-nums;
} }
.assistant-sidebar-form { .assistant-panel-chart-bar-track {
height: 14px;
background: var(--vscode-input-background);
border-radius: 3px;
overflow: hidden;
display: flex;
min-width: 60px;
}
.assistant-panel-chart-bar-fill {
height: 100%;
background: var(--vscode-charts-blue, #75beff);
border-radius: 3px;
min-width: 2px;
transition: width 0.3s ease;
}
.assistant-panel-chart-bar-segment {
height: 100%;
min-width: 1px;
transition: width 0.3s ease;
}
.assistant-panel-chart-bar-segment:first-child {
border-radius: 3px 0 0 3px;
}
.assistant-panel-chart-bar-segment:last-child {
border-radius: 0 3px 3px 0;
}
.assistant-panel-chart-bar-segment:only-child {
border-radius: 3px;
}
.assistant-panel-chart-value {
text-align: right;
font-variant-numeric: tabular-nums;
white-space: nowrap;
min-width: 24px;
}
.assistant-panel-chart-legend {
display: flex;
gap: 12px;
flex-wrap: wrap;
font-size: 11px;
padding-top: 4px;
border-top: 1px solid var(--vscode-panel-border);
}
.assistant-panel-chart-legend-item {
display: flex;
align-items: center;
gap: 4px;
}
.assistant-panel-chart-legend-swatch {
width: 10px;
height: 10px;
border-radius: 2px;
flex-shrink: 0;
}
.assistant-panel-form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
@@ -176,12 +245,12 @@
padding: 8px; padding: 8px;
} }
.assistant-sidebar-form-title { .assistant-panel-form-title {
margin: 0; margin: 0;
font-weight: 600; font-weight: 600;
} }
.assistant-sidebar-card { .assistant-panel-card {
border: 1px solid var(--vscode-panel-border); border: 1px solid var(--vscode-panel-border);
border-radius: 6px; border-radius: 6px;
padding: 8px; padding: 8px;
@@ -190,57 +259,57 @@
gap: 6px; gap: 6px;
} }
.assistant-sidebar-card h4, .assistant-panel-card h4,
.assistant-sidebar-card p { .assistant-panel-card p {
margin: 0; margin: 0;
} }
.assistant-sidebar-card-subtitle { .assistant-panel-card-subtitle {
font-size: 12px; font-size: 12px;
opacity: 0.8; opacity: 0.8;
} }
.assistant-sidebar-card-actions { .assistant-panel-card-actions {
display: flex; display: flex;
gap: 6px; gap: 6px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.assistant-sidebar-image { .assistant-panel-image {
margin: 0; margin: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 6px; gap: 6px;
} }
.assistant-sidebar-image img { .assistant-panel-image img {
max-width: 100%; max-width: 100%;
border-radius: 6px; border-radius: 6px;
border: 1px solid var(--vscode-panel-border); border: 1px solid var(--vscode-panel-border);
} }
.assistant-sidebar-image figcaption { .assistant-panel-image figcaption {
font-size: 12px; font-size: 12px;
opacity: 0.85; opacity: 0.85;
} }
.assistant-sidebar-tabs { .assistant-panel-tabs {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
} }
.assistant-sidebar-tab-strip { .assistant-panel-tab-strip {
display: flex; display: flex;
gap: 6px; gap: 6px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.assistant-sidebar-tab-button.active { .assistant-panel-tab-button.active {
border-color: var(--vscode-focusBorder); border-color: var(--vscode-focusBorder);
} }
.assistant-sidebar-tab-panel { .assistant-panel-tab-panel {
border: 1px solid var(--vscode-panel-border); border: 1px solid var(--vscode-panel-border);
border-radius: 6px; border-radius: 6px;
padding: 8px; padding: 8px;

View File

@@ -82,6 +82,56 @@ describe('A2UI generator', () => {
{ label: 'Feb', value: 20 }, { label: 'Feb', value: 20 },
]); ]);
}); });
it('creates stacked-bar chart with segment data', () => {
const messages = generateChart('conv-1', {
chartType: 'stacked-bar',
title: 'Posts by Year',
series: [
{
label: '2023',
value: 30,
segments: [
{ label: 'Published', value: 20 },
{ label: 'Draft', value: 10 },
],
},
{
label: '2024',
value: 45,
segments: [
{ label: 'Published', value: 35 },
{ label: 'Draft', value: 10 },
],
},
],
});
expect(messages).toHaveLength(3);
const updateMsg = messages[1] as Extract<A2UIServerMessage, { type: 'updateComponents' }>;
expect(updateMsg.components[0].properties.chartType).toBe('stacked-bar');
const dataMsg = messages[2] as Extract<A2UIServerMessage, { type: 'updateDataModel' }>;
expect(dataMsg.value).toEqual([
{
label: '2023',
value: 30,
segments: [
{ label: 'Published', value: 20 },
{ label: 'Draft', value: 10 },
],
},
{
label: '2024',
value: 45,
segments: [
{ label: 'Published', value: 35 },
{ label: 'Draft', value: 10 },
],
},
]);
});
}); });
describe('generateTable', () => { describe('generateTable', () => {

View File

@@ -0,0 +1,215 @@
import React from 'react';
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import { A2UIChart } from '../../../src/renderer/a2ui/components/A2UIChart';
import type { A2UIResolvedComponent, A2UIClientAction } from '../../../src/main/a2ui/types';
function makeChartComponent(
overrides: Partial<A2UIResolvedComponent> = {},
series?: unknown,
): A2UIResolvedComponent {
return {
id: 'chart-1',
type: 'chart',
properties: {
chartType: 'bar',
title: 'Test Chart',
...(overrides.properties ?? {}),
},
children: [],
boundValue: series ?? [
{ label: 'Alpha', value: 10 },
{ label: 'Beta', value: 25 },
{ label: 'Gamma', value: 15 },
],
...overrides,
};
}
const noopAction = vi.fn<(action: A2UIClientAction) => void>();
describe('A2UIChart', () => {
describe('bar chart tabular layout', () => {
it('renders chart title', () => {
const comp = makeChartComponent();
const { container } = render(
<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />,
);
expect(screen.getByText('Test Chart')).toBeInTheDocument();
expect(container.querySelector('.assistant-panel-chart-title')).not.toBeNull();
});
it('renders chart type label', () => {
const comp = makeChartComponent();
render(<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />);
expect(screen.getByText('bar')).toBeInTheDocument();
});
it('renders all series labels', () => {
const comp = makeChartComponent();
render(<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />);
expect(screen.getByText('Alpha')).toBeInTheDocument();
expect(screen.getByText('Beta')).toBeInTheDocument();
expect(screen.getByText('Gamma')).toBeInTheDocument();
});
it('renders all series values', () => {
const comp = makeChartComponent();
render(<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />);
expect(screen.getByText('10')).toBeInTheDocument();
expect(screen.getByText('25')).toBeInTheDocument();
expect(screen.getByText('15')).toBeInTheDocument();
});
it('renders bar chart items in a three-column grid layout', () => {
const comp = makeChartComponent();
const { container } = render(
<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />,
);
const items = container.querySelectorAll('.assistant-panel-chart-item');
expect(items).toHaveLength(3);
// Each item should have label, bar container, and value as separate elements
for (const item of items) {
const label = item.querySelector('.assistant-panel-chart-label');
const barContainer = item.querySelector('.assistant-panel-chart-bar-track');
const value = item.querySelector('.assistant-panel-chart-value');
expect(label).not.toBeNull();
expect(barContainer).not.toBeNull();
expect(value).not.toBeNull();
}
});
it('renders bar fill with correct percentage width', () => {
const comp = makeChartComponent({}, [
{ label: 'A', value: 50 },
{ label: 'B', value: 100 },
]);
const { container } = render(
<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />,
);
const fills = container.querySelectorAll('.assistant-panel-chart-bar-fill');
expect(fills).toHaveLength(2);
// A = 50/100 = 50%, B = 100/100 = 100%
expect((fills[0] as HTMLElement).style.width).toBe('50%');
expect((fills[1] as HTMLElement).style.width).toBe('100%');
});
it('renders without title when title is not provided', () => {
const comp = makeChartComponent({ properties: { chartType: 'bar' } });
const { container } = render(
<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />,
);
expect(container.querySelector('.assistant-panel-chart-title')).toBeNull();
});
it('renders empty state when series is empty', () => {
const comp = makeChartComponent({}, []);
const { container } = render(
<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />,
);
const items = container.querySelectorAll('.assistant-panel-chart-item');
expect(items).toHaveLength(0);
});
});
describe('stacked bar chart', () => {
const stackedSeries = [
{
label: '2023',
value: 30,
segments: [
{ label: 'Published', value: 20 },
{ label: 'Draft', value: 10 },
],
},
{
label: '2024',
value: 45,
segments: [
{ label: 'Published', value: 35 },
{ label: 'Draft', value: 10 },
],
},
];
it('renders stacked bar items', () => {
const comp = makeChartComponent(
{ properties: { chartType: 'stacked-bar', title: 'Posts by Year' } },
stackedSeries,
);
const { container } = render(
<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />,
);
const items = container.querySelectorAll('.assistant-panel-chart-item');
expect(items).toHaveLength(2);
});
it('renders multiple segment fills per bar', () => {
const comp = makeChartComponent(
{ properties: { chartType: 'stacked-bar', title: 'Posts' } },
stackedSeries,
);
const { container } = render(
<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />,
);
// Each bar should have multiple fill segments
const tracks = container.querySelectorAll('.assistant-panel-chart-bar-track');
expect(tracks).toHaveLength(2);
const firstBarSegments = tracks[0].querySelectorAll('.assistant-panel-chart-bar-segment');
expect(firstBarSegments).toHaveLength(2);
});
it('renders segment widths as proportion of total across all bars', () => {
const comp = makeChartComponent(
{ properties: { chartType: 'stacked-bar' } },
stackedSeries,
);
const { container } = render(
<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />,
);
// maxValue is 45 (2024 total)
// 2023 row: Published=20/45, Draft=10/45 → total bar width = 30/45
// We need segment widths relative to the bar track via the fill percentage
const tracks = container.querySelectorAll('.assistant-panel-chart-bar-track');
expect(tracks.length).toBe(2);
});
it('renders a legend for stacked bar charts', () => {
const comp = makeChartComponent(
{ properties: { chartType: 'stacked-bar', title: 'Posts' } },
stackedSeries,
);
const { container } = render(
<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />,
);
const legend = container.querySelector('.assistant-panel-chart-legend');
expect(legend).not.toBeNull();
expect(screen.getByText('Published')).toBeInTheDocument();
expect(screen.getByText('Draft')).toBeInTheDocument();
});
it('shows total value for stacked bars', () => {
const comp = makeChartComponent(
{ properties: { chartType: 'stacked-bar' } },
stackedSeries,
);
render(<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />);
expect(screen.getByText('30')).toBeInTheDocument();
expect(screen.getByText('45')).toBeInTheDocument();
});
it('does not render legend for regular bar charts', () => {
const comp = makeChartComponent(
{ properties: { chartType: 'bar' } },
[{ label: 'A', value: 10 }],
);
const { container } = render(
<A2UIChart component={comp} surfaceId="s1" onAction={noopAction} />,
);
expect(container.querySelector('.assistant-panel-chart-legend')).toBeNull();
});
});
});