@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/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.0",
10
- "@lumx/icons": "^3.9.2-alpha.0",
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.0"
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, waitFor } from '@testing-library/react';
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.only('should render in closeMode=hide', async () => {
163
- const { tooltip, anchorWrapper } = await setup({
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(anchorWrapper).toBeInTheDocument();
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?.parentElement).toBe(anchorWrapper);
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
- await waitFor(() => {
199
- expect(button).not.toHaveFocus();
200
- // Tooltip closed
201
- expect(tooltip).not.toBeInTheDocument();
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
- await waitFor(() => {
231
- expect(button).not.toHaveFocus();
232
- // Tooltip closed
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 { mergeRefs } from '@lumx/react/utils/mergeRefs';
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: 'hide',
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 && closeMode === 'unmount') {
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={mergeRefs(ref, setPopperElement, onPopperMount)}
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
- timer = setTimeout(() => {
34
+ const update = () => {
35
35
  setIsOpen(!!shouldOpen);
36
- }, duration) as any;
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;