Line data Source code
1 : import * as React from 'react';
2 : import * as styles from './query_profile_overlay.module.css';
3 :
4 : import { ButtonSize, ButtonVariant, IconButton } from '../../ui/foundations/button.js';
5 : import { useFocusTrap } from '../../ui/foundations/focus.js';
6 : import { Overlay, OverlaySize } from '../../ui/foundations/overlay.js';
7 : import { ScreenFullIcon, ScreenNormalIcon, SymbolIcon } from '../../ui/foundations/symbol_icon.js';
8 :
9 1 : const CloseIcon = SymbolIcon('x_16');
10 : // The plan has popups of its own (operator details), a click into them must not close the card
11 1 : const IGNORE_OUTSIDE_CLICK = () => {};
12 :
13 : interface Props {
14 : /// The plan viewer, it fills the card
15 : children: React.ReactNode;
16 : onClose: () => void;
17 : }
18 :
19 : /// The plan of a query in a large card over the page
20 1 : export function QueryProfileOverlay(props: Props): React.ReactElement {
21 1 : const dialogRef = React.useRef<HTMLElement | null>(null);
22 1 : const [expanded, setExpanded] = React.useState(false);
23 :
24 : // Focus the dialog, not its first button
25 1 : useFocusTrap({
26 1 : containerRef: dialogRef as React.RefObject<HTMLElement>,
27 1 : initialFocusRef: dialogRef as React.RefObject<HTMLElement>,
28 1 : restoreFocusOnCleanUp: true,
29 1 : });
30 :
31 1 : return (
32 1 : <Overlay
33 1 : centered
34 1 : width={OverlaySize.XXL}
35 1 : height={OverlaySize.XL}
36 1 : preventFocusOnOpen
37 1 : onEscape={props.onClose}
38 1 : onClickOutside={IGNORE_OUTSIDE_CLICK}
39 1 : expanded={expanded}
40 : >
41 1 : <section
42 1 : ref={dialogRef}
43 1 : className={styles.dialog}
44 1 : role="dialog"
45 1 : aria-modal="true"
46 1 : tabIndex={-1}
47 1 : aria-label="Query plan"
48 1 : data-testid="query-profile-overlay"
49 : >
50 1 : <header className={styles.header}>
51 1 : <h2 className={styles.title}>Query plan</h2>
52 1 : <div className={styles.spacer} />
53 1 : <IconButton
54 1 : variant={ButtonVariant.Invisible}
55 1 : size={ButtonSize.Small}
56 1 : aria-label={expanded ? 'Restore size' : 'Expand'}
57 1 : aria-pressed={expanded}
58 1 : data-testid="query-profile-overlay-expand"
59 1 : onClick={() => setExpanded(e => !e)}
60 : >
61 1 : {expanded ? <ScreenNormalIcon size={16} /> : <ScreenFullIcon size={16} />}
62 1 : </IconButton>
63 1 : <IconButton
64 1 : variant={ButtonVariant.Invisible}
65 1 : size={ButtonSize.Small}
66 1 : aria-label="Close query plan"
67 1 : data-testid="query-profile-overlay-close"
68 1 : onClick={props.onClose}
69 : >
70 1 : <CloseIcon size={16} />
71 1 : </IconButton>
72 1 : </header>
73 1 : <div className={styles.body}>
74 1 : {props.children}
75 1 : </div>
76 1 : </section>
77 1 : </Overlay>
78 : );
79 1 : }
|