@lumx/react 3.6.5-alpha.3 → 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 +64 -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 +12 -12
- package/src/utils/OnBeforeUnmount.tsx +20 -0
- package/src/utils/isFocusVisible.ts +1 -1
- 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
|
/**
|
|
@@ -12851,7 +12866,7 @@ Toolbar.defaultProps = DEFAULT_PROPS$10;
|
|
|
12851
12866
|
/**
|
|
12852
12867
|
* Add ref and ARIA attribute(s) in tooltip children or wrapped children.
|
|
12853
12868
|
* Button, IconButton, Icon and React HTML elements don't need to be wrapped but any other kind of children (array, fragment, custom components)
|
|
12854
|
-
* will be wrapped
|
|
12869
|
+
* will be wrapped in a <span>.
|
|
12855
12870
|
*
|
|
12856
12871
|
* @param children Original tooltip anchor.
|
|
12857
12872
|
* @param setAnchorElement Set tooltip anchor element.
|
|
@@ -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 */
|
|
@@ -12900,7 +12913,7 @@ const isFocusVisible = element => {
|
|
|
12900
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]');
|
|
12901
12914
|
} catch (_ignored) {
|
|
12902
12915
|
// Can fail on non browser env
|
|
12903
|
-
return
|
|
12916
|
+
return true;
|
|
12904
12917
|
}
|
|
12905
12918
|
};
|
|
12906
12919
|
|