@lumx/react 3.6.5-alpha.2 → 3.6.5-alpha.4
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.d.ts +5 -0
- package/index.js +69 -51
- package/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/autocomplete/Autocomplete.tsx +7 -0
- package/src/components/popover/useRestoreFocusOnClose.tsx +29 -17
- package/src/components/popover-dialog/PopoverDialog.stories.tsx +18 -18
- package/src/components/popover-dialog/PopoverDialog.test.tsx +58 -45
- package/src/components/tooltip/Tooltip.test.tsx +19 -0
- package/src/components/tooltip/useInjectTooltipRef.tsx +11 -11
- package/src/utils/OnBeforeUnmount.tsx +20 -0
- package/src/utils/isFocusVisible.ts +8 -2
- package/src/utils/useBeforeUnmountSentinel.tsx +0 -17
package/index.d.ts
CHANGED
|
@@ -155,6 +155,11 @@ interface AutocompleteProps extends GenericProps, HasTheme {
|
|
|
155
155
|
* @see {@link DropdownProps#closeOnEscape}
|
|
156
156
|
*/
|
|
157
157
|
closeOnEscape?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Whether the focus should go back on the anchor when dropdown closes and focus is within.
|
|
160
|
+
* @see {@link DropdownProps#focusAnchorOnClose}
|
|
161
|
+
*/
|
|
162
|
+
focusAnchorOnClose?: DropdownProps['focusAnchorOnClose'];
|
|
158
163
|
/**
|
|
159
164
|
* The function called on blur.
|
|
160
165
|
* @see {@link TextFieldProps#onBlur}
|
package/index.js
CHANGED
|
@@ -839,7 +839,7 @@ const useMergeRefs = function () {
|
|
|
839
839
|
refs);
|
|
840
840
|
};
|
|
841
841
|
|
|
842
|
-
const _excluded$2 = ["anchorToInput", "children", "chips", "className", "closeOnClick", "closeOnClickAway", "closeOnEscape", "disabled", "error", "fitToAnchorWidth", "hasError", "helper", "icon", "inputRef", "clearButtonProps", "isDisabled", "isRequired", "isOpen", "isValid", "label", "name", "offset", "onBlur", "onChange", "onClose", "onFocus", "onInfiniteScroll", "placeholder", "placement", "shouldFocusOnClose", "theme", "value", "textFieldProps"];
|
|
842
|
+
const _excluded$2 = ["anchorToInput", "children", "chips", "className", "closeOnClick", "closeOnClickAway", "closeOnEscape", "disabled", "error", "fitToAnchorWidth", "hasError", "helper", "icon", "inputRef", "clearButtonProps", "isDisabled", "isRequired", "isOpen", "isValid", "label", "name", "offset", "onBlur", "onChange", "onClose", "onFocus", "onInfiniteScroll", "placeholder", "placement", "shouldFocusOnClose", "theme", "value", "textFieldProps", "focusAnchorOnClose"];
|
|
843
843
|
|
|
844
844
|
/**
|
|
845
845
|
* Defines the props of the component.
|
|
@@ -907,7 +907,8 @@ const Autocomplete = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
907
907
|
shouldFocusOnClose,
|
|
908
908
|
theme,
|
|
909
909
|
value,
|
|
910
|
-
textFieldProps = {}
|
|
910
|
+
textFieldProps = {},
|
|
911
|
+
focusAnchorOnClose
|
|
911
912
|
} = props,
|
|
912
913
|
forwardedProps = _objectWithoutProperties(props, _excluded$2);
|
|
913
914
|
const inputAnchorRef = useRef(null);
|
|
@@ -944,6 +945,7 @@ const Autocomplete = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
944
945
|
closeOnClick: closeOnClick,
|
|
945
946
|
closeOnClickAway: closeOnClickAway,
|
|
946
947
|
closeOnEscape: closeOnEscape,
|
|
948
|
+
focusAnchorOnClose: focusAnchorOnClose,
|
|
947
949
|
fitToAnchorWidth: fitToAnchorWidth,
|
|
948
950
|
isOpen: isOpen,
|
|
949
951
|
offset: offset,
|
|
@@ -4058,52 +4060,65 @@ const skipRender = (predicate, Component) => {
|
|
|
4058
4060
|
return Wrapper;
|
|
4059
4061
|
};
|
|
4060
4062
|
|
|
4061
|
-
/**
|
|
4062
|
-
|
|
4063
|
+
/**
|
|
4064
|
+
* Helper component using useLayoutEffect to trigger a callback on before unmount.
|
|
4065
|
+
*
|
|
4066
|
+
* The callback must be wrapped in a React ref to avoid updating the `useLayoutEffect` before the un-mount
|
|
4067
|
+
*/
|
|
4068
|
+
const OnBeforeUnmount = _ref => {
|
|
4063
4069
|
let {
|
|
4064
|
-
|
|
4070
|
+
callback
|
|
4065
4071
|
} = _ref;
|
|
4066
|
-
useLayoutEffect(() =>
|
|
4072
|
+
useLayoutEffect(() => {
|
|
4073
|
+
return () => {
|
|
4074
|
+
var _callback$current;
|
|
4075
|
+
// On unmount
|
|
4076
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4077
|
+
(_callback$current = callback.current) === null || _callback$current === void 0 ? void 0 : _callback$current.call(callback);
|
|
4078
|
+
};
|
|
4079
|
+
},
|
|
4080
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4081
|
+
[]);
|
|
4067
4082
|
return null;
|
|
4068
4083
|
};
|
|
4069
4084
|
|
|
4070
|
-
/**
|
|
4071
|
-
* Provides a sentinel to inject the React tree that triggers the callback on before unmount.
|
|
4072
|
-
*/
|
|
4073
|
-
function useBeforeUnmountSentinel(onBeforeUnmount) {
|
|
4074
|
-
return React.useMemo(() => {
|
|
4075
|
-
if (!onBeforeUnmount) return undefined;
|
|
4076
|
-
return /*#__PURE__*/React.createElement(OnUnmount, {
|
|
4077
|
-
onBeforeUnmount: onBeforeUnmount
|
|
4078
|
-
});
|
|
4079
|
-
}, [onBeforeUnmount]);
|
|
4080
|
-
}
|
|
4081
|
-
|
|
4082
4085
|
/**
|
|
4083
4086
|
* Provides an unmount sentinel to inject in the popover to detect when it closes and restore the focus to the
|
|
4084
4087
|
* anchor if needed.
|
|
4085
4088
|
*/
|
|
4086
|
-
function useRestoreFocusOnClose(
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4089
|
+
function useRestoreFocusOnClose(_ref, popoverElement) {
|
|
4090
|
+
let {
|
|
4091
|
+
focusAnchorOnClose,
|
|
4092
|
+
anchorRef,
|
|
4093
|
+
parentElement
|
|
4094
|
+
} = _ref;
|
|
4095
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
4096
|
+
const onBeforeUnmountRef = React.useRef(() => {});
|
|
4097
|
+
const anchor = anchorRef.current;
|
|
4098
|
+
const elementToFocus =
|
|
4099
|
+
// Provided parent element
|
|
4100
|
+
(parentElement === null || parentElement === void 0 ? void 0 : parentElement.current) || (
|
|
4101
|
+
// Or first focusable element in anchor
|
|
4102
|
+
anchor ? getFirstAndLastFocusable(anchor).first : undefined) ||
|
|
4103
|
+
// Fallback to anchor
|
|
4104
|
+
anchor;
|
|
4105
|
+
React.useEffect(() => {
|
|
4106
|
+
if (!popoverElement || !focusAnchorOnClose || !elementToFocus) return;
|
|
4107
|
+
onBeforeUnmountRef.current = () => {
|
|
4091
4108
|
const isFocusWithin = popoverElement === null || popoverElement === void 0 ? void 0 : popoverElement.contains(document.activeElement);
|
|
4092
4109
|
if (!isFocusWithin) return;
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
anchor;
|
|
4101
|
-
elementToFocus === null || elementToFocus === void 0 ? void 0 : elementToFocus.focus({
|
|
4102
|
-
preventScroll: true
|
|
4103
|
-
});
|
|
4110
|
+
|
|
4111
|
+
// Focus on next render
|
|
4112
|
+
setTimeout(() => {
|
|
4113
|
+
elementToFocus.focus({
|
|
4114
|
+
preventScroll: true
|
|
4115
|
+
});
|
|
4116
|
+
}, 0);
|
|
4104
4117
|
};
|
|
4105
|
-
}, [
|
|
4106
|
-
return
|
|
4118
|
+
}, [anchor, elementToFocus, focusAnchorOnClose, popoverElement]);
|
|
4119
|
+
return /*#__PURE__*/React.createElement(OnBeforeUnmount, {
|
|
4120
|
+
callback: onBeforeUnmountRef
|
|
4121
|
+
});
|
|
4107
4122
|
}
|
|
4108
4123
|
|
|
4109
4124
|
/**
|
|
@@ -12861,30 +12876,28 @@ Toolbar.defaultProps = DEFAULT_PROPS$10;
|
|
|
12861
12876
|
* @return tooltip anchor.
|
|
12862
12877
|
*/
|
|
12863
12878
|
const useInjectTooltipRef = (children, setAnchorElement, isOpen, id, label) => {
|
|
12864
|
-
|
|
12865
|
-
const
|
|
12879
|
+
// Only add description when open
|
|
12880
|
+
const describedBy = isOpen ? id : undefined;
|
|
12866
12881
|
return useMemo(() => {
|
|
12867
|
-
var _element$props, _element$props2;
|
|
12868
12882
|
// Non-disabled element
|
|
12869
|
-
if (
|
|
12870
|
-
const
|
|
12883
|
+
if ( /*#__PURE__*/React.isValidElement(children) && children.props.disabled !== true && children.props.isDisabled !== true) {
|
|
12884
|
+
const ref = mergeRefs(children.ref, setAnchorElement);
|
|
12885
|
+
const props = _objectSpread2(_objectSpread2({}, children.props), {}, {
|
|
12871
12886
|
ref
|
|
12872
12887
|
});
|
|
12873
12888
|
|
|
12874
12889
|
// Add current tooltip to the aria-describedby if the label is not already present
|
|
12875
|
-
if (label !== props['aria-label']) {
|
|
12876
|
-
props['aria-describedby'] = [props['aria-describedby'],
|
|
12890
|
+
if (label !== props['aria-label'] && describedBy) {
|
|
12891
|
+
props['aria-describedby'] = [props['aria-describedby'], describedBy].filter(Boolean).join(' ');
|
|
12877
12892
|
}
|
|
12878
|
-
return /*#__PURE__*/cloneElement(
|
|
12893
|
+
return /*#__PURE__*/cloneElement(children, props);
|
|
12879
12894
|
}
|
|
12880
|
-
|
|
12881
|
-
// Else add a wrapper around the children
|
|
12882
12895
|
return /*#__PURE__*/React.createElement("div", {
|
|
12883
12896
|
className: "lumx-tooltip-anchor-wrapper",
|
|
12884
|
-
ref:
|
|
12885
|
-
"aria-describedby":
|
|
12897
|
+
ref: setAnchorElement,
|
|
12898
|
+
"aria-describedby": describedBy
|
|
12886
12899
|
}, children);
|
|
12887
|
-
}, [
|
|
12900
|
+
}, [children, setAnchorElement, describedBy, label]);
|
|
12888
12901
|
};
|
|
12889
12902
|
|
|
12890
12903
|
/** Return true if the browser does not support pointer hover */
|
|
@@ -12895,8 +12908,13 @@ const browserDoesNotSupportHover = () => {
|
|
|
12895
12908
|
|
|
12896
12909
|
/** Check if the focus is visible on the given element */
|
|
12897
12910
|
const isFocusVisible = element => {
|
|
12898
|
-
|
|
12899
|
-
|
|
12911
|
+
try {
|
|
12912
|
+
var _element$matches;
|
|
12913
|
+
return element === null || element === void 0 ? void 0 : (_element$matches = element.matches) === null || _element$matches === void 0 ? void 0 : _element$matches.call(element, ':focus-visible, [data-focus-visible-added]');
|
|
12914
|
+
} catch (_ignored) {
|
|
12915
|
+
// Can fail on non browser env
|
|
12916
|
+
return true;
|
|
12917
|
+
}
|
|
12900
12918
|
};
|
|
12901
12919
|
|
|
12902
12920
|
/**
|