@lumx/react 3.1.4 → 3.2.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 (144) hide show
  1. package/_internal/types.d.ts +16 -5
  2. package/index.d.ts +51 -4
  3. package/index.js +621 -417
  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 +132 -240
  19. package/src/components/dialog/Dialog.test.tsx +6 -30
  20. package/src/components/dialog/Dialog.tsx +17 -3
  21. package/src/components/dropdown/Dropdown.stories.tsx +1 -186
  22. package/src/components/flag/Flag.stories.tsx +33 -18
  23. package/src/components/flag/Flag.test.tsx +1 -8
  24. package/src/components/flex-box/FlexBox.stories.tsx +151 -238
  25. package/src/components/flex-box/FlexBox.test.tsx +9 -49
  26. package/src/components/generic-block/GenericBlock.stories.jsx +1 -1
  27. package/src/components/grid-column/GridColumn.stories.tsx +46 -0
  28. package/src/components/heading/Heading.stories.tsx +57 -95
  29. package/src/components/icon/Icon.stories.tsx +67 -70
  30. package/src/components/image-block/ImageBlock.stories.tsx +103 -47
  31. package/src/components/image-block/ImageBlock.test.tsx +12 -17
  32. package/src/components/inline-list/InlineList.stories.tsx +45 -29
  33. package/src/components/input-helper/InputHelper.stories.tsx +31 -25
  34. package/src/components/input-label/InputLabel.stories.tsx +33 -10
  35. package/src/components/lightbox/Lightbox.stories.tsx +39 -77
  36. package/src/components/lightbox/Lightbox.test.tsx +12 -17
  37. package/src/components/link/Link.stories.tsx +98 -128
  38. package/src/components/link-preview/LinkPreview.stories.tsx +48 -75
  39. package/src/components/list/List.stories.tsx +59 -84
  40. package/src/components/list/List.test.tsx +8 -17
  41. package/src/components/list/ListDivider.stories.tsx +9 -4
  42. package/src/components/list/ListDivider.test.tsx +12 -17
  43. package/src/components/list/ListItem.stories.tsx +97 -59
  44. package/src/components/list/ListItem.test.tsx +12 -17
  45. package/src/components/list/ListSubheader.stories.tsx +8 -5
  46. package/src/components/list/ListSubheader.test.tsx +12 -18
  47. package/src/components/message/Message.stories.tsx +51 -22
  48. package/src/components/mosaic/Mosaic.stories.tsx +78 -74
  49. package/src/components/mosaic/Mosaic.test.tsx +0 -31
  50. package/src/components/navigation/Navigation.stories.tsx +67 -0
  51. package/src/components/navigation/Navigation.test.tsx +58 -0
  52. package/src/components/navigation/Navigation.tsx +62 -0
  53. package/src/components/navigation/NavigationItem.test.tsx +37 -0
  54. package/src/components/navigation/NavigationItem.tsx +89 -0
  55. package/src/components/navigation/NavigationSection.test.tsx +126 -0
  56. package/src/components/navigation/NavigationSection.tsx +109 -0
  57. package/src/components/navigation/context.tsx +6 -0
  58. package/src/components/navigation/index.ts +1 -0
  59. package/src/components/notification/Notifications.stories.tsx +52 -47
  60. package/src/components/popover/Popover.stories.tsx +68 -201
  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/skeleton/SkeletonCircle.stories.tsx +37 -21
  68. package/src/components/skeleton/SkeletonCircle.test.tsx +12 -17
  69. package/src/components/skeleton/SkeletonRectangle.stories.tsx +74 -99
  70. package/src/components/skeleton/SkeletonRectangle.test.tsx +12 -17
  71. package/src/components/skeleton/SkeletonTypography.test.tsx +12 -17
  72. package/src/components/slider/Slider.stories.tsx +41 -25
  73. package/src/components/slider/Slider.test.tsx +12 -18
  74. package/src/components/slideshow/Slideshow.stories.tsx +31 -61
  75. package/src/components/slideshow/Slideshow.test.tsx +15 -23
  76. package/src/components/slideshow/SlideshowControls.stories.tsx +4 -6
  77. package/src/components/switch/Switch.stories.tsx +35 -32
  78. package/src/components/table/Table.test.tsx +12 -17
  79. package/src/components/tabs/Tabs.stories.tsx +4 -3
  80. package/src/components/text/Text.stories.tsx +130 -0
  81. package/src/components/text-field/TextField.stories.tsx +114 -148
  82. package/src/components/thumbnail/Thumbnail.stories.tsx +106 -255
  83. package/src/components/thumbnail/Thumbnail.test.tsx +12 -35
  84. package/src/components/tooltip/Tooltip.stories.tsx +51 -136
  85. package/src/components/user-block/UserBlock.stories.tsx +67 -56
  86. package/src/components/user-block/UserBlock.test.tsx +1 -5
  87. package/src/index.ts +1 -0
  88. package/src/stories/controls/color.ts +6 -0
  89. package/src/stories/controls/element.ts +6 -0
  90. package/src/stories/controls/focusPoint.ts +1 -0
  91. package/src/stories/controls/icons.ts +6 -0
  92. package/src/stories/{knobs → controls}/image.ts +6 -16
  93. package/src/stories/controls/selectArgType.ts +4 -0
  94. package/src/stories/controls/theme.ts +3 -0
  95. package/src/stories/controls/typography.ts +5 -0
  96. package/src/stories/controls/withUndefined.ts +1 -0
  97. package/src/stories/decorators/withChromaticForceScreenSize.tsx +8 -0
  98. package/src/stories/decorators/withCombinations.tsx +99 -0
  99. package/src/stories/decorators/withNestedProps.tsx +23 -0
  100. package/src/stories/{withResizableBox.tsx → decorators/withResizableBox.tsx} +6 -10
  101. package/src/stories/decorators/withValueOnChange.tsx +18 -0
  102. package/src/stories/decorators/withWrapper.tsx +19 -0
  103. package/src/stories/utils/CustomLink.tsx +8 -2
  104. package/src/stories/{knobs → utils}/lorem.ts +9 -9
  105. package/src/testing/utils/commonTestsSuiteRTL.ts +2 -3
  106. package/src/testing/utils/index.ts +0 -2
  107. package/src/untypped-modules.d.ts +0 -2
  108. package/src/utils/MaterialThemeSwitcher/MaterialThemeSwitcher.tsx +1 -1
  109. package/src/utils/ThemeContext.ts +4 -0
  110. package/src/utils/forwardRefPolymorphic.ts +9 -0
  111. package/src/utils/type.ts +28 -4
  112. package/src/components/alert-dialog/__snapshots__/AlertDialog.test.tsx.snap +0 -551
  113. package/src/components/avatar/__snapshots__/Avatar.test.tsx.snap +0 -681
  114. package/src/components/comment-block/__snapshots__/CommentBlock.test.tsx.snap +0 -92
  115. package/src/components/dialog/__snapshots__/Dialog.test.tsx.snap +0 -960
  116. package/src/components/expansion-panel/ExpansionPanel.stories.tsx +0 -65
  117. package/src/components/flag/__snapshots__/Flag.test.tsx.snap +0 -133
  118. package/src/components/flex-box/__snapshots__/FlexBox.test.tsx.snap +0 -492
  119. package/src/components/grid-column/GridColumn.stories.jsx +0 -56
  120. package/src/components/image-block/__snapshots__/ImageBlock.test.tsx.snap +0 -64
  121. package/src/components/lightbox/__snapshots__/Lightbox.test.tsx.snap +0 -194
  122. package/src/components/list/__snapshots__/List.test.tsx.snap +0 -360
  123. package/src/components/list/__snapshots__/ListDivider.test.tsx.snap +0 -7
  124. package/src/components/list/__snapshots__/ListItem.test.tsx.snap +0 -160
  125. package/src/components/list/__snapshots__/ListSubheader.test.tsx.snap +0 -9
  126. package/src/components/mosaic/__snapshots__/Mosaic.test.tsx.snap +0 -357
  127. package/src/components/post-block/__snapshots__/PostBlock.test.tsx.snap +0 -139
  128. package/src/components/skeleton/__snapshots__/SkeletonCircle.test.tsx.snap +0 -54
  129. package/src/components/skeleton/__snapshots__/SkeletonRectangle.test.tsx.snap +0 -177
  130. package/src/components/skeleton/__snapshots__/SkeletonTypography.test.tsx.snap +0 -174
  131. package/src/components/slider/__snapshots__/Slider.test.tsx.snap +0 -122
  132. package/src/components/slideshow/__snapshots__/Slideshow.test.tsx.snap +0 -157
  133. package/src/components/table/__snapshots__/Table.test.tsx.snap +0 -263
  134. package/src/components/text/Text.stories.jsx +0 -75
  135. package/src/components/thumbnail/__snapshots__/Thumbnail.test.tsx.snap +0 -130
  136. package/src/components/user-block/__snapshots__/UserBlock.test.tsx.snap +0 -362
  137. package/src/stories/chromaticForceScreenSize.tsx +0 -7
  138. package/src/stories/knobs/buttonKnob.ts +0 -9
  139. package/src/stories/knobs/emphasisKnob.ts +0 -8
  140. package/src/stories/knobs/enumKnob.ts +0 -14
  141. package/src/stories/knobs/focusKnob.ts +0 -3
  142. package/src/stories/knobs/sizeKnob.ts +0 -5
  143. package/src/stories/knobs/thumbnailsKnob.ts +0 -9
  144. package/src/testing/utils/itShouldRenderStories.tsx +0 -103
