mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-19 21:55:06 +00:00
fix(web-shell): address review feedback on enhanced table controls (#8041)
This commit is contained in:
parent
877c8e524c
commit
fb9ac4a34c
3 changed files with 114 additions and 7 deletions
|
|
@ -1,5 +1,6 @@
|
|||
.tableShell {
|
||||
position: relative;
|
||||
--toolbar-height: 38px;
|
||||
max-width: 100%;
|
||||
margin: 8px 0;
|
||||
overflow: visible;
|
||||
|
|
@ -52,7 +53,7 @@
|
|||
gap: 8px;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
height: 38px;
|
||||
height: var(--toolbar-height);
|
||||
padding: 4px 10px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
border-radius: 14px 14px 0 0;
|
||||
|
|
@ -128,8 +129,6 @@
|
|||
font-size: 12px;
|
||||
}
|
||||
|
||||
.densityTrigger:focus,
|
||||
.densityTrigger:focus-visible,
|
||||
.densityTrigger[data-state='open'] {
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
|
|
@ -179,9 +178,14 @@
|
|||
outline: none;
|
||||
}
|
||||
|
||||
.scroller:focus-visible {
|
||||
outline: 2px solid var(--agent-blue-500);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.frozenColumnShadow {
|
||||
position: absolute;
|
||||
top: 38px;
|
||||
top: var(--toolbar-height);
|
||||
bottom: 0;
|
||||
z-index: 9;
|
||||
width: 14px;
|
||||
|
|
|
|||
|
|
@ -1448,6 +1448,30 @@ describe('EnhancedMarkdownTable', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('spans the detail row across the filler column', () => {
|
||||
const container = renderTable();
|
||||
|
||||
for (const column of ['Team', 'Score']) {
|
||||
act(() => {
|
||||
button(container, `Resize ${column}`).dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
bubbles: true,
|
||||
key: 'ArrowRight',
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
expect(container.querySelector('[class*="fillerColumn"]')).not.toBeNull();
|
||||
|
||||
click(button(container, 'View details for row 1'));
|
||||
|
||||
const detailCell = container.querySelector<HTMLTableCellElement>(
|
||||
'[class*="detailCell"]',
|
||||
);
|
||||
expect(detailCell).not.toBeNull();
|
||||
expect(detailCell!.colSpan).toBe(4);
|
||||
});
|
||||
|
||||
it('resizes compact auto columns from their rendered width with keyboard arrows', () => {
|
||||
const container = renderTable();
|
||||
selectValue(button(container, 'Table density'), 'compact');
|
||||
|
|
@ -1645,6 +1669,68 @@ describe('EnhancedMarkdownTable', () => {
|
|||
).toBe('280px');
|
||||
});
|
||||
|
||||
it('repositions the frozen shadow when the ResizeObserver fires', () => {
|
||||
const callbacks: ResizeObserverCallback[] = [];
|
||||
const OriginalResizeObserver = globalThis.ResizeObserver;
|
||||
class CapturingResizeObserver {
|
||||
constructor(private readonly callback: ResizeObserverCallback) {
|
||||
callbacks.push(callback);
|
||||
}
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
(globalThis as { ResizeObserver?: unknown }).ResizeObserver =
|
||||
CapturingResizeObserver;
|
||||
|
||||
try {
|
||||
const container = renderWideTable();
|
||||
const shell = container.querySelector<HTMLElement>(
|
||||
'[class*="tableShell"]',
|
||||
);
|
||||
const header = button(container, 'Sort by Team').closest('th');
|
||||
expect(shell).not.toBeNull();
|
||||
expect(header).not.toBeNull();
|
||||
Object.defineProperty(shell, 'clientLeft', {
|
||||
configurable: true,
|
||||
value: 1,
|
||||
});
|
||||
Object.defineProperty(shell, 'getBoundingClientRect', {
|
||||
configurable: true,
|
||||
value: () => ({ left: 20 }) as DOMRect,
|
||||
});
|
||||
Object.defineProperty(header, 'getBoundingClientRect', {
|
||||
configurable: true,
|
||||
value: () => ({ right: 301 }) as DOMRect,
|
||||
});
|
||||
|
||||
freezeFirstColumn(container);
|
||||
|
||||
expect(
|
||||
container.querySelector<HTMLElement>('[class*="frozenColumnShadow"]')
|
||||
?.style.left,
|
||||
).toBe('280px');
|
||||
|
||||
Object.defineProperty(header, 'getBoundingClientRect', {
|
||||
configurable: true,
|
||||
value: () => ({ right: 351 }) as DOMRect,
|
||||
});
|
||||
act(() => {
|
||||
for (const cb of callbacks) {
|
||||
cb([], {} as ResizeObserver);
|
||||
}
|
||||
});
|
||||
|
||||
expect(
|
||||
container.querySelector<HTMLElement>('[class*="frozenColumnShadow"]')
|
||||
?.style.left,
|
||||
).toBe('330px');
|
||||
} finally {
|
||||
(globalThis as { ResizeObserver?: unknown }).ResizeObserver =
|
||||
OriginalResizeObserver;
|
||||
}
|
||||
});
|
||||
|
||||
it('dismisses the first-column context menu without clearing the active column', () => {
|
||||
const container = renderWideTable();
|
||||
|
||||
|
|
@ -2057,6 +2143,24 @@ describe('EnhancedMarkdownTable', () => {
|
|||
expect(detailsButton.getAttribute('aria-expanded')).toBe('true');
|
||||
});
|
||||
|
||||
it('falls back to window.scrollBy when no scroll ancestor exists', () => {
|
||||
const container = renderTable();
|
||||
const scrollBySpy = vi
|
||||
.spyOn(window, 'scrollBy')
|
||||
.mockImplementation(() => {});
|
||||
const detailsButton = button(container, 'View details for row 3');
|
||||
const row = detailsButton.closest('tr');
|
||||
expect(row).not.toBeNull();
|
||||
vi.spyOn(row!, 'getBoundingClientRect')
|
||||
.mockReturnValueOnce({ top: 120 } as DOMRect)
|
||||
.mockReturnValueOnce({ top: 280 } as DOMRect);
|
||||
|
||||
click(detailsButton);
|
||||
|
||||
expect(scrollBySpy).toHaveBeenCalledWith(0, 160);
|
||||
expect(detailsButton.getAttribute('aria-expanded')).toBe('true');
|
||||
});
|
||||
|
||||
it('shows statistics for a numeric selection', () => {
|
||||
const container = renderTable();
|
||||
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ const NUMBER_FILTER_LABEL_KEYS: Record<NumberFilterOperator, string> = {
|
|||
|
||||
export const MAX_ENHANCED_TABLE_ROWS = 500;
|
||||
export const MAX_ENHANCED_TABLE_COLUMNS = 50;
|
||||
// Must stay in sync with --action-column-width in EnhancedMarkdownTable.module.css
|
||||
const ACTION_COLUMN_WIDTH = 40;
|
||||
const DEFAULT_COLUMN_WIDTH = 160;
|
||||
const COMPACT_COLUMN_WIDTH = 72;
|
||||
|
|
@ -2175,9 +2176,8 @@ export function EnhancedTable({
|
|||
minWidth: number,
|
||||
fixedWidth: number,
|
||||
flexibleColumnCount: number,
|
||||
containerWidth: '100cqw' | '100%',
|
||||
): string =>
|
||||
`max(${minWidth}px, calc((${containerWidth} - ${ACTION_COLUMN_WIDTH}px - ${fixedWidth}px) / ${flexibleColumnCount}))`;
|
||||
`max(${minWidth}px, calc((100cqw - ${ACTION_COLUMN_WIDTH}px - ${fixedWidth}px) / ${flexibleColumnCount}))`;
|
||||
|
||||
const columnGroupStyle = (columnIndex: number): CSSProperties => {
|
||||
const width = columnWidths[columnIndex];
|
||||
|
|
@ -2190,7 +2190,6 @@ export function EnhancedTable({
|
|||
minWidth,
|
||||
fixedVisibleColumnWidth,
|
||||
flexibleColumnCount,
|
||||
'100cqw',
|
||||
),
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue