@automattic/vip-design-system 0.7.0 → 0.9.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 (121) hide show
  1. package/.eslines.json +10 -0
  2. package/.eslintignore +7 -0
  3. package/.eslintrc.json +23 -0
  4. package/.github/PULL_REQUEST_TEMPLATE.md +22 -0
  5. package/.github/workflows/codeql-analysis.yml +71 -0
  6. package/.github/workflows/nodejs.yaml +29 -0
  7. package/.prettierrc +9 -0
  8. package/.storybook/preview.js +8 -7
  9. package/README.md +52 -2
  10. package/babel.config.js +10 -10
  11. package/build/system/Avatar/Avatar.js +6 -2
  12. package/build/system/Avatar/Avatar.test.js +54 -0
  13. package/build/system/Badge/Badge.js +2 -2
  14. package/build/system/BlankState/BlankState.js +5 -4
  15. package/build/system/Button/Button.js +5 -1
  16. package/build/system/Card/Card.js +8 -5
  17. package/build/system/Code/Code.js +4 -4
  18. package/build/system/ConfirmationDialog/ConfirmationDialog.js +2 -2
  19. package/build/system/Dialog/Dialog.js +3 -0
  20. package/build/system/Dialog/DialogButton.js +2 -4
  21. package/build/system/Dialog/DialogContent.js +5 -5
  22. package/build/system/Form/Checkbox.js +52 -5
  23. package/build/system/Form/InlineSelect.js +32 -16
  24. package/build/system/Form/Input.js +9 -6
  25. package/build/system/Form/Label.js +2 -2
  26. package/build/system/Form/RadioBoxGroup.js +101 -0
  27. package/build/system/Form/SearchSelect.js +36 -56
  28. package/build/system/Form/Select.js +3 -3
  29. package/build/system/Form/Textarea.js +9 -6
  30. package/build/system/Form/Toggle.js +1 -1
  31. package/build/system/Form/ToggleGroup.js +78 -0
  32. package/build/system/Form/ToggleRow.js +6 -5
  33. package/build/system/Form/Validation.js +4 -4
  34. package/build/system/Form/index.js +8 -0
  35. package/build/system/Notice/Notice.js +58 -54
  36. package/build/system/Notification/Notification.js +4 -4
  37. package/build/system/OptionRow/OptionRow.js +12 -11
  38. package/build/system/ResourceList/ResourceItem.js +89 -0
  39. package/build/system/ResourceList/ResourceList.js +121 -0
  40. package/build/system/ResourceList/index.js +11 -0
  41. package/build/system/Table/TableRow.js +2 -2
  42. package/build/system/Tabs/TabItem.js +3 -3
  43. package/build/system/Tabs/Tabs.js +1 -1
  44. package/build/system/Time/index.js +91 -0
  45. package/build/system/Tooltip/Tooltip.js +49 -47
  46. package/build/system/Wizard/WizardStep.js +5 -5
  47. package/build/system/Wizard/WizardStepHorizontal.js +2 -2
  48. package/build/system/index.js +9 -2
  49. package/build/system/theme/colors.js +235 -131
  50. package/build/system/theme/index.js +140 -107
  51. package/package.json +48 -11
  52. package/src/system/Avatar/Avatar.js +6 -2
  53. package/src/system/Avatar/Avatar.stories.js +0 -5
  54. package/src/system/Avatar/Avatar.test.js +31 -0
  55. package/src/system/Badge/Badge.js +3 -3
  56. package/src/system/Badge/Badge.stories.js +0 -5
  57. package/src/system/BlankState/BlankState.js +5 -5
  58. package/src/system/BlankState/BlankState.stories.js +0 -5
  59. package/src/system/Box/Box.stories.js +0 -5
  60. package/src/system/Button/Button.js +6 -2
  61. package/src/system/Card/Card.js +6 -4
  62. package/src/system/Card/Card.stories.js +0 -5
  63. package/src/system/Code/Code.js +4 -4
  64. package/src/system/Code/Code.stories.js +4 -1
  65. package/src/system/ConfirmationDialog/ConfirmationDialog.js +2 -2
  66. package/src/system/ConfirmationDialog/ConfirmationDialog.stories.js +0 -5
  67. package/src/system/Dialog/Dialog.js +1 -1
  68. package/src/system/Dialog/Dialog.stories.js +0 -5
  69. package/src/system/Dialog/DialogButton.js +2 -3
  70. package/src/system/Dialog/DialogContent.js +17 -4
  71. package/src/system/Flex/Flex.stories.js +0 -5
  72. package/src/system/Form/Checkbox.js +44 -2
  73. package/src/system/Form/InlineSelect.js +30 -15
  74. package/src/system/Form/Input.js +6 -4
  75. package/src/system/Form/Input.stories.js +0 -5
  76. package/src/system/Form/Label.js +2 -2
  77. package/src/system/Form/RadioBoxGroup.js +68 -0
  78. package/src/system/Form/RadioBoxGroup.stories.js +37 -0
  79. package/src/system/Form/SearchSelect.js +35 -42
  80. package/src/system/Form/Select.js +18 -18
  81. package/src/system/Form/Select.stories.js +1 -1
  82. package/src/system/Form/Textarea.js +6 -4
  83. package/src/system/Form/Toggle.js +1 -1
  84. package/src/system/Form/ToggleGroup.js +63 -0
  85. package/src/system/Form/ToggleGroup.stories.js +34 -0
  86. package/src/system/Form/ToggleRow.js +5 -5
  87. package/src/system/Form/Validation.js +2 -2
  88. package/src/system/Form/index.js +3 -1
  89. package/src/system/Grid/Grid.stories.js +0 -5
  90. package/src/system/Heading/Heading.stories.js +0 -5
  91. package/src/system/Link/Link.stories.js +0 -5
  92. package/src/system/Notice/Notice.js +33 -28
  93. package/src/system/Notification/Notification.js +6 -6
  94. package/src/system/Notification/Notification.stories.js +0 -5
  95. package/src/system/OptionRow/OptionRow.js +37 -32
  96. package/src/system/OptionRow/OptionRow.stories.js +0 -5
  97. package/src/system/Progress/Progress.stories.js +0 -5
  98. package/src/system/ResourceList/ResourceItem.js +66 -0
  99. package/src/system/ResourceList/ResourceList.js +96 -0
  100. package/src/system/ResourceList/ResourceList.stories.js +300 -0
  101. package/src/system/ResourceList/index.js +7 -0
  102. package/src/system/Spinner/Spinner.stories.js +0 -5
  103. package/src/system/Table/Table.stories.js +0 -5
  104. package/src/system/Table/TableRow.js +3 -3
  105. package/src/system/Tabs/TabItem.js +3 -3
  106. package/src/system/Tabs/Tabs.js +1 -1
  107. package/src/system/Tabs/Tabs.stories.js +0 -5
  108. package/src/system/Text/Text.stories.js +0 -5
  109. package/src/system/{Timeline/Timeline.stories.js → Time/Time.stories.js} +5 -5
  110. package/src/system/Time/index.js +62 -0
  111. package/src/system/Tooltip/Tooltip.js +40 -35
  112. package/src/system/Tooltip/Tooltip.stories.js +0 -5
  113. package/src/system/Wizard/Wizard.js +7 -7
  114. package/src/system/Wizard/WizardStep.js +10 -10
  115. package/src/system/Wizard/WizardStepHorizontal.js +3 -3
  116. package/src/system/index.js +29 -4
  117. package/src/system/theme/colors.js +233 -129
  118. package/src/system/theme/index.js +298 -256
  119. package/test/setupAfterEnv.js +13 -0
  120. package/test/setupTests.js +4 -0
  121. package/src/system/Timeline/index.js +0 -40
