@lumx/react 3.1.5 → 3.2.1-alpha.0

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.
Files changed (147) hide show
  1. package/_internal/types.d.ts +16 -5
  2. package/index.d.ts +45 -4
  3. package/index.js +632 -423
  4. package/index.js.map +1 -1
  5. package/package.json +3 -3
  6. package/src/components/alert-dialog/AlertDialog.stories.tsx +96 -165
  7. package/src/components/alert-dialog/AlertDialog.test.tsx +15 -23
  8. package/src/components/avatar/Avatar.stories.tsx +91 -62
  9. package/src/components/avatar/Avatar.test.tsx +9 -24
  10. package/src/components/badge/Badge.stories.tsx +63 -38
  11. package/src/components/button/Button.stories.tsx +147 -139
  12. package/src/components/button/IconButton.stories.tsx +45 -0
  13. package/src/components/checkbox/Checkbox.stories.tsx +37 -30
  14. package/src/components/chip/Chip.stories.tsx +77 -15
  15. package/src/components/comment-block/CommentBlock.stories.tsx +90 -25
  16. package/src/components/comment-block/CommentBlock.test.tsx +12 -17
  17. package/src/components/date-picker/DatePickerField.stories.tsx +52 -83
  18. package/src/components/dialog/Dialog.stories.tsx +131 -293
  19. package/src/components/dialog/Dialog.test.tsx +0 -32
  20. package/src/components/dropdown/Dropdown.stories.tsx +1 -186
  21. package/src/components/flag/Flag.stories.tsx +33 -18
  22. package/src/components/flag/Flag.test.tsx +1 -8
  23. package/src/components/flex-box/FlexBox.stories.tsx +151 -238
  24. package/src/components/flex-box/FlexBox.test.tsx +9 -49
  25. package/src/components/generic-block/GenericBlock.stories.jsx +1 -1
  26. package/src/components/grid-column/GridColumn.stories.tsx +46 -0
  27. package/src/components/heading/Heading.stories.tsx +57 -95
  28. package/src/components/icon/Icon.stories.tsx +67 -70
  29. package/src/components/image-block/ImageBlock.stories.tsx +103 -47
  30. package/src/components/image-block/ImageBlock.test.tsx +12 -17
  31. package/src/components/inline-list/InlineList.stories.tsx +45 -29
  32. package/src/components/input-helper/InputHelper.stories.tsx +31 -25
  33. package/src/components/input-label/InputLabel.stories.tsx +33 -10
  34. package/src/components/lightbox/Lightbox.stories.tsx +39 -77
  35. package/src/components/lightbox/Lightbox.test.tsx +12 -17
  36. package/src/components/link/Link.stories.tsx +98 -128
  37. package/src/components/link-preview/LinkPreview.stories.tsx +48 -75
  38. package/src/components/list/List.stories.tsx +59 -84
  39. package/src/components/list/List.test.tsx +8 -17
  40. package/src/components/list/ListDivider.stories.tsx +9 -4
  41. package/src/components/list/ListDivider.test.tsx +12 -17
  42. package/src/components/list/ListItem.stories.tsx +97 -59
  43. package/src/components/list/ListItem.test.tsx +12 -17
  44. package/src/components/list/ListSubheader.stories.tsx +8 -5
  45. package/src/components/list/ListSubheader.test.tsx +12 -18
  46. package/src/components/message/Message.stories.tsx +51 -22
  47. package/src/components/mosaic/Mosaic.stories.tsx +78 -74
  48. package/src/components/mosaic/Mosaic.test.tsx +0 -31
  49. package/src/components/navigation/Navigation.stories.tsx +67 -0
  50. package/src/components/navigation/Navigation.test.tsx +58 -0
  51. package/src/components/navigation/Navigation.tsx +62 -0
  52. package/src/components/navigation/NavigationItem.test.tsx +37 -0
  53. package/src/components/navigation/NavigationItem.tsx +89 -0
  54. package/src/components/navigation/NavigationSection.test.tsx +126 -0
  55. package/src/components/navigation/NavigationSection.tsx +109 -0
  56. package/src/components/navigation/context.tsx +6 -0
  57. package/src/components/navigation/index.ts +1 -0
  58. package/src/components/notification/Notifications.stories.tsx +52 -47
  59. package/src/components/popover/Popover.stories.tsx +68 -201
  60. package/src/components/popover/Popover.tsx +7 -9
  61. package/src/components/popover-dialog/PopoverDialog.stories.tsx +26 -65
  62. package/src/components/post-block/PostBlock.test.tsx +12 -17
  63. package/src/components/progress/ProgressCircular.stories.tsx +24 -12
  64. package/src/components/progress/ProgressLinear.stories.tsx +6 -2
  65. package/src/components/radio-button/RadioButton.stories.tsx +35 -24
  66. package/src/components/select/Select.stories.tsx +19 -23
  67. package/src/components/select/SelectMultiple.stories.tsx +105 -2
  68. package/src/components/select/WithSelectContext.tsx +10 -4
  69. package/src/components/skeleton/SkeletonCircle.stories.tsx +37 -21
  70. package/src/components/skeleton/SkeletonCircle.test.tsx +12 -17
  71. package/src/components/skeleton/SkeletonRectangle.stories.tsx +74 -99
  72. package/src/components/skeleton/SkeletonRectangle.test.tsx +12 -17
  73. package/src/components/skeleton/SkeletonTypography.test.tsx +12 -17
  74. package/src/components/slider/Slider.stories.tsx +41 -25
  75. package/src/components/slider/Slider.test.tsx +12 -18
  76. package/src/components/slideshow/Slideshow.stories.tsx +31 -61
  77. package/src/components/slideshow/Slideshow.test.tsx +15 -23
  78. package/src/components/slideshow/SlideshowControls.stories.tsx +4 -6
  79. package/src/components/switch/Switch.stories.tsx +35 -32
  80. package/src/components/table/Table.test.tsx +12 -17
  81. package/src/components/tabs/Tabs.stories.tsx +4 -3
  82. package/src/components/text/Text.stories.tsx +130 -0
  83. package/src/components/text-field/TextField.stories.tsx +114 -148
  84. package/src/components/thumbnail/Thumbnail.stories.tsx +106 -255
  85. package/src/components/thumbnail/Thumbnail.test.tsx +12 -35
  86. package/src/components/tooltip/Tooltip.stories.tsx +51 -136
  87. package/src/components/user-block/UserBlock.stories.tsx +67 -56
  88. package/src/components/user-block/UserBlock.test.tsx +1 -5
  89. package/src/hooks/useFocusTrap.ts +2 -2
  90. package/src/index.ts +1 -0
  91. package/src/stories/controls/color.ts +6 -0
  92. package/src/stories/controls/element.ts +6 -0
  93. package/src/stories/controls/focusPoint.ts +1 -0
  94. package/src/stories/controls/icons.ts +6 -0
  95. package/src/stories/{knobs → controls}/image.ts +6 -16
  96. package/src/stories/controls/selectArgType.ts +4 -0
  97. package/src/stories/controls/theme.ts +3 -0
  98. package/src/stories/controls/typography.ts +5 -0
  99. package/src/stories/controls/withUndefined.ts +1 -0
  100. package/src/stories/decorators/withChromaticForceScreenSize.tsx +8 -0
  101. package/src/stories/decorators/withCombinations.tsx +99 -0
  102. package/src/stories/decorators/withNestedProps.tsx +23 -0
  103. package/src/stories/{withResizableBox.tsx → decorators/withResizableBox.tsx} +6 -10
  104. package/src/stories/decorators/withValueOnChange.tsx +18 -0
  105. package/src/stories/decorators/withWrapper.tsx +19 -0
  106. package/src/stories/utils/CustomLink.tsx +8 -2
  107. package/src/stories/{knobs → utils}/lorem.ts +9 -9
  108. package/src/testing/utils/commonTestsSuiteRTL.ts +2 -3
  109. package/src/testing/utils/index.ts +0 -2
  110. package/src/untypped-modules.d.ts +0 -2
  111. package/src/utils/MaterialThemeSwitcher/MaterialThemeSwitcher.tsx +1 -1
  112. package/src/utils/ThemeContext.ts +4 -0
  113. package/src/utils/forwardRefPolymorphic.ts +9 -0
  114. package/src/utils/type.ts +28 -4
  115. package/src/components/alert-dialog/__snapshots__/AlertDialog.test.tsx.snap +0 -558
  116. package/src/components/avatar/__snapshots__/Avatar.test.tsx.snap +0 -681
  117. package/src/components/comment-block/__snapshots__/CommentBlock.test.tsx.snap +0 -92
  118. package/src/components/dialog/__snapshots__/Dialog.test.tsx.snap +0 -1133
  119. package/src/components/expansion-panel/ExpansionPanel.stories.tsx +0 -65
  120. package/src/components/flag/__snapshots__/Flag.test.tsx.snap +0 -133
  121. package/src/components/flex-box/__snapshots__/FlexBox.test.tsx.snap +0 -492
  122. package/src/components/grid-column/GridColumn.stories.jsx +0 -56
  123. package/src/components/image-block/__snapshots__/ImageBlock.test.tsx.snap +0 -64
  124. package/src/components/lightbox/__snapshots__/Lightbox.test.tsx.snap +0 -194
  125. package/src/components/list/__snapshots__/List.test.tsx.snap +0 -360
  126. package/src/components/list/__snapshots__/ListDivider.test.tsx.snap +0 -7
  127. package/src/components/list/__snapshots__/ListItem.test.tsx.snap +0 -160
  128. package/src/components/list/__snapshots__/ListSubheader.test.tsx.snap +0 -9
  129. package/src/components/mosaic/__snapshots__/Mosaic.test.tsx.snap +0 -357
  130. package/src/components/post-block/__snapshots__/PostBlock.test.tsx.snap +0 -139
  131. package/src/components/skeleton/__snapshots__/SkeletonCircle.test.tsx.snap +0 -54
  132. package/src/components/skeleton/__snapshots__/SkeletonRectangle.test.tsx.snap +0 -177
  133. package/src/components/skeleton/__snapshots__/SkeletonTypography.test.tsx.snap +0 -174
  134. package/src/components/slider/__snapshots__/Slider.test.tsx.snap +0 -122
  135. package/src/components/slideshow/__snapshots__/Slideshow.test.tsx.snap +0 -157
  136. package/src/components/table/__snapshots__/Table.test.tsx.snap +0 -263
  137. package/src/components/text/Text.stories.jsx +0 -75
  138. package/src/components/thumbnail/__snapshots__/Thumbnail.test.tsx.snap +0 -130
  139. package/src/components/user-block/__snapshots__/UserBlock.test.tsx.snap +0 -362
  140. package/src/stories/chromaticForceScreenSize.tsx +0 -7
  141. package/src/stories/knobs/buttonKnob.ts +0 -9
  142. package/src/stories/knobs/emphasisKnob.ts +0 -8
  143. package/src/stories/knobs/enumKnob.ts +0 -14
  144. package/src/stories/knobs/focusKnob.ts +0 -3
  145. package/src/stories/knobs/sizeKnob.ts +0 -5
  146. package/src/stories/knobs/thumbnailsKnob.ts +0 -9
  147. package/src/testing/utils/itShouldRenderStories.tsx +0 -103
