Skip to content

Commit 577d39d

Browse files
pigarevaoksclaude
andcommitted
fix(table): use DrawerHeader with close button in preview drawer
Use DrawerHeader and DrawerClose in table preview drawer header instead of custom HStack layout. Extract shared SecurityPreviewContent component with showTitle flag to avoid content duplication. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 349712e commit 577d39d

File tree

6 files changed

+63
-8
lines changed

6 files changed

+63
-8
lines changed

packages/design-system/src/components/Table/Table.stories.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
METHOD_COLORS,
2929
multiplySecurityEvents,
3030
renderSecurityPreview,
31+
renderSecurityPreviewHeader,
32+
renderSecurityPreviewWithTitle,
3133
type SecurityEvent,
3234
type SecurityHeaderEntry,
3335
securityColumnHelper,
@@ -612,6 +614,7 @@ export const MasterCellWithActions: StoryFn<typeof meta> = () => {
612614
columnSizing={columnSizing}
613615
onColumnSizingChange={setColumnSizing}
614616
previewTrigger='button'
617+
renderPreviewHeader={renderSecurityPreviewHeader}
615618
renderPreviewContent={renderSecurityPreview}
616619
/>
617620
);
@@ -655,7 +658,7 @@ export const MasterCellWithPreviewDrawer: StoryFn<typeof meta> = () => {
655658
getRowId={row => row.id}
656659
sorting={sorting}
657660
onSortingChange={setSorting}
658-
renderPreviewContent={renderSecurityPreview}
661+
renderPreviewContent={renderSecurityPreviewWithTitle}
659662
/>
660663
);
661664
};

packages/design-system/src/components/Table/TableContext/TableProvider.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export const TableProvider = <T,>(props: TableProviderProps<T>) => {
8080
overscan = TABLE_VIRTUALIZATION_OVERSCAN,
8181
onEndReached,
8282
onEndReachedThreshold,
83+
renderPreviewHeader,
8384
renderPreviewContent,
8485
previewTrigger = 'master',
8586
previewRowId: previewRowIdProp,
@@ -331,6 +332,7 @@ export const TableProvider = <T,>(props: TableProviderProps<T>) => {
331332
onEndReachedThreshold,
332333
previewRowId,
333334
setPreviewRowId,
335+
renderPreviewHeader: renderPreviewHeader as ((row: Row<T>) => ReactNode) | undefined,
334336
renderPreviewContent: renderPreviewContent as ((row: Row<T>) => ReactNode) | undefined,
335337
previewTrigger,
336338
}),
@@ -359,6 +361,7 @@ export const TableProvider = <T,>(props: TableProviderProps<T>) => {
359361
onEndReached,
360362
onEndReachedThreshold,
361363
previewRowId,
364+
renderPreviewHeader,
362365
renderPreviewContent,
363366
previewTrigger,
364367
],

packages/design-system/src/components/Table/TableContext/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export interface TableContextValue<T> {
5656
// Preview drawer
5757
previewRowId: string | null;
5858
setPreviewRowId: (id: string | null) => void;
59+
renderPreviewHeader?: (row: Row<T>) => ReactNode;
5960
renderPreviewContent?: (row: Row<T>) => ReactNode;
6061
previewTrigger: 'master' | 'button';
6162
}

packages/design-system/src/components/Table/TablePreviewDrawer.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,28 @@ import { Drawer, DrawerBody, DrawerContent, DrawerHeader } from '../Drawer';
44
import { useTableContext } from './TableContext';
55