@@ -0,0 +1,63 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+ import PropTypes from 'prop-types';
7
+ import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
8
+
9
+ const ToggleGroup = ( { onChange, groupLabel, value, options, ...props } ) => (
10
+ <RadioGroupPrimitive.Root
11
+ onValueChange={ onChange }
12
+ value={ value }
13
+ aria-label={ groupLabel }
14
+ sx={ {
15
+ bg: 'backgroundSecondary',
16
+ p: 1,
17
+ display: 'flex',
18
+ alignItems: 'center',
19
+ } }
20
+ { ...props }
21
+ >
22
+ {
23
+ options.map( ( option, index ) => (
24
+ <RadioGroupPrimitive.Item
25
+ key={ option.value }
26
+ value={ option.value }
27
+ id={ `o${ index }` }
28
+ sx={ {
29
+ fontSize: 1,
30
+ color: 'muted',
31
+ background: 'none',
32
+ border: 'none',
33
+ cursor: 'pointer',
34
+ borderRadius: 1,
35
+ py: 1,
36
+ px: 2,
37
+ flex: '1 1 auto',
38
+ textAlign: 'center',
39
+ '&:hover': {
40
+ color: 'heading',
41
+ },
42
+ '&[data-state=checked]': {
43
+ backgroundColor: 'card',
44
+ boxShadow: 'low',
45
+ color: 'heading',
46
+ },
47
+ } }
48
+ >
49
+ { option.label }
50
+ </RadioGroupPrimitive.Item>
51
+ ) )
52
+ }
53
+ </RadioGroupPrimitive.Root>
54
+ );
55
+
56
+ ToggleGroup.propTypes = {
57
+ onChange: PropTypes.func,
58
+ options: PropTypes.array,
59
+ value: PropTypes.string,
60
+ groupLabel: PropTypes.string,
61
+ };
62
+
63
+ export { ToggleGroup };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { useState } from 'react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { ToggleGroup } from '..';
10
+
11
+ export default {
12
+ title: 'ToggleGroup',
13
+ component: ToggleGroup,
14
+ };
15
+
16
+ const options = [
17
+ {
18
+ label: 'One',
19
+ value: 'one',
20
+ },
21
+ {
22
+ label: 'Two',
23
+ value: 'two',
24
+ },
25
+ {
26
+ label: 'Three',
27
+ value: 'three',
28
+ },
29
+ ];
30
+
31
+ export const Default = () => {
32
+ const [ value, setValue ] = useState( 'one' );
33
+ return <ToggleGroup value={ value } onChange={ newValue => setValue( newValue ) } groupLabel="group" options={ options } />;
34
+ };
@@ -36,7 +36,7 @@ const ToggleRow = ( { image, badge, title, subTitle, body, meta, sx, ...props }
36
36
  flex: '0 0 auto',
37
37
  } }
