Skip to content

Commit 707fe01

Browse files
authored
chore(compass-components): add signal-popover component COMPASS-6828 (#4517)
chore(compass-components): add signal-popover component
1 parent 106c52a commit 707fe01

File tree

4 files changed

+480
-37
lines changed

4 files changed

+480
-37
lines changed

packages/compass-components/src/components/interactive-popover.tsx

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ const closeButtonStyles = css({
4343
});
4444

4545
type InteractivePopoverProps = {
46-
className: string;
47-
children: React.ReactElement;
46+
className?: string;
47+
children: React.ReactNode;
4848
trigger: (triggerProps: {
4949
onClick: (event: React.MouseEvent | React.TouchEvent) => void;
50-
ref: React.RefObject<HTMLButtonElement>;
50+
ref: React.LegacyRef<HTMLButtonElement>;
5151
children: React.ReactNode;
5252
}) => React.ReactElement;
5353
open: boolean;
@@ -56,7 +56,12 @@ type InteractivePopoverProps = {
5656
* List of selector to consider contained elements to skip closing on click
5757
*/
5858
containedElements?: string[];
59-
};
59+
containerClassName?: string;
60+
closeButtonClassName?: string;
61+
} & Pick<
62+
React.ComponentProps<typeof Popover>,
63+
'align' | 'justify' | 'spacing' | 'popoverZIndex'
64+
>;
6065

6166
function InteractivePopover({
6267
className,
@@ -65,6 +70,12 @@ function InteractivePopover({
6570
open,
6671
setOpen,
6772
containedElements = [],
73+
align,
74+
justify,
75+
spacing,
76+
popoverZIndex,
77+
containerClassName,
78+
closeButtonClassName,
6879
}: InteractivePopoverProps): React.ReactElement {
6980
const darkMode = useDarkMode();
7081
const triggerRef = useRef<HTMLButtonElement>(null);
@@ -141,47 +152,48 @@ function InteractivePopover({
141152
ref: triggerRef,
142153
children: (
143154
<Popover
144-
align="bottom"
145-
justify="start"
155+
align={align ?? 'bottom'}
156+
justify={justify ?? 'start'}
146157
active={open}
147158
adjustOnMutation
148159
usePortal
149-
spacing={0}
160+
spacing={spacing ?? 0}
150161
className={className}
151162
refEl={triggerRef}
163+
popoverZIndex={popoverZIndex}
152164
>
153-
{open && (
154-
<FocusTrap
155-
focusTrapOptions={{
156-
clickOutsideDeactivates: true,
157-
// Tests fail without a fallback. (https://github.com/focus-trap/focus-trap-react/issues/91)
158-
fallbackFocus: `#${closeButtonId}`,
159-
}}
165+
<FocusTrap
166+
active={open}
167+
focusTrapOptions={{
168+
clickOutsideDeactivates: true,
169+
// Tests fail without a fallback. (https://github.com/focus-trap/focus-trap-react/issues/91)
170+
fallbackFocus: `#${closeButtonId}`,
171+
}}
172+
>
173+
<div
174+
className={cx(
175+
contentContainerStyles,
176+
darkMode
177+
? contentContainerStylesDark
178+
: contentContainerStylesLight,
179+
containerClassName
180+
)}
181+
ref={popoverContentContainerRef}
160182
>
161-
<div
162-
className={cx(
163-
contentContainerStyles,
164-
darkMode
165-
? contentContainerStylesDark
166-
: contentContainerStylesLight
167-
)}
168-
ref={popoverContentContainerRef}
183+
{children}
184+
185+
<IconButton
186+
className={cx(closeButtonStyles, closeButtonClassName)}
187+
data-testid="interactive-popover-close-button"
188+
onClick={onClose}
189+
aria-label="Close"
190+
id={closeButtonId}
191+
ref={closeButtonRef}
169192
>
170-
{children}
171-
172-
<IconButton
173-
className={closeButtonStyles}
174-
data-testid="interactive-popover-close-button"
175-
onClick={onClose}
176-
aria-label="Close"
177-
id={closeButtonId}
178-
ref={closeButtonRef}
179-
>
180-
<Icon glyph="X" />
181-
</IconButton>
182-
</div>
183-
</FocusTrap>
184-
)}
193+
<Icon glyph="X" />
194+
</IconButton>
195+
</div>
196+
</FocusTrap>
185197
</Popover>
186198
),
187199
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
import { render, cleanup, screen } from '@testing-library/react';
3+
import userEvent from '@testing-library/user-event';
4+
import { SignalPopover } from './signal-popover';
5+
import { expect } from 'chai';
6+
7+
const signals = [
8+
{
9+
id: 'unbounded-array',
10+
title: 'Unbounded array detected',
11+
description:
12+
'As arrays get larger, queries and indexes on that array field become less efficient. Ensure your arrays are bounded to maintain optimal query performance.',
13+
learnMoreLink: 'https://example.com',
14+
primaryActionButtonLabel: 'Bound those arrays',
15+
},
16+
{
17+
id: 'bloated-docs',
18+
title: 'Possibly bloated documents',
19+
description:
20+
'Large documents can slow down queries by decreasing the number of documents that can be stored in RAM. Consider breaking up your data into more collections with smaller documents, and using references to consolidate the data you need.',
21+
learnMoreLink: 'https://example.com',
22+
},
23+
];
24+
25+
describe('SignalPopover', function () {
26+
afterEach(cleanup);
27+
28+
it('should render a signal', function () {
29+
render(<SignalPopover signals={signals[0]}></SignalPopover>);
30+
userEvent.click(screen.getByTestId('insight-badge-button'));
31+
expect(screen.getByText('Unbounded array detected')).to.exist;
32+
});
33+
34+
it('should render multiple signals', function () {
35+
render(<SignalPopover signals={signals}></SignalPopover>);
36+
userEvent.click(screen.getByTestId('insight-badge-button'));
37+
expect(screen.getByText('Unbounded array detected')).to.exist;
38+
userEvent.click(screen.getByTitle('Show next insight'));
39+
expect(screen.getByText('Possibly bloated documents')).to.exist;
40+
});
41+
});

0 commit comments

Comments
 (0)