Skip to content

Commit 25d26ab

Browse files
committed
popover-enhancements
1 parent cbdb905 commit 25d26ab

File tree

5 files changed

+30
-7
lines changed

5 files changed

+30
-7
lines changed

src/internal/components/chart-popover/index.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface ChartPopoverProps extends PopoverProps {
3333
</>)
3434
*/
3535
trackKey?: string | number;
36+
minHeight?: number;
3637

3738
/** Optional container element that prevents any clicks in there from dismissing the popover */
3839
container: Element | null;
@@ -53,6 +54,9 @@ export interface ChartPopoverProps extends PopoverProps {
5354

5455
/** Popover footer */
5556
footer?: React.ReactNode;
57+
58+
hoverDismissButton?: boolean;
59+
allowScrollToFit?: boolean;
5660
}
5761

5862
export default React.forwardRef(ChartPopover);
@@ -63,6 +67,8 @@ function ChartPopover(
6367
size = 'medium',
6468
fixedWidth = false,
6569
dismissButton = false,
70+
hoverDismissButton = false,
71+
allowScrollToFit = false,
6672
dismissAriaLabel,
6773

6874
children,
@@ -74,6 +80,7 @@ function ChartPopover(
7480
trackKey,
7581
onDismiss,
7682
container,
83+
minHeight,
7784

7885
onMouseEnter,
7986
onMouseLeave,
@@ -127,6 +134,7 @@ function ChartPopover(
127134
trackRef={trackRef}
128135
getTrack={getTrack}
129136
trackKey={trackKey}
137+
minHeight={minHeight}
130138
arrow={position => (
131139
<div className={clsx(popoverStyles.arrow, popoverStyles[`arrow-position-${position}`])}>
132140
<div className={popoverStyles['arrow-outer']} />
@@ -135,11 +143,12 @@ function ChartPopover(
135143
)}
136144
keepPosition={true}
137145
allowVerticalOverflow={true}
138-
allowScrollToFit={isPinned}
146+
allowScrollToFit={isPinned || allowScrollToFit}
139147
hoverArea={true}
140148
>
141149
<PopoverBody
142-
dismissButton={dismissButton}
150+
dismissButton={dismissButton || hoverDismissButton}
151+
autoFocusDismissButton={!(hoverDismissButton === true)}
143152
dismissAriaLabel={dismissAriaLabel}
144153
header={<span className={testClasses.header}>{title}</span>}
145154
onDismiss={onDismiss}

src/popover/body.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import styles from './styles.css.js';
1616

1717
export interface PopoverBodyProps {
1818
dismissButton: boolean;
19+
autoFocusDismissButton?: boolean;
1920
dismissAriaLabel: string | undefined;
2021
onDismiss: (() => void) | undefined;
2122

@@ -32,6 +33,7 @@ export interface PopoverBodyProps {
3233

3334
export default function PopoverBody({
3435
dismissButton: showDismissButton,
36+
autoFocusDismissButton = true,
3537
dismissAriaLabel,
3638
header,
3739
children,
@@ -61,11 +63,11 @@ export default function PopoverBody({
6163
// because we also want to focus the dismiss button when it
6264
// is added dynamically (e.g. in chart popovers)
6365
useEffect(() => {
64-
if (showDismissButton && !dismissButtonFocused.current) {
66+
if (showDismissButton && !dismissButtonFocused.current && autoFocusDismissButton) {
6567
dismissButtonRef.current?.focus({ preventScroll: true });
6668
}
6769
dismissButtonFocused.current = showDismissButton;
68-
}, [showDismissButton]);
70+
}, [showDismissButton, autoFocusDismissButton]);
6971

7072
const dismissButton = (showDismissButton ?? null) && (
7173
<div

src/popover/container.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ interface PopoverContainerProps {
2626
</>)
2727
*/
2828
trackKey?: string | number;
29+
minHeight?: number;
2930
position: PopoverProps.Position;
3031
zIndex?: React.CSSProperties['zIndex'];
3132
arrow: (position: InternalPosition | null) => React.ReactNode;
@@ -51,6 +52,7 @@ export default function PopoverContainer({
5152
trackRef,
5253
getTrack: externalGetTrack,
5354
trackKey,
55+
minHeight,
5456
arrow,
5557
children,
5658
zIndex,
@@ -96,6 +98,7 @@ export default function PopoverContainer({
9698
renderWithPortal,
9799
keepPosition,
98100
hideOnOverscroll,
101+
minHeight,
99102
});
100103

101104
// Recalculate position when properties change.

src/popover/use-popover-position.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default function usePopoverPosition({
2727
renderWithPortal,
2828
keepPosition,
2929
hideOnOverscroll,
30+
minHeight,
3031
}: {
3132
popoverRef: React.RefObject<HTMLDivElement | null>;
3233
bodyRef: React.RefObject<HTMLDivElement | null>;
@@ -39,6 +40,7 @@ export default function usePopoverPosition({
3940
renderWithPortal?: boolean;
4041
keepPosition?: boolean;
4142
hideOnOverscroll?: boolean;
43+
minHeight?: number;
4244
}) {
4345
const previousInternalPositionRef = useRef<InternalPosition | null>(null);
4446
const [popoverStyle, setPopoverStyle] = useState<Partial<Offset>>({});
@@ -130,6 +132,7 @@ export default function usePopoverPosition({
130132
viewport: viewportRect,
131133
renderWithPortal,
132134
allowVerticalOverflow,
135+
minHeight,
133136
});
134137

135138
// Get the position of the popover relative to the containing block.
@@ -212,6 +215,7 @@ export default function usePopoverPosition({
212215
allowVerticalOverflow,
213216
allowScrollToFit,
214217
hideOnOverscroll,
218+
minHeight,
215219
]
216220
);
217221
return { updatePositionHandler, popoverStyle, internalPosition, positionHandlerRef, isOverscrolling };

src/popover/utils/positions.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ export function calculatePosition({
239239
// the popover is only bound by the viewport if it is rendered in a portal
240240
renderWithPortal,
241241
allowVerticalOverflow,
242+
minHeight,
242243
}: {
243244
preferredPosition: PopoverProps.Position;
244245
fixedInternalPosition?: InternalPosition;
@@ -250,6 +251,7 @@ export function calculatePosition({
250251
// the popover is only bound by the viewport if it is rendered in a portal
251252
renderWithPortal?: boolean;
252253
allowVerticalOverflow?: boolean;
254+
minHeight?: number;
253255
}): CalculatedPosition {
254256
let bestOption: CandidatePosition | null = null;
255257

@@ -265,11 +267,14 @@ export function calculatePosition({
265267
? getIntersection([rect, viewport])
266268
: getIntersection([rect, viewport, container]);
267269

268-
const fitsWithoutOverflow =
269-
visibleArea && visibleArea.inlineSize === body.inlineSize && visibleArea.blockSize === body.blockSize;
270+
const fitsBlockSize =
271+
minHeight === undefined
272+
? visibleArea && visibleArea.blockSize === body.blockSize
273+
: visibleArea && visibleArea.blockSize >= minHeight;
274+
const fitsWithoutOverflow = visibleArea && visibleArea.inlineSize === body.inlineSize && fitsBlockSize;
270275

271276
if (fitsWithoutOverflow) {
272-
return { internalPosition, rect };
277+
return { internalPosition, rect, scrollable: visibleArea && visibleArea.blockSize < body.blockSize };
273278
}
274279

275280
const newOption = { rect, internalPosition, visibleArea };

0 commit comments

Comments
 (0)