66
export const TablePreviewDrawer: FC = () => {
7-
const { table, previewRowId, setPreviewRowId, renderPreviewContent } = useTableContext();
7+
const {
8+
table,
9+
previewRowId,
10+
setPreviewRowId,
11+
renderPreviewHeader,
12+
renderPreviewContent,
13+
previewTrigger,
14+
} = useTableContext();
815

916
const row = previewRowId ? table.getRowModel().rowsById[previewRowId] : undefined;
17+
const header =
18+
row && renderPreviewHeader && previewTrigger === 'button'
19+
? renderPreviewHeader(row)
20+
: undefined;
1021
const preview = row && renderPreviewContent ? renderPreviewContent(row) : undefined;
1122

1223
// Keep the last valid preview so drawer content doesn't flash empty during close animation
24+
const lastHeaderRef = useRef<ReactNode>(null);
1325
const lastPreviewRef = useRef<ReactNode>(null);
26+
if (header) lastHeaderRef.current = header;
1427
if (preview) lastPreviewRef.current = preview;
28+
const displayHeader = header ?? lastHeaderRef.current;
1529
const displayPreview = preview ?? lastPreviewRef.current;
1630

1731
if (!renderPreviewContent) return null;
@@ -28,9 +42,11 @@ export const TablePreviewDrawer: FC = () => {
2842
width={960}
2943
>
3044
<DrawerContent>
31-
<DrawerHeader>
32-
<span />
33-
</DrawerHeader>
45+
{displayHeader ?? (
46+
<DrawerHeader>
47+
<span />
48+
</DrawerHeader>
49+
)}
3450
<DrawerBody>{displayPreview}</DrawerBody>
3551
</DrawerContent>
3652
</Drawer>

packages/design-system/src/components/Table/mocks.tsx

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { abbreviateNumber } from '../../utils/abbreviateNumber';
44
import { Badge } from '../Badge';
55
import { InlineCodeSnippet } from '../CodeSnippet';
66
import type { CountryCode } from '../Country';
7+
import { DrawerHeader } from '../Drawer';
78
import {
89
DropdownMenu,
910
DropdownMenuContent,
@@ -611,12 +612,29 @@ export const createLargeGroupedData = (
611612
// Large flat data — for a Virtualized story
612613
// ---------------------------------------------------------------------------
613614

614-
/** Shared preview drawer content for security events */
615-
export const renderSecurityPreview = (row: { original: SecurityEvent }) => (
616-
<VStack gap={16}>
615+
/** Shared preview drawer header for security events */
616+
export const renderSecurityPreviewHeader = (row: { original: SecurityEvent }) => (
617+
<DrawerHeader>
617618
<Text size='lg' weight='medium'>
618619
{row.original.objectName}
619620
</Text>
621+
</DrawerHeader>
622+
);
623+
624+
/** Preview drawer content for security events */
625+
const SecurityPreviewContent = ({
626+
row,
627+
showTitle,
628+
}: {
629+
row: { original: SecurityEvent };
630+
showTitle?: boolean;
631+
}) => (
632+
<VStack gap={16}>
633+
{showTitle && (
634+
<Text size='lg' weight='medium'>
635+
{row.original.objectName}
636+
</Text>
637+
)}
620638
<HStack gap={8}>
621639
<Badge
622640
variant='dotted'
@@ -645,6 +663,18 @@ export const renderSecurityPreview = (row: { original: SecurityEvent }) => (
645663
</VStack>
646664
);
647665

666+
SecurityPreviewContent.displayName = 'SecurityPreviewContent';
667+
668+
/** Preview content for use with header (objectName shown in header) */
669+
export const renderSecurityPreview = (row: { original: SecurityEvent }) => (
670+
<SecurityPreviewContent row={row} />
671+
);
672+
673+
/** Preview content for use without header (includes objectName) */
674+
export const renderSecurityPreviewWithTitle = (row: { original: SecurityEvent }) => (
675+
<SecurityPreviewContent row={row} showTitle />
676+
);
677+
648678
/** Duplicate securityEvents N times with unique IDs */
649679
export const multiplySecurityEvents = (times = 4): SecurityEvent[] =>
650680
Array.from({ length: times }, (_, batch) =>

packages/design-system/src/components/Table/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ export interface TableProps<T> extends TestableProps {
216216
onEndReachedThreshold?: number;
217217

218218
// --- Preview drawer ---
219+
/** Render preview drawer header for a row. Rendered inside DrawerHeader. */
220+
renderPreviewHeader?: (row: TableRow<T>) => ReactNode;
219221
/** Render preview drawer content for a row. */
220222
renderPreviewContent?: (row: TableRow<T>) => ReactNode;
221223
/** How the preview drawer is triggered:

0 commit comments

Comments
 (0)