@@ -0,0 +1,109 @@
1
+ import React, { forwardRef, ReactNode, useRef, useState, useMemo, useContext } from 'react';
2
+
3
+ import { mdiChevronDown, mdiChevronUp } from '@lumx/icons';
4
+ import { Icon, Size, Text, Orientation, Popover, Placement, Theme } from '@lumx/react';
5
+ import uniqueId from 'lodash/uniqueId';
6
+ import classNames from 'classnames';
7
+ import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
8
+ import { HasClassName } from '@lumx/react/utils/type';
9
+ import { ThemeContext } from '@lumx/react/utils/ThemeContext';
10
+ import { CLASSNAME as ITEM_CLASSNAME } from './NavigationItem';
11
+ import { NavigationContext } from './context';
12
+
13
+ export interface NavigationSectionProps extends React.ComponentPropsWithoutRef<'li'>, HasClassName {
14
+ /** Items inside the section */
15
+ children: ReactNode;
16
+ /** Icon (SVG path). */
17
+ icon?: string;
18
+ /** Label content. */
19
+ label: string | ReactNode;
20
+ }
21
+
22
+ /**
23
+ * Component display name.
24
+ */
25
+ const COMPONENT_NAME = 'NavigationSection';
26
+
27
+ /**
28
+ * Component default class name and class prefix.
29
+ */
30
+ const CLASSNAME = getRootClassName(COMPONENT_NAME);
31
+
32
+ export const NavigationSection = Object.assign(
33
+ forwardRef<HTMLLIElement, NavigationSectionProps>((props, ref) => {
34
+ const { children, className, label, icon, ...forwardedProps } = props;
35
+ const [isOpen, setIsOpen] = useState(false);
36
+ const buttonRef = useRef<HTMLButtonElement>(null);
37
+ const sectionId = useMemo(() => uniqueId('section'), []);
38
+ const { orientation } = useContext(NavigationContext) || {};
39
+ const theme = useContext(ThemeContext);
40
+ const isDropdown = orientation === Orientation.horizontal;
41
+ return (
42
+ <li
43
+ className={classNames(
44
+ className,
45
+ CLASSNAME,
46
+ ITEM_CLASSNAME,
47
+ handleBasicClasses({
48
+ prefix: ITEM_CLASSNAME,
49
+ theme,
50
+ }),
51
+ )}
52
+ ref={ref}
53
+ {...forwardedProps}
54
+ >
55
+ <button
56
+ aria-controls={sectionId}
57
+ aria-expanded={isOpen}
58
+ className={classNames(`${ITEM_CLASSNAME}__link`)}
59
+ ref={buttonRef}
60
+ onClick={(event) => {
61
+ setIsOpen(!isOpen);
62
+ event.stopPropagation();
63
+ }}
64
+ type="button"
65
+ >
66
+ {icon ? <Icon className={`${ITEM_CLASSNAME}__icon`} icon={icon} size={Size.xs} /> : null}
67
+
68
+ <Text as="span" truncate className={`${ITEM_CLASSNAME}__label`} ref={ref}>
69
+ {label}
70
+ </Text>
71
+ <Icon
72
+ className={classNames(`${ITEM_CLASSNAME}__icon`, `${CLASSNAME}__chevron`)}
73
+ icon={isOpen ? mdiChevronUp : mdiChevronDown}
74
+ />
75
+ </button>
76
+ {isOpen &&
77
+ (isDropdown ? (
78
+ <Popover
79
+ anchorRef={buttonRef}
80
+ isOpen={isOpen}
81
+ placement={Placement.BOTTOM_START}
82
+ usePortal={false}
83
+ closeOnClickAway
84
+ closeOnEscape
85
+ onClick={() => setIsOpen(false)}
86
+ onClose={() => setIsOpen(false)}
87
+ zIndex={996}
88
+ >
89
+ <ThemeContext.Provider value={Theme.light}>
90
+ <ul className={`${CLASSNAME}__drawer--popover`} id={sectionId}>
91
+ <NavigationContext.Provider value={{ orientation: Orientation.vertical }}>
92
+ {children}
93
+ </NavigationContext.Provider>
94
+ </ul>
95
+ </ThemeContext.Provider>
96
+ </Popover>
97
+ ) : (
98
+ <ul className={`${CLASSNAME}__drawer`} id={sectionId}>
99
+ {children}
100
+ </ul>
101
+ ))}
102
+ </li>
103
+ );
104
+ }),
105
+ {
106
+ displayName: COMPONENT_NAME,
107
+ className: CLASSNAME,
108
+ },
109
+ );
@@ -0,0 +1,6 @@
1
+ import { createContext } from 'react';
2
+ import { Orientation } from '..';
3
+
4
+ export const NavigationContext = createContext<{ orientation?: Orientation } | undefined>({
5
+ orientation: Orientation.vertical,
6
+ });
@@ -0,0 +1 @@
1
+ export * from './Navigation';
@@ -1,12 +1,16 @@
1
- import React, { useState } from 'react';
2
-
3
- import { Button, Notification, Kind } from '@lumx/react';
1
+ import { Notification, Kind } from '@lumx/react';
4
2
  import { NOTIFICATION_TRANSITION_DURATION } from '@lumx/core/js/constants';
