@atlaskit/tooltip 18.8.5 → 18.9.1
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/CHANGELOG.md +22 -0
- package/dist/cjs/tooltip.js +83 -51
- package/dist/es2019/tooltip.js +81 -52
- package/dist/esm/tooltip.js +83 -51
- package/dist/types/tooltip.d.ts +1 -1
- package/dist/types/types.d.ts +24 -0
- package/dist/types-ts4.5/tooltip.d.ts +1 -1
- package/dist/types-ts4.5/types.d.ts +24 -0
- package/package.json +7 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @atlaskit/tooltip
|
|
2
2
|
|
|
3
|
+
## 18.9.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#164322](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/164322)
|
|
8
|
+
[`e9317ed13ba40`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/e9317ed13ba40) -
|
|
9
|
+
Internal refactors to state to reduce the number of JavaScript event listeners.
|
|
10
|
+
|
|
11
|
+
## 18.9.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [`be6f923511512`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/be6f923511512) - -
|
|
16
|
+
Added new prop: `canAppear`, which can be used to _conditionally_ show tooltips.
|
|
17
|
+
- Added new prop: `isScreenReaderAnnouncementDisabled` which can be used to disable hidden text
|
|
18
|
+
for tooltips. This is useful when the Tooltip `content` matches the Tooltip trigger content as
|
|
19
|
+
hidden text is not required.
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
|
|
3
25
|
## 18.8.5
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/dist/cjs/tooltip.js
CHANGED
|
@@ -14,6 +14,7 @@ var _bindEventListener = require("bind-event-listener");
|
|
|
14
14
|
var _analyticsNext = require("@atlaskit/analytics-next");
|
|
15
15
|
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
|
|
16
16
|
var _useCloseOnEscapePress = _interopRequireDefault(require("@atlaskit/ds-lib/use-close-on-escape-press"));
|
|
17
|
+
var _useStableRef = _interopRequireDefault(require("@atlaskit/ds-lib/use-stable-ref"));
|
|
17
18
|
var _openLayerObserver = require("@atlaskit/layering/experimental/open-layer-observer");
|
|
18
19
|
var _motion = require("@atlaskit/motion");
|
|
19
20
|
var _durations = require("@atlaskit/motion/durations");
|
|
@@ -33,7 +34,7 @@ var tooltipZIndex = _constants.layers.tooltip();
|
|
|
33
34
|
var analyticsAttributes = {
|
|
34
35
|
componentName: 'tooltip',
|
|
35
36
|
packageName: "@atlaskit/tooltip",
|
|
36
|
-
packageVersion: "18.
|
|
37
|
+
packageVersion: "18.9.1"
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
// Inverts motion direction
|
|
@@ -78,6 +79,7 @@ function Tooltip(_ref) {
|
|
|
78
79
|
onShow = _ref$onShow === void 0 ? _noop.default : _ref$onShow,
|
|
79
80
|
_ref$onHide = _ref.onHide,
|
|
80
81
|
onHide = _ref$onHide === void 0 ? _noop.default : _ref$onHide,
|
|
82
|
+
canAppear = _ref.canAppear,
|
|
81
83
|
_ref$hideTooltipOnCli = _ref.hideTooltipOnClick,
|
|
82
84
|
hideTooltipOnClick = _ref$hideTooltipOnCli === void 0 ? false : _ref$hideTooltipOnCli,
|
|
83
85
|
_ref$hideTooltipOnMou = _ref.hideTooltipOnMouseDown,
|
|
@@ -86,7 +88,9 @@ function Tooltip(_ref) {
|
|
|
86
88
|
_ref$strategy = _ref.strategy,
|
|
87
89
|
strategy = _ref$strategy === void 0 ? 'fixed' : _ref$strategy,
|
|
88
90
|
_ref$ignoreTooltipPoi = _ref.ignoreTooltipPointerEvents,
|
|
89
|
-
ignoreTooltipPointerEvents = _ref$ignoreTooltipPoi === void 0 ? false : _ref$ignoreTooltipPoi
|
|
91
|
+
ignoreTooltipPointerEvents = _ref$ignoreTooltipPoi === void 0 ? false : _ref$ignoreTooltipPoi,
|
|
92
|
+
_ref$isScreenReaderAn = _ref.isScreenReaderAnnouncementDisabled,
|
|
93
|
+
isScreenReaderAnnouncementDisabled = _ref$isScreenReaderAn === void 0 ? false : _ref$isScreenReaderAn;
|
|
90
94
|
var tooltipPosition = position === 'mouse' ? mousePosition : position;
|
|
91
95
|
var onShowHandler = (0, _analyticsNext.usePlatformLeafSyntheticEventHandler)(_objectSpread({
|
|
92
96
|
fn: onShow,
|
|
@@ -119,21 +123,15 @@ function Tooltip(_ref) {
|
|
|
119
123
|
}, []);
|
|
120
124
|
|
|
121
125
|
// Putting a few things into refs so that we don't have to break memoization
|
|
122
|
-
var
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
var stableState = (0, _useStableRef.default)(state);
|
|
127
|
+
// These props are placed in separate refs instead of a single object to reduce memory usage.
|
|
128
|
+
// Placing them in the same object previously caused an increase in the number of JavaScript event listeners
|
|
129
|
+
// before garbage collection.
|
|
130
|
+
var onShowHandlerStable = (0, _useStableRef.default)(onShowHandler);
|
|
131
|
+
var onHideHandlerStable = (0, _useStableRef.default)(onHideHandler);
|
|
132
|
+
var delayStable = (0, _useStableRef.default)(delay);
|
|
133
|
+
var canAppearStable = (0, _useStableRef.default)(canAppear);
|
|
128
134
|
var hasCalledShowHandler = (0, _react.useRef)(false);
|
|
129
|
-
(0, _react.useEffect)(function () {
|
|
130
|
-
lastState.current = state;
|
|
131
|
-
lastDelay.current = delay;
|
|
132
|
-
lastHandlers.current = {
|
|
133
|
-
onShowHandler: onShowHandler,
|
|
134
|
-
onHideHandler: onHideHandler
|
|
135
|
-
};
|
|
136
|
-
}, [delay, onHideHandler, onShowHandler, state]);
|
|
137
135
|
var start = (0, _react.useCallback)(function (api) {
|
|
138
136
|
// @ts-ignore
|
|
139
137
|
apiRef.current = api;
|
|
@@ -145,7 +143,7 @@ function Tooltip(_ref) {
|
|
|
145
143
|
}
|
|
146
144
|
// Only call onHideHandler if we have called onShowHandler
|
|
147
145
|
if (hasCalledShowHandler.current) {
|
|
148
|
-
|
|
146
|
+
onHideHandlerStable.current();
|
|
149
147
|
}
|
|
150
148
|
// @ts-ignore
|
|
151
149
|
apiRef.current = null;
|
|
@@ -153,7 +151,7 @@ function Tooltip(_ref) {
|
|
|
153
151
|
hasCalledShowHandler.current = false;
|
|
154
152
|
// just in case
|
|
155
153
|
setState('hide');
|
|
156
|
-
}, []);
|
|
154
|
+
}, [onHideHandlerStable]);
|
|
157
155
|
var abort = (0, _react.useCallback)(function () {
|
|
158
156
|
if (!apiRef.current) {
|
|
159
157
|
return;
|
|
@@ -161,11 +159,11 @@ function Tooltip(_ref) {
|
|
|
161
159
|
apiRef.current.abort();
|
|
162
160
|
// Only call onHideHandler if we have called onShowHandler
|
|
163
161
|
if (hasCalledShowHandler.current) {
|
|
164
|
-
|
|
162
|
+
onHideHandlerStable.current();
|
|
165
163
|
}
|
|
166
164
|
// @ts-ignore
|
|
167
165
|
apiRef.current = null;
|
|
168
|
-
}, []);
|
|
166
|
+
}, [onHideHandlerStable]);
|
|
169
167
|
(0, _react.useEffect)(function mount() {
|
|
170
168
|
return function unmount() {
|
|
171
169
|
if (apiRef.current) {
|
|
@@ -196,7 +194,8 @@ function Tooltip(_ref) {
|
|
|
196
194
|
}
|
|
197
195
|
});
|
|
198
196
|
}, []);
|
|
199
|
-
var
|
|
197
|
+
var tryShowTooltip = (0, _react.useCallback)(function (source) {
|
|
198
|
+
var _canAppearStable$curr;
|
|
200
199
|
/**
|
|
201
200
|
* Prevent tooltips from being shown during a drag. This can occur with
|
|
202
201
|
* the native drag and drop API, where some pointer events can fire
|
|
@@ -205,24 +204,45 @@ function Tooltip(_ref) {
|
|
|
205
204
|
if (isDraggingRef.current) {
|
|
206
205
|
return;
|
|
207
206
|
}
|
|
207
|
+
|
|
208
|
+
// Another tooltip is has been active but we still have the old `api`
|
|
209
|
+
// around. We need to finish up the last usage.
|
|
210
|
+
// Note: just being safe - this should not happen
|
|
208
211
|
if (apiRef.current && !apiRef.current.isActive()) {
|
|
209
212
|
abort();
|
|
210
213
|
}
|
|
211
214
|
|
|
212
|
-
//
|
|
215
|
+
// This tooltip is already active, we can exit
|
|
213
216
|
if (apiRef.current && apiRef.current.isActive()) {
|
|
214
217
|
apiRef.current.keep();
|
|
215
218
|
return;
|
|
216
219
|
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Check if tooltip is allowed to show.
|
|
223
|
+
*
|
|
224
|
+
* Once a tooltip has started, or has scheduled to start
|
|
225
|
+
* we won't be checking `canAppear` again.
|
|
226
|
+
*
|
|
227
|
+
* - We don't want tooltips to disappear once they are shown
|
|
228
|
+
* - For consistency, we start after a single positive `canAppear`.
|
|
229
|
+
* Otherwise the amount of times we ask consumers would depend on
|
|
230
|
+
* how many times we get a "mousemove", which _could_ lead to situations
|
|
231
|
+
* where moving the mouse could result in a different outcome to if
|
|
232
|
+
* the mouse was not moved.
|
|
233
|
+
*/
|
|
234
|
+
if (canAppearStable.current && !((_canAppearStable$curr = canAppearStable.current) !== null && _canAppearStable$curr !== void 0 && _canAppearStable$curr.call(canAppearStable))) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
217
237
|
var entry = {
|
|
218
238
|
source: source,
|
|
219
|
-
delay:
|
|
239
|
+
delay: delayStable.current,
|
|
220
240
|
show: function show(_ref3) {
|
|
221
241
|
var isImmediate = _ref3.isImmediate;
|
|
222
242
|
// Call the onShow handler if it hasn't been called yet
|
|
223
243
|
if (!hasCalledShowHandler.current) {
|
|
224
244
|
hasCalledShowHandler.current = true;
|
|
225
|
-
|
|
245
|
+
onShowHandlerStable.current();
|
|
226
246
|
}
|
|
227
247
|
setState(isImmediate ? 'show-immediate' : 'fade-in');
|
|
228
248
|
},
|
|
@@ -238,7 +258,7 @@ function Tooltip(_ref) {
|
|
|
238
258
|
};
|
|
239
259
|
var api = (0, _tooltipManager.show)(entry);
|
|
240
260
|
start(api);
|
|
241
|
-
}, [
|
|
261
|
+
}, [canAppearStable, delayStable, done, start, abort, onShowHandlerStable]);
|
|
242
262
|
var hideTooltipOnEsc = (0, _react.useCallback)(function () {
|
|
243
263
|
var _apiRef$current2;
|
|
244
264
|
(_apiRef$current2 = apiRef.current) === null || _apiRef$current2 === void 0 || _apiRef$current2.requestHide({
|
|
@@ -314,8 +334,8 @@ function Tooltip(_ref) {
|
|
|
314
334
|
} : {
|
|
315
335
|
type: 'keyboard'
|
|
316
336
|
};
|
|
317
|
-
|
|
318
|
-
}, [position,
|
|
337
|
+
tryShowTooltip(source);
|
|
338
|
+
}, [position, tryShowTooltip]);
|
|
319
339
|
|
|
320
340
|
// Ideally we would be using onMouseEnter here, but
|
|
321
341
|
// because we are binding the event to the target parent
|
|
@@ -355,10 +375,12 @@ function Tooltip(_ref) {
|
|
|
355
375
|
}
|
|
356
376
|
}, []);
|
|
357
377
|
var onFocus = (0, _react.useCallback)(function () {
|
|
358
|
-
|
|
378
|
+
// TODO: this does not play well with `hideTooltipOnMouseDown`
|
|
379
|
+
// as "focus" will occur after the "mousedown".
|
|
380
|
+
tryShowTooltip({
|
|
359
381
|
type: 'keyboard'
|
|
360
382
|
});
|
|
361
|
-
}, [
|
|
383
|
+
}, [tryShowTooltip]);
|
|
362
384
|
var onBlur = (0, _react.useCallback)(function () {
|
|
363
385
|
if (apiRef.current) {
|
|
364
386
|
apiRef.current.requestHide({
|
|
@@ -368,18 +390,19 @@ function Tooltip(_ref) {
|
|
|
368
390
|
}, []);
|
|
369
391
|
var onAnimationFinished = (0, _react.useCallback)(function (transition) {
|
|
370
392
|
// Using lastState here because motion is not picking up the latest value
|
|
371
|
-
if (transition === 'exiting' &&
|
|
393
|
+
if (transition === 'exiting' && stableState.current === 'fade-out' && apiRef.current) {
|
|
372
394
|
// @ts-ignore: refs are writeable
|
|
373
395
|
apiRef.current.finishHideAnimation();
|
|
374
396
|
}
|
|
375
|
-
}, []);
|
|
397
|
+
}, [stableState]);
|
|
376
398
|
|
|
377
399
|
// Doing a cast because typescript is struggling to narrow the type
|
|
378
400
|
var CastTargetContainer = TargetContainer;
|
|
379
|
-
var
|
|
401
|
+
var shouldRenderTooltipPopup = state !== 'hide' && Boolean(content);
|
|
402
|
+
var shouldRenderHiddenContent = !isScreenReaderAnnouncementDisabled && shouldRenderTooltipPopup;
|
|
380
403
|
var shouldRenderTooltipChildren = state !== 'hide' && state !== 'fade-out';
|
|
381
404
|
(0, _openLayerObserver.useNotifyOpenLayerObserver)({
|
|
382
|
-
isOpen:
|
|
405
|
+
isOpen: shouldRenderTooltipPopup
|
|
383
406
|
});
|
|
384
407
|
var getReferenceElement = function getReferenceElement() {
|
|
385
408
|
var _apiRef$current4;
|
|
@@ -389,7 +412,7 @@ function Tooltip(_ref) {
|
|
|
389
412
|
}
|
|
390
413
|
return targetRef.current || undefined;
|
|
391
414
|
};
|
|
392
|
-
var
|
|
415
|
+
var tooltipIdForHiddenContent = (0, _useUniqueId.default)('tooltip', shouldRenderHiddenContent);
|
|
393
416
|
var tooltipTriggerProps = {
|
|
394
417
|
onMouseOver: onMouseOver,
|
|
395
418
|
onMouseOut: onMouseOut,
|
|
@@ -408,38 +431,47 @@ function Tooltip(_ref) {
|
|
|
408
431
|
|
|
409
432
|
// This useEffect is purely for managing the aria attribute when using the
|
|
410
433
|
// wrapped children approach.
|
|
434
|
+
var isChildrenAFunction = typeof children === 'function';
|
|
411
435
|
(0, _react.useEffect)(function () {
|
|
412
|
-
|
|
413
|
-
// means they are using the render prop API, and that is implemented in a
|
|
414
|
-
// different way. If there is no target element yet or tooltipId, we also
|
|
415
|
-
// shouldn't do anything because there is nothing to operate on or with.
|
|
416
|
-
if (!containerRef.current || !targetRef.current || !tooltipId) {
|
|
436
|
+
if (isChildrenAFunction) {
|
|
417
437
|
return;
|
|
418
438
|
}
|
|
439
|
+
|
|
440
|
+
// If `children` is _not_ a function, we are stepping outside of the public
|
|
441
|
+
// API to add a `aria-describedby` attribute.
|
|
442
|
+
|
|
419
443
|
var target = targetRef.current;
|
|
420
|
-
if (
|
|
421
|
-
|
|
422
|
-
} else {
|
|
423
|
-
target.removeAttribute('aria-describedby');
|
|
444
|
+
if (!target || !tooltipIdForHiddenContent) {
|
|
445
|
+
return;
|
|
424
446
|
}
|
|
425
|
-
|
|
426
|
-
|
|
447
|
+
target.setAttribute('aria-describedby', tooltipIdForHiddenContent);
|
|
448
|
+
return function () {
|
|
449
|
+
return target.removeAttribute('aria-describedby');
|
|
450
|
+
};
|
|
451
|
+
}, [isChildrenAFunction, tooltipIdForHiddenContent]);
|
|
452
|
+
var hiddenContent = shouldRenderHiddenContent ? /*#__PURE__*/_react.default.createElement("span", {
|
|
427
453
|
"data-testid": testId ? "".concat(testId, "-hidden") : undefined,
|
|
428
454
|
hidden: true,
|
|
429
|
-
id:
|
|
430
|
-
}, typeof content === 'function' ? content({}) : content);
|
|
455
|
+
id: tooltipIdForHiddenContent
|
|
456
|
+
}, typeof content === 'function' ? content({}) : content) : null;
|
|
431
457
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, typeof children === 'function' ?
|
|
432
458
|
/*#__PURE__*/
|
|
433
459
|
// once we deprecate the wrapped approach, we can put the aria
|
|
434
460
|
// attribute back into the tooltipTriggerProps and make it required
|
|
435
461
|
// instead of optional in `types`
|
|
436
462
|
_react.default.createElement(_react.default.Fragment, null, children(_objectSpread(_objectSpread({}, tooltipTriggerProps), {}, {
|
|
437
|
-
'aria-describedby':
|
|
463
|
+
'aria-describedby': tooltipIdForHiddenContent,
|
|
438
464
|
ref: setDirectRef
|
|
439
|
-
})),
|
|
440
|
-
ref: setImplicitRefFromChildren
|
|
465
|
+
})), hiddenContent) : /*#__PURE__*/_react.default.createElement(CastTargetContainer, (0, _extends2.default)({}, tooltipTriggerProps, {
|
|
466
|
+
ref: setImplicitRefFromChildren
|
|
467
|
+
/**
|
|
468
|
+
* TODO: Why is role="presentation" added?
|
|
469
|
+
* - Is it only to "remove" the `Container` from screen readers?
|
|
470
|
+
* - Why is it added only to the `Container` but not to `tooltipTriggerProps`?
|
|
471
|
+
* - Should `role="presentation"` only be used if `shouldRenderHiddenContent == false`?
|
|
472
|
+
*/,
|
|
441
473
|
role: "presentation"
|
|
442
|
-
}), children,
|
|
474
|
+
}), children, hiddenContent), shouldRenderTooltipPopup ? /*#__PURE__*/_react.default.createElement(_portal.default, {
|
|
443
475
|
zIndex: tooltipZIndex
|
|
444
476
|
}, /*#__PURE__*/_react.default.createElement(_popper.Popper, {
|
|
445
477
|
placement: tooltipPosition,
|
package/dist/es2019/tooltip.js
CHANGED
|
@@ -4,6 +4,7 @@ import { bind } from 'bind-event-listener';
|
|
|
4
4
|
import { usePlatformLeafSyntheticEventHandler } from '@atlaskit/analytics-next';
|
|
5
5
|
import noop from '@atlaskit/ds-lib/noop';
|
|
6
6
|
import useCloseOnEscapePress from '@atlaskit/ds-lib/use-close-on-escape-press';
|
|
7
|
+
import useStableRef from '@atlaskit/ds-lib/use-stable-ref';
|
|
7
8
|
import { useNotifyOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
|
|
8
9
|
import { ExitingPersistence, FadeIn } from '@atlaskit/motion';
|
|
9
10
|
import { mediumDurationMs } from '@atlaskit/motion/durations';
|
|
@@ -19,7 +20,7 @@ const tooltipZIndex = layers.tooltip();
|
|
|
19
20
|
const analyticsAttributes = {
|
|
20
21
|
componentName: 'tooltip',
|
|
21
22
|
packageName: "@atlaskit/tooltip",
|
|
22
|
-
packageVersion: "18.
|
|
23
|
+
packageVersion: "18.9.1"
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
// Inverts motion direction
|
|
@@ -55,11 +56,13 @@ function Tooltip({
|
|
|
55
56
|
delay = 300,
|
|
56
57
|
onShow = noop,
|
|
57
58
|
onHide = noop,
|
|
59
|
+
canAppear,
|
|
58
60
|
hideTooltipOnClick = false,
|
|
59
61
|
hideTooltipOnMouseDown = false,
|
|
60
62
|
analyticsContext,
|
|
61
63
|
strategy = 'fixed',
|
|
62
|
-
ignoreTooltipPointerEvents = false
|
|
64
|
+
ignoreTooltipPointerEvents = false,
|
|
65
|
+
isScreenReaderAnnouncementDisabled = false
|
|
63
66
|
}) {
|
|
64
67
|
const tooltipPosition = position === 'mouse' ? mousePosition : position;
|
|
65
68
|
const onShowHandler = usePlatformLeafSyntheticEventHandler({
|
|
@@ -92,21 +95,15 @@ function Tooltip({
|
|
|
92
95
|
}, []);
|
|
93
96
|
|
|
94
97
|
// Putting a few things into refs so that we don't have to break memoization
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
const stableState = useStableRef(state);
|
|
99
|
+
// These props are placed in separate refs instead of a single object to reduce memory usage.
|
|
100
|
+
// Placing them in the same object previously caused an increase in the number of JavaScript event listeners
|
|
101
|
+
// before garbage collection.
|
|
102
|
+
const onShowHandlerStable = useStableRef(onShowHandler);
|
|
103
|
+
const onHideHandlerStable = useStableRef(onHideHandler);
|
|
104
|
+
const delayStable = useStableRef(delay);
|
|
105
|
+
const canAppearStable = useStableRef(canAppear);
|
|
101
106
|
const hasCalledShowHandler = useRef(false);
|
|
102
|
-
useEffect(() => {
|
|
103
|
-
lastState.current = state;
|
|
104
|
-
lastDelay.current = delay;
|
|
105
|
-
lastHandlers.current = {
|
|
106
|
-
onShowHandler,
|
|
107
|
-
onHideHandler
|
|
108
|
-
};
|
|
109
|
-
}, [delay, onHideHandler, onShowHandler, state]);
|
|
110
107
|
const start = useCallback(api => {
|
|
111
108
|
// @ts-ignore
|
|
112
109
|
apiRef.current = api;
|
|
@@ -118,7 +115,7 @@ function Tooltip({
|
|
|
118
115
|
}
|
|
119
116
|
// Only call onHideHandler if we have called onShowHandler
|
|
120
117
|
if (hasCalledShowHandler.current) {
|
|
121
|
-
|
|
118
|
+
onHideHandlerStable.current();
|
|
122
119
|
}
|
|
123
120
|
// @ts-ignore
|
|
124
121
|
apiRef.current = null;
|
|
@@ -126,7 +123,7 @@ function Tooltip({
|
|
|
126
123
|
hasCalledShowHandler.current = false;
|
|
127
124
|
// just in case
|
|
128
125
|
setState('hide');
|
|
129
|
-
}, []);
|
|
126
|
+
}, [onHideHandlerStable]);
|
|
130
127
|
const abort = useCallback(() => {
|
|
131
128
|
if (!apiRef.current) {
|
|
132
129
|
return;
|
|
@@ -134,11 +131,11 @@ function Tooltip({
|
|
|
134
131
|
apiRef.current.abort();
|
|
135
132
|
// Only call onHideHandler if we have called onShowHandler
|
|
136
133
|
if (hasCalledShowHandler.current) {
|
|
137
|
-
|
|
134
|
+
onHideHandlerStable.current();
|
|
138
135
|
}
|
|
139
136
|
// @ts-ignore
|
|
140
137
|
apiRef.current = null;
|
|
141
|
-
}, []);
|
|
138
|
+
}, [onHideHandlerStable]);
|
|
142
139
|
useEffect(function mount() {
|
|
143
140
|
return function unmount() {
|
|
144
141
|
if (apiRef.current) {
|
|
@@ -170,7 +167,8 @@ function Tooltip({
|
|
|
170
167
|
}
|
|
171
168
|
});
|
|
172
169
|
}, []);
|
|
173
|
-
const
|
|
170
|
+
const tryShowTooltip = useCallback(source => {
|
|
171
|
+
var _canAppearStable$curr;
|
|
174
172
|
/**
|
|
175
173
|
* Prevent tooltips from being shown during a drag. This can occur with
|
|
176
174
|
* the native drag and drop API, where some pointer events can fire
|
|
@@ -179,25 +177,46 @@ function Tooltip({
|
|
|
179
177
|
if (isDraggingRef.current) {
|
|
180
178
|
return;
|
|
181
179
|
}
|
|
180
|
+
|
|
181
|
+
// Another tooltip is has been active but we still have the old `api`
|
|
182
|
+
// around. We need to finish up the last usage.
|
|
183
|
+
// Note: just being safe - this should not happen
|
|
182
184
|
if (apiRef.current && !apiRef.current.isActive()) {
|
|
183
185
|
abort();
|
|
184
186
|
}
|
|
185
187
|
|
|
186
|
-
//
|
|
188
|
+
// This tooltip is already active, we can exit
|
|
187
189
|
if (apiRef.current && apiRef.current.isActive()) {
|
|
188
190
|
apiRef.current.keep();
|
|
189
191
|
return;
|
|
190
192
|
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Check if tooltip is allowed to show.
|
|
196
|
+
*
|
|
197
|
+
* Once a tooltip has started, or has scheduled to start
|
|
198
|
+
* we won't be checking `canAppear` again.
|
|
199
|
+
*
|
|
200
|
+
* - We don't want tooltips to disappear once they are shown
|
|
201
|
+
* - For consistency, we start after a single positive `canAppear`.
|
|
202
|
+
* Otherwise the amount of times we ask consumers would depend on
|
|
203
|
+
* how many times we get a "mousemove", which _could_ lead to situations
|
|
204
|
+
* where moving the mouse could result in a different outcome to if
|
|
205
|
+
* the mouse was not moved.
|
|
206
|
+
*/
|
|
207
|
+
if (canAppearStable.current && !((_canAppearStable$curr = canAppearStable.current) !== null && _canAppearStable$curr !== void 0 && _canAppearStable$curr.call(canAppearStable))) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
191
210
|
const entry = {
|
|
192
211
|
source,
|
|
193
|
-
delay:
|
|
212
|
+
delay: delayStable.current,
|
|
194
213
|
show: ({
|
|
195
214
|
isImmediate
|
|
196
215
|
}) => {
|
|
197
216
|
// Call the onShow handler if it hasn't been called yet
|
|
198
217
|
if (!hasCalledShowHandler.current) {
|
|
199
218
|
hasCalledShowHandler.current = true;
|
|
200
|
-
|
|
219
|
+
onShowHandlerStable.current();
|
|
201
220
|
}
|
|
202
221
|
setState(isImmediate ? 'show-immediate' : 'fade-in');
|
|
203
222
|
},
|
|
@@ -210,11 +229,11 @@ function Tooltip({
|
|
|
210
229
|
setState('before-fade-out');
|
|
211
230
|
}
|
|
212
231
|
},
|
|
213
|
-
done
|
|
232
|
+
done
|
|
214
233
|
};
|
|
215
234
|
const api = show(entry);
|
|
216
235
|
start(api);
|
|
217
|
-
}, [
|
|
236
|
+
}, [canAppearStable, delayStable, done, start, abort, onShowHandlerStable]);
|
|
218
237
|
const hideTooltipOnEsc = useCallback(() => {
|
|
219
238
|
var _apiRef$current2;
|
|
220
239
|
(_apiRef$current2 = apiRef.current) === null || _apiRef$current2 === void 0 ? void 0 : _apiRef$current2.requestHide({
|
|
@@ -290,8 +309,8 @@ function Tooltip({
|
|
|
290
309
|
} : {
|
|
291
310
|
type: 'keyboard'
|
|
292
311
|
};
|
|
293
|
-
|
|
294
|
-
}, [position,
|
|
312
|
+
tryShowTooltip(source);
|
|
313
|
+
}, [position, tryShowTooltip]);
|
|
295
314
|
|
|
296
315
|
// Ideally we would be using onMouseEnter here, but
|
|
297
316
|
// because we are binding the event to the target parent
|
|
@@ -331,10 +350,12 @@ function Tooltip({
|
|
|
331
350
|
}
|
|
332
351
|
}, []);
|
|
333
352
|
const onFocus = useCallback(() => {
|
|
334
|
-
|
|
353
|
+
// TODO: this does not play well with `hideTooltipOnMouseDown`
|
|
354
|
+
// as "focus" will occur after the "mousedown".
|
|
355
|
+
tryShowTooltip({
|
|
335
356
|
type: 'keyboard'
|
|
336
357
|
});
|
|
337
|
-
}, [
|
|
358
|
+
}, [tryShowTooltip]);
|
|
338
359
|
const onBlur = useCallback(() => {
|
|
339
360
|
if (apiRef.current) {
|
|
340
361
|
apiRef.current.requestHide({
|
|
@@ -344,18 +365,19 @@ function Tooltip({
|
|
|
344
365
|
}, []);
|
|
345
366
|
const onAnimationFinished = useCallback(transition => {
|
|
346
367
|
// Using lastState here because motion is not picking up the latest value
|
|
347
|
-
if (transition === 'exiting' &&
|
|
368
|
+
if (transition === 'exiting' && stableState.current === 'fade-out' && apiRef.current) {
|
|
348
369
|
// @ts-ignore: refs are writeable
|
|
349
370
|
apiRef.current.finishHideAnimation();
|
|
350
371
|
}
|
|
351
|
-
}, []);
|
|
372
|
+
}, [stableState]);
|
|
352
373
|
|
|
353
374
|
// Doing a cast because typescript is struggling to narrow the type
|
|
354
375
|
const CastTargetContainer = TargetContainer;
|
|
355
|
-
const
|
|
376
|
+
const shouldRenderTooltipPopup = state !== 'hide' && Boolean(content);
|
|
377
|
+
const shouldRenderHiddenContent = !isScreenReaderAnnouncementDisabled && shouldRenderTooltipPopup;
|
|
356
378
|
const shouldRenderTooltipChildren = state !== 'hide' && state !== 'fade-out';
|
|
357
379
|
useNotifyOpenLayerObserver({
|
|
358
|
-
isOpen:
|
|
380
|
+
isOpen: shouldRenderTooltipPopup
|
|
359
381
|
});
|
|
360
382
|
const getReferenceElement = () => {
|
|
361
383
|
var _apiRef$current4;
|
|
@@ -365,7 +387,7 @@ function Tooltip({
|
|
|
365
387
|
}
|
|
366
388
|
return targetRef.current || undefined;
|
|
367
389
|
};
|
|
368
|
-
const
|
|
390
|
+
const tooltipIdForHiddenContent = useUniqueId('tooltip', shouldRenderHiddenContent);
|
|
369
391
|
const tooltipTriggerProps = {
|
|
370
392
|
onMouseOver,
|
|
371
393
|
onMouseOut,
|
|
@@ -384,26 +406,27 @@ function Tooltip({
|
|
|
384
406
|
|
|
385
407
|
// This useEffect is purely for managing the aria attribute when using the
|
|
386
408
|
// wrapped children approach.
|
|
409
|
+
const isChildrenAFunction = typeof children === 'function';
|
|
387
410
|
useEffect(() => {
|
|
388
|
-
|
|
389
|
-
// means they are using the render prop API, and that is implemented in a
|
|
390
|
-
// different way. If there is no target element yet or tooltipId, we also
|
|
391
|
-
// shouldn't do anything because there is nothing to operate on or with.
|
|
392
|
-
if (!containerRef.current || !targetRef.current || !tooltipId) {
|
|
411
|
+
if (isChildrenAFunction) {
|
|
393
412
|
return;
|
|
394
413
|
}
|
|
414
|
+
|
|
415
|
+
// If `children` is _not_ a function, we are stepping outside of the public
|
|
416
|
+
// API to add a `aria-describedby` attribute.
|
|
417
|
+
|
|
395
418
|
const target = targetRef.current;
|
|
396
|
-
if (
|
|
397
|
-
|
|
398
|
-
} else {
|
|
399
|
-
target.removeAttribute('aria-describedby');
|
|
419
|
+
if (!target || !tooltipIdForHiddenContent) {
|
|
420
|
+
return;
|
|
400
421
|
}
|
|
401
|
-
|
|
402
|
-
|
|
422
|
+
target.setAttribute('aria-describedby', tooltipIdForHiddenContent);
|
|
423
|
+
return () => target.removeAttribute('aria-describedby');
|
|
424
|
+
}, [isChildrenAFunction, tooltipIdForHiddenContent]);
|
|
425
|
+
const hiddenContent = shouldRenderHiddenContent ? /*#__PURE__*/React.createElement("span", {
|
|
403
426
|
"data-testid": testId ? `${testId}-hidden` : undefined,
|
|
404
427
|
hidden: true,
|
|
405
|
-
id:
|
|
406
|
-
}, typeof content === 'function' ? content({}) : content);
|
|
428
|
+
id: tooltipIdForHiddenContent
|
|
429
|
+
}, typeof content === 'function' ? content({}) : content) : null;
|
|
407
430
|
return /*#__PURE__*/React.createElement(React.Fragment, null, typeof children === 'function' ?
|
|
408
431
|
/*#__PURE__*/
|
|
409
432
|
// once we deprecate the wrapped approach, we can put the aria
|
|
@@ -411,12 +434,18 @@ function Tooltip({
|
|
|
411
434
|
// instead of optional in `types`
|
|
412
435
|
React.createElement(React.Fragment, null, children({
|
|
413
436
|
...tooltipTriggerProps,
|
|
414
|
-
'aria-describedby':
|
|
437
|
+
'aria-describedby': tooltipIdForHiddenContent,
|
|
415
438
|
ref: setDirectRef
|
|
416
|
-
}),
|
|
417
|
-
ref: setImplicitRefFromChildren
|
|
439
|
+
}), hiddenContent) : /*#__PURE__*/React.createElement(CastTargetContainer, _extends({}, tooltipTriggerProps, {
|
|
440
|
+
ref: setImplicitRefFromChildren
|
|
441
|
+
/**
|
|
442
|
+
* TODO: Why is role="presentation" added?
|
|
443
|
+
* - Is it only to "remove" the `Container` from screen readers?
|
|
444
|
+
* - Why is it added only to the `Container` but not to `tooltipTriggerProps`?
|
|
445
|
+
* - Should `role="presentation"` only be used if `shouldRenderHiddenContent == false`?
|
|
446
|
+
*/,
|
|
418
447
|
role: "presentation"
|
|
419
|
-
}), children,
|
|
448
|
+
}), children, hiddenContent), shouldRenderTooltipPopup ? /*#__PURE__*/React.createElement(Portal, {
|
|
420
449
|
zIndex: tooltipZIndex
|
|
421
450
|
}, /*#__PURE__*/React.createElement(Popper, {
|
|
422
451
|
placement: tooltipPosition,
|
package/dist/esm/tooltip.js
CHANGED
|
@@ -8,6 +8,7 @@ import { bind } from 'bind-event-listener';
|
|
|
8
8
|
import { usePlatformLeafSyntheticEventHandler } from '@atlaskit/analytics-next';
|
|
9
9
|
import noop from '@atlaskit/ds-lib/noop';
|
|
10
10
|
import useCloseOnEscapePress from '@atlaskit/ds-lib/use-close-on-escape-press';
|
|
11
|
+
import useStableRef from '@atlaskit/ds-lib/use-stable-ref';
|
|
11
12
|
import { useNotifyOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
|
|
12
13
|
import { ExitingPersistence, FadeIn } from '@atlaskit/motion';
|
|
13
14
|
import { mediumDurationMs } from '@atlaskit/motion/durations';
|
|
@@ -23,7 +24,7 @@ var tooltipZIndex = layers.tooltip();
|
|
|
23
24
|
var analyticsAttributes = {
|
|
24
25
|
componentName: 'tooltip',
|
|
25
26
|
packageName: "@atlaskit/tooltip",
|
|
26
|
-
packageVersion: "18.
|
|
27
|
+
packageVersion: "18.9.1"
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
// Inverts motion direction
|
|
@@ -68,6 +69,7 @@ function Tooltip(_ref) {
|
|
|
68
69
|
onShow = _ref$onShow === void 0 ? noop : _ref$onShow,
|
|
69
70
|
_ref$onHide = _ref.onHide,
|
|
70
71
|
onHide = _ref$onHide === void 0 ? noop : _ref$onHide,
|
|
72
|
+
canAppear = _ref.canAppear,
|
|
71
73
|
_ref$hideTooltipOnCli = _ref.hideTooltipOnClick,
|
|
72
74
|
hideTooltipOnClick = _ref$hideTooltipOnCli === void 0 ? false : _ref$hideTooltipOnCli,
|
|
73
75
|
_ref$hideTooltipOnMou = _ref.hideTooltipOnMouseDown,
|
|
@@ -76,7 +78,9 @@ function Tooltip(_ref) {
|
|
|
76
78
|
_ref$strategy = _ref.strategy,
|
|
77
79
|
strategy = _ref$strategy === void 0 ? 'fixed' : _ref$strategy,
|
|
78
80
|
_ref$ignoreTooltipPoi = _ref.ignoreTooltipPointerEvents,
|
|
79
|
-
ignoreTooltipPointerEvents = _ref$ignoreTooltipPoi === void 0 ? false : _ref$ignoreTooltipPoi
|
|
81
|
+
ignoreTooltipPointerEvents = _ref$ignoreTooltipPoi === void 0 ? false : _ref$ignoreTooltipPoi,
|
|
82
|
+
_ref$isScreenReaderAn = _ref.isScreenReaderAnnouncementDisabled,
|
|
83
|
+
isScreenReaderAnnouncementDisabled = _ref$isScreenReaderAn === void 0 ? false : _ref$isScreenReaderAn;
|
|
80
84
|
var tooltipPosition = position === 'mouse' ? mousePosition : position;
|
|
81
85
|
var onShowHandler = usePlatformLeafSyntheticEventHandler(_objectSpread({
|
|
82
86
|
fn: onShow,
|
|
@@ -109,21 +113,15 @@ function Tooltip(_ref) {
|
|
|
109
113
|
}, []);
|
|
110
114
|
|
|
111
115
|
// Putting a few things into refs so that we don't have to break memoization
|
|
112
|
-
var
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
var stableState = useStableRef(state);
|
|
117
|
+
// These props are placed in separate refs instead of a single object to reduce memory usage.
|
|
118
|
+
// Placing them in the same object previously caused an increase in the number of JavaScript event listeners
|
|
119
|
+
// before garbage collection.
|
|
120
|
+
var onShowHandlerStable = useStableRef(onShowHandler);
|
|
121
|
+
var onHideHandlerStable = useStableRef(onHideHandler);
|
|
122
|
+
var delayStable = useStableRef(delay);
|
|
123
|
+
var canAppearStable = useStableRef(canAppear);
|
|
118
124
|
var hasCalledShowHandler = useRef(false);
|
|
119
|
-
useEffect(function () {
|
|
120
|
-
lastState.current = state;
|
|
121
|
-
lastDelay.current = delay;
|
|
122
|
-
lastHandlers.current = {
|
|
123
|
-
onShowHandler: onShowHandler,
|
|
124
|
-
onHideHandler: onHideHandler
|
|
125
|
-
};
|
|
126
|
-
}, [delay, onHideHandler, onShowHandler, state]);
|
|
127
125
|
var start = useCallback(function (api) {
|
|
128
126
|
// @ts-ignore
|
|
129
127
|
apiRef.current = api;
|
|
@@ -135,7 +133,7 @@ function Tooltip(_ref) {
|
|
|
135
133
|
}
|
|
136
134
|
// Only call onHideHandler if we have called onShowHandler
|
|
137
135
|
if (hasCalledShowHandler.current) {
|
|
138
|
-
|
|
136
|
+
onHideHandlerStable.current();
|
|
139
137
|
}
|
|
140
138
|
// @ts-ignore
|
|
141
139
|
apiRef.current = null;
|
|
@@ -143,7 +141,7 @@ function Tooltip(_ref) {
|
|
|
143
141
|
hasCalledShowHandler.current = false;
|
|
144
142
|
// just in case
|
|
145
143
|
setState('hide');
|
|
146
|
-
}, []);
|
|
144
|
+
}, [onHideHandlerStable]);
|
|
147
145
|
var abort = useCallback(function () {
|
|
148
146
|
if (!apiRef.current) {
|
|
149
147
|
return;
|
|
@@ -151,11 +149,11 @@ function Tooltip(_ref) {
|
|
|
151
149
|
apiRef.current.abort();
|
|
152
150
|
// Only call onHideHandler if we have called onShowHandler
|
|
153
151
|
if (hasCalledShowHandler.current) {
|
|
154
|
-
|
|
152
|
+
onHideHandlerStable.current();
|
|
155
153
|
}
|
|
156
154
|
// @ts-ignore
|
|
157
155
|
apiRef.current = null;
|
|
158
|
-
}, []);
|
|
156
|
+
}, [onHideHandlerStable]);
|
|
159
157
|
useEffect(function mount() {
|
|
160
158
|
return function unmount() {
|
|
161
159
|
if (apiRef.current) {
|
|
@@ -186,7 +184,8 @@ function Tooltip(_ref) {
|
|
|
186
184
|
}
|
|
187
185
|
});
|
|
188
186
|
}, []);
|
|
189
|
-
var
|
|
187
|
+
var tryShowTooltip = useCallback(function (source) {
|
|
188
|
+
var _canAppearStable$curr;
|
|
190
189
|
/**
|
|
191
190
|
* Prevent tooltips from being shown during a drag. This can occur with
|
|
192
191
|
* the native drag and drop API, where some pointer events can fire
|
|
@@ -195,24 +194,45 @@ function Tooltip(_ref) {
|
|
|
195
194
|
if (isDraggingRef.current) {
|
|
196
195
|
return;
|
|
197
196
|
}
|
|
197
|
+
|
|
198
|
+
// Another tooltip is has been active but we still have the old `api`
|
|
199
|
+
// around. We need to finish up the last usage.
|
|
200
|
+
// Note: just being safe - this should not happen
|
|
198
201
|
if (apiRef.current && !apiRef.current.isActive()) {
|
|
199
202
|
abort();
|
|
200
203
|
}
|
|
201
204
|
|
|
202
|
-
//
|
|
205
|
+
// This tooltip is already active, we can exit
|
|
203
206
|
if (apiRef.current && apiRef.current.isActive()) {
|
|
204
207
|
apiRef.current.keep();
|
|
205
208
|
return;
|
|
206
209
|
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Check if tooltip is allowed to show.
|
|
213
|
+
*
|
|
214
|
+
* Once a tooltip has started, or has scheduled to start
|
|
215
|
+
* we won't be checking `canAppear` again.
|
|
216
|
+
*
|
|
217
|
+
* - We don't want tooltips to disappear once they are shown
|
|
218
|
+
* - For consistency, we start after a single positive `canAppear`.
|
|
219
|
+
* Otherwise the amount of times we ask consumers would depend on
|
|
220
|
+
* how many times we get a "mousemove", which _could_ lead to situations
|
|
221
|
+
* where moving the mouse could result in a different outcome to if
|
|
222
|
+
* the mouse was not moved.
|
|
223
|
+
*/
|
|
224
|
+
if (canAppearStable.current && !((_canAppearStable$curr = canAppearStable.current) !== null && _canAppearStable$curr !== void 0 && _canAppearStable$curr.call(canAppearStable))) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
207
227
|
var entry = {
|
|
208
228
|
source: source,
|
|
209
|
-
delay:
|
|
229
|
+
delay: delayStable.current,
|
|
210
230
|
show: function show(_ref3) {
|
|
211
231
|
var isImmediate = _ref3.isImmediate;
|
|
212
232
|
// Call the onShow handler if it hasn't been called yet
|
|
213
233
|
if (!hasCalledShowHandler.current) {
|
|
214
234
|
hasCalledShowHandler.current = true;
|
|
215
|
-
|
|
235
|
+
onShowHandlerStable.current();
|
|
216
236
|
}
|
|
217
237
|
setState(isImmediate ? 'show-immediate' : 'fade-in');
|
|
218
238
|
},
|
|
@@ -228,7 +248,7 @@ function Tooltip(_ref) {
|
|
|
228
248
|
};
|
|
229
249
|
var api = show(entry);
|
|
230
250
|
start(api);
|
|
231
|
-
}, [
|
|
251
|
+
}, [canAppearStable, delayStable, done, start, abort, onShowHandlerStable]);
|
|
232
252
|
var hideTooltipOnEsc = useCallback(function () {
|
|
233
253
|
var _apiRef$current2;
|
|
234
254
|
(_apiRef$current2 = apiRef.current) === null || _apiRef$current2 === void 0 || _apiRef$current2.requestHide({
|
|
@@ -304,8 +324,8 @@ function Tooltip(_ref) {
|
|
|
304
324
|
} : {
|
|
305
325
|
type: 'keyboard'
|
|
306
326
|
};
|
|
307
|
-
|
|
308
|
-
}, [position,
|
|
327
|
+
tryShowTooltip(source);
|
|
328
|
+
}, [position, tryShowTooltip]);
|
|
309
329
|
|
|
310
330
|
// Ideally we would be using onMouseEnter here, but
|
|
311
331
|
// because we are binding the event to the target parent
|
|
@@ -345,10 +365,12 @@ function Tooltip(_ref) {
|
|
|
345
365
|
}
|
|
346
366
|
}, []);
|
|
347
367
|
var onFocus = useCallback(function () {
|
|
348
|
-
|
|
368
|
+
// TODO: this does not play well with `hideTooltipOnMouseDown`
|
|
369
|
+
// as "focus" will occur after the "mousedown".
|
|
370
|
+
tryShowTooltip({
|
|
349
371
|
type: 'keyboard'
|
|
350
372
|
});
|
|
351
|
-
}, [
|
|
373
|
+
}, [tryShowTooltip]);
|
|
352
374
|
var onBlur = useCallback(function () {
|
|
353
375
|
if (apiRef.current) {
|
|
354
376
|
apiRef.current.requestHide({
|
|
@@ -358,18 +380,19 @@ function Tooltip(_ref) {
|
|
|
358
380
|
}, []);
|
|
359
381
|
var onAnimationFinished = useCallback(function (transition) {
|
|
360
382
|
// Using lastState here because motion is not picking up the latest value
|
|
361
|
-
if (transition === 'exiting' &&
|
|
383
|
+
if (transition === 'exiting' && stableState.current === 'fade-out' && apiRef.current) {
|
|
362
384
|
// @ts-ignore: refs are writeable
|
|
363
385
|
apiRef.current.finishHideAnimation();
|
|
364
386
|
}
|
|
365
|
-
}, []);
|
|
387
|
+
}, [stableState]);
|
|
366
388
|
|
|
367
389
|
// Doing a cast because typescript is struggling to narrow the type
|
|
368
390
|
var CastTargetContainer = TargetContainer;
|
|
369
|
-
var
|
|
391
|
+
var shouldRenderTooltipPopup = state !== 'hide' && Boolean(content);
|
|
392
|
+
var shouldRenderHiddenContent = !isScreenReaderAnnouncementDisabled && shouldRenderTooltipPopup;
|
|
370
393
|
var shouldRenderTooltipChildren = state !== 'hide' && state !== 'fade-out';
|
|
371
394
|
useNotifyOpenLayerObserver({
|
|
372
|
-
isOpen:
|
|
395
|
+
isOpen: shouldRenderTooltipPopup
|
|
373
396
|
});
|
|
374
397
|
var getReferenceElement = function getReferenceElement() {
|
|
375
398
|
var _apiRef$current4;
|
|
@@ -379,7 +402,7 @@ function Tooltip(_ref) {
|
|
|
379
402
|
}
|
|
380
403
|
return targetRef.current || undefined;
|
|
381
404
|
};
|
|
382
|
-
var
|
|
405
|
+
var tooltipIdForHiddenContent = useUniqueId('tooltip', shouldRenderHiddenContent);
|
|
383
406
|
var tooltipTriggerProps = {
|
|
384
407
|
onMouseOver: onMouseOver,
|
|
385
408
|
onMouseOut: onMouseOut,
|
|
@@ -398,38 +421,47 @@ function Tooltip(_ref) {
|
|
|
398
421
|
|
|
399
422
|
// This useEffect is purely for managing the aria attribute when using the
|
|
400
423
|
// wrapped children approach.
|
|
424
|
+
var isChildrenAFunction = typeof children === 'function';
|
|
401
425
|
useEffect(function () {
|
|
402
|
-
|
|
403
|
-
// means they are using the render prop API, and that is implemented in a
|
|
404
|
-
// different way. If there is no target element yet or tooltipId, we also
|
|
405
|
-
// shouldn't do anything because there is nothing to operate on or with.
|
|
406
|
-
if (!containerRef.current || !targetRef.current || !tooltipId) {
|
|
426
|
+
if (isChildrenAFunction) {
|
|
407
427
|
return;
|
|
408
428
|
}
|
|
429
|
+
|
|
430
|
+
// If `children` is _not_ a function, we are stepping outside of the public
|
|
431
|
+
// API to add a `aria-describedby` attribute.
|
|
432
|
+
|
|
409
433
|
var target = targetRef.current;
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
} else {
|
|
413
|
-
target.removeAttribute('aria-describedby');
|
|
434
|
+
if (!target || !tooltipIdForHiddenContent) {
|
|
435
|
+
return;
|
|
414
436
|
}
|
|
415
|
-
|
|
416
|
-
|
|
437
|
+
target.setAttribute('aria-describedby', tooltipIdForHiddenContent);
|
|
438
|
+
return function () {
|
|
439
|
+
return target.removeAttribute('aria-describedby');
|
|
440
|
+
};
|
|
441
|
+
}, [isChildrenAFunction, tooltipIdForHiddenContent]);
|
|
442
|
+
var hiddenContent = shouldRenderHiddenContent ? /*#__PURE__*/React.createElement("span", {
|
|
417
443
|
"data-testid": testId ? "".concat(testId, "-hidden") : undefined,
|
|
418
444
|
hidden: true,
|
|
419
|
-
id:
|
|
420
|
-
}, typeof content === 'function' ? content({}) : content);
|
|
445
|
+
id: tooltipIdForHiddenContent
|
|
446
|
+
}, typeof content === 'function' ? content({}) : content) : null;
|
|
421
447
|
return /*#__PURE__*/React.createElement(React.Fragment, null, typeof children === 'function' ?
|
|
422
448
|
/*#__PURE__*/
|
|
423
449
|
// once we deprecate the wrapped approach, we can put the aria
|
|
424
450
|
// attribute back into the tooltipTriggerProps and make it required
|
|
425
451
|
// instead of optional in `types`
|
|
426
452
|
React.createElement(React.Fragment, null, children(_objectSpread(_objectSpread({}, tooltipTriggerProps), {}, {
|
|
427
|
-
'aria-describedby':
|
|
453
|
+
'aria-describedby': tooltipIdForHiddenContent,
|
|
428
454
|
ref: setDirectRef
|
|
429
|
-
})),
|
|
430
|
-
ref: setImplicitRefFromChildren
|
|
455
|
+
})), hiddenContent) : /*#__PURE__*/React.createElement(CastTargetContainer, _extends({}, tooltipTriggerProps, {
|
|
456
|
+
ref: setImplicitRefFromChildren
|
|
457
|
+
/**
|
|
458
|
+
* TODO: Why is role="presentation" added?
|
|
459
|
+
* - Is it only to "remove" the `Container` from screen readers?
|
|
460
|
+
* - Why is it added only to the `Container` but not to `tooltipTriggerProps`?
|
|
461
|
+
* - Should `role="presentation"` only be used if `shouldRenderHiddenContent == false`?
|
|
462
|
+
*/,
|
|
431
463
|
role: "presentation"
|
|
432
|
-
}), children,
|
|
464
|
+
}), children, hiddenContent), shouldRenderTooltipPopup ? /*#__PURE__*/React.createElement(Portal, {
|
|
433
465
|
zIndex: tooltipZIndex
|
|
434
466
|
}, /*#__PURE__*/React.createElement(Popper, {
|
|
435
467
|
placement: tooltipPosition,
|
package/dist/types/tooltip.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { type TooltipProps } from './types';
|
|
|
5
5
|
*
|
|
6
6
|
* A tooltip is a floating, non-actionable label used to explain a user interface element or feature.
|
|
7
7
|
*/
|
|
8
|
-
declare function Tooltip({ children, position, mousePosition, content, truncate, component: Container, tag: TargetContainer, testId, delay, onShow, onHide, hideTooltipOnClick, hideTooltipOnMouseDown, analyticsContext, strategy, ignoreTooltipPointerEvents, }: TooltipProps): JSX.Element;
|
|
8
|
+
declare function Tooltip({ children, position, mousePosition, content, truncate, component: Container, tag: TargetContainer, testId, delay, onShow, onHide, canAppear, hideTooltipOnClick, hideTooltipOnMouseDown, analyticsContext, strategy, ignoreTooltipPointerEvents, isScreenReaderAnnouncementDisabled, }: TooltipProps): JSX.Element;
|
|
9
9
|
declare namespace Tooltip {
|
|
10
10
|
var displayName: string;
|
|
11
11
|
}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -22,6 +22,11 @@ export interface TooltipProps {
|
|
|
22
22
|
* 2. Function which returns a `ReactNode`
|
|
23
23
|
* The benefit of the second approach is that it allows you to consume the `update` render prop.
|
|
24
24
|
* This `update` function can be called to manually recalculate the position of the tooltip.
|
|
25
|
+
*
|
|
26
|
+
* This content will be rendered into two places:
|
|
27
|
+
* 1. Into the tooltip
|
|
28
|
+
* 2. Into a hidden element for screen readers (unless `isScreenReaderAnnouncementDisabled` is set to `true`)
|
|
29
|
+
*
|
|
25
30
|
*/
|
|
26
31
|
content: ReactNode | (({ update }: {
|
|
27
32
|
update?: () => void;
|
|
@@ -55,6 +60,25 @@ export interface TooltipProps {
|
|
|
55
60
|
* When interacting with the target element using a keyboard, it will use this position against the target element instead.
|
|
56
61
|
*/
|
|
57
62
|
mousePosition?: PositionTypeBase;
|
|
63
|
+
/**
|
|
64
|
+
* Whether or not the tooltip can be displayed. Once a tooltip
|
|
65
|
+
* is scheduled to be displayed, or is already displayed, it will
|
|
66
|
+
* continue to be shown.
|
|
67
|
+
*
|
|
68
|
+
* @description
|
|
69
|
+
*
|
|
70
|
+
* `canAppear()` is called in response to user events, and
|
|
71
|
+
* not during the rendering of components.
|
|
72
|
+
*
|
|
73
|
+
*/
|
|
74
|
+
canAppear?: () => boolean;
|
|
75
|
+
/**
|
|
76
|
+
* By default tooltip content will be duplicated into a hidden element so
|
|
77
|
+
* it can be read out by a screen reader. Sometimes this is not ideal as
|
|
78
|
+
* it can result in the same content be announced twice. For those situations,
|
|
79
|
+
* you can leverage this prop to disable the duplicate hidden text.
|
|
80
|
+
*/
|
|
81
|
+
isScreenReaderAnnouncementDisabled?: boolean;
|
|
58
82
|
/**
|
|
59
83
|
* Function to be called when the tooltip will be shown. It's called when the
|
|
60
84
|
* tooltip begins to animate in.
|
|
@@ -5,7 +5,7 @@ import { type TooltipProps } from './types';
|
|
|
5
5
|
*
|
|
6
6
|
* A tooltip is a floating, non-actionable label used to explain a user interface element or feature.
|
|
7
7
|
*/
|
|
8
|
-
declare function Tooltip({ children, position, mousePosition, content, truncate, component: Container, tag: TargetContainer, testId, delay, onShow, onHide, hideTooltipOnClick, hideTooltipOnMouseDown, analyticsContext, strategy, ignoreTooltipPointerEvents, }: TooltipProps): JSX.Element;
|
|
8
|
+
declare function Tooltip({ children, position, mousePosition, content, truncate, component: Container, tag: TargetContainer, testId, delay, onShow, onHide, canAppear, hideTooltipOnClick, hideTooltipOnMouseDown, analyticsContext, strategy, ignoreTooltipPointerEvents, isScreenReaderAnnouncementDisabled, }: TooltipProps): JSX.Element;
|
|
9
9
|
declare namespace Tooltip {
|
|
10
10
|
var displayName: string;
|
|
11
11
|
}
|
|
@@ -22,6 +22,11 @@ export interface TooltipProps {
|
|
|
22
22
|
* 2. Function which returns a `ReactNode`
|
|
23
23
|
* The benefit of the second approach is that it allows you to consume the `update` render prop.
|
|
24
24
|
* This `update` function can be called to manually recalculate the position of the tooltip.
|
|
25
|
+
*
|
|
26
|
+
* This content will be rendered into two places:
|
|
27
|
+
* 1. Into the tooltip
|
|
28
|
+
* 2. Into a hidden element for screen readers (unless `isScreenReaderAnnouncementDisabled` is set to `true`)
|
|
29
|
+
*
|
|
25
30
|
*/
|
|
26
31
|
content: ReactNode | (({ update }: {
|
|
27
32
|
update?: () => void;
|
|
@@ -55,6 +60,25 @@ export interface TooltipProps {
|
|
|
55
60
|
* When interacting with the target element using a keyboard, it will use this position against the target element instead.
|
|
56
61
|
*/
|
|
57
62
|
mousePosition?: PositionTypeBase;
|
|
63
|
+
/**
|
|
64
|
+
* Whether or not the tooltip can be displayed. Once a tooltip
|
|
65
|
+
* is scheduled to be displayed, or is already displayed, it will
|
|
66
|
+
* continue to be shown.
|
|
67
|
+
*
|
|
68
|
+
* @description
|
|
69
|
+
*
|
|
70
|
+
* `canAppear()` is called in response to user events, and
|
|
71
|
+
* not during the rendering of components.
|
|
72
|
+
*
|
|
73
|
+
*/
|
|
74
|
+
canAppear?: () => boolean;
|
|
75
|
+
/**
|
|
76
|
+
* By default tooltip content will be duplicated into a hidden element so
|
|
77
|
+
* it can be read out by a screen reader. Sometimes this is not ideal as
|
|
78
|
+
* it can result in the same content be announced twice. For those situations,
|
|
79
|
+
* you can leverage this prop to disable the duplicate hidden text.
|
|
80
|
+
*/
|
|
81
|
+
isScreenReaderAnnouncementDisabled?: boolean;
|
|
58
82
|
/**
|
|
59
83
|
* Function to be called when the tooltip will be shown. It's called when the
|
|
60
84
|
* tooltip begins to animate in.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/tooltip",
|
|
3
|
-
"version": "18.
|
|
3
|
+
"version": "18.9.1",
|
|
4
4
|
"description": "A tooltip is a floating, non-actionable label used to explain a user interface element or feature.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@atlaskit/analytics-next": "^10.1.0",
|
|
43
|
-
"@atlaskit/ds-lib": "^3.
|
|
44
|
-
"@atlaskit/layering": "^0.7.
|
|
43
|
+
"@atlaskit/ds-lib": "^3.2.0",
|
|
44
|
+
"@atlaskit/layering": "^0.7.3",
|
|
45
45
|
"@atlaskit/motion": "^1.9.0",
|
|
46
46
|
"@atlaskit/popper": "^6.3.0",
|
|
47
47
|
"@atlaskit/portal": "^4.9.0",
|
|
48
48
|
"@atlaskit/theme": "^14.0.0",
|
|
49
|
-
"@atlaskit/tokens": "^2.
|
|
49
|
+
"@atlaskit/tokens": "^2.2.0",
|
|
50
50
|
"@babel/runtime": "^7.0.0",
|
|
51
51
|
"@emotion/react": "^11.7.1",
|
|
52
52
|
"bind-event-listener": "^3.0.0"
|
|
@@ -57,7 +57,8 @@
|
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@af/accessibility-testing": "*",
|
|
60
|
-
"@
|
|
60
|
+
"@af/integration-testing": "*",
|
|
61
|
+
"@atlaskit/button": "^20.3.0",
|
|
61
62
|
"@atlaskit/ssr": "*",
|
|
62
63
|
"@atlaskit/visual-regression": "*",
|
|
63
64
|
"@emotion/styled": "^11.0.0",
|
|
@@ -67,7 +68,7 @@
|
|
|
67
68
|
"jest-in-case": "^1.0.2",
|
|
68
69
|
"react-dom": "^16.8.0",
|
|
69
70
|
"react-redux": "^5.1.2",
|
|
70
|
-
"storybook-addon-performance": "^0.
|
|
71
|
+
"storybook-addon-performance": "^0.17.3",
|
|
71
72
|
"typescript": "~5.4.2",
|
|
72
73
|
"wait-for-expect": "^1.2.0"
|
|
73
74
|
},
|