@xqmsg/ui-core 0.14.1 → 0.14.2

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,5 +1,5 @@
1
1
  {
2
- "version": "0.14.1",
2
+ "version": "0.14.2",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -74,6 +74,8 @@ export function FormSection<
74
74
  isInvalid={!!form.formState.errors[name]}
75
75
  defaultValue={defaultValue}
76
76
  setValue={form.setValue}
77
+ setError={form.setError}
78
+ clearErrors={form.clearErrors}
77
79
  />
78
80
  )
79
81
  )}
@@ -96,12 +96,18 @@ const Template: Story<InputProps<StoryFormSchema>> = args => {
96
96
 
97
97
  const { form } = formHandler;
98
98
 
99
+ console.log(form.formState.errors);
100
+
99
101
  return (
100
102
  <Form formHandler={formHandler}>
101
103
  <Input
102
104
  {...args}
103
105
  inputType="multi-select"
104
106
  setValue={form.setValue}
107
+ setError={form.setError}
108
+ clearErrors={form.clearErrors}
109
+ isInvalid={!!form.formState.errors['prop5']?.message}
110
+ errorText={form.formState.errors['prop5']?.message}
105
111
  name="prop5"
106
112
  />
107
113
  <Input
@@ -127,6 +133,10 @@ const Template: Story<InputProps<StoryFormSchema>> = args => {
127
133
  name="prop"
128
134
  inputType="pilled-text"
129
135
  setValue={form.setValue}
136
+ setError={form.setError}
137
+ clearErrors={form.clearErrors}
138
+ isInvalid={!!form.formState.errors['prop']?.message}
139
+ errorText={form.formState.errors['prop']?.message}
130
140
  />
131
141
  <Input
132
142
  {...args}
@@ -9,6 +9,8 @@ import colors from '../../../theme/foundations/colors';
9
9
  import {
10
10
  Control,
11
11
  FieldValues,
12
+ UseFormClearErrors,
13
+ UseFormSetError,
12
14
  UseFormSetValue,
13
15
  useWatch,
14
16
  } from 'react-hook-form';
@@ -19,7 +21,11 @@ import Token from '../components/token';
19
21
  export interface StackedMultiSelectProps extends ReactSelectFieldProps {
20
22
  options: FieldOptions;
21
23
  setValue: UseFormSetValue<FieldValues>;
24
+ setError: UseFormSetError<FieldValues>;
25
+ clearErrors: UseFormClearErrors<FieldValues>;
22
26
  control: Control<FieldValues, any>;
27
+ // Number of allowed options
28
+ maxLength?: number;
23
29
  }
24
30
 
25
31
  /**
@@ -28,114 +34,160 @@ export interface StackedMultiSelectProps extends ReactSelectFieldProps {
28
34
  const StackedMultiSelect = React.forwardRef<
29
35
  HTMLInputElement,
30
36
  StackedMultiSelectProps
31
- >(({ options, setValue, control, name, placeholder, disabled }, _ref) => {
32
- const watchedValue = useWatch({ control, name: name as string });
33
- const dropdownRef = useRef(null);
34
-
35
- const [localValues, setLocalValues] = useState<FieldOptions>([]);
36
- const [localOptions, setLocalOptions] = useState<FieldOptions>(options);
37
- const [isFocussed, setIsFocussed] = useState(false);
38
-
39
- useOutsideClick({ ref: dropdownRef, handler: () => setIsFocussed(false) });
40
-
41
- // gets latest watched form value (common delimited) from RHF state and creates a list
42
- useEffect(() => {
43
- if (watchedValue !== undefined && !watchedValue.length) {
44
- setLocalValues([]);
45
- }
46
-
47
- if (watchedValue !== undefined && watchedValue?.length) {
48
- setLocalValues(
49
- watchedValue
50
- .split(',')
51
- .filter(Boolean)
52
- .map((value: string) =>
53
- options.find(option => option.value === value)
54
- )
55
- );
56
- }
57
- }, [options, watchedValue]);
58
-
59
- const handleChange = (option: FieldOption) => {
60
- const newValue = [...localValues, option]
61
- .map(({ value }) => value)
62
- .join(',');
63
-
64
- setValue(name as string, newValue, {
65
- shouldValidate: true,
66
- shouldDirty: true,
37
+ >(
38
+ (
39
+ {
40
+ options,
41
+ setValue,
42
+ control,
43
+ name,
44
+ placeholder,
45
+ disabled,
46
+ maxLength,
47
+ setError,
48
+ },
49
+ _ref
50
+ ) => {
51
+ const watchedValue = useWatch({ control, name: name as string });
52
+ const dropdownRef = useRef(null);
53
+
54
+ const [localValues, setLocalValues] = useState<FieldOptions>([]);
55
+ const [localOptions, setLocalOptions] = useState<FieldOptions>(options);
56
+ const [isFocussed, setIsFocussed] = useState(false);
57
+
58
+ useOutsideClick({
59
+ ref: dropdownRef,
60
+ handler: () => {
61
+ if (maxLength && localValues.length > maxLength) {
62
+ setError(name as string, {
63
+ message: `Exceeded maximum of ${maxLength} options`,
64
+ });
65
+ }
66
+
67
+ return setIsFocussed(false);
68
+ },
67
69
  });
68
70
 
69
- setLocalOptions(prevLocalOptions =>
70
- prevLocalOptions.filter(prevLocalOption => prevLocalOption !== option)
71
- );
71
+ // gets latest watched form value (common delimited) from RHF state and creates a list
72
+ useEffect(() => {
73
+ if (watchedValue !== undefined && !watchedValue.length) {
74
+ setLocalValues([]);
75
+ }
76
+
77
+ if (
78
+ maxLength &&
79
+ watchedValue !== undefined &&
80
+ watchedValue.length <= maxLength &&
81
+ watchedValue?.length
82
+ ) {
83
+ setLocalValues(
84
+ watchedValue
85
+ .split(',')
86
+ .filter(Boolean)
87
+ .map((value: string) =>
88
+ options.find(option => option.value === value)
89
+ )
90
+ );
91
+ }
92
+ }, [maxLength, options, watchedValue]);
93
+
94
+ const handleChange = (option: FieldOption) => {
95
+ console.log(localValues.length, maxLength);
96
+
97
+ if (maxLength && localValues.length > maxLength) {
98
+ return setError(name as string, {
99
+ message: `Exceeded maximum of ${maxLength} options`,
100
+ });
101
+ }
102
+
103
+ const newValue = [...localValues, option]
104
+ .map(({ value }) => value)
105
+ .join(',');
106
+
107
+ setValue(name as string, newValue, {
108
+ shouldValidate: true,
109
+ shouldDirty: true,
110
+ });
111
+
112
+ setLocalOptions(prevLocalOptions =>
113
+ prevLocalOptions.filter(prevLocalOption => prevLocalOption !== option)
114
+ );
72
115
 
73
- setLocalValues(prevLocalValues => [...prevLocalValues, option]);
74
- };
116
+ setLocalValues(prevLocalValues => [...prevLocalValues, option]);
117
+ };
75
118
 
76
- const handleDelete = (option: FieldOption) => {
77
- const newValue = localValues
78
- .filter(localValue => localValue !== option)
79
- .map(({ value }) => value)
80
- .join(',');
119
+ const handleDelete = (option: FieldOption) => {
120
+ const newValue = localValues
121
+ .filter(localValue => localValue !== option)
122
+ .map(({ value }) => value)
123
+ .join(',');
81
124
 
82
- setValue(name as string, newValue, {
83
- shouldValidate: true,
84
- shouldDirty: true,
85
- });
125
+ setValue(name as string, newValue, {
126
+ shouldValidate: true,
127
+ shouldDirty: true,
128
+ });
86
129
 
87
- setLocalOptions(prevLocalOptions =>
88
- [...prevLocalOptions, option].sort((a, b) => a.sortValue - b.sortValue)
89
- );
130
+ setLocalOptions(prevLocalOptions =>
131
+ [...prevLocalOptions, option].sort((a, b) => a.sortValue - b.sortValue)
132
+ );
90
133
 
91
- setLocalValues(prevLocalValues =>
92
- prevLocalValues.filter(prevLocalValue => prevLocalValue !== option)
93
- );
94
- };
95
-
96
- return (
97
- <Box ref={dropdownRef} position="relative">
98
- <Flex
99
- fontSize="13px"
100
- border={isFocussed ? '2px solid' : '1px solid'}
101
- borderColor={isFocussed ? colors.border.focus : colors.border.default}
102
- py="5px"
103
- pl="8px"
104
- borderRadius="4px"
105
- alignItems="center"
106
- justifyContent="space-between"
107
- onClick={() => !disabled && setIsFocussed(true)}
108
- bg={disabled ? colors.fill.light.quaternary : '#ffffff'}
109
- cursor={disabled ? 'not-allowed' : 'pointer'}
110
- >
111
- <Flex height="28px" alignItems="center">
112
- {localValues.length ? (
113
- localValues.map(option => (
114
- <Box mr="4px">
115
- <Token
116
- label={option.label}
117
- onDelete={() => handleDelete(option)}
118
- />
119
- </Box>
120
- ))
121
- ) : (
122
- <Text color={colors.label.secondary.light} fontSize="13px">
123
- {placeholder}
124
- </Text>
125
- )}
126
- </Flex>
127
- <Flex width="39px" justifyContent="center" alignItems="center">
128
- <Image src={SubtractIcon} alt="subtract" boxSize="16px" />
134
+ setLocalValues(prevLocalValues =>
135
+ prevLocalValues.filter(prevLocalValue => prevLocalValue !== option)
136
+ );
137
+ };
138
+
139
+ return (
140
+ <Box ref={dropdownRef} position="relative">
141
+ <Flex
142
+ fontSize="13px"
143
+ border={isFocussed ? '2px solid' : '1px solid'}
144
+ borderColor={isFocussed ? colors.border.focus : colors.border.default}
145
+ py="5px"
146
+ pl="8px"
147
+ borderRadius="4px"
148
+ alignItems="center"
149
+ justifyContent="space-between"
150
+ onClick={() => !disabled && setIsFocussed(true)}
151
+ bg={disabled ? colors.fill.light.quaternary : '#ffffff'}
152
+ cursor={disabled ? 'not-allowed' : 'pointer'}
153
+ >
154
+ <Flex
155
+ height="28px"
156
+ alignItems="center"
157
+ overflowX="auto"
158
+ maxWidth="90%"
159
+ style={{
160
+ scrollbarWidth: 'none' /* Firefox */,
161
+ }}
162
+ >
163
+ {localValues.length ? (
164
+ localValues.map(option => (
165
+ <Box mr="4px">
166
+ <Token
167
+ label={option.label}
168
+ onDelete={() => handleDelete(option)}
169
+ />
170
+ </Box>
171
+ ))
172
+ ) : (
173
+ <Text color={colors.label.secondary.light} fontSize="13px">
174
+ {placeholder}
175
+ </Text>
176
+ )}
177
+ </Flex>
178
+ <Flex width="39px" justifyContent="center" alignItems="center">
179
+ <Image src={SubtractIcon} alt="subtract" boxSize="16px" />
180
+ </Flex>
129
181
  </Flex>
130
- </Flex>
131
- {isFocussed && (
132
- <Dropdown
133
- onSelectItem={option => handleChange(option)}
134
- options={localOptions}
135
- />
136
- )}
137
- </Box>
138
- );
139
- });
182
+ {isFocussed && (
183
+ <Dropdown
184
+ onSelectItem={option => handleChange(option)}
185
+ options={localOptions}
186
+ />
187
+ )}
188
+ </Box>
189
+ );
190
+ }
191
+ );
140
192
 
141
193
  export default StackedMultiSelect;