@lumx/react 3.9.2-alpha.0 → 3.9.2-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +49 -42
- package/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/popover/usePopoverStyle.tsx +1 -10
- package/src/components/tooltip/Tooltip.test.tsx +15 -19
- package/src/components/tooltip/Tooltip.tsx +6 -5
- package/src/components/tooltip/useTooltipOpen.tsx +6 -3
- package/src/constants.ts +5 -0
- package/src/hooks/usePopper.ts +9 -0
package/package.json
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
"url": "https://github.com/lumapps/design-system/issues"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@lumx/core": "^3.9.2-alpha.
|
|
10
|
-
"@lumx/icons": "^3.9.2-alpha.
|
|
9
|
+
"@lumx/core": "^3.9.2-alpha.2",
|
|
10
|
+
"@lumx/icons": "^3.9.2-alpha.2",
|
|
11
11
|
"@popperjs/core": "^2.5.4",
|
|
12
12
|
"body-scroll-lock": "^3.1.5",
|
|
13
13
|
"classnames": "^2.3.2",
|
|
@@ -110,5 +110,5 @@
|
|
|
110
110
|
"build:storybook": "storybook build"
|
|
111
111
|
},
|
|
112
112
|
"sideEffects": false,
|
|
113
|
-
"version": "3.9.2-alpha.
|
|
113
|
+
"version": "3.9.2-alpha.2"
|
|
114
114
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import { usePopper } from 'react-popper';
|
|
3
2
|
import memoize from 'lodash/memoize';
|
|
4
3
|
import { detectOverflow } from '@popperjs/core';
|
|
5
4
|
|
|
6
5
|
import { DOCUMENT, WINDOW } from '@lumx/react/constants';
|
|
7
6
|
import { PopoverProps } from '@lumx/react/components/popover/Popover';
|
|
7
|
+
import { usePopper } from '@lumx/react/hooks/usePopper';
|
|
8
8
|
import { ARROW_SIZE, FitAnchorWidth, Placement } from './constants';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -104,12 +104,6 @@ export function usePopoverStyle({
|
|
|
104
104
|
}: Options): Output {
|
|
105
105
|
const [popperElement, setPopperElement] = useState<null | HTMLElement>(null);
|
|
106
106
|
|
|
107
|
-
if (navigator.userAgent.includes('jsdom')) {
|
|
108
|
-
// Skip all logic; we don't need popover positioning in jsdom.
|
|
109
|
-
return { styles: {}, attributes: {}, isPositioned: true, popperElement, setPopperElement };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
113
107
|
const [arrowElement, setArrowElement] = useState<null | HTMLElement>(null);
|
|
114
108
|
|
|
115
109
|
const actualOffset: [number, number] = [offset?.along ?? 0, (offset?.away ?? 0) + (hasArrow ? ARROW_SIZE : 0)];
|
|
@@ -142,16 +136,13 @@ export function usePopoverStyle({
|
|
|
142
136
|
);
|
|
143
137
|
}
|
|
144
138
|
|
|
145
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
146
139
|
const { styles, attributes, state, update } = usePopper(anchorRef.current, popperElement, { placement, modifiers });
|
|
147
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
148
140
|
useEffect(() => {
|
|
149
141
|
update?.();
|
|
150
142
|
}, [children, update]);
|
|
151
143
|
|
|
152
144
|
const position = state?.placement ?? placement;
|
|
153
145
|
|
|
154
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
155
146
|
const popoverStyle = useMemo(() => {
|
|
156
147
|
const newStyles = { ...style, ...styles.popper, zIndex };
|
|
157
148
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
import { Button, IconButton } from '@lumx/react';
|
|
4
|
-
import { screen, render
|
|
4
|
+
import { screen, render } from '@testing-library/react';
|
|
5
5
|
import { queryAllByTagName, queryByClassName } from '@lumx/react/testing/utils/queries';
|
|
6
6
|
import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
|
|
7
7
|
import userEvent from '@testing-library/user-event';
|
|
@@ -159,18 +159,17 @@ describe(`<${Tooltip.displayName}>`, () => {
|
|
|
159
159
|
expect(ref.current === element).toBe(true);
|
|
160
160
|
});
|
|
161
161
|
|
|
162
|
-
it
|
|
163
|
-
const { tooltip
|
|
162
|
+
it('should render in closeMode=hide', async () => {
|
|
163
|
+
const { tooltip } = await setup({
|
|
164
164
|
label: 'Tooltip label',
|
|
165
165
|
children: <Button>Anchor</Button>,
|
|
166
166
|
closeMode: 'hide',
|
|
167
|
+
forceOpen: false,
|
|
167
168
|
});
|
|
168
|
-
screen.logTestingPlaygroundURL()
|
|
169
169
|
expect(tooltip).toBeInTheDocument();
|
|
170
|
-
expect(
|
|
171
|
-
expect(anchorWrapper).toHaveAttribute('aria-describedby', tooltip?.id);
|
|
170
|
+
expect(tooltip).toHaveClass('lumx-tooltip--is-hidden');
|
|
172
171
|
const button = screen.queryByRole('button', { name: 'Anchor' });
|
|
173
|
-
expect(button
|
|
172
|
+
expect(button).toHaveAttribute('aria-describedby', tooltip?.id);
|
|
174
173
|
});
|
|
175
174
|
});
|
|
176
175
|
|
|
@@ -194,12 +193,11 @@ describe(`<${Tooltip.displayName}>`, () => {
|
|
|
194
193
|
expect(button).toHaveAttribute('aria-describedby', tooltip?.id);
|
|
195
194
|
|
|
196
195
|
// Un-hover anchor button
|
|
197
|
-
userEvent.unhover(button);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
});
|
|
196
|
+
await userEvent.unhover(button);
|
|
197
|
+
|
|
198
|
+
expect(button).not.toHaveFocus();
|
|
199
|
+
// Tooltip closed
|
|
200
|
+
expect(tooltip).not.toBeInTheDocument();
|
|
203
201
|
});
|
|
204
202
|
|
|
205
203
|
it('should activate on hover anchor and then tooltip', async () => {
|
|
@@ -226,12 +224,10 @@ describe(`<${Tooltip.displayName}>`, () => {
|
|
|
226
224
|
expect(button).toHaveAttribute('aria-describedby', tooltip?.id);
|
|
227
225
|
|
|
228
226
|
// Un-hover tooltip
|
|
229
|
-
userEvent.unhover(tooltip);
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
expect(tooltip).not.toBeInTheDocument();
|
|
234
|
-
});
|
|
227
|
+
await userEvent.unhover(tooltip);
|
|
228
|
+
expect(button).not.toHaveFocus();
|
|
229
|
+
// Tooltip closed
|
|
230
|
+
expect(tooltip).not.toBeInTheDocument();
|
|
235
231
|
});
|
|
236
232
|
|
|
237
233
|
it('should activate on anchor focus visible and close on escape', async () => {
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
2
|
import React, { forwardRef, ReactNode, useState } from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
|
-
import { usePopper } from 'react-popper';
|
|
5
4
|
|
|
6
5
|
import classNames from 'classnames';
|
|
7
6
|
|
|
8
7
|
import { DOCUMENT } from '@lumx/react/constants';
|
|
9
8
|
import { Comp, GenericProps, HasCloseMode } from '@lumx/react/utils/type';
|
|
10
9
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
11
|
-
import {
|
|
10
|
+
import { useMergeRefs } from '@lumx/react/utils/mergeRefs';
|
|
12
11
|
import { Placement } from '@lumx/react/components/popover';
|
|
13
12
|
import { TooltipContextProvider } from '@lumx/react/components/tooltip/context';
|
|
14
13
|
import { useId } from '@lumx/react/hooks/useId';
|
|
15
14
|
|
|
16
15
|
import { useInjectTooltipRef } from './useInjectTooltipRef';
|
|
17
16
|
import { useTooltipOpen } from './useTooltipOpen';
|
|
17
|
+
import { usePopper } from '@lumx/react/hooks/usePopper';
|
|
18
18
|
|
|
19
19
|
/** Position of the tooltip relative to the anchor element. */
|
|
20
20
|
export type TooltipPlacement = Extract<Placement, 'top' | 'right' | 'bottom' | 'left'>;
|
|
@@ -50,7 +50,7 @@ const CLASSNAME = getRootClassName(COMPONENT_NAME);
|
|
|
50
50
|
*/
|
|
51
51
|
const DEFAULT_PROPS: Partial<TooltipProps> = {
|
|
52
52
|
placement: Placement.BOTTOM,
|
|
53
|
-
closeMode: '
|
|
53
|
+
closeMode: 'unmount',
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -68,7 +68,7 @@ const ARROW_SIZE = 8;
|
|
|
68
68
|
export const Tooltip: Comp<TooltipProps, HTMLDivElement> = forwardRef((props, ref) => {
|
|
69
69
|
const { label, children, className, delay, placement, forceOpen, closeMode, ...forwardedProps } = props;
|
|
70
70
|
// Disable in SSR.
|
|
71
|
-
if (!DOCUMENT
|
|
71
|
+
if (!DOCUMENT) {
|
|
72
72
|
return <>{children}</>;
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -94,13 +94,14 @@ export const Tooltip: Comp<TooltipProps, HTMLDivElement> = forwardRef((props, re
|
|
|
94
94
|
|
|
95
95
|
const labelLines = label ? label.split('\n') : [];
|
|
96
96
|
|
|
97
|
+
const tooltipRef = useMergeRefs(ref, setPopperElement, onPopperMount);
|
|
97
98
|
return (
|
|
98
99
|
<>
|
|
99
100
|
<TooltipContextProvider>{wrappedChildren}</TooltipContextProvider>
|
|
100
101
|
{isMounted &&
|
|
101
102
|
createPortal(
|
|
102
103
|
<div
|
|
103
|
-
ref={
|
|
104
|
+
ref={tooltipRef}
|
|
104
105
|
{...forwardedProps}
|
|
105
106
|
id={id}
|
|
106
107
|
role="tooltip"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MutableRefObject, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { browserDoesNotSupportHover } from '@lumx/react/utils/browserDoesNotSupportHover';
|
|
3
|
-
import { TOOLTIP_HOVER_DELAY, TOOLTIP_LONG_PRESS_DELAY } from '@lumx/react/constants';
|
|
3
|
+
import { IS_BROWSER, TOOLTIP_HOVER_DELAY, TOOLTIP_LONG_PRESS_DELAY } from '@lumx/react/constants';
|
|
4
4
|
import { useCallbackOnEscape } from '@lumx/react/hooks/useCallbackOnEscape';
|
|
5
5
|
import { isFocusVisible } from '@lumx/react/utils/isFocusVisible';
|
|
6
6
|
|
|
@@ -31,9 +31,12 @@ export function useTooltipOpen(delay: number | undefined, anchorElement: HTMLEle
|
|
|
31
31
|
// Run timer to defer updating the isOpen state.
|
|
32
32
|
const deferUpdate = (duration: number) => {
|
|
33
33
|
if (timer) clearTimeout(timer);
|
|
34
|
-
|
|
34
|
+
const update = () => {
|
|
35
35
|
setIsOpen(!!shouldOpen);
|
|
36
|
-
}
|
|
36
|
+
};
|
|
37
|
+
// Skip timeout in fake browsers
|
|
38
|
+
if (!IS_BROWSER) update();
|
|
39
|
+
else timer = setTimeout(update, duration) as any;
|
|
37
40
|
};
|
|
38
41
|
|
|
39
42
|
const hoverNotSupported = browserDoesNotSupportHover();
|
package/src/constants.ts
CHANGED
|
@@ -15,3 +15,8 @@ export const WINDOW = typeof window !== 'undefined' ? window : undefined;
|
|
|
15
15
|
* Optional global `document` instance (not defined when running SSR).
|
|
16
16
|
*/
|
|
17
17
|
export const DOCUMENT = typeof document !== 'undefined' ? document : undefined;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if we are running in a true browser
|
|
21
|
+
*/
|
|
22
|
+
export const IS_BROWSER = typeof navigator !== 'undefined' && !navigator.userAgent.includes('jsdom');
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { usePopper as usePopperHook } from 'react-popper';
|
|
2
|
+
import { IS_BROWSER } from '@lumx/react/constants';
|
|
3
|
+
|
|
4
|
+
/** Stub usePopper for use outside of browsers */
|
|
5
|
+
const useStubPopper: typeof usePopperHook = (_a, _p, { placement }: any) =>
|
|
6
|
+
({ attributes: { 'data-popper-placement': placement }, styles: {} }) as any;
|
|
7
|
+
|
|
8
|
+
/** Switch hook implementation between environment */
|
|
9
|
+
export const usePopper: typeof usePopperHook = IS_BROWSER ? usePopperHook : useStubPopper;
|