Skip to content

Commit bb3539d

Browse files
Update Drawers to do a proper focus trap (#1290)
Improves user reported issues that clicking outside the modal would click elements unexpectedly, also improves accessibility (keyboard focus trap & esc to exit) Fixes HDX-2642
1 parent ff86d40 commit bb3539d

16 files changed

+137
-119
lines changed

.changeset/happy-spies-tickle.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/app": patch
3+
---
4+
5+
improve drawer a11y

packages/app/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@
8484
"react-hotkeys-hook": "^4.3.7",
8585
"react-json-tree": "^0.17.0",
8686
"react-markdown": "^8.0.4",
87-
"react-modern-drawer": "^1.2.0",
8887
"react-papaparse": "^4.4.0",
8988
"react-query": "^3.39.3",
9089
"react-select": "^5.7.0",

packages/app/src/KubernetesDashboardPage.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ import { parseTimeQuery, useTimeQuery } from './timeQuery';
5151
import { KubePhase } from './types';
5252
import { formatNumber, formatUptime } from './utils';
5353

54-
import 'react-modern-drawer/dist/index.css';
55-
5654
const makeId = () => Math.floor(100000000 * Math.random()).toString(36);
5755

5856
const getKubePhaseNumber = (phase: string) => {

packages/app/src/NamespaceDetailsSidePanel.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as React from 'react';
22
import Link from 'next/link';
3-
import Drawer from 'react-modern-drawer';
43
import { StringParam, useQueryParam, withDefault } from 'use-query-params';
54
import { tcFromSource } from '@hyperdx/common-utils/dist/metadata';
65
import { TSource } from '@hyperdx/common-utils/dist/types';
@@ -9,6 +8,7 @@ import {
98
Badge,
109
Box,
1110
Card,
11+
Drawer,
1212
Flex,
1313
Grid,
1414
ScrollArea,
@@ -334,14 +334,17 @@ export default function NamespaceDetailsSidePanel({
334334

335335
return (
336336
<Drawer
337-
enableOverlay
338-
overlayOpacity={0.1}
339-
duration={0}
340-
open={!!namespaceName}
337+
opened={!!namespaceName}
341338
onClose={handleClose}
342-
direction="right"
343-
size={'80vw'}
339+
position="right"
340+
size="80vw"
341+
withCloseButton={false}
344342
zIndex={drawerZIndex}
343+
styles={{
344+
body: {
345+
padding: 0,
346+
},
347+
}}
345348
>
346349
<ZIndexContext.Provider value={drawerZIndex}>
347350
<div className={styles.panel}>

packages/app/src/NodeDetailsSidePanel.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as React from 'react';
22
import Link from 'next/link';
3-
import Drawer from 'react-modern-drawer';
43
import { StringParam, useQueryParam, withDefault } from 'use-query-params';
54
import { tcFromSource } from '@hyperdx/common-utils/dist/metadata';
65
import {
@@ -12,6 +11,7 @@ import {
1211
Badge,
1312
Box,
1413
Card,
14+
Drawer,
1515
Flex,
1616
Grid,
1717
ScrollArea,
@@ -350,14 +350,17 @@ export default function NodeDetailsSidePanel({
350350

351351
return (
352352
<Drawer
353-
enableOverlay
354-
overlayOpacity={0.1}
355-
duration={0}
356-
open={!!nodeName}
353+
opened={!!nodeName}
357354
onClose={handleClose}
358-
direction="right"
359-
size={'80vw'}
355+
position="right"
356+
size="80vw"
357+
withCloseButton={false}
360358
zIndex={drawerZIndex}
359+
styles={{
360+
body: {
361+
padding: 0,
362+
},
363+
}}
361364
>
362365
<ZIndexContext.Provider value={drawerZIndex}>
363366
<div className={styles.panel} data-testid="k8s-node-details-panel">

packages/app/src/PodDetailsSidePanel.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import * as React from 'react';
22
import Link from 'next/link';
3-
import Drawer from 'react-modern-drawer';
43
import { StringParam, useQueryParam, withDefault } from 'use-query-params';
54
import { tcFromSource } from '@hyperdx/common-utils/dist/metadata';
65
import { TSource } from '@hyperdx/common-utils/dist/types';
76
import {
87
Anchor,
98
Box,
109
Card,
10+
Drawer,
1111
Flex,
1212
Grid,
1313
ScrollArea,
@@ -342,14 +342,18 @@ export default function PodDetailsSidePanel({
342342

343343
return (
344344
<Drawer
345-
enableOverlay={rowId == null}
346-
overlayOpacity={0.1}
347-
duration={0}
348-
open={!!podName}
345+
opened={!!podName}
349346
onClose={handleClose}
350-
direction="right"
347+
position="right"
351348
size={isNested ? '70vw' : '80vw'}
349+
withCloseButton={false}
352350
zIndex={drawerZIndex}
351+
styles={{
352+
body: {
353+
padding: 0,
354+
height: '100vh',
355+
},
356+
}}
353357
>
354358
<ZIndexContext.Provider value={drawerZIndex}>
355359
<div className={styles.panel} data-testid="k8s-pod-details-panel">

packages/app/src/SessionSidePanel.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,20 @@ import { useState } from 'react';
22
import { Button } from 'react-bootstrap';
33
import CopyToClipboard from 'react-copy-to-clipboard';
44
import { useHotkeys } from 'react-hotkeys-hook';
5-
import Drawer from 'react-modern-drawer';
65
import {
76
DateRange,
87
SearchCondition,
98
SearchConditionLanguage,
109
TSource,
1110
} from '@hyperdx/common-utils/dist/types';
11+
import { Drawer } from '@mantine/core';
1212
import { notifications } from '@mantine/notifications';
1313

1414
import { Session } from './sessions';
1515
import SessionSubpanel from './SessionSubpanel';
1616
import { formatDistanceToNowStrictShort } from './utils';
1717
import { ZIndexContext } from './zIndex';
1818

19-
import 'react-modern-drawer/dist/index.css';
20-
2119
export default function SessionSidePanel({
2220
traceSource,
2321
sessionSource,
@@ -74,20 +72,24 @@ export default function SessionSidePanel({
7472

7573
return (
7674
<Drawer
77-
customIdSuffix={`session-side-panel-${sessionId}`}
78-
duration={0}
79-
overlayOpacity={0.5}
80-
open={sessionId != null}
75+
opened={sessionId != null}
8176
onClose={() => {
8277
if (!subDrawerOpen) {
8378
onClose();
8479
}
8580
}}
86-
direction="right"
87-
size={'82vw'}
88-
style={{ background: '#0F1216' }}
89-
className="border-start border-dark"
81+
position="right"
82+
size="82vw"
83+
withCloseButton={false}
9084
zIndex={zIndex}
85+
styles={{
86+
body: {
87+
padding: 0,
88+
background: '#0F1216',
89+
height: '100vh',
90+
},
91+
}}
92+
className="border-start border-dark"
9193
>
9294
<ZIndexContext.Provider value={zIndex}>
9395
<div className="d-flex flex-column h-100">

packages/app/src/components/DBRowSidePanel.tsx

Lines changed: 53 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@ import { isString } from 'lodash';
1212
import { parseAsStringEnum, useQueryState } from 'nuqs';
1313
import { ErrorBoundary } from 'react-error-boundary';
1414
import { useHotkeys } from 'react-hotkeys-hook';
15-
import Drawer from 'react-modern-drawer';
1615
import { TSource } from '@hyperdx/common-utils/dist/types';
1716
import { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
18-
import { Box, OptionalPortal, Stack } from '@mantine/core';
17+
import { Box, Drawer, Stack } from '@mantine/core';
1918
import { useClickOutside } from '@mantine/hooks';
2019

2120
import DBRowSidePanelHeader, {
@@ -36,7 +35,6 @@ import { RowOverviewPanel } from './DBRowOverviewPanel';
3635
import { DBSessionPanel, useSessionId } from './DBSessionPanel';
3736
import DBTracePanel from './DBTracePanel';
3837

39-
import 'react-modern-drawer/dist/index.css';
4038
import styles from '@/../styles/LogSidePanel.module.scss';
4139

4240
export type RowSidePanelContextProps = {
@@ -504,52 +502,57 @@ export default function DBRowSidePanelErrorBoundary({
504502
}, ['mouseup', 'touchend']);
505503

506504
return (
507-
<OptionalPortal withinPortal={!isNestedPanel}>
508-
<Drawer
509-
data-testid="row-side-panel"
510-
customIdSuffix={`log-side-panel-${rowId}`}
511-
duration={300}
512-
open={rowId != null}
513-
onClose={() => {
514-
if (!subDrawerOpen) {
515-
_onClose();
516-
}
517-
}}
518-
direction="right"
519-
size={`${width}vw`}
520-
zIndex={drawerZIndex}
521-
enableOverlay={subDrawerOpen}
522-
>
523-
<ZIndexContext.Provider value={drawerZIndex}>
524-
<div className={styles.panel} ref={drawerRef}>
525-
<Box className={styles.panelDragBar} onMouseDown={startResize} />
526-
527-
<ErrorBoundary
528-
fallbackRender={error => (
529-
<Stack>
530-
<div className="text-danger px-2 py-1 m-2 fs-7 font-monospace bg-danger-transparent p-4">
531-
An error occurred while rendering this event.
532-
</div>
533-
534-
<div className="px-2 py-1 m-2 fs-7 font-monospace bg-dark-grey p-4">
535-
{error?.error?.message}
536-
</div>
537-
</Stack>
538-
)}
539-
>
540-
<DBRowSidePanel
541-
source={source}
542-
rowId={rowId}
543-
onClose={_onClose}
544-
isNestedPanel={isNestedPanel}
545-
breadcrumbPath={breadcrumbPath}
546-
setSubDrawerOpen={setSubDrawerOpen}
547-
onBreadcrumbClick={onBreadcrumbClick}
548-
/>
549-
</ErrorBoundary>
550-
</div>
551-
</ZIndexContext.Provider>
552-
</Drawer>
553-
</OptionalPortal>
505+
<Drawer
506+
opened={rowId != null}
507+
withCloseButton={false}
508+
withinPortal={!isNestedPanel}
509+
onClose={() => {
510+
if (!subDrawerOpen) {
511+
_onClose();
512+
}
513+
}}
514+
position="right"
515+
size={`${width}vw`}
516+
styles={{
517+
body: {
518+
padding: '0',
519+
height: '100vh',
520+
},
521+
}}
522+
zIndex={drawerZIndex}
523+
>
524+
<ZIndexContext.Provider value={drawerZIndex}>
525+
<div
526+
className={styles.panel}
527+
ref={drawerRef}
528+
data-testid="row-side-panel"
529+
>
530+
<Box className={styles.panelDragBar} onMouseDown={startResize} />
531+
<ErrorBoundary
532+
fallbackRender={error => (
533+
<Stack>
534+
<div className="text-danger px-2 py-1 m-2 fs-7 font-monospace bg-danger-transparent p-4">
535+
An error occurred while rendering this event.
536+
</div>
537+
538+
<div className="px-2 py-1 m-2 fs-7 font-monospace bg-dark-grey p-4">
539+
{error?.error?.message}
540+
</div>
541+
</Stack>
542+
)}
543+
>
544+
<DBRowSidePanel
545+
source={source}
546+
rowId={rowId}
547+
onClose={_onClose}
548+
isNestedPanel={isNestedPanel}
549+
breadcrumbPath={breadcrumbPath}
550+
setSubDrawerOpen={setSubDrawerOpen}
551+
onBreadcrumbClick={onBreadcrumbClick}
552+
/>
553+
</ErrorBoundary>
554+
</div>
555+
</ZIndexContext.Provider>
556+
</Drawer>
554557
);
555558
}

packages/app/src/components/DBRowTable.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,7 @@ export const RawLogTable = memo(
897897
<DBRowTableFieldWithPopover
898898
cellValue={cellValue}
899899
wrapLinesEnabled={wrapLinesEnabled}
900+
tableContainerRef={tableContainerRef}
900901
columnName={
901902
(cell.column.columnDef.meta as any)
902903
?.column

packages/app/src/components/DBTable/DBRowTableFieldWithPopover.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ export interface DBRowTableFieldWithPopoverProps {
1515
cellValue: unknown;
1616
wrapLinesEnabled: boolean;
1717
columnName?: string;
18+
tableContainerRef?: React.RefObject<HTMLDivElement>;
1819
isChart?: boolean;
1920
}
2021

2122
export const DBRowTableFieldWithPopover = ({
2223
children,
2324
cellValue,
25+
tableContainerRef,
2426
wrapLinesEnabled,
2527
columnName,
2628
isChart = false,
@@ -151,7 +153,7 @@ export const DBRowTableFieldWithPopover = ({
151153
position="top-start"
152154
offset={5}
153155
opened={opened}
154-
zIndex={1}
156+
portalProps={{ target: tableContainerRef?.current ?? undefined }}
155157
>
156158
<Popover.Target>
157159
<span

0 commit comments

Comments
 (0)