@@ -1,86 +1,62 @@
1
- import { mdiHelp, mdiPrinter } from '@lumx/icons';
2
- import {
3
- Button,
4
- Dialog,
5
- Dropdown,
6
- FlexBox,
7
- Icon,
8
- IconButton,
9
- Orientation,
10
- Placement,
11
- Size,
12
- Switch,
13
- Tooltip,
14
- } from '@lumx/react';
15
- import { select, text } from '@storybook/addon-knobs';
16
- import noop from 'lodash/noop';
17
- import range from 'lodash/range';
18
- import React, { Fragment, useRef, useState } from 'react';
1
+ import { Button, Dialog, Dropdown, Placement, Tooltip } from '@lumx/react';
2
+ import React, { useState } from 'react';
3
+ import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
4
+ import { withChromaticForceScreenSize } from '@lumx/react/stories/decorators/withChromaticForceScreenSize';
19
5
 
20
- export default { title: 'LumX components/tooltip/Tooltip' };
6
+ const placements = [Placement.TOP, Placement.BOTTOM, Placement.RIGHT, Placement.LEFT];
21
7
 
22
- export const ForceOpen = () => {
23
- return (
24
- <Tooltip
25
- forceOpen
26
- placement={select(
27
- 'placement',
28
- [Placement.TOP, Placement.BOTTOM, Placement.RIGHT, Placement.LEFT],
29
- Placement.TOP,
30
- )}
31
- label={text('label', 'Tooltip label')}
32
- >
33
- <Button>Button</Button>
34
- </Tooltip>
35
- );
8
+ export default {
9
+ title: 'LumX components/tooltip/Tooltip',
10
+ component: Tooltip,
11
+ args: Tooltip.defaultProps,
12
+ argTypes: {
13
+ placement: getSelectArgType(placements),
14
+ },
15
+ decorators: [
16
+ // Force minimum chromatic screen size to make sure the dialog appears in view.
17
+ withChromaticForceScreenSize(),
18
+ ],
36
19
  };
37
20
 
38
- export const InlineTooltip = () => (
39
- <div className="lumx-spacing-margin-huge">
40
- Some text with a
41
- <Tooltip label="extremely complex and difficult to follow" forceOpen>
42
- <abbr style={{ borderBottom: '1px dotted', margin: '0 4px' }}>convoluted</abbr>
43
- </Tooltip>
44
- word in it. And some text with a contextual help at the end
45
- <Tooltip
46
- label="A contextual help is help that is displayed in your product or web site"
47
- placement={Placement.TOP}
48
- forceOpen
49
- >
50
- <Icon icon={mdiHelp} size={Size.xxs} style={{ display: 'inline-block', verticalAlign: 'super' }} />
51
- </Tooltip>
52
- .
53
- </div>
54
- );
21
+ /** Simple tooltip on a button*/
22
+ export const OnAButton = {
23
+ args: {
24
+ label: 'Tooltip label',
25
+ children: <Button>Button</Button>,
26
+ },
27
+ };
55
28
 
56
- export const MultilineTooltip = () => (
57
- <>
58
- <Tooltip label="Only one sentence.">
59
- <Button>One line</Button>
60
- </Tooltip>
61
- <Tooltip label={'First sentence.\nSecond sentence.\nThird sentence.\n'}>
62
- <Button>Multiline</Button>
63
- </Tooltip>
64
- </>
65
- );
29
+ /** Simple tooltip on a button*/
30
+ export const OnADisabledButton = {
31
+ args: {
32
+ ...OnAButton.args,
33
+ children: <Button isDisabled>Button</Button>,
34
+ },
35
+ };
66
36
 
67
- export const EmptyTooltip = () => (
68
- <>
69
- <Tooltip label="">
70
- <Button>Empty</Button>
71
- </Tooltip>
72
- <Tooltip label={false}>
73
- <Button>Conditionnaly not displayed</Button>
74
- </Tooltip>
75
- </>
76
- );
37
+ /** Forcing the tooltip to appear */
38
+ export const ForceOpen = {
39
+ args: {
40
+ ...OnAButton.args,
41
+ forceOpen: true,
42
+ },
43
+ };
44
+
45
+ /** Display a multiline tooltip */
46
+ export const MultilineTooltip = {
47
+ args: {
48
+ ...OnAButton.args,
49
+ label: 'First sentence.\nSecond sentence.\nThird sentence.\n',
50
+ },
51
+ };
77
52
 
78
- export const TooltipWithDropdown = () => {
53
+ /** Tooltip should hide when a dropdown opens */
54
+ export const TooltipWithDropdown = (props: any) => {
79
55
  const [button, setButton] = useState<HTMLElement | null>(null);
80
56
  const [isOpen, setOpen] = useState(false);
81
57
  return (
82
58
  <>
83
- <Tooltip label={!isOpen && 'Tooltip'} placement="top">
59
+ <Tooltip label={!isOpen && 'Tooltip'} {...props}>
84
60
  <Button ref={setButton} onClick={() => setOpen((o) => !o)}>
85
61
  Anchor
86
62
  </Button>
@@ -92,66 +68,16 @@ export const TooltipWithDropdown = () => {
92
68
  );
93
69
  };
94
70
 
95
- export const TooltipOnDisabledButton = () => {
96
- const [disabled, setDisabled] = useState(false);
97
-
98
- return (
99
- <>
100
- <Switch isChecked={disabled} onChange={setDisabled}>
101
- Toggle button disabled
102
- </Switch>
103
- <Tooltip label="Tooltip on disabled button">
104
- <Button isDisabled={disabled} onClick={noop}>
105
- Click to disable button
106
- </Button>
107
- </Tooltip>
108
- </>
109
- );
110
- };
111
-
112
- export const TooltipOnDifferentComponents = () => {
113
- return (
114
- <FlexBox orientation={Orientation.horizontal} className="lumx-spacing-margin-top-huge">
115
- <FlexBox fillSpace />
116
- <FlexBox fillSpace>
117
- <Tooltip forceOpen label="Tooltip on Button">
118
- <Button>Button</Button>
119
- </Tooltip>
120
- </FlexBox>
121
- <FlexBox fillSpace>
122
- <IconButton icon={mdiPrinter} label="Tooltip on IconButton" tooltipProps={{ forceOpen: true }} />
123
- </FlexBox>
124
- <FlexBox fillSpace>
125
- <Tooltip forceOpen label="Tooltip on Icon">
126
- <Icon icon={mdiPrinter} style={{ display: 'inline-block' }} />
127
- </Tooltip>
128
- </FlexBox>
129
- <FlexBox fillSpace>
130
- <Tooltip forceOpen label="Tooltip on shaped Icon">
131
- <Icon icon={mdiPrinter} hasShape />
132
- </Tooltip>
133
- </FlexBox>
134
- <FlexBox fillSpace>
135
- <Tooltip forceOpen label="Tooltip on word">
136
- <span>word</span>
137
- </Tooltip>
138
- </FlexBox>
139
- </FlexBox>
140
- );
141
- };
142
-
71
+ /** Tooltip should hide when the anchor is hidden */
143
72
  export const HideTooltipOnHiddenAnchor = () => {
144
- const anchorRef = useRef(null);
145
73
  const [isOpen, setOpen] = useState(false);
146
74
  return (
147
75
  <>
148
- The tooltip should show when the button is hovered but it should disappear when the popover get in-between
76
+ The tooltip should show when the button is hovered but it should disappear when the dialog get in-between
149
77
  the mouse and the button
150
78
  <br />
151
79
  <Tooltip label="Tooltip label">
152
- <Button ref={anchorRef} onClick={() => setOpen((wasOpen) => !wasOpen)}>
153
- Open popover
154
- </Button>
80
+ <Button onClick={() => setOpen((wasOpen) => !wasOpen)}>Open dialog</Button>
155
81
  </Tooltip>
156
82
  <Dialog isOpen={isOpen} onClose={() => setOpen(false)}>
157
83
  Dialog
@@ -159,14 +85,3 @@ export const HideTooltipOnHiddenAnchor = () => {
159
85
  </>
160
86
  );
161
87
  };
162
-
163
- export const StressTest = () => {
164
- return range(1000).map((i) => (
165
- <Fragment key={i}>
166
- <Tooltip label={`Label ${i}`}>
167
- <Button>Button {i}</Button>
168
- </Tooltip>
169
- <br />
170
- </Fragment>
171
- ));
172
- };
@@ -1,76 +1,87 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { mdiStar } from '@lumx/icons';
4
- import { Badge, ColorPalette, Icon, Size } from '@lumx/react';
5
- import { avatarImageKnob } from '@lumx/react/stories/knobs/image';
4
+ import { Badge, ColorPalette, Icon, Orientation, Size } from '@lumx/react';
6
5
  import { CustomLink } from '@lumx/react/stories/utils/CustomLink';
7
6
 
7
+ import { AVATAR_IMAGES } from '@lumx/react/stories/controls/image';
8
+ import { withCombinations } from '@lumx/react/stories/decorators/withCombinations';
9
+ import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
8
10
  import { UserBlock } from './UserBlock';
9
11
 
10
- export default { title: 'LumX components/user-block/UserBlock' };
11
-
12
- const logAction = (action: string) => () => console.log(action);
13
12
  const sizes = [Size.s, Size.m, Size.l];
14
13
 
15
- export const Default = ({ theme }: any) => (
16
- <UserBlock
17
- theme={theme}
18
- name="Emmitt O. Lum"
19
- fields={['Creative developer', 'Denpasar']}
20
- avatarProps={{ image: avatarImageKnob() }}
21
- onMouseEnter={logAction('Mouse entered')}
22
- onMouseLeave={logAction('Mouse left')}
23
- />
24
- );
14
+ export default {
15
+ title: 'LumX components/user-block/UserBlock',
16
+ component: UserBlock,
17
+ args: UserBlock.defaultProps,
18
+ argTypes: {
19
+ size: getSelectArgType(sizes),
20
+ orientation: getSelectArgType(Orientation),
21
+ },
22
+ };
23
+
24
+ /** Only an avatar */
25
+ export const AvatarOnly = {
26
+ args: { avatarProps: { image: AVATAR_IMAGES.avatar1 } },
27
+ };
28
+
29
+ /** Avatar and name */
30
+ export const AvatarAndName = {
31
+ args: { ...AvatarOnly.args, name: 'Emmitt O. Lum' },
32
+ };
33
+
34
+ /** Avatar, name and secondary fields */
35
+ export const AvatarAndNameAndSecondaryFields = {
36
+ args: { ...AvatarAndName.args, fields: ['Creative developer', 'Denpasar'] },
37
+ };
25
38
 
26
- export const Sizes = ({ theme }: any) =>
27
- sizes.map((size) => (
28
- <UserBlock
29
- key={size}
30
- theme={theme}
31
- name="Emmitt O. Lum"
32
- fields={['Creative developer', 'Denpasar']}
33
- avatarProps={{ image: avatarImageKnob() }}
34
- size={size}
35
- onMouseEnter={logAction('Mouse entered')}
36
- onMouseLeave={logAction('Mouse left')}
37
- />
38
- ));
39
+ /** Size variants */
40
+ export const SizesAndOrientations = {
41
+ ...AvatarAndNameAndSecondaryFields,
42
+ decorators: [
43
+ withCombinations({
44
+ combinations: {
45
+ rows: { key: 'size', options: sizes },
46
+ cols: { key: 'orientation', options: Object.values(Orientation) },
47
+ },
48
+ }),
49
+ ],
50
+ };
39
51
 
40
- export const Clickable = ({ theme }: any) => {
41
- const baseProps = {
42
- theme,
43
- name: 'Emmitt O. Lum',
44
- nameProps: { 'aria-label': 'Emmitt O. Lum - open user profile' },
45
- fields: ['Creative developer', 'Denpasar'],
46
- avatarProps: { image: avatarImageKnob() },
47
- } as any;
48
- return (
49
- <>
50
- <p>As a button</p>
51
- <UserBlock {...baseProps} onClick={logAction('UserBlock clicked')} />
52
+ /** Setting `onClick` to use it as a button */
53
+ export const AsButton = {
54
+ ...AvatarAndNameAndSecondaryFields,
55
+ argTypes: { onClick: { action: true } },
56
+ };
52
57
 
53
- <p>As a link</p>
54
- <UserBlock {...baseProps} linkProps={{ href: 'https://example.com' }} />
58
+ /** Setting the `linkProps` prop to use it as a link */
59
+ export const AsLink = {
60
+ args: {
61
+ ...AvatarAndNameAndSecondaryFields.args,
62
+ linkProps: { href: 'https://example.com' },
63
+ },
64
+ };
55
65
 
56
- <p>As a custom link component</p>
57
- <UserBlock {...baseProps} linkAs={CustomLink} />
58
- </>
59
- );
66
+ /** Setting the `linkAs` prop to inject a custom link component */
67
+ export const AsCustomLink = {
68
+ args: {
69
+ ...AvatarAndNameAndSecondaryFields.args,
70
+ linkAs: CustomLink,
71
+ },
60
72
  };
61
73
 
62
- export const WithBadge = ({ theme }: any) => (
63
- <UserBlock
64
- theme={theme}
65
- name="Emmitt O. Lum"
66
- fields={['Creative developer', 'Denpasar']}
67
- avatarProps={{
68
- image: avatarImageKnob(),
74
+ /** Setting the `avatarProps.badge` prop to inject a badge */
75
+ export const WithBadge = {
76
+ args: {
77
+ ...AvatarAndNameAndSecondaryFields.args,
78
+ avatarProps: {
79
+ ...AvatarAndNameAndSecondaryFields.args.avatarProps,
69
80
  badge: (
70
81
  <Badge color={ColorPalette.blue}>
71
82
  <Icon icon={mdiStar} />
72
83
  </Badge>
73
84
  ),
74
- }}
75
- />
76
- );
85
+ },
86
+ },
87
+ };
@@ -3,10 +3,8 @@ import React, { ReactElement } from 'react';
3
3
  import { mount, shallow } from 'enzyme';
4
4
  import 'jest-enzyme';
5
5
 
6
- import { commonTestsSuite, itShouldRenderStories, Wrapper } from '@lumx/react/testing/utils';
7
-
6
+ import { commonTestsSuite, Wrapper } from '@lumx/react/testing/utils';
8
7
  import { UserBlock, UserBlockProps } from './UserBlock';
9
- import * as stories from './UserBlock.stories';
10
8
 
11
9
  const CLASSNAME = UserBlock.className as string;
12
10
 
@@ -24,8 +22,6 @@ const setup = ({ ...propsOverride }: Partial<UserBlockProps> = {}, shallowRender
24
22
  describe(`<${UserBlock.displayName}>`, () => {
25
23
  // 1. Test render via snapshot.
26
24
  describe('Snapshots and structure', () => {
27
- itShouldRenderStories(stories, UserBlock);
28
-
29
25
  it('should forward name props', () => {
30
26
  const { wrapper } = setup({ name: 'John Doe', nameProps: { 'data-custom-attribute': true } });
31
27
 
package/src/index.ts CHANGED
@@ -34,6 +34,7 @@ export * from './components/link-preview';
34
34
  export * from './components/list';
35
35
  export * from './components/message';
36
36
  export * from './components/mosaic';
37
+ export * from './components/navigation';
37
38
  export * from './components/notification';
38
39
  export * from './components/popover';
39
40
  export * from './components/popover-dialog';
@@ -0,0 +1,6 @@
1
+ import { ColorPalette, ColorVariant } from '@lumx/react';
2
+ import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
3
+
4
+ export const ALL_COLORS = Object.values(ColorPalette);
5
+ export const colorArgType = getSelectArgType(ALL_COLORS);
6
+ export const colorVariantArgType = getSelectArgType(ColorVariant);
@@ -0,0 +1,6 @@
1
+ import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
2
+ import { HeadingElement, TextElement } from '@lumx/react';
3
+
4
+ export const HEADING_ELEMENTS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as HeadingElement[];
5
+ export const headingElementArgType = getSelectArgType<HeadingElement>(HEADING_ELEMENTS);
6
+ export const textElementArgType = getSelectArgType<TextElement>(['p', 'span', ...HEADING_ELEMENTS]);
@@ -0,0 +1 @@
1
+ export const focusPoint = { control: { type: 'range', min: -1, max: 1, step: 0.01 } };
@@ -0,0 +1,6 @@
1
+ import * as icons from '@lumx/icons';
2
+
3
+ export const iconArgType = {
4
+ control: { type: 'select' },
5
+ options: icons,
6
+ };
@@ -1,4 +1,4 @@
1
- import { select } from '@storybook/addon-knobs';
1
+ import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
2
2
 
3
3
  const avatar1 = '/demo-assets/avatar1.jpg';
4
4
  const avatar2 = '/demo-assets/avatar2.jpg';
@@ -19,23 +19,13 @@ export const AVATAR_IMAGES = { avatar1, avatar2, avatar3, avatar4 };
19
19
  export const SQUARE_IMAGES = { square1, square2 };
20
20
  export const LANDSCAPE_IMAGES = { landscape1, landscape1s200, landscape2, landscape3 };
21
21
  export const PORTRAIT_IMAGES = { portrait1, portrait1s200, portrait2, portrait3 };
22
-
23
22
  export const IMAGES = { ...LANDSCAPE_IMAGES, ...PORTRAIT_IMAGES, ...SQUARE_IMAGES, ...AVATAR_IMAGES };
24
23
 
25
- export const avatarImageKnob = (name = 'Avatar', value = Object.values(AVATAR_IMAGES)[0], groupId?: string) =>
26
- select(name, AVATAR_IMAGES, value, groupId);
27
-
28
- export const landscapeImageKnob = (name = 'Image', value = Object.values(LANDSCAPE_IMAGES)[0], groupId?: string) =>
29
- select(name, LANDSCAPE_IMAGES, value, groupId);
30
-
31
- export const portraitImageKnob = (name = 'Image', value = Object.values(PORTRAIT_IMAGES)[0], groupId?: string) =>
32
- select(name, PORTRAIT_IMAGES, value, groupId);
33
-
34
- export const squareImageKnob = (name = 'Image', value = Object.values(SQUARE_IMAGES)[0], groupId?: string) =>
35
- select(name, SQUARE_IMAGES, value, groupId);
36
-
37
- export const imageKnob = (name = 'Image', value = Object.values(IMAGES)[0], groupId?: string) =>
38
- select(name, IMAGES, value, groupId);
24
+ export const avatarImageArgType = getSelectArgType(AVATAR_IMAGES);
25
+ export const squareImageArgType = getSelectArgType(SQUARE_IMAGES);
26
+ export const landscapeImageArgType = getSelectArgType(LANDSCAPE_IMAGES);
27
+ export const portraitImageArgType = getSelectArgType(PORTRAIT_IMAGES);
28
+ export const imageArgType = getSelectArgType(IMAGES);
39
29
 
40
30
  type Size = { width: number; height: number };
41
31
 
@@ -0,0 +1,4 @@
1
+ export const getSelectArgType = <E>(options: Array<E> | Record<string, E>) => ({
2
+ control: { type: 'select' },
3
+ options,
4
+ });
@@ -0,0 +1,3 @@
1
+ import { Theme } from '@lumx/react';
2
+
3
+ export const themeArgType = { control: 'inline-radio', options: Theme };
@@ -0,0 +1,5 @@
1
+ import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
2
+ import { TypographyCustom, TypographyInterface } from '@lumx/react';
3
+
4
+ export const ALL_TYPOGRAPHY = [...Object.values(TypographyInterface), ...Object.values(TypographyCustom)];
5
+ export const allTypographyArgType = getSelectArgType(ALL_TYPOGRAPHY);
@@ -0,0 +1 @@
1
+ export const withUndefined = <E>(options: Array<E> | Record<string, E>) => [undefined, ...Object.values(options)];
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import isChromatic from 'chromatic/isChromatic';
3
+
4
+ /** Story decorator used to force a minimum screen size when running in chromatic */
5
+ export const withChromaticForceScreenSize = () => {
6
+ // eslint-disable-next-line react/display-name
7
+ return (story: any) => (isChromatic() ? <div style={{ minWidth: 1200, minHeight: 800 }}>{story()}</div> : story());
8
+ };
@@ -0,0 +1,99 @@
1
+ /* eslint-disable react/display-name,jsx-a11y/control-has-associated-label */
2
+ import React from 'react';
3
+ import isEmpty from 'lodash/isEmpty';
4
+
5
+ type PropCombination = Record<string, Record<string, unknown>>;
6
+ type PropArrayCombination = { key: string; options: Array<any> };
7
+ type Combination = PropArrayCombination | PropCombination;
8
+
9
+ const toProps = <E,>(arr: Array<E>, prop: string) =>
10
+ Object.fromEntries(arr.map((value) => [value || '', { [prop]: value }]));
11
+
12
+ const isArrayConfig = (c?: Combination): c is PropArrayCombination => !!c?.key;
13
+
14
+ function formatProps(config?: Combination): PropCombination {
15
+ if (isEmpty(config)) return { '': {} };
16
+ if (isArrayConfig(config)) return toProps(config.options, config.key);
17
+ return config as PropCombination;
18
+ }
19
+
20
+ /**
21
+ * SB decorator generating a tables of combination of props (max 3 levels of props)
22
+ */
23
+ export const withCombinations = ({
24
+ combinations,
25
+ tableStyle,
26
+ firstColStyle,
27
+ cellStyle,
28
+ combinator = Object.assign,
29
+ }: {
30
+ /** Props combinations */
31
+ combinations: {
32
+ rows?: Combination;
33
+ cols?: Combination;
34
+ sections?: Combination;
35
+ };
36
+ /** Inject style on table */
37
+ tableStyle?: React.CSSProperties;
38
+ /** Inject style on first col */
39
+ firstColStyle?: React.CSSProperties;
40
+ /** Inject style on cells */
41
+ cellStyle?: React.CSSProperties;
42
+ /** Combinator function */
43
+ combinator?: (a: any, b: any) => any;
44
+ }) => (Story: any, ctx: any) => {
45
+ const rows = formatProps(combinations.rows);
46
+ const cols = formatProps(combinations.cols);
47
+ const sections = formatProps(combinations.sections);
48
+
49
+ return (
50
+ <>
51
+ {Object.entries(sections).map(([level2Key, level2Value]) => (
52
+ <div key={level2Key}>
53
+ {level2Key && <h2>{level2Key}</h2>}
54
+
55
+ <table style={{ ...tableStyle, borderCollapse: 'separate', borderSpacing: 8 }}>
56
+ {combinations.cols && (
57
+ <thead>
58
+ <tr>
59
+ <th />
60
+ {Object.keys(cols).map((key) => (
61
+ <th key={key}>
62
+ <small>{key}</small>
63
+ </th>
64
+ ))}
65
+ </tr>
66
+ </thead>
67
+ )}
68
+ <tbody>
69
+ {Object.entries(rows).map(([level0Key, level0Value], i1) => (
70
+ <tr key={i1}>
71
+ <th style={{ ...firstColStyle, textAlign: 'left' }}>
72
+ <small>{level0Key}</small>
73
+ </th>
74
+ {Object.entries(cols).map(([level1Key, level1Value]) => {
75
+ const args = [level0Value, level1Value, level2Value].reduce(
76
+ (acc, value) => combinator(acc, value),
77
+ { ...ctx.args },
78
+ );
79
+ return (
80
+ <td
81
+ key={level1Key}
82
+ style={{
83
+ textAlign: combinations.cols ? 'center' : undefined,
84
+ ...cellStyle,
85
+ }}
86
+ >
87
+ <Story args={args} />
88
+ </td>
89
+ );
90
+ })}
91
+ </tr>
92
+ ))}
93
+ </tbody>
94
+ </table>
95
+ </div>
96
+ ))}
97
+ </>
98
+ );
99
+ };
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import set from 'lodash/set';
3
+
4
+ /**
5
+ * Convert flattened props into nested props
6
+ *
7
+ * @example: toNestedProps({ 'foo.bar': 4 }) => { foo: { bar: 4 } }
8
+ */
9
+ export function toNestedProps(flattenedProps: Record<string, any>): Record<string, any> {
10
+ const props = {};
11
+ for (const [key, value] of Object.entries(flattenedProps)) {
12
+ set(props, key, value);
13
+ }
14
+ return props;
15
+ }
16
+
17
+ /**
18
+ * SB decorator converting args from flattened props to nested props
19
+ */
20
+ export const withNestedProps = () => {
21
+ // eslint-disable-next-line react/display-name
22
+ return (Story: any, ctx: any) => <Story args={toNestedProps(ctx.args)} />;
23
+ };
@@ -1,18 +1,14 @@
1
- import React from 'react';
1
+ import { withWrapper } from './withWrapper';
2
2
 
3
3
  /** Storybook decorator wrapping story in a resizable box */
4
- // eslint-disable-next-line react/display-name
5
- export const withResizableBox = ({ width = 150, height = 50 } = {}) => (Story: any) => (
6
- <div
7
- style={{
4
+ export const withResizableBox = ({ width = 150, height = 50 } = {}) =>
5
+ withWrapper({
6
+ style: {
8
7
  display: 'flex',
9
8
  width,
10
9
  height,
11
10
  border: '1px solid red',
12
11
  resize: 'both',
13
12
  overflow: 'hidden',
14
- }}
15
- >
16
- <Story />
17
- </div>
18
- );
13
+ },
14
+ });
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import over from 'lodash/over';
3
+ import identity from 'lodash/identity';
4
+
5
+ export const withValueOnChange = ({
6
+ valueProp = 'value',
7
+ valueTransform = identity,
8
+ }: {
9
+ valueProp?: string;
10
+ valueTransform?: (v: any) => any;
11
+ } = {}) => {
12
+ // eslint-disable-next-line react/display-name
13
+ return (Story: any, ctx: any) => {
14
+ const [value, onChange] = React.useState(ctx.args[valueProp]);
15
+ const args = { ...ctx.args, onChange: over([onChange, ctx.args.onChange]), [valueProp]: valueTransform(value) };
16
+ return <Story args={args} />;
17
+ };
18
+ };