5
- import { action } from '@storybook/addon-actions';
6
- import { chromaticForceScreenSize } from '../../stories/chromaticForceScreenSize';
3
+ import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
4
+ import { withChromaticForceScreenSize } from '../../stories/decorators/withChromaticForceScreenSize';
7
5
 
8
6
  export default {
9
7
  title: 'LumX components/notification/Notification',
8
+ component: Notification,
9
+ args: Notification.defaultProps,
10
+ argTypes: {
11
+ type: getSelectArgType(Kind),
12
+ onClick: { action: true },
13
+ },
10
14
  parameters: {
11
15
  // Delay Chromatic snapshot to wait for notification to open.
12
16
  chromatic: {
@@ -15,63 +19,64 @@ export default {
15
19
  },
16
20
  },
17
21
  // Force minimum chromatic screen size to make sure the dialog appears in view.
18
- decorators: [chromaticForceScreenSize],
22
+ decorators: [withChromaticForceScreenSize()],
19
23
  };
20
24
 
21
- const properties = {
22
- error: {
25
+ /**
26
+ * Error notification
27
+ */
28
+ export const Error = {
29
+ args: {
30
+ type: Kind.error,
23
31
  content: 'Error',
24
- onClick: action('onClick'),
25
32
  isOpen: true,
26
- type: Kind.error,
27
33
  },
28
- info: {
29
- content: 'Info',
30
- onClick: action('onClick'),
31
- isOpen: true,
34
+ };
35
+
36
+ /**
37
+ * Info notification
38
+ */
39
+ export const Info = {
40
+ args: {
32
41
  type: Kind.info,
33
- },
34
- infoWithCallback: {
35
- onActionClick: action('onClick'),
36
- actionLabel: 'Undo',
37
- content: 'Info with callback',
38
- onClick: action('onClick'),
42
+ content: 'Info',
39
43
  isOpen: true,
40
- type: Kind.info,
41
44
  },
42
- success: {
45
+ };
46
+
47
+ /**
48
+ * Success notification
49
+ */
50
+ export const Success = {
51
+ args: {
52
+ type: Kind.success,
43
53
  content: 'Success',
44
- onClick: action('onClick'),
45
54
  isOpen: true,
46
- type: Kind.success,
47
55
  },
56
+ };
48
57
 
49
- warning: {
58
+ /**
59
+ * Warning notification
60
+ */
61
+ export const Warning = {
62
+ args: {
63
+ type: Kind.warning,
50
64
  content: 'Warning',
51
- onClick: action('onClick'),
52
65
  isOpen: true,
53
- type: Kind.warning,
54
66
  },
55
67
  };
56
68
 
57
- export const Error = () => <Notification {...properties.error} />;
58
-
59
- export const Info = () => <Notification {...properties.info} />;
60
-
61
- export const InfoWithCallback = () => <Notification {...properties.infoWithCallback} />;
62
-
63
- export const Success = () => <Notification {...properties.success} />;
64
-
65
- export const Warning = () => <Notification {...properties.warning} />;
66
-
67
- export const OnTrigger = () => {
68
- const [isOpen, setIsOpen] = useState(false);
69
- const onClick = () => setIsOpen(!isOpen);
70
-
71
- return (
72
- <>
73
- <Button onClick={onClick}>{!isOpen ? 'Show Notification' : 'Close Notification'}</Button>,
74
- <Notification {...properties.error} isOpen={isOpen} />,
75
- </>
76
- );
69
+ /**
70
+ * Info notification with action button
71
+ */
72
+ export const InfoWithAction = {
73
+ argTypes: {
74
+ onActionClick: { action: true },
75
+ },
76
+ args: {
77
+ type: Kind.info,
78
+ content: 'InfoWithAction',
79
+ actionLabel: 'Undo',
80
+ isOpen: true,
81
+ },
77
82
  };
@@ -1,132 +1,91 @@
1
- import React, { RefObject, useEffect, useRef, useState } from 'react';
1
+ /* eslint-disable react-hooks/rules-of-hooks */
2
+ import React, { useEffect, useRef, useState } from 'react';
2
3
 
3
4
  import { mdiAccount, mdiBell } from '@lumx/icons';
4
5
  import {
5
- Alignment,
6
6
  Chip,
7
- Button,
8
7
  Emphasis,
9
- FlexBox,
8
+ Text,
10
9
  Icon,
11
10
  IconButton,
12
11
  List,
13
12
  ListItem,
14
- Orientation,
15
13
  Placement,
16
14
  Popover,
17
15
  Size,
16
+ Elevation,
18
17
  } from '@lumx/react';
19
- import { boolean, select } from '@storybook/addon-knobs';
20
18
  import range from 'lodash/range';
21
- import startCase from 'lodash/startCase';
22
-
23
- export default { title: 'LumX components/popover/Popover' };
24
-
25
- const DEFAULT_PROPS = Popover.defaultProps as any;
26
-
27
- export const Positions = ({ theme }: any) => {
28
- const popovers: Array<[Placement, RefObject<any>]> = [
29
- [Placement.LEFT, useRef(null)],
30
- [Placement.TOP, useRef(null)],
31
- [Placement.BOTTOM, useRef(null)],
32
- [Placement.RIGHT, useRef(null)],
33
- ];
34
- const hasArrow = boolean('Has arrow', DEFAULT_PROPS.hasArrow as any);
35
- const elevation: any = select('Elevation', [5, 4, 3, 2, 1], DEFAULT_PROPS.elevation);
36
- const alignOptions = { middle: '', start: '-start', end: '-end' };
37
- const align = select('Placement variant', alignOptions, alignOptions.middle);
38
-
39
- return (
40
- <FlexBox style={{ padding: 80 }} orientation={Orientation.horizontal}>
41
- {popovers.map(([placement, ref]) => {
42
- const placementVariant = (placement + align) as any;
43
- return (
44
- <FlexBox key={placement} fillSpace vAlign={Alignment.center} hAlign={Alignment.center}>
45
- <Chip ref={ref} theme={theme} size={Size.s}>
46
- {startCase(placementVariant).toUpperCase()}
47
- </Chip>
48
-
49
- <Popover
50
- isOpen
51
- theme={theme}
52
- anchorRef={ref}
53
- placement={placementVariant}
54
- elevation={elevation}
55
- hasArrow={hasArrow}
56
- >
57
- <div className="lumx-spacing-margin-huge">Popover</div>
58
- </Popover>
59
- </FlexBox>
60
- );
61
- })}
62
- </FlexBox>
63
- );
19
+ import { withCombinations } from '@lumx/react/stories/decorators/withCombinations';
20
+ import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
21
+ import { withChromaticForceScreenSize } from '@lumx/react/stories/decorators/withChromaticForceScreenSize';
22
+
23
+ export default {
24
+ title: 'LumX components/popover/Popover',
25
+ component: Popover,
26
+ args: Popover.defaultProps,
27
+ argTypes: {
28
+ isOpen: { control: 'boolean' },
29
+ hasArrow: { control: 'boolean' },
30
+ placement: getSelectArgType(Placement),
31
+ elevation: getSelectArgType<Elevation>([1, 2, 3, 4, 5]),
32
+ },
33
+ decorators: [
34
+ // Force minimum chromatic screen size to make sure the dialog appears in view.
35
+ withChromaticForceScreenSize(),
36
+ ],
64
37
  };
65
38
 
66
- export const Auto = ({ theme }: any) => {
67
- const demoPopperStyle = {
68
- alignItems: 'center',
69
- display: 'flex',
70
- height: 100,
71
- justifyContent: 'center',
72
- width: 200,
73
- };
74
-
75
- const container = {
76
- width: '200%',
77
- height: '2000px',
78
- alignItems: 'center',
79
- display: 'flex',
80
- justifyContent: 'center',
81
- };
82
-
83
- const anchorRef = useRef(null);
84
-
85
- return (
86
- <div style={container}>
87
- <div>
88
- <Chip ref={anchorRef} theme={theme} size={Size.s}>
39
+ /**
40
+ * Simple
41
+ */
42
+ export const Simple = {
43
+ args: {
44
+ children: (
45
+ <Text as="p" className="lumx-spacing-padding-big">
46
+ Popover
47
+ </Text>
48
+ ),
49
+ },
50
+ render(props: any) {
51
+ const anchorRef = React.useRef(null);
52
+ return (
53
+ <>
54
+ <Chip ref={anchorRef} size="s" className="lumx-spacing-margin-big">
89
55
  Anchor
90
56
  </Chip>
91
- </div>
92
- <Popover theme={theme} anchorRef={anchorRef} placement={Placement.AUTO} isOpen>
93
- <div style={demoPopperStyle}>Popover</div>
94
- </Popover>
95
- </div>
96
- );
57
+ <Popover anchorRef={anchorRef} isOpen {...props} />
58
+ </>
59
+ );
60
+ },
97
61
  };
98
62
 
99
- export const Top = ({ theme }: any) => {
100
- const demoPopperStyle = {
101
- alignItems: 'center',
102
- display: 'flex',
103
- height: 100,
104
- justifyContent: 'center',
105
- width: 200,
106
- };
107
-
108
- const container = {
109
- width: '200%',
110
- height: '2000px',
111
- alignItems: 'center',
112
- display: 'flex',
113
- justifyContent: 'center',
114
- };
115
-
116
- const anchorRef = useRef(null);
117
-
118
- return (
119
- <div style={container}>
120
- <div>
121
- <Chip ref={anchorRef} theme={theme} size={Size.s}>
122
- Anchor
123
- </Chip>
124
- </div>
125
- <Popover theme={theme} anchorRef={anchorRef} placement={Placement.TOP} isOpen>
126
- <div style={demoPopperStyle}>Popover</div>
127
- </Popover>
128
- </div>
129
- );
63
+ /**
64
+ * Popover not using React.createPortal
65
+ */
66
+ export const WithoutPortal = { ...Simple, args: { ...Simple.args, usePortal: false } };
67
+
68
+ /**
69
+ * All base placements
70
+ */
71
+ export const Placements = {
72
+ ...Simple,
73
+ argTypes: {
74
+ placement: { control: false },
75
+ },
76
+ decorators: [
77
+ withCombinations({
78
+ cellStyle: {
79
+ padding: 80,
80
+ },
81
+ combinations: {
82
+ cols: {
83
+ key: 'placement',
84
+ options: [Placement.TOP, Placement.RIGHT, Placement.BOTTOM, Placement.LEFT],
85
+ },
86
+ },
87
+ }),
88
+ ],
130
89
  };
131
90
 
132
91
  export const WithUpdatingChildren = ({ theme }: any) => {
@@ -223,98 +182,6 @@ export const WithScrollingPopover = ({ theme }: any) => {
223
182
  );
224
183
  };
225
184
 
226
- export const WithoutPortal = ({ theme }: any) => {
227
- const popovers: Array<[Placement, RefObject<any>]> = [
228
- [Placement.LEFT, useRef(null)],
229
- [Placement.TOP, useRef(null)],
230
- [Placement.BOTTOM, useRef(null)],
231
- [Placement.RIGHT, useRef(null)],
232
- ];
233
- const hasArrow = boolean('Has arrow', DEFAULT_PROPS.hasArrow as any);
234
- const elevation: any = select('Elevation', [5, 4, 3, 2, 1], DEFAULT_PROPS.elevation);
235
- const alignOptions = { middle: '', start: '-start', end: '-end' };
236
- const align = select('Placement variant', alignOptions, alignOptions.middle);
237
-
238
- return (
239
- <FlexBox style={{ padding: 80 }} orientation={Orientation.horizontal}>
240
- {popovers.map(([placement, ref]) => {
241
- const placementVariant = (placement + align) as any;
242
- return (
243
- <FlexBox key={placement} fillSpace vAlign={Alignment.center} hAlign={Alignment.center}>
244
- <Chip ref={ref} theme={theme} size={Size.s}>
245
- {startCase(placementVariant).toUpperCase()}
246
- </Chip>
247
-
248
- <Popover
249
- isOpen
250
- theme={theme}
251
- anchorRef={ref}
252
- placement={placementVariant}
253
- elevation={elevation}
254
- hasArrow={hasArrow}
255
- usePortal={false}
256
- >
257
- <div className="lumx-spacing-margin-huge">Popover</div>
258
- </Popover>
259
- </FlexBox>
260
- );
261
- })}
262
- </FlexBox>
263
- );
264
- };
265
-
266
- export const NestedWithoutPortal = () => {
267
- const boundaryRef = React.useRef(document.querySelector('body'));
268
-
269
- const Entry = ({ children, placement = Placement.RIGHT_START }: any) => {
270
- const rootButtonRef = React.useRef(null);
271
- const [rootOpen, setRootOpen] = React.useState(false);
272
-
273
- return (
274
- <>
275
- <li ref={rootButtonRef}>
276
- <Button onClick={() => setRootOpen(!rootOpen)}>Click</Button>
277
- </li>
278
- {children && (
279
- <Popover
280
- boundaryRef={boundaryRef}
281
- fitWithinViewportHeight
282
- //offset={{ along: -16 }}
283
- placement={placement}
284
- isOpen={rootOpen}
285
- anchorRef={rootButtonRef}
286
- usePortal={false}
287
- >
288
- <div className="lumx-spacing-margin-huge" style={{ overflowY: 'auto' }}>
289
- <List style={{ minWidth: '150px' }}>{children}</List>
290
- </div>
291
- </Popover>
292
- )}
293
- </>
294
- );
295
- };
296
-
297
- return (
298
- <div style={{ height: '100vh', width: '50px' }}>
299
- <List>
300
- <Entry placement={Placement.BOTTOM_START}>
301
- <Entry />
302
- <Entry />
303
- <Entry>
304
- <Entry />
305
- <Entry />
306
- <Entry>
307
- <Entry>
308
- <Entry />
309
- </Entry>
310
- </Entry>
311
- </Entry>
312
- </Entry>
313
- </List>
314
- </div>
315
- );
316
- };
317
-
318
185
  export const FitToAnchorWidth = ({ theme }: any) => {
319
186
  const demoPopperStyle = {
320
187
  alignItems: 'center',
@@ -173,18 +173,16 @@ const _InnerPopover: Comp<PopoverProps, HTMLDivElement> = forwardRef((props, ref
173
173
  * unless specifically requested not to.
174
174
  */
175
175
  if (isFocusedWithin.current && focusAnchorOnClose) {
176
- if (parentElement?.current) {
177
- parentElement?.current.focus();
178
- }
179
-
180
- const firstFocusable = anchorRef?.current && getFirstAndLastFocusable(anchorRef?.current).first;
181
- if (firstFocusable) {
176
+ let elementToFocus = parentElement?.current;
177
+ if (!elementToFocus && anchorRef?.current) {
182
178
  // Focus the first focusable element in anchor.
183
- firstFocusable.focus();
184
- } else {
179
+ elementToFocus = getFirstAndLastFocusable(anchorRef.current).first;
180
+ }
181
+ if (!elementToFocus) {
185
182
  // Fallback on the anchor element.
186
- anchorRef?.current?.focus();
183
+ elementToFocus = anchorRef?.current;
187
184
  }
185
+ elementToFocus?.focus({ preventScroll: true });
188
186
  }
189
187
 
190
188
  onClose();
@@ -1,75 +1,36 @@
1
- import React, { useRef, useState } from 'react';
2
- import { mdiMenu, mdiSettings } from '@lumx/icons';
3
- import { Placement } from '@lumx/react';
1
+ import React from 'react';
2
+ import { useBooleanState } from '@lumx/react/hooks/useBooleanState';
3
+ import { PopoverDialog } from '.';
4
+ import { Button } from '../button';
4
5
 
5
- import { PopoverDialog, PopoverDialogProps } from '.';
6
- import { Emphasis, Orientation, Size, Typography } from '..';
7
- import { Button, IconButton } from '../button';
8
- import { FlexBox } from '../flex-box';
9
- import { Heading } from '../heading';
10
- import { List, ListItem } from '../list';
11
- import { Toolbar } from '../toolbar';
6
+ export default {
7
+ title: 'LumX components/popover-dialog/PopoverDialog',
8
+ component: PopoverDialog,
9
+ };
12
10
 
13
- const WithButton = (Story: any, context: any) => {
14
- const anchorRef = useRef(null);
15
- const [isOpen, setIsOpen] = useState<boolean>(context?.args?.isOpen || false);
11
+ /**
12
+ * Example PopoverDialog using a button as a trigger
13
+ */
14
+ export const WithButtonTrigger = () => {
15
+ const anchorRef = React.useRef(null);
16
+ const [isOpen, close, open] = useBooleanState(true);
16
17
 
17
18
  return (
18
19
  <>
19
- <Button ref={anchorRef} onClick={() => setIsOpen((current) => !current)}>
20
+ <Button id="trigger-button-1" ref={anchorRef} onClick={open}>
20
21
  Open popover
21
22
  </Button>
22
- <Story anchorRef={anchorRef} isOpen={isOpen} onClose={() => setIsOpen(false)} />
23
- </>
24
- );
25
- };
26
-
27
- const dialogHeaderId = 'dialog-header';
28
-
29
- const DemoPopoverContent = () => (
30
- <FlexBox orientation={Orientation.vertical}>
31
- <Toolbar
32
- label={
33
- <Heading id="dialogHeaderId" typography={Typography.headline}>
34
- Title
35
- </Heading>
36
- }
37
- after={<IconButton label="Settings" icon={mdiSettings} emphasis={Emphasis.low} />}
38
- />
39
- <List>
40
- <ListItem size={Size.huge} after={<IconButton label="Menu" icon={mdiMenu} size={Size.s} />}>
41
- List Item With Actions
42
- </ListItem>
43
- <ListItem
44
- size={Size.huge}
45
- linkProps={{
46
- href: 'http://google.com',
47
- }}
23
+ <PopoverDialog
24
+ aria-labelledby="trigger-button-1"
25
+ anchorRef={anchorRef}
26
+ isOpen={isOpen}
27
+ onClose={close}
28
+ placement="bottom"
48
29
  >
49
- Clickable list item
50
- </ListItem>
51
- </List>
52
- </FlexBox>
53
- );
54
-
55
- export default {
56
- title: 'LumX components/popover-dialog/PopoverDialog',
57
- component: PopoverDialog,
58
- decorators: [WithButton],
59
- args: {
60
- children: <DemoPopoverContent />,
61
- 'aria-labelledby': dialogHeaderId,
62
- placement: Placement.BOTTOM,
63
- },
64
- };
65
-
66
- const Template = (args: PopoverDialogProps, context: any) => {
67
- const { anchorRef, isOpen, onClose } = context;
68
- return (
69
- <PopoverDialog {...args} anchorRef={anchorRef} isOpen={isOpen} onClose={onClose}>
70
- {args.children}
71
- </PopoverDialog>
30
+ <Button className="lumx-spacing-margin-huge" onClick={close}>
31
+ Close
32
+ </Button>
33
+ </PopoverDialog>
34
+ </>
72
35
  );
73
36
  };
74
-
75
- export const Default = Template.bind({});