@spothero/ui 15.7.1 → 15.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spothero/ui",
3
- "version": "15.7.1",
3
+ "version": "15.9.0",
4
4
  "description": "SpotHero's React component UI library.",
5
5
  "main": "v2/index.js",
6
6
  "repository": "https://github.com/spothero/fe-monorepo",
@@ -88,10 +88,10 @@
88
88
  "@spothero/npm-publisher": "*",
89
89
  "@spothero/prettier-config": "*",
90
90
  "@spothero/stylelint-config": "*",
91
- "@storybook/addon-actions": "6.3.13",
92
- "@storybook/addon-essentials": "6.3.13",
93
- "@storybook/addon-links": "6.3.13",
94
- "@storybook/react": "6.3.13",
91
+ "@storybook/addon-actions": "6.5.10",
92
+ "@storybook/addon-essentials": "6.5.10",
93
+ "@storybook/addon-links": "6.5.10",
94
+ "@storybook/react": "6.5.10",
95
95
  "@testing-library/jest-dom": "5.11.9",
96
96
  "@testing-library/react": "11.2.5",
97
97
  "@testing-library/user-event": "12.8.1",
@@ -132,7 +132,7 @@
132
132
  "@emotion/react": "11.1.5",
133
133
  "@emotion/styled": "11.1.5",
134
134
  "@spothero/utils": "*",
135
- "@storybook/addon-a11y": "6.3.4",
135
+ "@storybook/addon-a11y": "6.5.10",
136
136
  "axe-core": "4.1.3",
137
137
  "chartist": "0.11.4",
138
138
  "chartist-plugin-legend": "0.6.2",
