@lumx/react 2.2.18 → 2.2.19
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/esm/_internal/Tooltip2.js +3 -7
- package/esm/_internal/Tooltip2.js.map +1 -1
- package/esm/_internal/UserBlock.js +9 -2
- package/esm/_internal/UserBlock.js.map +1 -1
- package/esm/_internal/useFocusTrap.js +22 -13
- package/esm/_internal/useFocusTrap.js.map +1 -1
- package/esm/_internal/user-block.js +1 -0
- package/esm/_internal/user-block.js.map +1 -1
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/package.json +5 -5
- package/src/components/dialog/Dialog.stories.tsx +4 -1
- package/src/components/dialog/__snapshots__/Dialog.test.tsx.snap +85 -77
- package/src/components/tooltip/Tooltip.tsx +2 -5
- package/src/components/user-block/UserBlock.stories.tsx +4 -4
- package/src/components/user-block/UserBlock.tsx +9 -3
- package/src/components/user-block/__snapshots__/UserBlock.test.tsx.snap +51 -8
- package/src/hooks/useBooleanState.tsx +4 -10
- package/src/hooks/useFocusTrap.ts +2 -28
- package/src/stories/generated/Dialog/Demos.stories.tsx +1 -0
- package/src/utils/focus/getFirstAndLastFocusable.test.ts +128 -0
- package/src/utils/focus/getFirstAndLastFocusable.ts +27 -0
- package/types.d.ts +1 -1
|
@@ -211,20 +211,16 @@ var ARROW_SIZE = 8;
|
|
|
211
211
|
var Tooltip = forwardRef(function (props, ref) {
|
|
212
212
|
var _ref, _attributes$popper;
|
|
213
213
|
|
|
214
|
-
if (!DOCUMENT) {
|
|
215
|
-
// Can't render in SSR.
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
214
|
var label = props.label,
|
|
220
215
|
children = props.children,
|
|
221
216
|
className = props.className,
|
|
222
217
|
delay = props.delay,
|
|
223
218
|
placement = props.placement,
|
|
224
219
|
forceOpen = props.forceOpen,
|
|
225
|
-
forwardedProps = _objectWithoutProperties(props, ["label", "children", "className", "delay", "placement", "forceOpen"]);
|
|
220
|
+
forwardedProps = _objectWithoutProperties(props, ["label", "children", "className", "delay", "placement", "forceOpen"]); // Disable in SSR or without a label.
|
|
221
|
+
|
|
226
222
|
|
|
227
|
-
if (!label) {
|
|
223
|
+
if (!DOCUMENT || !label) {
|
|
228
224
|
return React.createElement(React.Fragment, null, children);
|
|
229
225
|
}
|
|
230
226
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip2.js","sources":["../../../src/components/tooltip/useInjectTooltipRef.tsx","../../../src/utils/browserDoesNotSupportHover.ts","../../../src/components/tooltip/useTooltipOpen.tsx","../../../src/components/tooltip/Tooltip.tsx"],"sourcesContent":["import { mergeRefs } from '@lumx/react/utils/mergeRefs';\nimport get from 'lodash/get';\nimport React, { cloneElement, ReactNode, useMemo } from 'react';\n\n/**\n * Add ref and ARIA attribute(s) in tooltip children or wrapped children.\n * Button, IconButton, Icon and React HTML elements don't need to be wrapped but any other kind of children (array, fragment, custom components)\n * will be wrapped in a <span>.\n *\n * @param children Original tooltip anchor.\n * @param setAnchorElement Set tooltip anchor element.\n * @param isOpen Whether the tooltip is open or not.\n * @param id Tooltip id.\n * @return tooltip anchor.\n */\nexport const useInjectTooltipRef = (\n children: ReactNode,\n setAnchorElement: (e: HTMLDivElement) => void,\n isOpen: boolean,\n id: string,\n): ReactNode => {\n return useMemo(() => {\n const ariaProps = { 'aria-describedby': isOpen ? id : undefined };\n if (\n children &&\n get(children, '$$typeof') &&\n get(children, 'props.disabled') !== true &&\n get(children, 'props.isDisabled') !== true\n ) {\n const element = children as any;\n\n return cloneElement(element, {\n ...element.props,\n ...ariaProps,\n ref: mergeRefs(element.ref, setAnchorElement),\n });\n }\n return (\n <div className=\"lumx-tooltip-anchor-wrapper\" ref={setAnchorElement} {...ariaProps}>\n {children}\n </div>\n );\n }, [isOpen, id, children, setAnchorElement]);\n};\n","/** Return true if the browser does not support pointer hover */\nexport const browserDoesNotSupportHover = (): boolean => !!window.matchMedia?.('(hover: none)').matches;\n","import { onEscapePressed } from '@lumx/react/utils';\nimport { useEffect, useState } from 'react';\nimport { browserDoesNotSupportHover } from '@lumx/react/utils/browserDoesNotSupportHover';\nimport { TOOLTIP_HOVER_DELAY, TOOLTIP_LONG_PRESS_DELAY } from '@lumx/react/constants';\n\n/**\n * Hook controlling tooltip visibility using mouse hover the anchor and delay.\n *\n * @param delay Delay in millisecond to display the tooltip.\n * @param anchorElement Tooltip anchor element.\n * @return whether or not to show the tooltip.\n */\nexport function useTooltipOpen(delay: number | undefined, anchorElement: HTMLElement | null): boolean {\n const [isOpen, setIsOpen] = useState(false);\n\n useEffect(() => {\n if (!anchorElement) {\n return undefined;\n }\n let timer: number | undefined;\n let openStartTime: number | undefined;\n let shouldOpen: boolean | undefined;\n\n // Run timer to defer updating the isOpen state.\n const deferUpdate = (duration: number) => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n setIsOpen(!!shouldOpen);\n }, duration) as any;\n };\n\n const hoverNotSupported = browserDoesNotSupportHover();\n const hasTouch = 'ontouchstart' in window;\n\n // Adapt open/close delay\n const openDelay = delay || (hoverNotSupported ? TOOLTIP_LONG_PRESS_DELAY.open : TOOLTIP_HOVER_DELAY.open);\n const closeDelay = hoverNotSupported ? TOOLTIP_LONG_PRESS_DELAY.close : TOOLTIP_HOVER_DELAY.close;\n\n // Open (or/and cancel closing) of tooltip.\n const open = () => {\n if (shouldOpen && !timer) return;\n shouldOpen = true;\n openStartTime = Date.now();\n deferUpdate(openDelay);\n };\n\n // Close or cancel opening of tooltip\n const close = (overrideDelay = closeDelay) => {\n if (!shouldOpen && !timer) return;\n shouldOpen = false;\n deferUpdate(overrideDelay);\n };\n const closeImmediately = () => close(0);\n\n /**\n * Handle touchend event\n * If `touchend` comes before the open delay => cancel tooltip (close immediate).\n * Else if `touchend` comes after the open delay => tooltip takes priority, the anchor's default touch end event is prevented.\n */\n const touchEnd = (evt: Event) => {\n if (!openStartTime) return;\n if (Date.now() - openStartTime >= openDelay) {\n // Tooltip take priority, event prevented.\n evt.stopPropagation();\n evt.preventDefault();\n anchorElement.focus();\n // Close with delay.\n close();\n } else {\n // Close immediately.\n closeImmediately();\n }\n };\n\n // Adapt event to browsers with or without `hover` support.\n const events: Array<[Node, Event['type'], any]> = hoverNotSupported\n ? [\n [anchorElement, hasTouch ? 'touchstart' : 'mousedown', open],\n [anchorElement, hasTouch ? 'touchend' : 'mouseup', touchEnd],\n ]\n : [\n [anchorElement, 'mouseenter', open],\n [anchorElement, 'mouseleave', close],\n [anchorElement, 'mouseup', closeImmediately],\n ];\n\n // Events always applied no matter the browser:.\n events.push(\n // Open on focus.\n [anchorElement, 'focusin', open],\n // Close on lost focus.\n [anchorElement, 'focusout', closeImmediately],\n // Close on ESC keydown\n [anchorElement, 'keydown', onEscapePressed(closeImmediately)],\n );\n\n // Attach events\n for (const [node, eventType, eventHandler] of events) {\n node.addEventListener(eventType, eventHandler);\n }\n return () => {\n // Clear pending timers.\n if (timer) clearTimeout(timer);\n\n // Detach events.\n for (const [node, eventType, eventHandler] of events) {\n node.removeEventListener(eventType, eventHandler);\n }\n };\n }, [anchorElement, delay]);\n\n return isOpen;\n}\n","/* eslint-disable react-hooks/rules-of-hooks */\nimport React, { forwardRef, ReactNode, useMemo, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { usePopper } from 'react-popper';\nimport { uid } from 'uid';\n\nimport classNames from 'classnames';\n\nimport { Placement } from '@lumx/react/components/popover/Popover';\n\nimport { DOCUMENT } from '@lumx/react/constants';\n\nimport { Comp, GenericProps, getRootClassName, handleBasicClasses } from '@lumx/react/utils';\nimport { mergeRefs } from '@lumx/react/utils/mergeRefs';\n\nimport { useInjectTooltipRef } from './useInjectTooltipRef';\nimport { useTooltipOpen } from './useTooltipOpen';\n\n/** Position of the tooltip relative to the anchor element. */\nexport type TooltipPlacement = Extract<Placement, 'top' | 'right' | 'bottom' | 'left'>;\n\n/**\n * Defines the props of the component.\n */\nexport interface TooltipProps extends GenericProps {\n /** Anchor (element on which we activate the tooltip). */\n children: ReactNode;\n /** Delay (in ms) before closing the tooltip. */\n delay?: number;\n /** Whether the tooltip is displayed even without the mouse hovering the anchor. */\n forceOpen?: boolean;\n /** Label text. */\n label?: string | null | false;\n /** Placement of the tooltip relative to the anchor. */\n placement?: TooltipPlacement;\n}\n\n/**\n * Component display name.\n */\nconst COMPONENT_NAME = 'Tooltip';\n\n/**\n * Component default class name and class prefix.\n */\nconst CLASSNAME = getRootClassName(COMPONENT_NAME);\n\n/**\n * Component default props.\n */\nconst DEFAULT_PROPS: Partial<TooltipProps> = {\n placement: Placement.BOTTOM,\n};\n\n/**\n * Arrow size (in pixel).\n */\nconst ARROW_SIZE = 8;\n\n/**\n * Tooltip component.\n *\n * @param props Component props.\n * @param ref Component ref.\n * @return React element.\n */\nexport const Tooltip: Comp<TooltipProps, HTMLDivElement> = forwardRef((props, ref) => {\n if (!DOCUMENT) {\n // Can't render in SSR.\n return null;\n }\n const { label, children, className, delay, placement, forceOpen, ...forwardedProps } = props;\n if (!label) {\n return <>{children}</>;\n }\n\n const id = useMemo(() => `tooltip-${uid()}`, []);\n\n const [popperElement, setPopperElement] = useState<null | HTMLElement>(null);\n const [anchorElement, setAnchorElement] = useState<null | HTMLElement>(null);\n const { styles, attributes } = usePopper(anchorElement, popperElement, {\n placement,\n modifiers: [\n {\n name: 'offset',\n options: { offset: [0, ARROW_SIZE] },\n },\n ],\n });\n\n const position = attributes?.popper?.['data-popper-placement'] ?? placement;\n const isOpen = useTooltipOpen(delay, anchorElement) || forceOpen;\n const wrappedChildren = useInjectTooltipRef(children, setAnchorElement, isOpen as boolean, id);\n\n return (\n <>\n {wrappedChildren}\n {isOpen &&\n createPortal(\n <div\n ref={mergeRefs(ref, setPopperElement)}\n {...forwardedProps}\n id={id}\n role=\"tooltip\"\n aria-label={label}\n className={classNames(className, handleBasicClasses({ prefix: CLASSNAME, position }))}\n style={styles.popper}\n {...attributes.popper}\n >\n <div className={`${CLASSNAME}__arrow`} />\n <div className={`${CLASSNAME}__inner`}>\n {label.indexOf('\\n') !== -1\n ? label.split('\\n').map((sentence: string) => <p key={sentence}>{sentence}</p>)\n : label}\n </div>\n </div>,\n document.body,\n )}\n </>\n );\n});\nTooltip.displayName = COMPONENT_NAME;\nTooltip.className = CLASSNAME;\nTooltip.defaultProps = DEFAULT_PROPS;\n"],"names":["useInjectTooltipRef","children","setAnchorElement","isOpen","id","useMemo","ariaProps","undefined","get","element","cloneElement","props","ref","mergeRefs","browserDoesNotSupportHover","window","matchMedia","matches","useTooltipOpen","delay","anchorElement","useState","setIsOpen","useEffect","timer","openStartTime","shouldOpen","deferUpdate","duration","clearTimeout","setTimeout","hoverNotSupported","hasTouch","openDelay","TOOLTIP_LONG_PRESS_DELAY","open","TOOLTIP_HOVER_DELAY","closeDelay","close","Date","now","overrideDelay","closeImmediately","touchEnd","evt","stopPropagation","preventDefault","focus","events","push","onEscapePressed","node","eventType","eventHandler","addEventListener","removeEventListener","COMPONENT_NAME","CLASSNAME","getRootClassName","DEFAULT_PROPS","placement","Placement","BOTTOM","ARROW_SIZE","Tooltip","forwardRef","DOCUMENT","label","className","forceOpen","forwardedProps","uid","popperElement","setPopperElement","usePopper","modifiers","name","options","offset","styles","attributes","position","popper","wrappedChildren","createPortal","classNames","handleBasicClasses","prefix","indexOf","split","map","sentence","document","body","displayName","defaultProps"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Tooltip2.js","sources":["../../../src/components/tooltip/useInjectTooltipRef.tsx","../../../src/utils/browserDoesNotSupportHover.ts","../../../src/components/tooltip/useTooltipOpen.tsx","../../../src/components/tooltip/Tooltip.tsx"],"sourcesContent":["import { mergeRefs } from '@lumx/react/utils/mergeRefs';\nimport get from 'lodash/get';\nimport React, { cloneElement, ReactNode, useMemo } from 'react';\n\n/**\n * Add ref and ARIA attribute(s) in tooltip children or wrapped children.\n * Button, IconButton, Icon and React HTML elements don't need to be wrapped but any other kind of children (array, fragment, custom components)\n * will be wrapped in a <span>.\n *\n * @param children Original tooltip anchor.\n * @param setAnchorElement Set tooltip anchor element.\n * @param isOpen Whether the tooltip is open or not.\n * @param id Tooltip id.\n * @return tooltip anchor.\n */\nexport const useInjectTooltipRef = (\n children: ReactNode,\n setAnchorElement: (e: HTMLDivElement) => void,\n isOpen: boolean,\n id: string,\n): ReactNode => {\n return useMemo(() => {\n const ariaProps = { 'aria-describedby': isOpen ? id : undefined };\n if (\n children &&\n get(children, '$$typeof') &&\n get(children, 'props.disabled') !== true &&\n get(children, 'props.isDisabled') !== true\n ) {\n const element = children as any;\n\n return cloneElement(element, {\n ...element.props,\n ...ariaProps,\n ref: mergeRefs(element.ref, setAnchorElement),\n });\n }\n return (\n <div className=\"lumx-tooltip-anchor-wrapper\" ref={setAnchorElement} {...ariaProps}>\n {children}\n </div>\n );\n }, [isOpen, id, children, setAnchorElement]);\n};\n","/** Return true if the browser does not support pointer hover */\nexport const browserDoesNotSupportHover = (): boolean => !!window.matchMedia?.('(hover: none)').matches;\n","import { onEscapePressed } from '@lumx/react/utils';\nimport { useEffect, useState } from 'react';\nimport { browserDoesNotSupportHover } from '@lumx/react/utils/browserDoesNotSupportHover';\nimport { TOOLTIP_HOVER_DELAY, TOOLTIP_LONG_PRESS_DELAY } from '@lumx/react/constants';\n\n/**\n * Hook controlling tooltip visibility using mouse hover the anchor and delay.\n *\n * @param delay Delay in millisecond to display the tooltip.\n * @param anchorElement Tooltip anchor element.\n * @return whether or not to show the tooltip.\n */\nexport function useTooltipOpen(delay: number | undefined, anchorElement: HTMLElement | null): boolean {\n const [isOpen, setIsOpen] = useState(false);\n\n useEffect(() => {\n if (!anchorElement) {\n return undefined;\n }\n let timer: number | undefined;\n let openStartTime: number | undefined;\n let shouldOpen: boolean | undefined;\n\n // Run timer to defer updating the isOpen state.\n const deferUpdate = (duration: number) => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n setIsOpen(!!shouldOpen);\n }, duration) as any;\n };\n\n const hoverNotSupported = browserDoesNotSupportHover();\n const hasTouch = 'ontouchstart' in window;\n\n // Adapt open/close delay\n const openDelay = delay || (hoverNotSupported ? TOOLTIP_LONG_PRESS_DELAY.open : TOOLTIP_HOVER_DELAY.open);\n const closeDelay = hoverNotSupported ? TOOLTIP_LONG_PRESS_DELAY.close : TOOLTIP_HOVER_DELAY.close;\n\n // Open (or/and cancel closing) of tooltip.\n const open = () => {\n if (shouldOpen && !timer) return;\n shouldOpen = true;\n openStartTime = Date.now();\n deferUpdate(openDelay);\n };\n\n // Close or cancel opening of tooltip\n const close = (overrideDelay = closeDelay) => {\n if (!shouldOpen && !timer) return;\n shouldOpen = false;\n deferUpdate(overrideDelay);\n };\n const closeImmediately = () => close(0);\n\n /**\n * Handle touchend event\n * If `touchend` comes before the open delay => cancel tooltip (close immediate).\n * Else if `touchend` comes after the open delay => tooltip takes priority, the anchor's default touch end event is prevented.\n */\n const touchEnd = (evt: Event) => {\n if (!openStartTime) return;\n if (Date.now() - openStartTime >= openDelay) {\n // Tooltip take priority, event prevented.\n evt.stopPropagation();\n evt.preventDefault();\n anchorElement.focus();\n // Close with delay.\n close();\n } else {\n // Close immediately.\n closeImmediately();\n }\n };\n\n // Adapt event to browsers with or without `hover` support.\n const events: Array<[Node, Event['type'], any]> = hoverNotSupported\n ? [\n [anchorElement, hasTouch ? 'touchstart' : 'mousedown', open],\n [anchorElement, hasTouch ? 'touchend' : 'mouseup', touchEnd],\n ]\n : [\n [anchorElement, 'mouseenter', open],\n [anchorElement, 'mouseleave', close],\n [anchorElement, 'mouseup', closeImmediately],\n ];\n\n // Events always applied no matter the browser:.\n events.push(\n // Open on focus.\n [anchorElement, 'focusin', open],\n // Close on lost focus.\n [anchorElement, 'focusout', closeImmediately],\n // Close on ESC keydown\n [anchorElement, 'keydown', onEscapePressed(closeImmediately)],\n );\n\n // Attach events\n for (const [node, eventType, eventHandler] of events) {\n node.addEventListener(eventType, eventHandler);\n }\n return () => {\n // Clear pending timers.\n if (timer) clearTimeout(timer);\n\n // Detach events.\n for (const [node, eventType, eventHandler] of events) {\n node.removeEventListener(eventType, eventHandler);\n }\n };\n }, [anchorElement, delay]);\n\n return isOpen;\n}\n","/* eslint-disable react-hooks/rules-of-hooks */\nimport React, { forwardRef, ReactNode, useMemo, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { usePopper } from 'react-popper';\nimport { uid } from 'uid';\n\nimport classNames from 'classnames';\n\nimport { Placement } from '@lumx/react/components/popover/Popover';\n\nimport { DOCUMENT } from '@lumx/react/constants';\n\nimport { Comp, GenericProps, getRootClassName, handleBasicClasses } from '@lumx/react/utils';\nimport { mergeRefs } from '@lumx/react/utils/mergeRefs';\n\nimport { useInjectTooltipRef } from './useInjectTooltipRef';\nimport { useTooltipOpen } from './useTooltipOpen';\n\n/** Position of the tooltip relative to the anchor element. */\nexport type TooltipPlacement = Extract<Placement, 'top' | 'right' | 'bottom' | 'left'>;\n\n/**\n * Defines the props of the component.\n */\nexport interface TooltipProps extends GenericProps {\n /** Anchor (element on which we activate the tooltip). */\n children: ReactNode;\n /** Delay (in ms) before closing the tooltip. */\n delay?: number;\n /** Whether the tooltip is displayed even without the mouse hovering the anchor. */\n forceOpen?: boolean;\n /** Label text. */\n label?: string | null | false;\n /** Placement of the tooltip relative to the anchor. */\n placement?: TooltipPlacement;\n}\n\n/**\n * Component display name.\n */\nconst COMPONENT_NAME = 'Tooltip';\n\n/**\n * Component default class name and class prefix.\n */\nconst CLASSNAME = getRootClassName(COMPONENT_NAME);\n\n/**\n * Component default props.\n */\nconst DEFAULT_PROPS: Partial<TooltipProps> = {\n placement: Placement.BOTTOM,\n};\n\n/**\n * Arrow size (in pixel).\n */\nconst ARROW_SIZE = 8;\n\n/**\n * Tooltip component.\n *\n * @param props Component props.\n * @param ref Component ref.\n * @return React element.\n */\nexport const Tooltip: Comp<TooltipProps, HTMLDivElement> = forwardRef((props, ref) => {\n const { label, children, className, delay, placement, forceOpen, ...forwardedProps } = props;\n // Disable in SSR or without a label.\n if (!DOCUMENT || !label) {\n return <>{children}</>;\n }\n\n const id = useMemo(() => `tooltip-${uid()}`, []);\n\n const [popperElement, setPopperElement] = useState<null | HTMLElement>(null);\n const [anchorElement, setAnchorElement] = useState<null | HTMLElement>(null);\n const { styles, attributes } = usePopper(anchorElement, popperElement, {\n placement,\n modifiers: [\n {\n name: 'offset',\n options: { offset: [0, ARROW_SIZE] },\n },\n ],\n });\n\n const position = attributes?.popper?.['data-popper-placement'] ?? placement;\n const isOpen = useTooltipOpen(delay, anchorElement) || forceOpen;\n const wrappedChildren = useInjectTooltipRef(children, setAnchorElement, isOpen as boolean, id);\n\n return (\n <>\n {wrappedChildren}\n {isOpen &&\n createPortal(\n <div\n ref={mergeRefs(ref, setPopperElement)}\n {...forwardedProps}\n id={id}\n role=\"tooltip\"\n aria-label={label}\n className={classNames(className, handleBasicClasses({ prefix: CLASSNAME, position }))}\n style={styles.popper}\n {...attributes.popper}\n >\n <div className={`${CLASSNAME}__arrow`} />\n <div className={`${CLASSNAME}__inner`}>\n {label.indexOf('\\n') !== -1\n ? label.split('\\n').map((sentence: string) => <p key={sentence}>{sentence}</p>)\n : label}\n </div>\n </div>,\n document.body,\n )}\n </>\n );\n});\nTooltip.displayName = COMPONENT_NAME;\nTooltip.className = CLASSNAME;\nTooltip.defaultProps = DEFAULT_PROPS;\n"],"names":["useInjectTooltipRef","children","setAnchorElement","isOpen","id","useMemo","ariaProps","undefined","get","element","cloneElement","props","ref","mergeRefs","browserDoesNotSupportHover","window","matchMedia","matches","useTooltipOpen","delay","anchorElement","useState","setIsOpen","useEffect","timer","openStartTime","shouldOpen","deferUpdate","duration","clearTimeout","setTimeout","hoverNotSupported","hasTouch","openDelay","TOOLTIP_LONG_PRESS_DELAY","open","TOOLTIP_HOVER_DELAY","closeDelay","close","Date","now","overrideDelay","closeImmediately","touchEnd","evt","stopPropagation","preventDefault","focus","events","push","onEscapePressed","node","eventType","eventHandler","addEventListener","removeEventListener","COMPONENT_NAME","CLASSNAME","getRootClassName","DEFAULT_PROPS","placement","Placement","BOTTOM","ARROW_SIZE","Tooltip","forwardRef","label","className","forceOpen","forwardedProps","DOCUMENT","uid","popperElement","setPopperElement","usePopper","modifiers","name","options","offset","styles","attributes","position","popper","wrappedChildren","createPortal","classNames","handleBasicClasses","prefix","indexOf","split","map","sentence","document","body","displayName","defaultProps"],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ import { g as getRootClassName, c as classnames, h as handleBasicClasses } from
|
|
|
5
5
|
import isEmpty from 'lodash/isEmpty';
|
|
6
6
|
import { A as Avatar } from './Avatar2.js';
|
|
7
7
|
import { L as Link } from './Link2.js';
|
|
8
|
+
import set from 'lodash/set';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Component display name.
|
|
@@ -77,10 +78,15 @@ var UserBlock = forwardRef(function (props, ref) {
|
|
|
77
78
|
linkAs: linkAs,
|
|
78
79
|
color: ColorPalette.dark
|
|
79
80
|
}));
|
|
81
|
+
} // Disable avatar focus since the name block is the same link / same button.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if (avatarProps) {
|
|
85
|
+
set(avatarProps, ['thumbnailProps', 'tabIndex'], -1);
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
return React.createElement(NameComponent, nProps, name);
|
|
83
|
-
}, [isClickable, linkAs, linkProps, name, nameProps, onClick]);
|
|
89
|
+
}, [avatarProps, isClickable, linkAs, linkProps, name, nameProps, onClick]);
|
|
84
90
|
var fieldsBlock = fields && componentSize !== Size.s && React.createElement("div", {
|
|
85
91
|
className: "".concat(CLASSNAME, "__fields")
|
|
86
92
|
}, fields.map(function (field, idx) {
|
|
@@ -103,7 +109,8 @@ var UserBlock = forwardRef(function (props, ref) {
|
|
|
103
109
|
onMouseEnter: onMouseEnter
|
|
104
110
|
}), avatarProps && React.createElement(Avatar, _extends({
|
|
105
111
|
linkAs: linkAs,
|
|
106
|
-
linkProps: linkProps
|
|
112
|
+
linkProps: linkProps,
|
|
113
|
+
alt: ""
|
|
107
114
|
}, avatarProps, {
|
|
108
115
|
className: classnames("".concat(CLASSNAME, "__avatar"), avatarProps.className),
|
|
109
116
|
size: componentSize,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserBlock.js","sources":["../../../src/components/user-block/UserBlock.tsx"],"sourcesContent":["import React, { forwardRef, ReactNode } from 'react';\nimport isEmpty from 'lodash/isEmpty';\nimport classNames from 'classnames';\n\nimport { Avatar, ColorPalette, Link, Orientation, Size, Theme } from '@lumx/react';\nimport { Comp, GenericProps, getRootClassName, handleBasicClasses } from '@lumx/react/utils';\n\nimport { AvatarProps } from '../avatar/Avatar';\n\n/**\n * User block sizes.\n */\nexport type UserBlockSize = Extract<Size, 's' | 'm' | 'l'>;\n\n/**\n * Defines the props of the component.\n */\nexport interface UserBlockProps extends GenericProps {\n /** Props to pass to the avatar. */\n avatarProps?: AvatarProps;\n /** Additional fields used to describe the user. */\n fields?: string[];\n /** Props to pass to the link wrapping the avatar thumbnail. */\n linkProps?: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;\n /** Custom react component for the link (can be used to inject react router Link). */\n linkAs?: 'a' | any;\n /** Multiple action toolbar content. */\n multipleActions?: ReactNode;\n /** User name. */\n name?: string;\n /** Props to pass to the name block. */\n nameProps?: GenericProps;\n /** Orientation. */\n orientation?: Orientation;\n /** Simple action toolbar content. */\n simpleAction?: ReactNode;\n /** Size variant. */\n size?: UserBlockSize;\n /** Theme adapting the component to light or dark background. */\n theme?: Theme;\n /** On click callback. */\n onClick?(): void;\n /** On mouse enter callback. */\n onMouseEnter?(): void;\n /** On mouse leave callback. */\n onMouseLeave?(): void;\n}\n\n/**\n * Component display name.\n */\nconst COMPONENT_NAME = 'UserBlock';\n\n/**\n * Component default class name and class prefix.\n */\nconst CLASSNAME = getRootClassName(COMPONENT_NAME);\n\n/**\n * Component default props.\n */\nconst DEFAULT_PROPS: Partial<UserBlockProps> = {\n orientation: Orientation.horizontal,\n size: Size.m,\n theme: Theme.light,\n};\n\n/**\n * UserBlock component.\n *\n * @param props Component props.\n * @param ref Component ref.\n * @return React element.\n */\nexport const UserBlock: Comp<UserBlockProps, HTMLDivElement> = forwardRef((props, ref) => {\n const {\n avatarProps,\n className,\n fields,\n linkProps,\n linkAs,\n multipleActions,\n name,\n nameProps,\n onClick,\n onMouseEnter,\n onMouseLeave,\n orientation,\n simpleAction,\n size,\n theme,\n ...forwardedProps\n } = props;\n let componentSize = size;\n\n // Special case - When using vertical orientation force the size to be Sizes.l.\n if (orientation === Orientation.vertical) {\n componentSize = Size.l;\n }\n\n const shouldDisplayActions: boolean = orientation === Orientation.vertical;\n\n const isLink = Boolean(linkProps?.href || linkAs);\n const isClickable = !!onClick || isLink;\n\n const nameBlock: ReactNode = React.useMemo(() => {\n if (isEmpty(name)) {\n return null;\n }\n let NameComponent: any = 'span';\n const nProps: any = {\n ...nameProps,\n className: classNames(`${CLASSNAME}__name`, linkProps?.className, nameProps?.className),\n };\n if (isClickable) {\n NameComponent = Link;\n Object.assign(nProps, {\n ...linkProps,\n onClick,\n linkAs,\n color: ColorPalette.dark,\n });\n }\n return <NameComponent {...nProps}>{name}</NameComponent>;\n }, [isClickable, linkAs, linkProps, name, nameProps, onClick]);\n\n const fieldsBlock: ReactNode = fields && componentSize !== Size.s && (\n <div className={`${CLASSNAME}__fields`}>\n {fields.map((field: string, idx: number) => (\n <span key={idx} className={`${CLASSNAME}__field`}>\n {field}\n </span>\n ))}\n </div>\n );\n\n return (\n <div\n ref={ref}\n {...forwardedProps}\n className={classNames(\n className,\n handleBasicClasses({ prefix: CLASSNAME, orientation, size: componentSize, theme, isClickable }),\n )}\n onMouseLeave={onMouseLeave}\n onMouseEnter={onMouseEnter}\n >\n {avatarProps && (\n <Avatar\n linkAs={linkAs}\n linkProps={linkProps}\n {...avatarProps}\n className={classNames(`${CLASSNAME}__avatar`, avatarProps.className)}\n size={componentSize}\n onClick={onClick}\n theme={theme}\n />\n )}\n {(fields || name) && (\n <div className={`${CLASSNAME}__wrapper`}>\n {nameBlock}\n {fieldsBlock}\n </div>\n )}\n {shouldDisplayActions && simpleAction && <div className={`${CLASSNAME}__action`}>{simpleAction}</div>}\n {shouldDisplayActions && multipleActions && (\n <div className={`${CLASSNAME}__actions`}>{multipleActions}</div>\n )}\n </div>\n );\n});\nUserBlock.displayName = COMPONENT_NAME;\nUserBlock.className = CLASSNAME;\nUserBlock.defaultProps = DEFAULT_PROPS;\n"],"names":["COMPONENT_NAME","CLASSNAME","getRootClassName","DEFAULT_PROPS","orientation","Orientation","horizontal","size","Size","m","theme","Theme","light","UserBlock","forwardRef","props","ref","avatarProps","className","fields","linkProps","linkAs","multipleActions","name","nameProps","onClick","onMouseEnter","onMouseLeave","simpleAction","forwardedProps","componentSize","vertical","l","shouldDisplayActions","isLink","Boolean","href","isClickable","nameBlock","React","useMemo","isEmpty","NameComponent","nProps","classNames","Link","Object","assign","color","ColorPalette","dark","fieldsBlock","s","map","field","idx","handleBasicClasses","prefix","displayName","defaultProps"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UserBlock.js","sources":["../../../src/components/user-block/UserBlock.tsx"],"sourcesContent":["import React, { forwardRef, ReactNode } from 'react';\nimport isEmpty from 'lodash/isEmpty';\nimport classNames from 'classnames';\nimport set from 'lodash/set';\n\nimport { Avatar, ColorPalette, Link, Orientation, Size, Theme } from '@lumx/react';\nimport { Comp, GenericProps, getRootClassName, handleBasicClasses } from '@lumx/react/utils';\n\nimport { AvatarProps } from '../avatar/Avatar';\n\n/**\n * User block sizes.\n */\nexport type UserBlockSize = Extract<Size, 's' | 'm' | 'l'>;\n\n/**\n * Defines the props of the component.\n */\nexport interface UserBlockProps extends GenericProps {\n /** Props to pass to the avatar. */\n avatarProps?: Omit<AvatarProps, 'alt'>;\n /** Additional fields used to describe the user. */\n fields?: string[];\n /** Props to pass to the link wrapping the avatar thumbnail. */\n linkProps?: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;\n /** Custom react component for the link (can be used to inject react router Link). */\n linkAs?: 'a' | any;\n /** Multiple action toolbar content. */\n multipleActions?: ReactNode;\n /** User name. */\n name?: string;\n /** Props to pass to the name block. */\n nameProps?: GenericProps;\n /** Orientation. */\n orientation?: Orientation;\n /** Simple action toolbar content. */\n simpleAction?: ReactNode;\n /** Size variant. */\n size?: UserBlockSize;\n /** Theme adapting the component to light or dark background. */\n theme?: Theme;\n /** On click callback. */\n onClick?(): void;\n /** On mouse enter callback. */\n onMouseEnter?(): void;\n /** On mouse leave callback. */\n onMouseLeave?(): void;\n}\n\n/**\n * Component display name.\n */\nconst COMPONENT_NAME = 'UserBlock';\n\n/**\n * Component default class name and class prefix.\n */\nconst CLASSNAME = getRootClassName(COMPONENT_NAME);\n\n/**\n * Component default props.\n */\nconst DEFAULT_PROPS: Partial<UserBlockProps> = {\n orientation: Orientation.horizontal,\n size: Size.m,\n theme: Theme.light,\n};\n\n/**\n * UserBlock component.\n *\n * @param props Component props.\n * @param ref Component ref.\n * @return React element.\n */\nexport const UserBlock: Comp<UserBlockProps, HTMLDivElement> = forwardRef((props, ref) => {\n const {\n avatarProps,\n className,\n fields,\n linkProps,\n linkAs,\n multipleActions,\n name,\n nameProps,\n onClick,\n onMouseEnter,\n onMouseLeave,\n orientation,\n simpleAction,\n size,\n theme,\n ...forwardedProps\n } = props;\n let componentSize = size;\n\n // Special case - When using vertical orientation force the size to be Sizes.l.\n if (orientation === Orientation.vertical) {\n componentSize = Size.l;\n }\n\n const shouldDisplayActions: boolean = orientation === Orientation.vertical;\n\n const isLink = Boolean(linkProps?.href || linkAs);\n const isClickable = !!onClick || isLink;\n\n const nameBlock: ReactNode = React.useMemo(() => {\n if (isEmpty(name)) {\n return null;\n }\n let NameComponent: any = 'span';\n const nProps: any = {\n ...nameProps,\n className: classNames(`${CLASSNAME}__name`, linkProps?.className, nameProps?.className),\n };\n if (isClickable) {\n NameComponent = Link;\n Object.assign(nProps, {\n ...linkProps,\n onClick,\n linkAs,\n color: ColorPalette.dark,\n });\n }\n // Disable avatar focus since the name block is the same link / same button.\n if (avatarProps) {\n set(avatarProps, ['thumbnailProps', 'tabIndex'], -1);\n }\n return <NameComponent {...nProps}>{name}</NameComponent>;\n }, [avatarProps, isClickable, linkAs, linkProps, name, nameProps, onClick]);\n\n const fieldsBlock: ReactNode = fields && componentSize !== Size.s && (\n <div className={`${CLASSNAME}__fields`}>\n {fields.map((field: string, idx: number) => (\n <span key={idx} className={`${CLASSNAME}__field`}>\n {field}\n </span>\n ))}\n </div>\n );\n\n return (\n <div\n ref={ref}\n {...forwardedProps}\n className={classNames(\n className,\n handleBasicClasses({ prefix: CLASSNAME, orientation, size: componentSize, theme, isClickable }),\n )}\n onMouseLeave={onMouseLeave}\n onMouseEnter={onMouseEnter}\n >\n {avatarProps && (\n <Avatar\n linkAs={linkAs}\n linkProps={linkProps}\n alt=\"\"\n {...(avatarProps as any)}\n className={classNames(`${CLASSNAME}__avatar`, avatarProps.className)}\n size={componentSize}\n onClick={onClick}\n theme={theme}\n />\n )}\n {(fields || name) && (\n <div className={`${CLASSNAME}__wrapper`}>\n {nameBlock}\n {fieldsBlock}\n </div>\n )}\n {shouldDisplayActions && simpleAction && <div className={`${CLASSNAME}__action`}>{simpleAction}</div>}\n {shouldDisplayActions && multipleActions && (\n <div className={`${CLASSNAME}__actions`}>{multipleActions}</div>\n )}\n </div>\n );\n});\nUserBlock.displayName = COMPONENT_NAME;\nUserBlock.className = CLASSNAME;\nUserBlock.defaultProps = DEFAULT_PROPS;\n"],"names":["COMPONENT_NAME","CLASSNAME","getRootClassName","DEFAULT_PROPS","orientation","Orientation","horizontal","size","Size","m","theme","Theme","light","UserBlock","forwardRef","props","ref","avatarProps","className","fields","linkProps","linkAs","multipleActions","name","nameProps","onClick","onMouseEnter","onMouseLeave","simpleAction","forwardedProps","componentSize","vertical","l","shouldDisplayActions","isLink","Boolean","href","isClickable","nameBlock","React","useMemo","isEmpty","NameComponent","nProps","classNames","Link","Object","assign","color","ColorPalette","dark","set","fieldsBlock","s","map","field","idx","handleBasicClasses","prefix","displayName","defaultProps"],"mappings":"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"}
|
|
@@ -2,8 +2,14 @@ import { useEffect } from 'react';
|
|
|
2
2
|
import { D as DOCUMENT } from './constants.js';
|
|
3
3
|
|
|
4
4
|
/** CSS selector listing all tabbable elements. */
|
|
5
|
+
var TABBABLE_ELEMENTS_SELECTOR = "a[href], button, textarea, input:not([type=\"hidden\"]), [tabindex]";
|
|
6
|
+
/** CSS selector matching element that are disabled (should not receive focus). */
|
|
5
7
|
|
|
6
|
-
var
|
|
8
|
+
var DISABLED_SELECTOR = "[tabindex=\"-1\"], [disabled]:not([disabled=\"false\"]), [aria-disabled]:not([aria-disabled=\"false\"])";
|
|
9
|
+
|
|
10
|
+
var isNotDisabled = function isNotDisabled(element) {
|
|
11
|
+
return !element.matches(DISABLED_SELECTOR);
|
|
12
|
+
};
|
|
7
13
|
/**
|
|
8
14
|
* Get first and last elements focusable in an element.
|
|
9
15
|
*
|
|
@@ -11,20 +17,24 @@ var TABBABLE_ELEMENTS_SELECTOR = "a[href]:not([tabindex=\"-1\"], [disabled], [ar
|
|
|
11
17
|
* @return first and last focusable elements
|
|
12
18
|
*/
|
|
13
19
|
|
|
14
|
-
function getFocusable(parentElement) {
|
|
15
|
-
var focusableElements = parentElement.querySelectorAll(TABBABLE_ELEMENTS_SELECTOR);
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
function getFirstAndLastFocusable(parentElement) {
|
|
22
|
+
var focusableElements = Array.from(parentElement.querySelectorAll(TABBABLE_ELEMENTS_SELECTOR)); // First non disabled element.
|
|
23
|
+
|
|
24
|
+
var first = focusableElements.find(isNotDisabled); // Last non disabled element.
|
|
25
|
+
|
|
26
|
+
var last = focusableElements.reverse().find(isNotDisabled);
|
|
27
|
+
|
|
28
|
+
if (last && first) {
|
|
29
|
+
return {
|
|
30
|
+
first: first,
|
|
31
|
+
last: last
|
|
32
|
+
};
|
|
19
33
|
}
|
|
20
34
|
|
|
21
|
-
|
|
22
|
-
var last = focusableElements[focusableElements.length - 1];
|
|
23
|
-
return {
|
|
24
|
-
first: first,
|
|
25
|
-
last: last
|
|
26
|
-
};
|
|
35
|
+
return {};
|
|
27
36
|
}
|
|
37
|
+
|
|
28
38
|
/**
|
|
29
39
|
* Add a key down event handler to the given root element (document.body by default) to trap the move of focus
|
|
30
40
|
* (TAB and SHIFT-TAB keys) inside the given focusZoneElement.
|
|
@@ -35,7 +45,6 @@ function getFocusable(parentElement) {
|
|
|
35
45
|
* @param rootElement The element on which the key down event will be placed.
|
|
36
46
|
*/
|
|
37
47
|
|
|
38
|
-
|
|
39
48
|
function useFocusTrap(focusZoneElement, focusElement) {
|
|
40
49
|
var rootElement = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DOCUMENT === null || DOCUMENT === void 0 ? void 0 : DOCUMENT.body;
|
|
41
50
|
useEffect(function () {
|
|
@@ -55,7 +64,7 @@ function useFocusTrap(focusZoneElement, focusElement) {
|
|
|
55
64
|
return;
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
var focusable =
|
|
67
|
+
var focusable = getFirstAndLastFocusable(focusZoneElement); // Prevent focus switch if no focusable available.
|
|
59
68
|
|
|
60
69
|
if (!focusable.first) {
|
|
61
70
|
evt.preventDefault();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFocusTrap.js","sources":["../../../src/hooks/useFocusTrap.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"useFocusTrap.js","sources":["../../../src/utils/focus/getFirstAndLastFocusable.ts","../../../src/hooks/useFocusTrap.ts"],"sourcesContent":["/** CSS selector listing all tabbable elements. */\nconst TABBABLE_ELEMENTS_SELECTOR = `a[href], button, textarea, input:not([type=\"hidden\"]), [tabindex]`;\n\n/** CSS selector matching element that are disabled (should not receive focus). */\nconst DISABLED_SELECTOR = `[tabindex=\"-1\"], [disabled]:not([disabled=\"false\"]), [aria-disabled]:not([aria-disabled=\"false\"])`;\n\nconst isNotDisabled = (element: HTMLElement) => !element.matches(DISABLED_SELECTOR);\n\n/**\n * Get first and last elements focusable in an element.\n *\n * @param parentElement The element in which to search focusable elements.\n * @return first and last focusable elements\n */\nexport function getFirstAndLastFocusable(parentElement: HTMLElement) {\n const focusableElements = Array.from(parentElement.querySelectorAll<HTMLElement>(TABBABLE_ELEMENTS_SELECTOR));\n\n // First non disabled element.\n const first = focusableElements.find(isNotDisabled);\n // Last non disabled element.\n const last = focusableElements.reverse().find(isNotDisabled);\n\n if (last && first) {\n return { first, last };\n }\n return {};\n}\n","import { useEffect } from 'react';\n\nimport { DOCUMENT } from '@lumx/react/constants';\nimport { getFirstAndLastFocusable } from '@lumx/react/utils/focus/getFirstAndLastFocusable';\n\n/**\n * Add a key down event handler to the given root element (document.body by default) to trap the move of focus\n * (TAB and SHIFT-TAB keys) inside the given focusZoneElement.\n * Will focus the given focus element when activating the focus trap.\n *\n * @param focusZoneElement The element in which to trap the focus.\n * @param focusElement The element to focus when the focus trap is activated.\n * @param rootElement The element on which the key down event will be placed.\n */\nexport function useFocusTrap(\n focusZoneElement: HTMLElement | null,\n focusElement?: HTMLElement | null,\n rootElement = DOCUMENT?.body,\n): void {\n useEffect(() => {\n if (rootElement && focusZoneElement) {\n (document.activeElement as HTMLElement)?.blur();\n if (focusElement) {\n focusElement.focus();\n }\n\n const onKeyDown = (evt: KeyboardEvent) => {\n const { key } = evt;\n if (key !== 'Tab') {\n return;\n }\n const focusable = getFirstAndLastFocusable(focusZoneElement);\n\n // Prevent focus switch if no focusable available.\n if (!focusable.first) {\n evt.preventDefault();\n return;\n }\n\n if (\n // No previous focus\n !document.activeElement ||\n // Previous focus is at the end of the focus zone.\n (!evt.shiftKey && document.activeElement === focusable.last) ||\n // Previous focus is outside the focus zone\n !focusZoneElement.contains(document.activeElement)\n ) {\n focusable.first.focus();\n evt.preventDefault();\n return;\n }\n\n if (\n // Focus order reversed\n evt.shiftKey &&\n // Previous focus is at the start of the focus zone.\n document.activeElement === focusable.first\n ) {\n focusable.last.focus();\n evt.preventDefault();\n }\n };\n rootElement.addEventListener('keydown', onKeyDown);\n return () => rootElement.removeEventListener('keydown', onKeyDown);\n }\n return undefined;\n }, [focusElement, focusZoneElement, rootElement]);\n}\n"],"names":["TABBABLE_ELEMENTS_SELECTOR","DISABLED_SELECTOR","isNotDisabled","element","matches","getFirstAndLastFocusable","parentElement","focusableElements","Array","from","querySelectorAll","first","find","last","reverse","useFocusTrap","focusZoneElement","focusElement","rootElement","DOCUMENT","body","useEffect","document","activeElement","blur","focus","onKeyDown","evt","key","focusable","preventDefault","shiftKey","contains","addEventListener","removeEventListener","undefined"],"mappings":";;;AAAA;AACA,IAAMA,0BAA0B,wEAAhC;AAEA;;AACA,IAAMC,iBAAiB,4GAAvB;;AAEA,IAAMC,aAAa,GAAG,SAAhBA,aAAgB,CAACC,OAAD;AAAA,SAA0B,CAACA,OAAO,CAACC,OAAR,CAAgBH,iBAAhB,CAA3B;AAAA,CAAtB;AAEA;;;;;;;;AAMO,SAASI,wBAAT,CAAkCC,aAAlC,EAA8D;AACjE,MAAMC,iBAAiB,GAAGC,KAAK,CAACC,IAAN,CAAWH,aAAa,CAACI,gBAAd,CAA4CV,0BAA5C,CAAX,CAA1B,CADiE;;AAIjE,MAAMW,KAAK,GAAGJ,iBAAiB,CAACK,IAAlB,CAAuBV,aAAvB,CAAd,CAJiE;;AAMjE,MAAMW,IAAI,GAAGN,iBAAiB,CAACO,OAAlB,GAA4BF,IAA5B,CAAiCV,aAAjC,CAAb;;AAEA,MAAIW,IAAI,IAAIF,KAAZ,EAAmB;AACf,WAAO;AAAEA,MAAAA,KAAK,EAALA,KAAF;AAASE,MAAAA,IAAI,EAAJA;AAAT,KAAP;AACH;;AACD,SAAO,EAAP;AACH;;ACrBD;;;;;;;;;;AASO,SAASE,YAAT,CACHC,gBADG,EAEHC,YAFG,EAIC;AAAA,MADJC,WACI,uEADUC,QACV,aADUA,QACV,uBADUA,QAAQ,CAAEC,IACpB;AACJC,EAAAA,SAAS,CAAC,YAAM;AACZ,QAAIH,WAAW,IAAIF,gBAAnB,EAAqC;AAAA;;AACjC,cAACM,QAAQ,CAACC,aAAV,8CAAyCC,IAAzC;;AACA,UAAIP,YAAJ,EAAkB;AACdA,QAAAA,YAAY,CAACQ,KAAb;AACH;;AAED,UAAMC,SAAS,GAAG,SAAZA,SAAY,CAACC,GAAD,EAAwB;AAAA,YAC9BC,GAD8B,GACtBD,GADsB,CAC9BC,GAD8B;;AAEtC,YAAIA,GAAG,KAAK,KAAZ,EAAmB;AACf;AACH;;AACD,YAAMC,SAAS,GAAGxB,wBAAwB,CAACW,gBAAD,CAA1C,CALsC;;AAQtC,YAAI,CAACa,SAAS,CAAClB,KAAf,EAAsB;AAClBgB,UAAAA,GAAG,CAACG,cAAJ;AACA;AACH;;AAED;AAEI,SAACR,QAAQ,CAACC,aAAV;AAEC,SAACI,GAAG,CAACI,QAAL,IAAiBT,QAAQ,CAACC,aAAT,KAA2BM,SAAS,CAAChB,IAFvD;AAIA,SAACG,gBAAgB,CAACgB,QAAjB,CAA0BV,QAAQ,CAACC,aAAnC,CANL,EAOE;AACEM,UAAAA,SAAS,CAAClB,KAAV,CAAgBc,KAAhB;AACAE,UAAAA,GAAG,CAACG,cAAJ;AACA;AACH;;AAED;AAEIH,QAAAA,GAAG,CAACI,QAAJ;AAEAT,QAAAA,QAAQ,CAACC,aAAT,KAA2BM,SAAS,CAAClB,KAJzC,EAKE;AACEkB,UAAAA,SAAS,CAAChB,IAAV,CAAeY,KAAf;AACAE,UAAAA,GAAG,CAACG,cAAJ;AACH;AACJ,OAnCD;;AAoCAZ,MAAAA,WAAW,CAACe,gBAAZ,CAA6B,SAA7B,EAAwCP,SAAxC;AACA,aAAO;AAAA,eAAMR,WAAW,CAACgB,mBAAZ,CAAgC,SAAhC,EAA2CR,SAA3C,CAAN;AAAA,OAAP;AACH;;AACD,WAAOS,SAAP;AACH,GA/CQ,EA+CN,CAAClB,YAAD,EAAeD,gBAAf,EAAiCE,WAAjC,CA/CM,CAAT;AAgDH;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-block.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"user-block.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
|
package/esm/index.js
CHANGED
|
@@ -85,5 +85,6 @@ export { T as ThumbnailAspectRatio, a as ThumbnailVariant } from './_internal/ty
|
|
|
85
85
|
export { T as Toolbar } from './_internal/Toolbar2.js';
|
|
86
86
|
export { T as Tooltip } from './_internal/Tooltip2.js';
|
|
87
87
|
export { a as Uploader, U as UploaderVariant } from './_internal/Uploader2.js';
|
|
88
|
+
import 'lodash/set';
|
|
88
89
|
export { U as UserBlock } from './_internal/UserBlock.js';
|
|
89
90
|
//# sourceMappingURL=index.js.map
|
package/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@juggle/resize-observer": "^3.2.0",
|
|
10
|
-
"@lumx/core": "^2.2.
|
|
11
|
-
"@lumx/icons": "^2.2.
|
|
10
|
+
"@lumx/core": "^2.2.19",
|
|
11
|
+
"@lumx/icons": "^2.2.19",
|
|
12
12
|
"@popperjs/core": "^2.5.4",
|
|
13
13
|
"body-scroll-lock": "^3.1.5",
|
|
14
14
|
"classnames": "^2.2.6",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"rollup-plugin-typescript-paths": "^1.2.2",
|
|
73
73
|
"test-data-bot": "^0.8.0",
|
|
74
74
|
"ts-jest": "^26.2.0",
|
|
75
|
-
"tsconfig-paths-webpack-plugin": "^3.
|
|
75
|
+
"tsconfig-paths-webpack-plugin": "^3.3.0",
|
|
76
76
|
"typescript": "^4.1.2",
|
|
77
77
|
"yargs": "^15.4.1",
|
|
78
78
|
"yarn": "^1.19.1"
|
|
@@ -120,6 +120,6 @@
|
|
|
120
120
|
"build:storybook": "cd storybook && ./build"
|
|
121
121
|
},
|
|
122
122
|
"sideEffects": false,
|
|
123
|
-
"version": "2.2.
|
|
124
|
-
"gitHead": "
|
|
123
|
+
"version": "2.2.19",
|
|
124
|
+
"gitHead": "075b14a949b8ed5897d5fa8087ea4ff993d90eb5"
|
|
125
125
|
}
|
|
@@ -283,7 +283,8 @@ export const ScrollableDialogWithHeaderAndFooter = ({ theme }: any) => {
|
|
|
283
283
|
);
|
|
284
284
|
};
|
|
285
285
|
|
|
286
|
-
|
|
286
|
+
/** Test dialog focus trap (focus is contained inside the dialog) with all kinds of focusable and non-focusable items */
|
|
287
|
+
export const DialogFocusTrap = ({ theme }: any) => {
|
|
287
288
|
const { button, buttonRef, closeDialog, isOpen } = useOpenButton(theme);
|
|
288
289
|
const [textValue, setTextValue] = useState('value');
|
|
289
290
|
const [checkboxValue, setCheckboxValue] = useState(false);
|
|
@@ -372,6 +373,8 @@ export const DialogWithFocusableElements = ({ theme }: any) => {
|
|
|
372
373
|
|
|
373
374
|
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
|
|
374
375
|
<div tabIndex={0}>Focus div</div>
|
|
376
|
+
|
|
377
|
+
<Button isDisabled={false}>Button explicitly not disabled (should focus)</Button>
|
|
375
378
|
</div>
|
|
376
379
|
</Dialog>
|
|
377
380
|
</>
|
|
@@ -1,82 +1,6 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
-
exports[`<Dialog> Snapshots and structure should render story
|
|
4
|
-
<Fragment>
|
|
5
|
-
<Button
|
|
6
|
-
emphasis="high"
|
|
7
|
-
onClick={[Function]}
|
|
8
|
-
size="m"
|
|
9
|
-
theme="light"
|
|
10
|
-
>
|
|
11
|
-
Open dialog
|
|
12
|
-
</Button>
|
|
13
|
-
<Dialog
|
|
14
|
-
isOpen={true}
|
|
15
|
-
onClose={[Function]}
|
|
16
|
-
parentElement={
|
|
17
|
-
Object {
|
|
18
|
-
"current": undefined,
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
size="big"
|
|
22
|
-
>
|
|
23
|
-
<div
|
|
24
|
-
className="lumx-spacing-padding"
|
|
25
|
-
>
|
|
26
|
-
|
|
27
|
-
Nihil hic munitissimus habendi senatus locus, nihil horum? At nos hinc posthac, sitientis piros
|
|
28
|
-
Afros. Magna pars studiorum, prodita quaerimus. Integer legentibus erat a ante historiarum
|
|
29
|
-
dapibus. Praeterea iter est quasdam res quas ex communi. Ullamco laboris nisi ut aliquid ex ea
|
|
30
|
-
commodi consequat. Inmensae subtilitatis, obscuris et malesuada fames. Me non paenitet nullum
|
|
31
|
-
festiviorem excogitasse ad hoc. Cum ceteris in veneratione tui montes, nascetur mus. Etiam
|
|
32
|
-
habebis sem dicantur magna mollis euismod. Quis aute iure reprehenderit in voluptate velit esse.
|
|
33
|
-
Phasellus laoreet lorem vel dolor tempus vehicula. Ambitioni dedisse scripsisse iudicaretur.
|
|
34
|
-
Paullum deliquit, ponderibus modulisque suis ratio utitur. Ab illo tempore, ab est sed
|
|
35
|
-
immemorabili. Nec dubitamus multa iter quae et nos invenerat. Tu quoque, Brute, fili mi, nihil
|
|
36
|
-
timor populi, nihil! Morbi fringilla convallis sapien, id pulvinar odio volutpat. Cras mattis
|
|
37
|
-
iudicium purus sit amet fermentum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus.
|
|
38
|
-
Quisque ut dolor gravida, placerat libero vel, euismod. Unam incolunt Belgae, aliam Aquitani,
|
|
39
|
-
tertiam. Cras mattis iudicium purus sit amet fermentum
|
|
40
|
-
</div>
|
|
41
|
-
<footer>
|
|
42
|
-
<Toolbar
|
|
43
|
-
after={
|
|
44
|
-
<Button
|
|
45
|
-
emphasis="low"
|
|
46
|
-
onClick={[Function]}
|
|
47
|
-
size="m"
|
|
48
|
-
theme="light"
|
|
49
|
-
>
|
|
50
|
-
Close
|
|
51
|
-
</Button>
|
|
52
|
-
}
|
|
53
|
-
/>
|
|
54
|
-
</footer>
|
|
55
|
-
</Dialog>
|
|
56
|
-
<AlertDialog
|
|
57
|
-
confirmProps={
|
|
58
|
-
Object {
|
|
59
|
-
"label": "Confirm",
|
|
60
|
-
"onClick": [Function],
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
isOpen={false}
|
|
64
|
-
kind="info"
|
|
65
|
-
onClose={[Function]}
|
|
66
|
-
parentElement={
|
|
67
|
-
Object {
|
|
68
|
-
"current": undefined,
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
size="tiny"
|
|
72
|
-
title="Default (info)"
|
|
73
|
-
>
|
|
74
|
-
Consequat deserunt officia aute laborum tempor anim sint est.
|
|
75
|
-
</AlertDialog>
|
|
76
|
-
</Fragment>
|
|
77
|
-
`;
|
|
78
|
-
|
|
79
|
-
exports[`<Dialog> Snapshots and structure should render story DialogWithFocusableElements 1`] = `
|
|
3
|
+
exports[`<Dialog> Snapshots and structure should render story DialogFocusTrap 1`] = `
|
|
80
4
|
<Fragment>
|
|
81
5
|
<Button
|
|
82
6
|
emphasis="high"
|
|
@@ -226,11 +150,95 @@ exports[`<Dialog> Snapshots and structure should render story DialogWithFocusabl
|
|
|
226
150
|
>
|
|
227
151
|
Focus div
|
|
228
152
|
</div>
|
|
153
|
+
<Button
|
|
154
|
+
emphasis="high"
|
|
155
|
+
isDisabled={false}
|
|
156
|
+
size="m"
|
|
157
|
+
theme="light"
|
|
158
|
+
>
|
|
159
|
+
Button explicitly not disabled (should focus)
|
|
160
|
+
</Button>
|
|
229
161
|
</div>
|
|
230
162
|
</Dialog>
|
|
231
163
|
</Fragment>
|
|
232
164
|
`;
|
|
233
165
|
|
|
166
|
+
exports[`<Dialog> Snapshots and structure should render story DialogWithAlertDialog 1`] = `
|
|
167
|
+
<Fragment>
|
|
168
|
+
<Button
|
|
169
|
+
emphasis="high"
|
|
170
|
+
onClick={[Function]}
|
|
171
|
+
size="m"
|
|
172
|
+
theme="light"
|
|
173
|
+
>
|
|
174
|
+
Open dialog
|
|
175
|
+
</Button>
|
|
176
|
+
<Dialog
|
|
177
|
+
isOpen={true}
|
|
178
|
+
onClose={[Function]}
|
|
179
|
+
parentElement={
|
|
180
|
+
Object {
|
|
181
|
+
"current": undefined,
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
size="big"
|
|
185
|
+
>
|
|
186
|
+
<div
|
|
187
|
+
className="lumx-spacing-padding"
|
|
188
|
+
>
|
|
189
|
+
|
|
190
|
+
Nihil hic munitissimus habendi senatus locus, nihil horum? At nos hinc posthac, sitientis piros
|
|
191
|
+
Afros. Magna pars studiorum, prodita quaerimus. Integer legentibus erat a ante historiarum
|
|
192
|
+
dapibus. Praeterea iter est quasdam res quas ex communi. Ullamco laboris nisi ut aliquid ex ea
|
|
193
|
+
commodi consequat. Inmensae subtilitatis, obscuris et malesuada fames. Me non paenitet nullum
|
|
194
|
+
festiviorem excogitasse ad hoc. Cum ceteris in veneratione tui montes, nascetur mus. Etiam
|
|
195
|
+
habebis sem dicantur magna mollis euismod. Quis aute iure reprehenderit in voluptate velit esse.
|
|
196
|
+
Phasellus laoreet lorem vel dolor tempus vehicula. Ambitioni dedisse scripsisse iudicaretur.
|
|
197
|
+
Paullum deliquit, ponderibus modulisque suis ratio utitur. Ab illo tempore, ab est sed
|
|
198
|
+
immemorabili. Nec dubitamus multa iter quae et nos invenerat. Tu quoque, Brute, fili mi, nihil
|
|
199
|
+
timor populi, nihil! Morbi fringilla convallis sapien, id pulvinar odio volutpat. Cras mattis
|
|
200
|
+
iudicium purus sit amet fermentum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus.
|
|
201
|
+
Quisque ut dolor gravida, placerat libero vel, euismod. Unam incolunt Belgae, aliam Aquitani,
|
|
202
|
+
tertiam. Cras mattis iudicium purus sit amet fermentum
|
|
203
|
+
</div>
|
|
204
|
+
<footer>
|
|
205
|
+
<Toolbar
|
|
206
|
+
after={
|
|
207
|
+
<Button
|
|
208
|
+
emphasis="low"
|
|
209
|
+
onClick={[Function]}
|
|
210
|
+
size="m"
|
|
211
|
+
theme="light"
|
|
212
|
+
>
|
|
213
|
+
Close
|
|
214
|
+
</Button>
|
|
215
|
+
}
|
|
216
|
+
/>
|
|
217
|
+
</footer>
|
|
218
|
+
</Dialog>
|
|
219
|
+
<AlertDialog
|
|
220
|
+
confirmProps={
|
|
221
|
+
Object {
|
|
222
|
+
"label": "Confirm",
|
|
223
|
+
"onClick": [Function],
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
isOpen={false}
|
|
227
|
+
kind="info"
|
|
228
|
+
onClose={[Function]}
|
|
229
|
+
parentElement={
|
|
230
|
+
Object {
|
|
231
|
+
"current": undefined,
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
size="tiny"
|
|
235
|
+
title="Default (info)"
|
|
236
|
+
>
|
|
237
|
+
Consequat deserunt officia aute laborum tempor anim sint est.
|
|
238
|
+
</AlertDialog>
|
|
239
|
+
</Fragment>
|
|
240
|
+
`;
|
|
241
|
+
|
|
234
242
|
exports[`<Dialog> Snapshots and structure should render story DialogWithHeaderFooterAndDivider 1`] = `
|
|
235
243
|
<Fragment>
|
|
236
244
|
<Button
|
|
@@ -65,12 +65,9 @@ const ARROW_SIZE = 8;
|
|
|
65
65
|
* @return React element.
|
|
66
66
|
*/
|
|
67
67
|
export const Tooltip: Comp<TooltipProps, HTMLDivElement> = forwardRef((props, ref) => {
|
|
68
|
-
if (!DOCUMENT) {
|
|
69
|
-
// Can't render in SSR.
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
68
|
const { label, children, className, delay, placement, forceOpen, ...forwardedProps } = props;
|
|
73
|
-
|
|
69
|
+
// Disable in SSR or without a label.
|
|
70
|
+
if (!DOCUMENT || !label) {
|
|
74
71
|
return <>{children}</>;
|
|
75
72
|
}
|
|
76
73
|
|
|
@@ -17,7 +17,7 @@ export const Default = ({ theme }: any) => (
|
|
|
17
17
|
theme={theme}
|
|
18
18
|
name="Emmitt O. Lum"
|
|
19
19
|
fields={['Creative developer', 'Denpasar']}
|
|
20
|
-
avatarProps={{ image: avatarImageKnob()
|
|
20
|
+
avatarProps={{ image: avatarImageKnob() }}
|
|
21
21
|
onMouseEnter={logAction('Mouse entered')}
|
|
22
22
|
onMouseLeave={logAction('Mouse left')}
|
|
23
23
|
/>
|
|
@@ -30,7 +30,7 @@ export const Sizes = ({ theme }: any) =>
|
|
|
30
30
|
theme={theme}
|
|
31
31
|
name="Emmitt O. Lum"
|
|
32
32
|
fields={['Creative developer', 'Denpasar']}
|
|
33
|
-
avatarProps={{ image: avatarImageKnob()
|
|
33
|
+
avatarProps={{ image: avatarImageKnob() }}
|
|
34
34
|
size={size}
|
|
35
35
|
onMouseEnter={logAction('Mouse entered')}
|
|
36
36
|
onMouseLeave={logAction('Mouse left')}
|
|
@@ -41,8 +41,9 @@ export const Clickable = ({ theme }: any) => {
|
|
|
41
41
|
const baseProps = {
|
|
42
42
|
theme,
|
|
43
43
|
name: 'Emmitt O. Lum',
|
|
44
|
+
nameProps: { 'aria-label': 'Emmitt O. Lum - open user profile' },
|
|
44
45
|
fields: ['Creative developer', 'Denpasar'],
|
|
45
|
-
avatarProps: { image: avatarImageKnob()
|
|
46
|
+
avatarProps: { image: avatarImageKnob() },
|
|
46
47
|
} as any;
|
|
47
48
|
return (
|
|
48
49
|
<>
|
|
@@ -65,7 +66,6 @@ export const WithBadge = ({ theme }: any) => (
|
|
|
65
66
|
fields={['Creative developer', 'Denpasar']}
|
|
66
67
|
avatarProps={{
|
|
67
68
|
image: avatarImageKnob(),
|
|
68
|
-
alt: 'Avatar',
|
|
69
69
|
badge: (
|
|
70
70
|
<Badge color={ColorPalette.blue}>
|
|
71
71
|
<Icon icon={mdiStar} />
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { forwardRef, ReactNode } from 'react';
|
|
2
2
|
import isEmpty from 'lodash/isEmpty';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
+
import set from 'lodash/set';
|
|
4
5
|
|
|
5
6
|
import { Avatar, ColorPalette, Link, Orientation, Size, Theme } from '@lumx/react';
|
|
6
7
|
import { Comp, GenericProps, getRootClassName, handleBasicClasses } from '@lumx/react/utils';
|
|
@@ -17,7 +18,7 @@ export type UserBlockSize = Extract<Size, 's' | 'm' | 'l'>;
|
|
|
17
18
|
*/
|
|
18
19
|
export interface UserBlockProps extends GenericProps {
|
|
19
20
|
/** Props to pass to the avatar. */
|
|
20
|
-
avatarProps?: AvatarProps
|
|
21
|
+
avatarProps?: Omit<AvatarProps, 'alt'>;
|
|
21
22
|
/** Additional fields used to describe the user. */
|
|
22
23
|
fields?: string[];
|
|
23
24
|
/** Props to pass to the link wrapping the avatar thumbnail. */
|
|
@@ -121,8 +122,12 @@ export const UserBlock: Comp<UserBlockProps, HTMLDivElement> = forwardRef((props
|
|
|
121
122
|
color: ColorPalette.dark,
|
|
122
123
|
});
|
|
123
124
|
}
|
|
125
|
+
// Disable avatar focus since the name block is the same link / same button.
|
|
126
|
+
if (avatarProps) {
|
|
127
|
+
set(avatarProps, ['thumbnailProps', 'tabIndex'], -1);
|
|
128
|
+
}
|
|
124
129
|
return <NameComponent {...nProps}>{name}</NameComponent>;
|
|
125
|
-
}, [isClickable, linkAs, linkProps, name, nameProps, onClick]);
|
|
130
|
+
}, [avatarProps, isClickable, linkAs, linkProps, name, nameProps, onClick]);
|
|
126
131
|
|
|
127
132
|
const fieldsBlock: ReactNode = fields && componentSize !== Size.s && (
|
|
128
133
|
<div className={`${CLASSNAME}__fields`}>
|
|
@@ -149,7 +154,8 @@ export const UserBlock: Comp<UserBlockProps, HTMLDivElement> = forwardRef((props
|
|
|
149
154
|
<Avatar
|
|
150
155
|
linkAs={linkAs}
|
|
151
156
|
linkProps={linkProps}
|
|
152
|
-
|
|
157
|
+
alt=""
|
|
158
|
+
{...(avatarProps as any)}
|
|
153
159
|
className={classNames(`${CLASSNAME}__avatar`, avatarProps.className)}
|
|
154
160
|
size={componentSize}
|
|
155
161
|
onClick={onClick}
|
|
@@ -6,17 +6,23 @@ Array [
|
|
|
6
6
|
className="lumx-user-block lumx-user-block--orientation-horizontal lumx-user-block--size-m lumx-user-block--theme-light lumx-user-block--is-clickable"
|
|
7
7
|
>
|
|
8
8
|
<Avatar
|
|
9
|
-
alt="
|
|
9
|
+
alt=""
|
|
10
10
|
className="lumx-user-block__avatar"
|
|
11
11
|
image="/demo-assets/avatar1.jpg"
|
|
12
12
|
onClick={[Function]}
|
|
13
13
|
size="m"
|
|
14
14
|
theme="light"
|
|
15
|
+
thumbnailProps={
|
|
16
|
+
Object {
|
|
17
|
+
"tabIndex": -1,
|
|
18
|
+
}
|
|
19
|
+
}
|
|
15
20
|
/>
|
|
16
21
|
<div
|
|
17
22
|
className="lumx-user-block__wrapper"
|
|
18
23
|
>
|
|
19
24
|
<Link
|
|
25
|
+
aria-label="Emmitt O. Lum - open user profile"
|
|
20
26
|
className="lumx-user-block__name"
|
|
21
27
|
color="dark"
|
|
22
28
|
onClick={[Function]}
|
|
@@ -45,7 +51,7 @@ Array [
|
|
|
45
51
|
className="lumx-user-block lumx-user-block--orientation-horizontal lumx-user-block--size-m lumx-user-block--theme-light lumx-user-block--is-clickable"
|
|
46
52
|
>
|
|
47
53
|
<Avatar
|
|
48
|
-
alt="
|
|
54
|
+
alt=""
|
|
49
55
|
className="lumx-user-block__avatar"
|
|
50
56
|
image="/demo-assets/avatar1.jpg"
|
|
51
57
|
linkProps={
|
|
@@ -55,11 +61,17 @@ Array [
|
|
|
55
61
|
}
|
|
56
62
|
size="m"
|
|
57
63
|
theme="light"
|
|
64
|
+
thumbnailProps={
|
|
65
|
+
Object {
|
|
66
|
+
"tabIndex": -1,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
58
69
|
/>
|
|
59
70
|
<div
|
|
60
71
|
className="lumx-user-block__wrapper"
|
|
61
72
|
>
|
|
62
73
|
<Link
|
|
74
|
+
aria-label="Emmitt O. Lum - open user profile"
|
|
63
75
|
className="lumx-user-block__name"
|
|
64
76
|
color="dark"
|
|
65
77
|
href="https://example.com"
|
|
@@ -88,17 +100,23 @@ Array [
|
|
|
88
100
|
className="lumx-user-block lumx-user-block--orientation-horizontal lumx-user-block--size-m lumx-user-block--theme-light lumx-user-block--is-clickable"
|
|
89
101
|
>
|
|
90
102
|
<Avatar
|
|
91
|
-
alt="
|
|
103
|
+
alt=""
|
|
92
104
|
className="lumx-user-block__avatar"
|
|
93
105
|
image="/demo-assets/avatar1.jpg"
|
|
94
106
|
linkAs={[Function]}
|
|
95
107
|
size="m"
|
|
96
108
|
theme="light"
|
|
109
|
+
thumbnailProps={
|
|
110
|
+
Object {
|
|
111
|
+
"tabIndex": -1,
|
|
112
|
+
}
|
|
113
|
+
}
|
|
97
114
|
/>
|
|
98
115
|
<div
|
|
99
116
|
className="lumx-user-block__wrapper"
|
|
100
117
|
>
|
|
101
118
|
<Link
|
|
119
|
+
aria-label="Emmitt O. Lum - open user profile"
|
|
102
120
|
className="lumx-user-block__name"
|
|
103
121
|
color="dark"
|
|
104
122
|
linkAs={[Function]}
|
|
@@ -133,11 +151,16 @@ exports[`<UserBlock> Snapshots and structure should render story 'Default' 1`] =
|
|
|
133
151
|
onMouseLeave={[Function]}
|
|
134
152
|
>
|
|
135
153
|
<Avatar
|
|
136
|
-
alt="
|
|
154
|
+
alt=""
|
|
137
155
|
className="lumx-user-block__avatar"
|
|
138
156
|
image="/demo-assets/avatar1.jpg"
|
|
139
157
|
size="m"
|
|
140
158
|
theme="light"
|
|
159
|
+
thumbnailProps={
|
|
160
|
+
Object {
|
|
161
|
+
"tabIndex": -1,
|
|
162
|
+
}
|
|
163
|
+
}
|
|
141
164
|
/>
|
|
142
165
|
<div
|
|
143
166
|
className="lumx-user-block__wrapper"
|
|
@@ -175,11 +198,16 @@ Array [
|
|
|
175
198
|
onMouseLeave={[Function]}
|
|
176
199
|
>
|
|
177
200
|
<Avatar
|
|
178
|
-
alt="
|
|
201
|
+
alt=""
|
|
179
202
|
className="lumx-user-block__avatar"
|
|
180
203
|
image="/demo-assets/avatar1.jpg"
|
|
181
204
|
size="s"
|
|
182
205
|
theme="light"
|
|
206
|
+
thumbnailProps={
|
|
207
|
+
Object {
|
|
208
|
+
"tabIndex": -1,
|
|
209
|
+
}
|
|
210
|
+
}
|
|
183
211
|
/>
|
|
184
212
|
<div
|
|
185
213
|
className="lumx-user-block__wrapper"
|
|
@@ -197,11 +225,16 @@ Array [
|
|
|
197
225
|
onMouseLeave={[Function]}
|
|
198
226
|
>
|
|
199
227
|
<Avatar
|
|
200
|
-
alt="
|
|
228
|
+
alt=""
|
|
201
229
|
className="lumx-user-block__avatar"
|
|
202
230
|
image="/demo-assets/avatar1.jpg"
|
|
203
231
|
size="m"
|
|
204
232
|
theme="light"
|
|
233
|
+
thumbnailProps={
|
|
234
|
+
Object {
|
|
235
|
+
"tabIndex": -1,
|
|
236
|
+
}
|
|
237
|
+
}
|
|
205
238
|
/>
|
|
206
239
|
<div
|
|
207
240
|
className="lumx-user-block__wrapper"
|
|
@@ -235,11 +268,16 @@ Array [
|
|
|
235
268
|
onMouseLeave={[Function]}
|
|
236
269
|
>
|
|
237
270
|
<Avatar
|
|
238
|
-
alt="
|
|
271
|
+
alt=""
|
|
239
272
|
className="lumx-user-block__avatar"
|
|
240
273
|
image="/demo-assets/avatar1.jpg"
|
|
241
274
|
size="l"
|
|
242
275
|
theme="light"
|
|
276
|
+
thumbnailProps={
|
|
277
|
+
Object {
|
|
278
|
+
"tabIndex": -1,
|
|
279
|
+
}
|
|
280
|
+
}
|
|
243
281
|
/>
|
|
244
282
|
<div
|
|
245
283
|
className="lumx-user-block__wrapper"
|
|
@@ -275,7 +313,7 @@ exports[`<UserBlock> Snapshots and structure should render story 'WithBadge' 1`]
|
|
|
275
313
|
className="lumx-user-block lumx-user-block--orientation-horizontal lumx-user-block--size-m lumx-user-block--theme-light"
|
|
276
314
|
>
|
|
277
315
|
<Avatar
|
|
278
|
-
alt="
|
|
316
|
+
alt=""
|
|
279
317
|
badge={
|
|
280
318
|
<Badge
|
|
281
319
|
color="blue"
|
|
@@ -289,6 +327,11 @@ exports[`<UserBlock> Snapshots and structure should render story 'WithBadge' 1`]
|
|
|
289
327
|
image="/demo-assets/avatar1.jpg"
|
|
290
328
|
size="m"
|
|
291
329
|
theme="light"
|
|
330
|
+
thumbnailProps={
|
|
331
|
+
Object {
|
|
332
|
+
"tabIndex": -1,
|
|
333
|
+
}
|
|
334
|
+
}
|
|
292
335
|
/>
|
|
293
336
|
<div
|
|
294
337
|
className="lumx-user-block__wrapper"
|
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
export const useBooleanState = (defaultValue: boolean): [boolean, () => void, () => void, () => void] => {
|
|
4
4
|
const [booleanValue, setBoolean] = useState<boolean>(defaultValue);
|
|
5
5
|
|
|
6
|
-
const setToFalse = () =>
|
|
7
|
-
setBoolean(false);
|
|
8
|
-
};
|
|
6
|
+
const setToFalse = useCallback(() => setBoolean(false), []);
|
|
9
7
|
|
|
10
|
-
const setToTrue = () =>
|
|
11
|
-
setBoolean(true);
|
|
12
|
-
};
|
|
8
|
+
const setToTrue = useCallback(() => setBoolean(true), []);
|
|
13
9
|
|
|
14
|
-
const toggleBoolean = () =>
|
|
15
|
-
setBoolean(!booleanValue);
|
|
16
|
-
};
|
|
10
|
+
const toggleBoolean = useCallback(() => setBoolean((previousValue) => !previousValue), []);
|
|
17
11
|
|
|
18
12
|
return [booleanValue, setToFalse, setToTrue, toggleBoolean];
|
|
19
13
|
};
|
|
@@ -1,33 +1,7 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
import { DOCUMENT } from '@lumx/react/constants';
|
|
4
|
-
|
|
5
|
-
/** CSS selector listing all tabbable elements. */
|
|
6
|
-
const TABBABLE_ELEMENTS_SELECTOR = `a[href]:not([tabindex="-1"], [disabled], [aria-disabled]),
|
|
7
|
-
button:not([tabindex="-1"], [disabled], [aria-disabled]),
|
|
8
|
-
textarea:not([tabindex="-1"], [disabled], [aria-disabled]),
|
|
9
|
-
input[type="text"]:not([tabindex="-1"], [disabled], [aria-disabled]),
|
|
10
|
-
input[type="radio"]:not([tabindex="-1"], [disabled], [aria-disabled]),
|
|
11
|
-
input[type="checkbox"]:not([tabindex="-1"], [disabled], [aria-disabled]),
|
|
12
|
-
[tabindex]:not([tabindex="-1"], [disabled], [aria-disabled])`;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Get first and last elements focusable in an element.
|
|
16
|
-
*
|
|
17
|
-
* @param parentElement The element in which to search focusable elements.
|
|
18
|
-
* @return first and last focusable elements
|
|
19
|
-
*/
|
|
20
|
-
function getFocusable(parentElement: HTMLElement) {
|
|
21
|
-
const focusableElements = parentElement.querySelectorAll<HTMLElement>(TABBABLE_ELEMENTS_SELECTOR);
|
|
22
|
-
|
|
23
|
-
if (focusableElements.length <= 0) {
|
|
24
|
-
return {};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const first = focusableElements[0];
|
|
28
|
-
const last = focusableElements[focusableElements.length - 1];
|
|
29
|
-
return { first, last };
|
|
30
|
-
}
|
|
4
|
+
import { getFirstAndLastFocusable } from '@lumx/react/utils/focus/getFirstAndLastFocusable';
|
|
31
5
|
|
|
32
6
|
/**
|
|
33
7
|
* Add a key down event handler to the given root element (document.body by default) to trap the move of focus
|
|
@@ -55,7 +29,7 @@ export function useFocusTrap(
|
|
|
55
29
|
if (key !== 'Tab') {
|
|
56
30
|
return;
|
|
57
31
|
}
|
|
58
|
-
const focusable =
|
|
32
|
+
const focusable = getFirstAndLastFocusable(focusZoneElement);
|
|
59
33
|
|
|
60
34
|
// Prevent focus switch if no focusable available.
|
|
61
35
|
if (!focusable.first) {
|
|
@@ -6,4 +6,5 @@ export default { title: 'LumX components/dialog/Dialog Demos' };
|
|
|
6
6
|
export { App as Alert } from './alert';
|
|
7
7
|
export { App as Confirm } from './confirm';
|
|
8
8
|
export { App as Default } from './default';
|
|
9
|
+
export { App as Isloading } from './isloading';
|
|
9
10
|
export { App as Sizes } from './sizes';
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { getFirstAndLastFocusable } from '@lumx/react/utils/focus/getFirstAndLastFocusable';
|
|
2
|
+
|
|
3
|
+
function htmlToElement(html: string): any {
|
|
4
|
+
const template = document.createElement('template');
|
|
5
|
+
template.innerHTML = html.trim();
|
|
6
|
+
return template.content.firstChild;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
describe(getFirstAndLastFocusable.name, () => {
|
|
10
|
+
it('should get empty', () => {
|
|
11
|
+
const element = htmlToElement(`<div></div>`);
|
|
12
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
13
|
+
expect(focusable).toEqual({});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should get single item', () => {
|
|
17
|
+
const element = htmlToElement(`<div><button /></div>`);
|
|
18
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
19
|
+
expect(focusable.last).toBe(focusable.first);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should get first and last', () => {
|
|
23
|
+
const element = htmlToElement(`
|
|
24
|
+
<div>
|
|
25
|
+
<div>Non focusable div</div>
|
|
26
|
+
<button>Simple button</button>
|
|
27
|
+
<div>Non focusable div</div>
|
|
28
|
+
<input />
|
|
29
|
+
<div>Non focusable div</div>
|
|
30
|
+
</div>
|
|
31
|
+
`);
|
|
32
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
33
|
+
expect(focusable.first).toMatchInlineSnapshot(`
|
|
34
|
+
<button>
|
|
35
|
+
Simple button
|
|
36
|
+
</button>
|
|
37
|
+
`);
|
|
38
|
+
expect(focusable.first).toMatchInlineSnapshot(`
|
|
39
|
+
<button>
|
|
40
|
+
Simple button
|
|
41
|
+
</button>
|
|
42
|
+
`);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('match focusable elements', () => {
|
|
46
|
+
it('should match input element', () => {
|
|
47
|
+
const element = htmlToElement(`<div><input /></div>`);
|
|
48
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
49
|
+
expect(focusable.first).toMatchInlineSnapshot(`<input />`);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should match link element', () => {
|
|
53
|
+
const element = htmlToElement(`<div><a href="#" /></div>`);
|
|
54
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
55
|
+
expect(focusable.first).toMatchInlineSnapshot(`
|
|
56
|
+
<a
|
|
57
|
+
href="#"
|
|
58
|
+
/>
|
|
59
|
+
`);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should match textarea element', () => {
|
|
63
|
+
const element = htmlToElement(`<div><textarea /></div>`);
|
|
64
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
65
|
+
expect(focusable.first).toMatchInlineSnapshot(`
|
|
66
|
+
<textarea>
|
|
67
|
+
</div>
|
|
68
|
+
</textarea>
|
|
69
|
+
`);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should match element with tabindex', () => {
|
|
73
|
+
const element = htmlToElement(`<div><span tabindex="0" /></div>`);
|
|
74
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
75
|
+
expect(focusable.first).toMatchInlineSnapshot(`
|
|
76
|
+
<span
|
|
77
|
+
tabindex="0"
|
|
78
|
+
/>
|
|
79
|
+
`);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should keep disabled=false', () => {
|
|
83
|
+
const element = htmlToElement(`<div><button disabled="false" /><button /></div>`);
|
|
84
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
85
|
+
expect(focusable.first).toMatchInlineSnapshot(`
|
|
86
|
+
<button
|
|
87
|
+
disabled="false"
|
|
88
|
+
/>
|
|
89
|
+
`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should keep aria-disabled=false', () => {
|
|
93
|
+
const element = htmlToElement(`<div><button aria-disabled="false" /><button /></div>`);
|
|
94
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
95
|
+
expect(focusable.first).toMatchInlineSnapshot(`
|
|
96
|
+
<button
|
|
97
|
+
aria-disabled="false"
|
|
98
|
+
/>
|
|
99
|
+
`);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('skip disabled elements', () => {
|
|
104
|
+
it('should skip disabled', () => {
|
|
105
|
+
const element = htmlToElement(`<div><button disabled /><button /></div>`);
|
|
106
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
107
|
+
expect(focusable.first).toMatchInlineSnapshot(`<button />`);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should skip aria-disabled', () => {
|
|
111
|
+
const element = htmlToElement(`<div><button aria-disabled /><button /></div>`);
|
|
112
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
113
|
+
expect(focusable.first).toMatchInlineSnapshot(`<button />`);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should skip tabindex=-1', () => {
|
|
117
|
+
const element = htmlToElement(`<div><button tabindex="-1" /><button /></div>`);
|
|
118
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
119
|
+
expect(focusable.first).toMatchInlineSnapshot(`<button />`);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should skip input type hidden', () => {
|
|
123
|
+
const element = htmlToElement(`<div><input type="hidden" /><button /></div>`);
|
|
124
|
+
const focusable = getFirstAndLastFocusable(element);
|
|
125
|
+
expect(focusable.first).toMatchInlineSnapshot(`<button />`);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** CSS selector listing all tabbable elements. */
|
|
2
|
+
const TABBABLE_ELEMENTS_SELECTOR = `a[href], button, textarea, input:not([type="hidden"]), [tabindex]`;
|
|
3
|
+
|
|
4
|
+
/** CSS selector matching element that are disabled (should not receive focus). */
|
|
5
|
+
const DISABLED_SELECTOR = `[tabindex="-1"], [disabled]:not([disabled="false"]), [aria-disabled]:not([aria-disabled="false"])`;
|
|
6
|
+
|
|
7
|
+
const isNotDisabled = (element: HTMLElement) => !element.matches(DISABLED_SELECTOR);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get first and last elements focusable in an element.
|
|
11
|
+
*
|
|
12
|
+
* @param parentElement The element in which to search focusable elements.
|
|
13
|
+
* @return first and last focusable elements
|
|
14
|
+
*/
|
|
15
|
+
export function getFirstAndLastFocusable(parentElement: HTMLElement) {
|
|
16
|
+
const focusableElements = Array.from(parentElement.querySelectorAll<HTMLElement>(TABBABLE_ELEMENTS_SELECTOR));
|
|
17
|
+
|
|
18
|
+
// First non disabled element.
|
|
19
|
+
const first = focusableElements.find(isNotDisabled);
|
|
20
|
+
// Last non disabled element.
|
|
21
|
+
const last = focusableElements.reverse().find(isNotDisabled);
|
|
22
|
+
|
|
23
|
+
if (last && first) {
|
|
24
|
+
return { first, last };
|
|
25
|
+
}
|
|
26
|
+
return {};
|
|
27
|
+
}
|
package/types.d.ts
CHANGED
|
@@ -2646,7 +2646,7 @@ export declare type UserBlockSize = Extract<Size, "s" | "m" | "l">;
|
|
|
2646
2646
|
*/
|
|
2647
2647
|
export interface UserBlockProps extends GenericProps {
|
|
2648
2648
|
/** Props to pass to the avatar. */
|
|
2649
|
-
avatarProps?: AvatarProps
|
|
2649
|
+
avatarProps?: Omit<AvatarProps, "alt">;
|
|
2650
2650
|
/** Additional fields used to describe the user. */
|
|
2651
2651
|
fields?: string[];
|
|
2652
2652
|
/** Props to pass to the link wrapping the avatar thumbnail. */
|