@lumx/react 3.9.2-alpha.5 → 3.9.2-alpha.7

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.5",
10
- "@lumx/icons": "^3.9.2-alpha.5",
9
+ "@lumx/core": "^3.9.2-alpha.7",
10
+ "@lumx/icons": "^3.9.2-alpha.7",
11
11
  "@popperjs/core": "^2.5.4",
12
12
  "body-scroll-lock": "^3.1.5",
13
13
  "classnames": "^2.3.2",
@@ -57,7 +57,7 @@
57
57
  "jest-environment-jsdom": "29.1.2",
58
58
  "react": "^17.0.2",
59
59
  "react-dom": "^17.0.2",
60
- "rollup": "2.26.10",
60
+ "rollup": "2.79.2",
61
61
  "rollup-plugin-analyzer": "^3.3.0",
62
62
  "rollup-plugin-babel": "^4.4.0",
63
63
  "rollup-plugin-cleaner": "^1.0.0",
@@ -110,5 +110,5 @@
110
110
  "build:storybook": "storybook build"
111
111
  },
112
112
  "sideEffects": false,
113
- "version": "3.9.2-alpha.5"
113
+ "version": "3.9.2-alpha.7"
114
114
  }
@@ -145,11 +145,18 @@ describe(`<${ImageLightbox.displayName}>`, () => {
145
145
 
146
146
  // Focus moved to the close button
147
147
  const imageLightbox = queries.getImageLightbox();
148
- expect(queries.queryCloseButton(imageLightbox)).toHaveFocus();
148
+ const closeButton = queries.queryCloseButton(imageLightbox);
149
+ expect(closeButton).toHaveFocus();
150
+ const tooltip = screen.getByRole('tooltip', { name: 'Close' });
151
+ expect(tooltip).toBeInTheDocument();
149
152
 
150
153
  // Image lightbox opened on the correct image
151
154
  expect(queries.queryImage(imageLightbox, 'Image 2')).toBeInTheDocument();
152
155
 
156
+ // Close tooltip
157
+ await userEvent.keyboard('{escape}');
158
+ expect(tooltip).not.toBeInTheDocument();
159
+
153
160
  // Close on escape
154
161
  await userEvent.keyboard('{escape}');
155
162
  expect(imageLightbox).not.toBeInTheDocument();
@@ -41,13 +41,12 @@ export function useImageLightbox<P extends Partial<ImageLightboxProps>>(
41
41
  const propsRef = React.useRef(props);
42
42
 
43
43
  React.useEffect(() => {
44
- const newProps = { ...props };
45
- if (newProps?.images) {
46
- newProps.images = newProps.images.map((image) => ({ imgRef: React.createRef(), ...image }));
47
- }
48
- propsRef.current = newProps;
44
+ propsRef.current = props;
49
45
  }, [props]);
50
46
 
47
+ // Keep reference for each image elements
48
+ const imageRefsRef = React.useRef<Array<React.RefObject<HTMLImageElement>>>([]);
49
+
51
50
  const currentImageRef = React.useRef<HTMLImageElement>(null);
52
51
  const [imageLightboxProps, setImageLightboxProps] = React.useState(
53
52
  () => ({ ...EMPTY_PROPS, ...props }) as ManagedProps & P,
@@ -61,8 +60,8 @@ export function useImageLightbox<P extends Partial<ImageLightboxProps>>(
61
60
  if (!currentImage) {
62
61
  return;
63
62
  }
64
- const currentIndex = propsRef.current?.images?.findIndex(
65
- ({ imgRef }) => (imgRef as any)?.current === currentImage,
63
+ const currentIndex = imageRefsRef.current.findIndex(
64
+ (imageRef) => imageRef.current === currentImage,
66
65
  ) as number;
67
66
 
68
67
  await startViewTransition({
@@ -83,12 +82,20 @@ export function useImageLightbox<P extends Partial<ImageLightboxProps>>(
83
82
  // If we find an image inside the trigger, animate it in transition with the opening image
84
83
  const triggerImage = triggerImageRefs[activeImageIndex as any]?.current || findImage(triggerElement);
85
84
 
86
- // Inject the trigger image as loading placeholder for better loading state
87
- const imagesWithFallbackSize = propsRef.current?.images?.map((image, idx) => {
88
- if (triggerImage && idx === activeImageIndex && !image.loadingPlaceholderImageRef) {
89
- return { ...image, loadingPlaceholderImageRef: { current: triggerImage } };
85
+ // Inject refs to improve transition and loading style
86
+ const images = propsRef.current?.images?.map((image, idx) => {
87
+ // Get or create image reference
88
+ let imgRef = imageRefsRef.current[idx];
89
+ if (!imgRef) {
90
+ imgRef = React.createRef();
91
+ imageRefsRef.current[idx] = imgRef;
90
92
  }
91
- return image;
93
+
94
+ // Try to use the trigger image as the loading placeholder
95
+ const loadingPlaceholderImageRef =
96
+ triggerImage && idx === activeImageIndex ? { current: triggerImage } : undefined;
97
+
98
+ return { loadingPlaceholderImageRef, ...image, imgRef };
92
99
  });
93
100
 
94
101
  await startViewTransition({
@@ -104,7 +111,7 @@ export function useImageLightbox<P extends Partial<ImageLightboxProps>>(
104
111
  close();
105
112
  prevProps?.onClose?.();
106
113
  },
107
- images: imagesWithFallbackSize,
114
+ images,
108
115
  activeImageIndex: activeImageIndex || 0,
109
116
  }));
110
117
  },
@@ -2,8 +2,10 @@ import { Button, Dialog, Dropdown, Placement, Tooltip } from '@lumx/react';
2
2
  import React, { useState } from 'react';
3
3
  import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
4
4
  import { withChromaticForceScreenSize } from '@lumx/react/stories/decorators/withChromaticForceScreenSize';
5
+ import { ARIA_LINK_MODES } from '@lumx/react/components/tooltip/constants';
5
6
 
6
7
  const placements = [Placement.TOP, Placement.BOTTOM, Placement.RIGHT, Placement.LEFT];
8
+ const CLOSE_MODES = ['hide', 'unmount'];
7
9
 
8
10
  export default {
9
11
  title: 'LumX components/tooltip/Tooltip',
@@ -11,6 +13,9 @@ export default {
11
13
  args: Tooltip.defaultProps,
12
14
  argTypes: {
13
15
  placement: getSelectArgType(placements),
16
+ children: { control: false },
17
+ closeMode: { control: { type: 'inline-radio' }, options: CLOSE_MODES },
18
+ ariaLinkMode: { control: { type: 'inline-radio' }, options: ARIA_LINK_MODES },
14
19
  },
15
20
  decorators: [
16
21
  // Force minimum chromatic screen size to make sure the dialog appears in view.
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
 
3
- import { Button, IconButton } from '@lumx/react';
3
+ import { Button } from '@lumx/react';
4
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';
@@ -51,7 +51,6 @@ describe(`<${Tooltip.displayName}>`, () => {
51
51
  // Default placement
52
52
  expect(tooltip).toHaveAttribute('data-popper-placement', 'bottom');
53
53
  expect(anchorWrapper).toBeInTheDocument();
54
- expect(anchorWrapper).toHaveAttribute('aria-describedby', tooltip?.id);
55
54
  });
56
55
 
57
56
  it('should render with custom placement', async () => {
@@ -65,25 +64,6 @@ describe(`<${Tooltip.displayName}>`, () => {
65
64
  expect(tooltip).toHaveAttribute('data-popper-placement', 'top');
66
65
  });
67
66
 
68
- it('should wrap unknown children and not add aria-describedby when closed', async () => {
69
- const { anchorWrapper } = await setup({
70
- label: 'Tooltip label',
71
- children: 'Anchor',
72
- forceOpen: false,
73
- });
74
- expect(anchorWrapper).not.toHaveAttribute('aria-describedby');
75
- });
76
-
77
- it('should not wrap Button and not add aria-describedby when closed', async () => {
78
- await setup({
79
- label: 'Tooltip label',
80
- children: <Button>Anchor</Button>,
81
- forceOpen: false,
82
- });
83
- const button = screen.queryByRole('button', { name: 'Anchor' });
84
- expect(button).not.toHaveAttribute('aria-describedby');
85
- });
86
-
87
67
  it('should not wrap Button', async () => {
88
68
  const { tooltip, anchorWrapper } = await setup({
89
69
  label: 'Tooltip label',
@@ -96,35 +76,6 @@ describe(`<${Tooltip.displayName}>`, () => {
96
76
  expect(button).toHaveAttribute('aria-describedby', tooltip?.id);
97
77
  });
98
78
 
99
- it('should not add aria-describedby if button label is the same as tooltip label', async () => {
100
- const label = 'Tooltip label';
101
- render(<IconButton label={label} tooltipProps={{ forceOpen: true }} />);
102
- const tooltip = screen.queryByRole('tooltip', { name: label });
103
- expect(tooltip).toBeInTheDocument();
104
- const button = screen.queryByRole('button', { name: label });
105
- expect(button).not.toHaveAttribute('aria-describedby');
106
- });
107
-
108
- it('should keep anchor aria-describedby if button label is the same as tooltip label', async () => {
109
- const label = 'Tooltip label';
110
- render(<IconButton label={label} aria-describedby=":header-1:" tooltipProps={{ forceOpen: true }} />);
111
- const tooltip = screen.queryByRole('tooltip', { name: label });
112
- expect(tooltip).toBeInTheDocument();
113
- const button = screen.queryByRole('button', { name: label });
114
- expect(button).toHaveAttribute('aria-describedby', ':header-1:');
115
- });
116
-
117
- it('should concat aria-describedby if already exists', async () => {
118
- const { tooltip } = await setup({
119
- label: 'Tooltip label',
120
- children: <Button aria-describedby=":header-1:">Anchor</Button>,
121
- forceOpen: true,
122
- });
123
- expect(tooltip).toBeInTheDocument();
124
- const button = screen.queryByRole('button', { name: 'Anchor' });
125
- expect(button).toHaveAttribute('aria-describedby', `:header-1: ${tooltip?.id}`);
126
- });
127
-
128
79
  it('should wrap disabled Button', async () => {
129
80
  const { tooltip, anchorWrapper } = await setup({
130
81
  label: 'Tooltip label',
@@ -172,17 +123,166 @@ describe(`<${Tooltip.displayName}>`, () => {
172
123
  expect(ref.current === element).toBe(true);
173
124
  });
174
125
 
175
- it('should render in closeMode=hide', async () => {
176
- const { tooltip } = await setup({
177
- label: 'Tooltip label',
178
- children: <Button>Anchor</Button>,
179
- closeMode: 'hide',
180
- forceOpen: false,
126
+ describe('closeMode="hide"', () => {
127
+ it('should not render with empty label', async () => {
128
+ const { tooltip, anchorWrapper } = await setup({
129
+ label: undefined,
130
+ forceOpen: true,
131
+ closeMode: 'hide',
132
+ });
133
+ expect(tooltip).not.toBeInTheDocument();
134
+ expect(anchorWrapper).not.toBeInTheDocument();
135
+ });
136
+
137
+ it('should render hidden', async () => {
138
+ const { tooltip } = await setup({
139
+ label: 'Tooltip label',
140
+ children: <Button>Anchor</Button>,
141
+ closeMode: 'hide',
142
+ forceOpen: false,
143
+ });
144
+ expect(tooltip).toBeInTheDocument();
145
+ expect(tooltip).toHaveClass('lumx-tooltip--is-hidden');
146
+
147
+ const anchor = screen.getByRole('button', { name: 'Anchor' });
148
+ await userEvent.hover(anchor);
149
+ expect(tooltip).not.toHaveClass('lumx-tooltip--is-hidden');
150
+ });
151
+ });
152
+
153
+ describe('ariaLinkMode="aria-describedby"', () => {
154
+ it('should add aria-describedby on anchor on open', async () => {
155
+ await setup({
156
+ label: 'Tooltip label',
157
+ forceOpen: false,
158
+ children: <Button aria-describedby=":description1:">Anchor</Button>,
159
+ });
160
+ const anchor = screen.getByRole('button', { name: 'Anchor' });
161
+ expect(anchor).toHaveAttribute('aria-describedby', ':description1:');
162
+
163
+ await userEvent.hover(anchor);
164
+ const tooltip = screen.queryByRole('tooltip');
165
+ expect(anchor).toHaveAttribute('aria-describedby', `:description1: ${tooltip?.id}`);
166
+ });
167
+
168
+ it('should always add aria-describedby on anchor with closeMode="hide"', async () => {
169
+ const { tooltip } = await setup({
170
+ label: 'Tooltip label',
171
+ forceOpen: false,
172
+ children: <Button aria-describedby=":description1:">Anchor</Button>,
173
+ closeMode: 'hide',
174
+ });
175
+ const anchor = screen.getByRole('button', { name: 'Anchor' });
176
+ expect(anchor).toHaveAttribute('aria-describedby', `:description1: ${tooltip?.id}`);
177
+ });
178
+
179
+ it('should skip aria-describedby if anchor has label', async () => {
180
+ const { tooltip } = await setup({
181
+ label: 'Tooltip label',
182
+ forceOpen: true,
183
+ children: (
184
+ <Button aria-describedby=":description1:" aria-label="Tooltip label">
185
+ Anchor
186
+ </Button>
187
+ ),
188
+ });
189
+ expect(tooltip).toBeInTheDocument();
190
+ expect(screen.getByRole('button')).toHaveAttribute('aria-describedby', `:description1:`);
191
+ });
192
+
193
+ it('should add aria-describedby on anchor wrapper on open', async () => {
194
+ const { anchorWrapper } = await setup({
195
+ label: 'Tooltip label',
196
+ forceOpen: false,
197
+ children: 'Anchor',
198
+ });
199
+ expect(anchorWrapper).not.toHaveAttribute('aria-describedby');
200
+
201
+ await userEvent.hover(anchorWrapper as any);
202
+ const tooltip = screen.queryByRole('tooltip');
203
+ expect(anchorWrapper).toHaveAttribute('aria-describedby', tooltip?.id);
204
+ });
205
+
206
+ it('should always add aria-describedby on anchor wrapper with closeMode="hide"', async () => {
207
+ const { tooltip, anchorWrapper } = await setup({
208
+ label: 'Tooltip label',
209
+ forceOpen: false,
210
+ children: 'Anchor',
211
+ closeMode: 'hide',
212
+ });
213
+ expect(anchorWrapper).toHaveAttribute('aria-describedby', `${tooltip?.id}`);
214
+ });
215
+ });
216
+
217
+ describe('ariaLinkMode="aria-labelledby"', () => {
218
+ it('should add aria-labelledby on anchor on open', async () => {
219
+ await setup({
220
+ label: 'Tooltip label',
221
+ forceOpen: false,
222
+ children: <Button aria-labelledby=":label1:">Anchor</Button>,
223
+ ariaLinkMode: 'aria-labelledby',
224
+ });
225
+ const anchor = screen.getByRole('button', { name: 'Anchor' });
226
+ expect(anchor).toHaveAttribute('aria-labelledby', ':label1:');
227
+
228
+ await userEvent.hover(anchor);
229
+ const tooltip = screen.queryByRole('tooltip');
230
+ expect(anchor).toHaveAttribute('aria-labelledby', `:label1: ${tooltip?.id}`);
231
+ });
232
+
233
+ it('should always add aria-labelledby on anchor with closeMode="hide"', async () => {
234
+ const label = 'Tooltip label';
235
+ const { tooltip } = await setup({
236
+ label,
237
+ forceOpen: false,
238
+ children: <Button aria-labelledby=":label1:">Anchor</Button>,
239
+ ariaLinkMode: 'aria-labelledby',
240
+ closeMode: 'hide',
241
+ });
242
+ const anchor = screen.queryByRole('button', { name: label });
243
+ expect(anchor).toBeInTheDocument();
244
+ expect(anchor).toHaveAttribute('aria-labelledby', `:label1: ${tooltip?.id}`);
245
+ });
246
+
247
+ it('should skip aria-labelledby if anchor has label', async () => {
248
+ const { tooltip } = await setup({
249
+ label: 'Tooltip label',
250
+ forceOpen: true,
251
+ children: (
252
+ <Button aria-labelledby=":label1:" aria-label="Tooltip label">
253
+ Anchor
254
+ </Button>
255
+ ),
256
+ ariaLinkMode: 'aria-labelledby',
257
+ });
258
+ expect(tooltip).toBeInTheDocument();
259
+ expect(screen.getByRole('button')).toHaveAttribute('aria-labelledby', `:label1:`);
260
+ });
261
+
262
+ it('should add aria-labelledby on anchor wrapper on open', async () => {
263
+ const { anchorWrapper } = await setup({
264
+ label: 'Tooltip label',
265
+ forceOpen: false,
266
+ children: 'Anchor',
267
+ ariaLinkMode: 'aria-labelledby',
268
+ });
269
+ expect(anchorWrapper).not.toHaveAttribute('aria-labelledby');
270
+
271
+ await userEvent.hover(anchorWrapper as any);
272
+ const tooltip = screen.queryByRole('tooltip');
273
+ expect(anchorWrapper).toHaveAttribute('aria-labelledby', tooltip?.id);
274
+ });
275
+
276
+ it('should always add aria-labelledby on anchor wrapper with closeMode="hide"', async () => {
277
+ const { tooltip, anchorWrapper } = await setup({
278
+ label: 'Tooltip label',
279
+ forceOpen: false,
280
+ children: 'Anchor',
281
+ ariaLinkMode: 'aria-labelledby',
282
+ closeMode: 'hide',
283
+ });
284
+ expect(anchorWrapper).toHaveAttribute('aria-labelledby', `${tooltip?.id}`);
181
285
  });
182
- expect(tooltip).toBeInTheDocument();
183
- expect(tooltip).toHaveClass('lumx-tooltip--is-hidden');
184
- const button = screen.queryByRole('button', { name: 'Anchor' });
185
- expect(button).toHaveAttribute('aria-describedby', tooltip?.id);
186
286
  });
187
287
  });
188
288
 
@@ -203,7 +303,6 @@ describe(`<${Tooltip.displayName}>`, () => {
203
303
  // Tooltip opened
204
304
  tooltip = await screen.findByRole('tooltip', { name: 'Tooltip label' });
205
305
  expect(tooltip).toBeInTheDocument();
206
- expect(button).toHaveAttribute('aria-describedby', tooltip?.id);
207
306
 
208
307
  // Un-hover anchor button
209
308
  await userEvent.unhover(button);
@@ -11,10 +11,11 @@ import { useMergeRefs } from '@lumx/react/utils/mergeRefs';
11
11
  import { Placement } from '@lumx/react/components/popover';
12
12
  import { TooltipContextProvider } from '@lumx/react/components/tooltip/context';
13
13
  import { useId } from '@lumx/react/hooks/useId';
14
+ import { usePopper } from '@lumx/react/hooks/usePopper';
14
15
 
16
+ import { ARIA_LINK_MODES } from '@lumx/react/components/tooltip/constants';
15
17
  import { useInjectTooltipRef } from './useInjectTooltipRef';
16
18
  import { useTooltipOpen } from './useTooltipOpen';
17
- import { usePopper } from '@lumx/react/hooks/usePopper';
18
19
 
19
20
  /** Position of the tooltip relative to the anchor element. */
20
21
  export type TooltipPlacement = Extract<Placement, 'top' | 'right' | 'bottom' | 'left'>;
@@ -33,6 +34,8 @@ export interface TooltipProps extends GenericProps, HasCloseMode {
33
34
  label?: string | null | false;
34
35
  /** Placement of the tooltip relative to the anchor. */
35
36
  placement?: TooltipPlacement;
37
+ /** Choose how the tooltip text should link to the anchor */
38
+ ariaLinkMode?: (typeof ARIA_LINK_MODES)[number];
36
39
  }
37
40
 
38
41
  /**
@@ -51,6 +54,7 @@ const CLASSNAME = getRootClassName(COMPONENT_NAME);
51
54
  const DEFAULT_PROPS: Partial<TooltipProps> = {
52
55
  placement: Placement.BOTTOM,
53
56
  closeMode: 'unmount',
57
+ ariaLinkMode: 'aria-describedby',
54
58
  };
55
59
 
56
60
  /**
@@ -66,7 +70,8 @@ const ARROW_SIZE = 8;
66
70
  * @return React element.
67
71
  */
68
72
  export const Tooltip: Comp<TooltipProps, HTMLDivElement> = forwardRef((props, ref) => {
69
- const { label, children, className, delay, placement, forceOpen, closeMode, ...forwardedProps } = props;
73
+ const { label, children, className, delay, placement, forceOpen, closeMode, ariaLinkMode, ...forwardedProps } =
74
+ props;
70
75
  // Disable in SSR.
71
76
  if (!DOCUMENT) {
72
77
  return <>{children}</>;
@@ -89,8 +94,15 @@ export const Tooltip: Comp<TooltipProps, HTMLDivElement> = forwardRef((props, re
89
94
  const position = attributes?.popper?.['data-popper-placement'] ?? placement;
90
95
  const { isOpen: isActivated, onPopperMount } = useTooltipOpen(delay, anchorElement);
91
96
  const isOpen = (isActivated || forceOpen) && !!label;
92
- const isMounted = isOpen || closeMode === 'hide';
93
- const wrappedChildren = useInjectTooltipRef(children, setAnchorElement, isMounted, id, label);
97
+ const isMounted = !!label && (isOpen || closeMode === 'hide');
98
+ const wrappedChildren = useInjectTooltipRef({
99
+ children,
100
+ setAnchorElement,
101
+ isMounted,
102
+ id,
103
+ label,
104
+ ariaLinkMode: ariaLinkMode as any,
105
+ });
94
106
 
95
107
  const labelLines = label ? label.split('\n') : [];
96
108
 
@@ -0,0 +1 @@
1
+ export const ARIA_LINK_MODES = ['aria-describedby', 'aria-labelledby'] as const;
@@ -2,27 +2,30 @@ import React, { cloneElement, ReactNode, useMemo } from 'react';
2
2
 
3
3
  import { mergeRefs } from '@lumx/react/utils/mergeRefs';
4
4
 
5
+ interface Options {
6
+ /** Original tooltip anchor */
7
+ children: ReactNode;
8
+ /** Set tooltip anchor element */
9
+ setAnchorElement: (e: HTMLDivElement) => void;
10
+ /** Whether the tooltip is open or not */
11
+ isMounted: boolean | undefined;
12
+ /** Tooltip id */
13
+ id: string;
14
+ /** Tooltip label*/
15
+ label?: string | null | false;
16
+ /** Choose how the tooltip text should link to the anchor */
17
+ ariaLinkMode: 'aria-describedby' | 'aria-labelledby';
18
+ }
19
+
5
20
  /**
6
21
  * Add ref and ARIA attribute(s) in tooltip children or wrapped children.
7
22
  * Button, IconButton, Icon and React HTML elements don't need to be wrapped but any other kind of children (array, fragment, custom components)
8
23
  * will be wrapped in a <span>.
9
- *
10
- * @param children Original tooltip anchor.
11
- * @param setAnchorElement Set tooltip anchor element.
12
- * @param isOpen Whether the tooltip is open or not.
13
- * @param id Tooltip id.
14
- * @param label Tooltip label.
15
- * @return tooltip anchor.
16
24
  */
17
- export const useInjectTooltipRef = (
18
- children: ReactNode,
19
- setAnchorElement: (e: HTMLDivElement) => void,
20
- isOpen: boolean | undefined,
21
- id: string,
22
- label?: string | null | false,
23
- ): ReactNode => {
24
- // Only add description when open
25
- const describedBy = isOpen ? id : undefined;
25
+ export const useInjectTooltipRef = (options: Options): ReactNode => {
26
+ const { children, setAnchorElement, isMounted, id, label, ariaLinkMode } = options;
27
+ // Only add link when mounted
28
+ const linkId = isMounted ? id : undefined;
26
29
 
27
30
  return useMemo(() => {
28
31
  if (!label) return children;
@@ -32,18 +35,21 @@ export const useInjectTooltipRef = (
32
35
  const ref = mergeRefs((children as any).ref, setAnchorElement);
33
36
  const props = { ...children.props, ref };
34
37
 
35
- // Add current tooltip to the aria-describedby if the label is not already present
36
- if (label !== props['aria-label'] && describedBy) {
37
- props['aria-describedby'] = [props['aria-describedby'], describedBy].filter(Boolean).join(' ');
38
+ // Do not add label/description if the tooltip label is already in aria-label
39
+ if (linkId && label !== props['aria-label']) {
40
+ if (props[ariaLinkMode]) props[ariaLinkMode] += ' ';
41
+ else props[ariaLinkMode] = '';
42
+ props[ariaLinkMode] += linkId;
38
43
  }
39
44
 
40
45
  return cloneElement(children, props);
41
46
  }
42
47
 
48
+ const aria = linkId ? { [ariaLinkMode]: linkId } : undefined;
43
49
  return (
44
- <div className="lumx-tooltip-anchor-wrapper" ref={setAnchorElement} aria-describedby={describedBy}>
50
+ <div className="lumx-tooltip-anchor-wrapper" ref={setAnchorElement} {...aria}>
45
51
  {children}
46
52
  </div>
47
53
  );
48
- }, [children, setAnchorElement, describedBy, label]);
54
+ }, [label, children, setAnchorElement, linkId, ariaLinkMode]);
49
55
  };
package/src/utils/type.ts CHANGED
@@ -59,7 +59,6 @@ export interface HasClassName {
59
59
  className?: string;
60
60
  }
61
61
 
62
-
63
62
  export interface HasCloseMode {
64
63
  /**
65
64
  * Choose how the children are hidden when closed