@@ -15,6 +15,7 @@ const AutoSuggestSelect = forwardRef(
15
15
  isInvalid,
16
16
  isDisabled,
17
17
  isRequired,
18
+ isOptional,
18
19
  getOptions,
19
20
  onChange,
20
21
  iconSrc,
@@ -135,6 +136,7 @@ const AutoSuggestSelect = forwardRef(
135
136
  <FormControl
136
137
  errorMessage={errorMessage}
137
138
  isRequired={isRequired}
139
+ isOptional={isOptional}
138
140
  helperText={helperText}
139
141
  label={label}
140
142
  inputId={id}
@@ -171,7 +173,7 @@ AutoSuggestSelect.propTypes = {
171
173
  placeholder: PropTypes.string,
172
174
  /** Optional helper text displayed below the select */
173
175
  helperText: PropTypes.string,
174
- /** Error message that would dispplay under the select */
176
+ /** Error message that would display under the select */
175
177
  errorMessage: PropTypes.string,
176
178
  /** Boolean that sets whether the select is valid */
177
179
  isInvalid: PropTypes.bool,
@@ -179,6 +181,8 @@ AutoSuggestSelect.propTypes = {
179
181
  isDisabled: PropTypes.bool,
180
182
  /** Boolean that sets whether the select is required */
181
183
  isRequired: PropTypes.bool,
184
+ /** Boolean that sets whether the select is optional */
185
+ isOptional: PropTypes.bool,
182
186
  /** Function that is called with the value typed into the input that returns a list of options for the select */
183
187
  getOptions: PropTypes.func.isRequired,
184
188
  /** Function that is called when an option is selected, it returns the label and value of the selection */
@@ -0,0 +1,29 @@
1
+ import React, {forwardRef} from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import {Divider as ChakraDivider} from '@chakra-ui/react';
4
+ import {baseStyles, colorScheme} from './Divider.styles';
5
+
6
+ const Divider = forwardRef(({variant, colorScheme: borderColor, ...props}, ref) => {
7
+ return (
8
+ <ChakraDivider
9
+ {...baseStyles}
10
+ {...colorScheme[borderColor]}
11
+ variant={variant}
12
+ {...props}
13
+ ref={ref}
14
+ />
15
+ )});
16
+
17
+ Divider.propTypes = {
18
+ /** Color scheme used */
19
+ colorScheme: PropTypes.oneOf(['low', 'medium']),
20
+ /** The styling that will be applied to the HR tag */
21
+ variant: PropTypes.oneOf(['solid', 'dashed']),
22
+ };
23
+
24
+ Divider.defaultProps = {
25
+ variant: 'solid',
26
+ colorScheme: 'medium',
27
+ };
28
+
29
+ export default Divider;
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+
3
+ import {colorScheme} from './Divider.styles';
4
+
5
+ import Component from './Divider';
6
+
7
+ import {Box, Text} from '@chakra-ui/react';
8
+
9
+ export default {
10
+ title: 'v2/Divider',
11
+ component: Component,
12
+ argTypes: {
13
+ colorScheme: {
14
+ control: {
15
+ type: 'select',
16
+ options: Object.keys(colorScheme),
17
+ },
18
+ },
19
+ variant: {
20
+ control: {
21
+ type: 'select',
22
+ options: ['solid', 'dashed'],
23
+ },
24
+ },
25
+ },
26
+ };
27
+
28
+
29
+ const Template = props => (
30
+ <Box>
31
+ <Text>Over HowdyHowdyHowdy</Text>
32
+ <Component {...props}/>
33
+ <Text>Under HowdyHowdyHowdy</Text>
34
+ </Box>
35
+ );
36
+
37
+ export const Divider = Template.bind({});
38
+
39
+ Divider.args = {
40
+ variant: 'solid',
41
+ colorScheme: 'medium',
42
+ };
@@ -0,0 +1,15 @@
1
+ import merge from 'lodash/merge';
2
+ import chakraDefaultTheme from '@chakra-ui/theme';
3
+
4
+ export const colorScheme = {
5
+ low: {
6
+ borderColor: 'gray.100'
7
+ },
8
+ medium: {
9
+ borderColor: 'gray.200'
10
+ },
11
+ };
12
+
13
+ export default merge(chakraDefaultTheme.components.Divider, {
14
+ colorScheme,
15
+ });
@@ -5,6 +5,7 @@ import {
5
5
  FormHelperText,
6
6
  FormLabel,
7
7
  FormErrorMessage,
8
+ Text,
8
9
  } from '@chakra-ui/react';
9
10
 
10
11
  const FormControl = forwardRef(
@@ -30,21 +31,34 @@ const FormControl = forwardRef(
30
31
  <FormLabel
31
32
  color="gray.600"
32
33
  fontWeight="semibold"
33
- mb={2}
34
+ marginBottom={helperText ? 0 : 1}
34
35
  fontSize="sm"
35
36
  htmlFor={inputId}
36
37
  as={isFieldset ? 'legend' : 'label'}
38
+ requiredIndicator=""
39
+ optionalIndicator={
40
+ props?.isOptional && !props?.isRequired ? (
41
+ <Text
42
+ marginLeft={1}
43
+ as="span"
44
+ variant="caption"
45
+ color="text.secondary.light"
46
+ >
47
+ Optional
48
+ </Text>
49
+ ) : null
50
+ }
37
51
  >
38
52
  {label}
39
53
  </FormLabel>
40
54
  )}
41
- {children}
42
55
  {helperText && (
43
- <FormHelperText color="gray.600" mt={1} fontSize="xs">
56
+ <FormHelperText color="gray.600" marginBottom={1} fontSize="xs">
44
57
  {helperText}
45
58
  </FormHelperText>
46
59
  )}
47
- <FormErrorMessage color="error" mt={1} fontSize="sm">
60
+ {children}
61
+ <FormErrorMessage color="error" mt={1} fontSize="xs">
48
62
  {errorMessage}
49
63
  </FormErrorMessage>
50
64
  </ChakraFormControl>
@@ -13,6 +13,7 @@ const Input = forwardRef(
13
13
  isInvalid,
14
14
  isDisabled,
15
15
  isRequired,
16
+ isOptional,
16
17
  ...props
17
18
  },
18
19
  ref
@@ -24,6 +25,7 @@ const Input = forwardRef(
24
25
  isInvalid={isInvalid}
25
26
  isDisabled={isDisabled}
26
27
  isRequired={isRequired}
28
+ isOptional={isOptional}
27
29
  errorMessage={errorMessage}
28
30
  helperText={helperText}
29
31
  label={label}
@@ -43,6 +45,7 @@ Input.propTypes = {
43
45
  isInvalid: PropTypes.bool,
44
46
  isDisabled: PropTypes.bool,
45
47
  isRequired: PropTypes.bool,
48
+ isOptional: PropTypes.bool,
46
49
  };
47
50
 
48
51
  export default Input;
@@ -2,12 +2,15 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
 
4
4
  import Component from './Input';
5
+ import {useTheme} from '@chakra-ui/react';
5
6
 
6
7
  export default {
7
8
  title: 'v2/Input',
8
9
  component: Component,
9
10
  parameters: {
11
+ importBy: 'Input',
10
12
  removeBaseHtmlClass: true,
13
+ chakraLink: 'https://chakra-ui.com/docs/components/input',
11
14
  },
12
15
  };
13
16
 
@@ -23,6 +26,7 @@ InputTemplate.propTypes = {
23
26
  isInvalid: PropTypes.bool,
24
27
  isDisabled: PropTypes.bool,
25
28
  isReadOnly: PropTypes.bool,
29
+ isOptional: PropTypes.bool,
26
30
  };
27
31
 
28
32
  export const Input = InputTemplate.bind({});
@@ -43,6 +47,7 @@ Input.argTypes = {
43
47
  };
44
48
 
45
49
  Input.args = {
50
+ id: 'input_id',
46
51
  placeholder: 'Placeholder text',
47
52
  label: 'Label',
48
53
  helperText: 'Helper text',
@@ -51,4 +56,5 @@ Input.args = {
51
56
  isDisabled: false,
52
57
  isReadOnly: false,
53
58
  isRequired: false,
59
+ isOptional: false,
54
60
  };
@@ -1,6 +1,16 @@
1
1
  import merge from 'lodash/merge';
2
2
  import chakraDefaultTheme from '@chakra-ui/theme';
3
3
 
4
+ const baseStyle = {
5
+ field: {
6
+ _placeholder: {
7
+ color: 'text.secondary.light',
8
+ fontWeight: 'normal',
9
+ _disabled: {color: 'text.secondary.light', opacity: 1},
10
+ },
11
+ },
12
+ };
13
+
4
14
  const variants = {
5
15
  outline: {
6
16
  field: {
@@ -12,7 +22,7 @@ const variants = {
12
22
  bg: 'inherit',
13
23
  fontWeight: 'normal',
14
24
  boxShadow: 'none !important',
15
- _placeholder: {color: 'gray.600', fontWeight: 'normal'},
25
+ _placeholder: {color: 'text.secondary.light', fontWeight: 'normal'},
16
26
  _focus: {
17
27
  borderColor: 'brandBlue',
18
28
  },
@@ -20,7 +30,7 @@ const variants = {
20
30
  _readOnly: {boxShadow: 'none !important', userSelect: 'all'},
21
31
  _disabled: {
22
32
  backgroundColor: 'gray.50',
23
- opacity: 0.4,
33
+ opacity: 1,
24
34
  cursor: 'not-allowed',
25
35
  },
26
36
  _invalid: {
@@ -34,5 +44,6 @@ const variants = {
34
44
  };
35
45
 
36
46
  export default merge(chakraDefaultTheme.components.Input, {
47
+ baseStyle,
37
48
  variants,
38
49
  });
@@ -1,7 +1,8 @@
1
+ import React, {forwardRef} from 'react';
1
2
  import cn from 'classnames';
2
3
  import PropTypes from 'prop-types';
3
- import React, {forwardRef} from 'react';
4
- import {Select as ChakraSelect} from '@chakra-ui/react';
4
+ import ReactSelect from 'react-select';
5
+
5
6
  import IconChevronDown from '@spothero/icons/chevron-down';
6
7
 
7
8
  import Icon from '../Icon/Icon';
@@ -16,27 +17,151 @@ const Select = forwardRef(
16
17
  isInvalid,
17
18
  isDisabled,
18
19
  isRequired,
20
+ isOptional,
21
+ options,
19
22
  ...props
20
23
  },
21
24
  ref
22
25
  ) => {
23
26
  const classes = cn({'FormElement-contains-error': isInvalid});
24
27
 
28
+ const baseText = {
29
+ fontSize: 'var(--chakra-fontSizes-base)',
30
+ color: 'var(--chakra-colors-text-primary-light)',
31
+ };
32
+
33
+ /** React Select doesn't use Chakra but it does use Emotion so it's not too difficult to use values from our Chakra theme. Here is the documentation for React Select styling: https://react-select.com/styles */
34
+ const customStyles = {
35
+ select: (provided, state) => ({
36
+ ...provided,
37
+
38
+ '&:hover': {
39
+ cursor: state.isDisabled ? 'not-allowed' : 'auto',
40
+ },
41
+ }),
42
+ menu: provided => ({
43
+ ...provided,
44
+ ...baseText,
45
+ borderColor: 'var(--chakra-colors-gray-200)',
46
+ borderWidth: '1px',
47
+ boxShadow: 'none',
48
+ }),
49
+ control: (provided, state) => ({
50
+ ...provided,
51
+ borderColor: isInvalid
52
+ ? 'var(--chakra-colors-error)'
53
+ : state.isFocused
54
+ ? 'var(--chakra-colors-primary-default)'
55
+ : 'var(--chakra-colors-gray-200)',
56
+ borderWidth: '1px',
57
+ borderRadius: '4px',
58
+ backgroundColor: state.isDisabled
59
+ ? 'var(--chakra-colors-gray-50)'
60
+ : 'transparent',
61
+ boxShadow: 'none',
62
+ height: 'var(--chakra-sizes-10)',
63
+ minWidth: 'var(--chakra-sizes-48)',
64
+ width: props.width && props.width,
65
+
66
+ '&:hover': {
67
+ borderColor: isInvalid
68
+ ? 'var(--chakra-colors-error)'
69
+ : state.isFocused
70
+ ? 'var(--chakra-colors-primary-default)'
71
+ : 'var(--chakra-colors-gray-200)',
72
+ },
73
+ }),
74
+ input: (provided, state) => ({
75
+ ...provided,
76
+ ...baseText,
77
+ borderRadius: '4px',
78
+ color: state.isDisabled
79
+ ? 'var(--chakra-colors-text-secondary-light)'
80
+ : 'var(--chakra-colors-text-primary-light)',
81
+ backgroundColor: state.isDisabled
82
+ ? 'var(--chakra-colors-gray-50)'
83
+ : 'transparent',
84
+ }),
85
+ placeholder: provided => ({
86
+ ...provided,
87
+ fontSize: 'var(--chakra-fontSizes-base)',
88
+ color: 'var(--chakra-colors-gray-600)',
89
+ }),
90
+ singleValue: (provided, state) => ({
91
+ ...provided,
92
+ ...baseText,
93
+ color: state.isDisabled
94
+ ? 'var(--chakra-colors-text-secondary-light)'
95
+ : 'var(--chakra-colors-text-primary-light)',
96
+
97
+ '&:hover': {
98
+ cursor: state.isDisabled ? 'not-allowed' : 'auto',
99
+ },
100
+ }),
101
+ option: (provided, state) => ({
102
+ ...provided,
103
+ backgroundColor: state.isSelected
104
+ ? 'var(--chakra-colors-gray-200)'
105
+ : state.isFocused
106
+ ? 'var(--chakra-colors-gray-50)'
107
+ : 'transparent',
108
+ color: 'var(--chakra-colors-text-primary-light)',
109
+ fontWeight: state.isSelected
110
+ ? 'var(--chakra-fontWeights-semibold)'
111
+ : 'var(--chakra-fontWeights-normal)',
112
+
113
+ ':active': {
114
+ fontWeight: 'var(--chakra-fontWeights-semibold)',
115
+ color: 'var(--chakra-colors-text-primary-light)',
116
+ backgroundColor:
117
+ !state.isDisabled && 'var(--chakra-colors-gray-200)',
118
+ },
119
+ }),
120
+ };
121
+
122
+ const dropdownIndicatorStyles = {
123
+ width: 18,
124
+ display: 'flex',
125
+ marginRight: 'var(--chakra-space-4)',
126
+ marginLeft: 'var(--chakra-space-7)',
127
+ color: 'var(--chakra-colors-gray-600)',
128
+ '&:hover': {
129
+ color: 'var(--chakra-colors-gray-600)',
130
+ },
131
+ };
132
+
133
+ const DropdownIndicator = ({innerRef, innerProps}) => (
134
+ <Icon
135
+ as={IconChevronDown}
136
+ style={dropdownIndicatorStyles}
137
+ ref={innerRef}
138
+ {...innerProps}
139
+ />
140
+ );
141
+
25
142
  return (
26
143
  <FormControl
27
144
  isInvalid={isInvalid}
28
145
  isDisabled={isDisabled}
29
146
  isRequired={isRequired}
30
147
  errorMessage={errorMessage}
148
+ isOptional={isOptional}
31
149
  helperText={helperText}
32
150
  label={label}
33
151
  inputId={props.id}
152
+ minWidth={48}
153
+ width={props.width && props.width}
34
154
  >
35
- <ChakraSelect
36
- icon={<Icon as={IconChevronDown} />}
37
- iconSize={12}
38
- fontWeight="regular"
39
- fontSize="sm"
155
+ <ReactSelect
156
+ options={options?.length && options}
157
+ isSearchable={false}
158
+ isDisabled={isDisabled}
159
+ components={{
160
+ DropdownIndicator,
161
+ IndicatorSeparator: () => null,
162
+ }}
163
+ maxMenuHeight={280}
164
+ styles={customStyles}
40
165
  ref={ref}
41
166
  className={classes}
42
167
  {...props}
@@ -48,12 +173,16 @@ const Select = forwardRef(
48
173
 
49
174
  Select.propTypes = {
50
175
  id: PropTypes.string.isRequired,
176
+ options: PropTypes.array,
51
177
  label: PropTypes.string,
178
+ isRequired: PropTypes.bool,
52
179
  helperText: PropTypes.string,
180
+ placeholder: PropTypes.string,
53
181
  errorMessage: PropTypes.string,
54
182
  isInvalid: PropTypes.bool,
55
183
  isDisabled: PropTypes.bool,
56
184
  isRequired: PropTypes.bool,
185
+ isOptional: PropTypes.bool,
57
186
  };
58
187
 
59
188
  export default Select;
@@ -11,14 +11,7 @@ export default {
11
11
  },
12
12
  };
13
13
 
14
- const SelectTemplate = props => (
15
- <Component variant="outline" maxWidth="200px" {...props}>
16
- <option value="one">One</option>
17
- <option value="two">Two</option>
18
- <option value="three">Three</option>
19
- <option value="four">Four</option>
20
- </Component>
21
- );
14
+ const SelectTemplate = props => <Component options {...props} />;
22
15
 
23
16
  SelectTemplate.propTypes = {
24
17
  placeholder: PropTypes.string,
@@ -28,32 +21,54 @@ SelectTemplate.propTypes = {
28
21
  isInvalid: PropTypes.bool,
29
22
  isDisabled: PropTypes.bool,
30
23
  isReadOnly: PropTypes.bool,
24
+ isOptional: PropTypes.bool,
31
25
  };
32
26
 
33
27
  export const Select = SelectTemplate.bind({});
34
28
 
35
29
  Select.argTypes = {
36
- placeholder: {
37
- control: {type: 'text'},
30
+ id: {control: {type: 'text'}},
31
+ options: {
32
+ control: {type: 'object'},
38
33
  },
39
34
  label: {
40
35
  control: {type: 'text'},
41
36
  },
37
+ isRequired: {control: {type: 'boolean'}},
42
38
  helperText: {
43
39
  control: {type: 'text'},
44
40
  },
41
+ placeholder: {
42
+ control: {type: 'text'},
43
+ },
45
44
  errorMessage: {
46
45
  control: {type: 'text'},
47
46
  },
47
+ isInvalid: {control: {type: 'boolean'}},
48
+ isDisabled: {control: {type: 'boolean'}},
48
49
  };
49
50
 
50
51
  Select.args = {
51
- placeholder: 'Placeholder text',
52
+ id: 'input_id',
53
+ options: [
54
+ {value: 'One', label: 'Option One'},
55
+ {value: 'Two', label: 'Option Two'},
56
+ {value: 'Three', label: 'Option Three'},
57
+ {value: 'Four', label: 'Option Four'},
58
+ {value: 'Five', label: 'Option Five'},
59
+ {value: 'Six', label: 'Option Six'},
60
+ {value: 'Seven', label: 'Option Seven'},
61
+ {value: 'Eight', label: 'Option Eight'},
62
+ {value: 'Nine', label: 'Option Nine'},
63
+ {value: 'Ten', label: 'Option Ten'},
64
+ ],
52
65
  label: 'Label',
66
+ isRequired: false,
53
67
  helperText: 'Helper text',
68
+ placeholder: 'Select One...',
54
69
  errorMessage: 'Error message',
55
70
  isInvalid: false,
56
71
  isDisabled: false,
57
72
  isReadOnly: false,
58
- isRequired: false,
73
+ isOptional: false,
59
74
  };
@@ -24,6 +24,7 @@ export {default as Image} from './Image/Image';
24
24
  export {default as Loader} from './Loader/Loader';
25
25
  export {default as Checkbox} from './Checkbox';
26
26
  export {default as Input} from './Input';
27
+ export {default as Divider} from './Divider/Divider';
27
28
  export {
28
29
  Popover,
29
30
  PopoverAnchor,
@@ -7,8 +7,8 @@ export {default as Button} from './Button/Button.styles';
7
7
  export {default as GridItem} from './Grid/GridItem.styles';
8
8
  export {default as Spinner} from './Spinner/Spinner.styles';
9
9
  export {default as Heading} from './Heading/Heading.styles';
10
+ export {default as Divider} from './Divider/Divider.styles';
10
11
  export {default as Tabs} from './Tabs/styles';
11
- export {default as Select} from './Select/styles';
12
12
  export {default as Checkbox} from './Checkbox/styles';
13
13
  export {default as Switch} from './Switch/styles';
14
14
  export {default as Popover} from './Popover/styles';