38
38
  >
39
- <img src={ image } width={ 32 } sx={ { display: 'block' } } />
39
+ <img src={ image } width={ 32 } sx={ { display: 'block' } } alt="Icon representing a toggle" />
40
40
  </Card>
41
41
  </Box>
42
42
  ) }
@@ -57,11 +57,11 @@ const ToggleRow = ( { image, badge, title, subTitle, body, meta, sx, ...props }
57
57
  );
58
58
 
59
59
  ToggleRow.propTypes = {
60
- image: PropTypes.string,
60
+ image: PropTypes.oneOfType( [ PropTypes.object, PropTypes.string ] ),
61
61
  badge: PropTypes.string,
62
- title: PropTypes.string,
63
- subTitle: PropTypes.string,
64
- body: PropTypes.string,
62
+ title: PropTypes.node,
63
+ subTitle: PropTypes.node,
64
+ body: PropTypes.node,
65
65
  meta: PropTypes.node,
66
66
  sx: PropTypes.object,
67
67
  };
@@ -15,8 +15,8 @@ const Validation = ( { children, isValid, ...props } ) => {
15
15
  const Icon = isValid ? MdCheckCircle : MdErrorOutline;
16
16
 
17
17
  return (
18
- <Heading variant="h5" as="p" sx={ { color: isValid ? 'green.50' : 'red.50' } } { ...props }>
19
- <Icon sx={ { mr: 1, position: 'relative', top: '0.125em' } } />
18
+ <Heading variant="h5" as="p" sx={ { color: isValid ? 'success' : 'error', display: 'flex', alignItems: 'center' } } { ...props }>
19
+ <Icon sx={ { mr: 1 } } />
20
20
  { children }
21
21
  </Heading>
22
22
  );
@@ -6,9 +6,11 @@ import { Label } from './Label';
6
6
  import { Toggle } from './Toggle';
7
7
  import { Validation } from './Validation';
8
8
  import { ToggleRow } from './ToggleRow';
9
+ import { ToggleGroup } from './ToggleGroup';
9
10
  import { Select } from './Select';
10
11
  import { Radio } from './Radio';
12
+ import { RadioBoxGroup } from './RadioBoxGroup';
11
13
  import { Textarea } from './Textarea';
12
14
  import { Checkbox } from './Checkbox';
13
15
 
14
- export { Input, Label, Radio, Select, Textarea, Toggle, ToggleRow, Validation, Checkbox };
16
+ export { Input, Label, Radio, RadioBoxGroup, ToggleGroup, Select, Textarea, Toggle, ToggleRow, Validation, Checkbox };
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import React from 'react';
5
-
6
1
  /**
7
2
  * Internal dependencies
8
3
  */
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import React from 'react';
5
-
6
1
  /**
7
2
  * Internal dependencies
8
3
  */
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import React from 'react';
5
-
6
1
  /**
7
2
  * Internal dependencies
8
3
  */
@@ -5,59 +5,62 @@
5
5
  */
6
6
  import PropTypes from 'prop-types';
7
7
  import { MdError, MdWarning, MdCheckCircle, MdInfo } from 'react-icons/md';
8
- import { useColorMode } from 'theme-ui';
9
8
 
10
9
  /**
11
10
  * Internal dependencies
12
11
  */
13
12
  import { Box, Flex, Heading, Card } from '../';
14
13
 
15
- const Notice = ( { variant = 'warning', inline = false, children, title, sx = {}, ...props } ) => {
16
- const [colorMode, _] = useColorMode()
17
-
18
- let color = 'yellow';
14
+ const NoticeIcon = ( { color, variant } ) => {
19
15
  let Icon = MdWarning;
20
16
 
21
17
  switch ( variant ) {
22
18
  case 'info':
23
- color = 'blue';
24
19
  Icon = MdInfo;
25
20
  break;
26
21
  case 'alert':
27
- color = 'red';
28
22
  Icon = MdError;
29
23
  break;
30
24
  case 'success':
31
- color = 'green';
32
25
  Icon = MdCheckCircle;
33
26
  break;
34
27
  }
35
28
 
36
- let modeColor = {
37
- color: `${ color }.70`,
38
- bg: inline ? 'transparent' : `${ color }.0`
39
- };
40
-
41
- if ( colorMode === 'dark' ) {
42
- modeColor = {
43
- color: `${ color }.40`,
44
- bg: undefined,
45
- }
46
- }
47
-
48
- const NoticeIcon = ({ color }) => (
29
+ return (
49
30
  <Icon sx={ { marginRight: 2, color, flex: '0 0 auto' } }/>
50
31
  );
32
+ };
33
+
34
+ NoticeIcon.propTypes = {
35
+ color: PropTypes.string,
36
+ variant: PropTypes.string,
37
+ };
38
+
39
+ const Notice = ( { variant = 'warning', inline = false, children, title, sx = {}, ...props } ) => {
40
+ let color = 'yellow';
41
+
42
+ switch ( variant ) {
43
+ case 'info':
44
+ color = 'blue';
45
+ break;
46
+ case 'alert':
47
+ color = 'red';
48
+ break;
49
+ case 'success':
50
+ color = 'green';
51
+ break;
52
+ }
51
53
 
52
54
  return (
53
55
  <Card
54
56
  sx={ {
55
57
  boxShadow: 'none',
56
58
  borderRadius: 2,
59
+ bg: inline ? 'transparent' : `${ color }.10`,
57
60
  p: inline ? 0 : 3,
58
- bg: modeColor.bg,
61
+ color: `${ color }.90`,
59
62
  a: {
60
- color: modeColor.color,
63
+ color: `${ color }.90`,
61
64
  textDecoration: 'underline',
62
65
  border: 'none',
63
66
  },
@@ -71,11 +74,11 @@ const Notice = ( { variant = 'warning', inline = false, children, title, sx = {}
71
74
  <Flex sx={ {
72
75
  alignItems: 'center',
73
76
  } }>
74
- <NoticeIcon color={modeColor.color} />
77
+ <NoticeIcon color={`${ color }.100`} variant={ variant } />
75
78
  </Flex>
76
79
 
77
80
  <Box sx={ { ml: 23 } }>
78
- { title && <Heading variant="h4" as="p" sx={ { color: modeColor.color, mb: 0 } }>{ title }</Heading> }
81
+ { title && <Heading variant="h4" as="p" sx={ { color: `${ color }.100`, mb: 0 } }>{ title }</Heading> }
79
82
  { children }
80
83
  </Box>
81
84
  </Flex>
@@ -84,10 +87,12 @@ const Notice = ( { variant = 'warning', inline = false, children, title, sx = {}
84
87
  };
85
88
 
86
89
  Notice.propTypes = {
87
- variant: PropTypes.string,
88
- title: PropTypes.string,
89
- inline: PropTypes.bool,
90
90
  children: PropTypes.node,
91
+ color: PropTypes.string,
92
+ inline: PropTypes.bool,
93
+ sx: PropTypes.object,
94
+ title: PropTypes.node,
95
+ variant: PropTypes.string,
91
96
  };
92
97
 
93
98
  export { Notice };
@@ -11,13 +11,13 @@ import PropTypes from 'prop-types';
11
11
  */
12
12
  import { Box, Button, Card, Flex, Heading, Text } from '../';
13
13
 
14
- const Notification = ({ title, body, status = 'success', onClose }) => (
14
+ const Notification = ( { title, body, status = 'success', onClose } ) => (
15
15
  <Card
16
16
  sx={{
17
17
  boxShadow: 'medium',
18
18
  width: 320,
19
19
  position: 'relative',
20
- variant: `notification.${status}`,
20
+ variant: `notification.${ status }`,
21
21
  }}
22
22
  >
23
23
  <Button
@@ -29,9 +29,9 @@ const Notification = ({ title, body, status = 'success', onClose }) => (
29
29
  </Button>
30
30
  <Flex sx={{ alignItems: 'center' }}>
31
31
  {status === 'error' ? (
32
- <MdError sx={{ color: 'red.50', flex: '0 0 auto' }} />
32
+ <MdError sx={{ color: 'error', flex: '0 0 auto' }} />
33
33
  ) : (
34
- <MdCheckCircle sx={{ color: 'green.50', flex: '0 0 auto' }} />
34
+ <MdCheckCircle sx={{ color: 'success', flex: '0 0 auto' }} />
35
35
  )}
36
36
  <Box sx={{ flex: '1 1 auto', ml: 3 }}>
37
37
  <Heading variant="h4" sx={{ mb: 0 }}>
@@ -44,8 +44,8 @@ const Notification = ({ title, body, status = 'success', onClose }) => (
44
44
  );
45
45
 
46
46
  Notification.propTypes = {
47
- title: PropTypes.string,
48
- body: PropTypes.string,
47
+ title: PropTypes.node,
48
+ body: PropTypes.node,
49
49
  status: PropTypes.string,
50
50
  onClose: PropTypes.func,
51
51
  };
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import React from 'react';
5
-
6
1
  /**
7
2
  * Internal dependencies
8
3
  */
@@ -10,9 +10,9 @@ import PropTypes from 'prop-types';
10
10
  /**
11
11
  * Internal dependencies
12
12
  */
13
- import { Badge, Box, Card, Grid, Heading, Text } from '..';
13
+ import { Badge, Box, Grid, Heading, Text } from '..';
14
14
 
15
- const OptionRow = ({
15
+ const OptionRow = ( {
16
16
  image,
17
17
  icon,
18
18
  badge,
@@ -25,33 +25,34 @@ const OptionRow = ({
25
25
  small = false,
26
26
  disabled = false,
27
27
  ...props
28
- }) => {
28
+ } ) => {
29
29
  const mergedCard = disabled
30
30
  ? {
31
- border: '1px solid',
32
- borderColor: 'border',
33
- background: 'none',
34
- boxShadow: 'none',
35
- }
31
+ border: '1px solid',
32
+ borderColor: 'border',
33
+ background: 'none',
34
+ boxShadow: 'none',
35
+ color: 'grey.70',
36
+ }
36
37
  : {};
37
38
 
38
39
  const inlineStyles = inline
39
40
  ? {
40
- py: 2,
41
- px: 2,
42
- mx: -2,
43
- }
41
+ py: 2,
42
+ px: 2,
43
+ mx: -2,
44
+ }
44
45
  : {
45
- py: 3,
46
- px: [3, 3, small ? 3 : 5],
47
- borderBottom: '1px solid',
48
- borderColor: 'border',
49
- };
46
+ py: 3,
47
+ px: [ 3, 3, small ? 3 : 5 ],
48
+ borderBottom: '1px solid',
49
+ borderColor: 'border',
50
+ };
50
51
  return (
51
52
  <Grid
52
53
  to={to}
53
- columns={[1, 1, 'auto 1fr auto']}
54
- gap={[3, 3, `${small ? 3 : 4}`]}
54
+ columns={[ 1, 1, 'auto 1fr auto' ]}
55
+ gap={[ 3, 3, `${ small ? 3 : 4 }` ]}
55
56
  {...props}
56
57
  sx={{
57
58
  alignItems: 'center',
@@ -66,24 +67,28 @@ const OptionRow = ({
66
67
  >
67
68
  <Box>
68
69
  {image ? (
69
- <Card
70
+ <Box
70
71
  sx={{
71
- display: ['inline-block', 'inline-block', 'block'],
72
- p: small ? 3 : 24,
73
- boxShadow: 'low',
72
+ display: [ 'inline-block', 'inline-block', 'block' ],
73
+ p: small ? 12 : 20,
74
74
  flex: '0 0 auto',
75
- svg: {
76
- display: 'block',
77
- },
75
+ bg: 'brand.70',
76
+ color: 'brand.10',
77
+ borderRadius: 1,
78
78
  ...mergedCard,
79
79
  }}
80
80
  >
81
- {React.isValidElement(image) ? (
81
+ {React.isValidElement( image ) ? (
82
82
  image
83
83
  ) : (
84
- <img src={image} width={small ? 32 : 48} sx={{ display: 'block' }} />
84
+ <img
85
+ src={image}
86
+ width={small ? 32 : 48}
87
+ sx={{ display: 'block' }}
88
+ alt="Image representing the list item"
89
+ />
85
90
  )}
86
- </Card>
91
+ </Box>
87
92
  ) : (
88
93
  icon && icon
89
94
  )}
@@ -103,13 +108,13 @@ const OptionRow = ({
103
108
  };
104
109
 
105
110
  OptionRow.propTypes = {
106
- image: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
111
+ image: PropTypes.oneOfType( [ PropTypes.object, PropTypes.string ] ),
107
112
  icon: PropTypes.node,
108
113
  badge: PropTypes.string,
109
114
  label: PropTypes.node,
110
115
  inline: PropTypes.bool,
111
- subTitle: PropTypes.string,
112
- body: PropTypes.string,
116
+ subTitle: PropTypes.node,
117
+ body: PropTypes.node,
113
118
  meta: PropTypes.node,
114
119
  to: PropTypes.string,
115
120
  small: PropTypes.bool,
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import React from 'react';
5
-
6
1
  /**
7
2
  * Internal dependencies
8
3
  */
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import React from 'react';
5
-
6
1
  /**
7
2
  * Internal dependencies
8
3
  */
@@ -0,0 +1,66 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+ import PropTypes from 'prop-types';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { Box, Flex, Time } from '..';
12
+
13
+ const ResourceItem = ( {
14
+ children,
15
+ item,
16
+ renderActions,
17
+ relativeTime = false,
18
+ timeOnly = false,
19
+ dateKey,
20
+ icon = null,
21
+ } ) => {
22
+ return (
23
+ <Flex
24
+ sx={{
25
+ alignItems: 'center',
26
+ gap: 3,
27
+ }}
28
+ >
29
+ {icon}
30
+ <Box sx={{ flex: '1 1 auto' }}>{children}</Box>
31
+ <Flex
32
+ sx={{
33
+ flex: '0 0 auto',
34
+ alignItems: 'center',
35
+ gap: 3,
36
+ }}
37
+ >
38
+ <Time
39
+ className="time"
40
+ relativeTime={relativeTime}
41
+ timeOnly={timeOnly}
42
+ time={item[ dateKey ]}
43
+ sx={{ color: 'muted', mb: 0, textAlign: 'right', flex: '0 0 auto' }}
44
+ />
45
+ {renderActions && (
46
+ <Flex className="actions" sx={{ alignItems: 'center', gap: 3 }}>
47
+ <Box sx={{ width: 4, height: 4, borderRadius: 4, bg: 'border' }} />
48
+ {renderActions( item )}
49
+ </Flex>
50
+ )}
51
+ </Flex>
52
+ </Flex>
53
+ );
54
+ };
55
+
56
+ ResourceItem.propTypes = {
57
+ children: PropTypes.node,
58
+ item: PropTypes.object,
59
+ icon: PropTypes.node,
60
+ relativeTime: PropTypes.bool,
61
+ timeOnly: PropTypes.bool,
62
+ dateKey: PropTypes.string,
63
+ renderActions: PropTypes.func,
64
+ };
65
+
66
+ export { ResourceItem };
@@ -0,0 +1,96 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+ import PropTypes from 'prop-types';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { Box, Heading } from '..';
12
+
13
+ const formatterOptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
14
+
15
+ const formatDate = date => {
16
+ const today = new Date();
17
+ if ( date.getFullYear() !== today.getFullYear() ) {
18
+ return date.toLocaleDateString( formatterOptions );
19
+ } else if ( date.getMonth() !== today.getMonth() ) {
20
+ return date.toLocaleDateString( formatterOptions );
21
+ } else if ( date.getDate() < today.getDate() - 1 ) {
22
+ return date.toLocaleDateString( formatterOptions );
23
+ } else if ( date.getDate() === today.getDate() - 1 ) {
24
+ return 'Yesterday';
25
+ }
26
+ return 'Today';
27
+ };
28
+
29
+ const StyledListItem = props => (
30
+ <Box
31
+ as="li"
32
+ sx={{
33
+ py: 2,
34
+ borderBottom: '1px solid',
35
+ borderColor: 'border',
36
+ listStyleType: 'none',
37
+ margin: 0,
38
+ px: 0,
39
+ }}
40
+ {...props}
41
+ />
42
+ );
43
+
44
+ const ResourceList = ( { groupedByDay = false, items, renderItem, dateKey } ) => {
45
+ let groupedItems = {};
46
+ if ( groupedByDay ) {
47
+ groupedItems = items?.reduce( ( itemGroups, item ) => {
48
+ const formattedDate = formatDate( item[ dateKey ] );
49
+ const itemsAtDate = itemGroups[ formattedDate ];
50
+
51
+ return {
52
+ ...itemGroups,
53
+ [ formattedDate ]: itemsAtDate ? [ ...itemsAtDate, item ] : [ item ],
54
+ };
55
+ }, {} );
56
+ }
57
+
58
+ const renderItemList = itemsList =>
59
+ itemsList.map( ( item, index ) => <StyledListItem key={index}>{renderItem( item )}</StyledListItem> );
60
+
61
+ return (
62
+ <Box as="ul" sx={{ listStyleType: 'none', m: 0, p: 0 }}>
63
+ {groupedByDay
64
+ ? Object.keys( groupedItems ).map( ( groupName, index ) => (
65
+ <Box sx={{ mb: 4 }} key={index}>
66
+ <Heading variant="h4" as="h4" sx={{ mb: 3 }}>
67
+ {groupName}
68
+ </Heading>
69
+ <Box
70
+ as="ul"
71
+ sx={{
72
+ listStyleType: 'none',
73
+ m: 0,
74
+ p: 0,
75
+ borderTop: '1px solid',
76
+ borderColor: 'border',
77
+ }}
78
+ >
79
+ {renderItemList( groupedItems[ groupName ] )}
80
+ </Box>
81
+ </Box>
82
+ ) )
83
+ : renderItemList( items )}
84
+ </Box>
85
+ );
86
+ };
87
+
88
+ ResourceList.propTypes = {
89
+ groupedByDay: PropTypes.bool,
90
+ items: PropTypes.array,
91
+ renderItem: PropTypes.func,
92
+ relativeTime: PropTypes.bool,
93
+ dateKey: PropTypes.string,
94
+ };
95
+
96
+ export { ResourceList };