@popsure/dirty-swan 0.57.8 → 0.58.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 (63) hide show
  1. package/dist/cjs/index.js +98 -63
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/lib/components/input/checkbox/index.d.ts +2 -1
  4. package/dist/cjs/lib/components/input/checkbox/index.stories.d.ts +11 -7
  5. package/dist/cjs/lib/components/input/radio/index.d.ts +3 -1
  6. package/dist/cjs/lib/components/input/radio/index.stories.d.ts +10 -2
  7. package/dist/cjs/lib/components/multiDropzone/utils/index.d.ts +9 -2
  8. package/dist/esm/{TableSection-a26ba0c5.js → TableSection-ebace923.js} +1 -1
  9. package/dist/esm/{TableSection-a26ba0c5.js.map → TableSection-ebace923.js.map} +1 -1
  10. package/dist/esm/components/chip/index.js +2 -2
  11. package/dist/esm/components/chip/index.js.map +1 -1
  12. package/dist/esm/components/comparisonTable/components/TableInfoButton/index.js +1 -1
  13. package/dist/esm/components/comparisonTable/components/TableInfoButton/index.js.map +1 -1
  14. package/dist/esm/components/input/checkbox/index.js +19 -16
  15. package/dist/esm/components/input/checkbox/index.js.map +1 -1
  16. package/dist/esm/components/input/checkbox/index.stories.js +24 -20
  17. package/dist/esm/components/input/checkbox/index.stories.js.map +1 -1
  18. package/dist/esm/components/input/index.js +1 -1
  19. package/dist/esm/components/input/index.js.map +1 -1
  20. package/dist/esm/components/input/radio/index.js +23 -21
  21. package/dist/esm/components/input/radio/index.js.map +1 -1
  22. package/dist/esm/components/input/radio/index.stories.js +12 -3
  23. package/dist/esm/components/input/radio/index.stories.js.map +1 -1
  24. package/dist/esm/components/input/radio/index.test.js +1 -0
  25. package/dist/esm/components/input/radio/index.test.js.map +1 -1
  26. package/dist/esm/components/multiDropzone/UploadFileCell/index.js +6 -7
  27. package/dist/esm/components/multiDropzone/UploadFileCell/index.js.map +1 -1
  28. package/dist/esm/components/multiDropzone/index.js +51 -17
  29. package/dist/esm/components/multiDropzone/index.js.map +1 -1
  30. package/dist/esm/components/multiDropzone/index.stories.js +1 -0
  31. package/dist/esm/components/multiDropzone/index.stories.js.map +1 -1
  32. package/dist/esm/components/multiDropzone/index.test.js +1 -0
  33. package/dist/esm/components/multiDropzone/index.test.js.map +1 -1
  34. package/dist/esm/components/table/Table.js +1 -1
  35. package/dist/esm/components/table/Table.stories.js +1 -1
  36. package/dist/esm/components/table/Table.test.js +1 -1
  37. package/dist/esm/components/table/components/TableContents/TableContents.js +1 -1
  38. package/dist/esm/components/table/components/TableContents/TableContents.test.js +1 -1
  39. package/dist/esm/components/table/components/TableSection/TableSection.js +1 -1
  40. package/dist/esm/components/table/components/TableSection/TableSection.test.js +1 -1
  41. package/dist/esm/index.js +1 -1
  42. package/dist/esm/lib/components/input/checkbox/index.d.ts +2 -1
  43. package/dist/esm/lib/components/input/checkbox/index.stories.d.ts +11 -7
  44. package/dist/esm/lib/components/input/radio/index.d.ts +3 -1
  45. package/dist/esm/lib/components/input/radio/index.stories.d.ts +10 -2
  46. package/dist/esm/lib/components/multiDropzone/utils/index.d.ts +9 -2
  47. package/package.json +1 -1
  48. package/src/lib/components/chip/index.tsx +1 -0
  49. package/src/lib/components/chip/style.module.scss +5 -0
  50. package/src/lib/components/comparisonTable/components/TableInfoButton/index.tsx +2 -0
  51. package/src/lib/components/input/checkbox/index.stories.tsx +81 -58
  52. package/src/lib/components/input/checkbox/index.tsx +11 -2
  53. package/src/lib/components/input/checkbox/styles.module.scss +4 -0
  54. package/src/lib/components/input/index.tsx +2 -0
  55. package/src/lib/components/input/radio/index.stories.tsx +17 -2
  56. package/src/lib/components/input/radio/index.tsx +11 -2
  57. package/src/lib/components/input/radio/styles.module.scss +5 -1
  58. package/src/lib/components/multiDropzone/UploadFileCell/index.tsx +25 -19
  59. package/src/lib/components/multiDropzone/UploadFileCell/style.module.scss +11 -29
  60. package/src/lib/components/multiDropzone/index.tsx +17 -5
  61. package/src/lib/components/multiDropzone/style.module.scss +12 -9
  62. package/src/lib/components/multiDropzone/utils/index.test.ts +128 -45
  63. package/src/lib/components/multiDropzone/utils/index.ts +89 -36
@@ -1,4 +1,3 @@
1
-
2
1
  import { useState } from 'react';
3
2
  import { Checkbox, CheckboxProps } from '.';
4
3
  import { images } from '../../../util/images';
@@ -9,28 +8,36 @@ const story = {
9
8
  component: Checkbox,
10
9
  argTypes: {
11
10
  options: {
12
- description: 'Object that contains the possible options for rendering in the input.',
11
+ description:
12
+ 'Object that contains the possible options for rendering in the input.',
13
13
  },
14
14
  value: {
15
15
  description: 'Current checked values.',
16
16
  },
17
+ fieldLegend: {
18
+ description:
19
+ 'Accessibility property that describes the purpose of a group of checkbox buttons, read aloud by screen readers to provide context.',
20
+ },
17
21
  onChange: {
18
22
  description: 'Function called everytime a value changes.',
19
23
  action: true,
20
24
  table: {
21
- category: "Callbacks",
25
+ category: 'Callbacks',
22
26
  },
23
27
  },
24
28
  wide: {
25
- description: 'Property that defines if options should fill 100% of available horizontal space',
26
- defaultValue: false
29
+ description:
30
+ 'Property that defines if options should fill 100% of available horizontal space',
31
+ defaultValue: false,
27
32
  },
28
33
  bordered: {
29
34
  control: 'boolean',
30
- description: 'Property that defines if checkbox should show the border around each label',
35
+ description:
36
+ 'Property that defines if checkbox should show the border around each label',
31
37
  },
32
38
  inlineLayout: {
33
- description: 'Property that defines if options should show inline instead of block. Check inline checkbox options story for examples.',
39
+ description:
40
+ 'Property that defines if options should show inline instead of block. Check inline checkbox options story for examples.',
34
41
  },
35
42
  className: {
36
43
  description: 'ClassNames for custom styling',
@@ -38,133 +45,144 @@ const story = {
38
45
  },
39
46
  args: {
40
47
  options: {
41
- CAT:{
48
+ CAT: {
42
49
  title: 'Cat',
43
- description: 'At least 1'
50
+ description: 'At least 1',
44
51
  },
45
- DOG:{
52
+ DOG: {
46
53
  title: 'Dog',
47
- description: 'At least 2'
54
+ description: 'At least 2',
48
55
  },
49
- NONE:{
56
+ NONE: {
50
57
  title: 'None',
51
- description: 'No pets'
52
- }
58
+ description: 'No pets',
59
+ },
53
60
  },
61
+ fieldLegend: 'Owned pets',
54
62
  wide: false,
55
63
  bordered: true,
56
64
  inlineLayout: false,
57
65
  classNames: {
58
66
  container: '',
59
67
  label: '',
60
- option: ''
68
+ option: '',
61
69
  },
62
70
  value: [],
63
- className: ''
64
- }
71
+ className: '',
72
+ },
65
73
  };
66
74
 
67
- export const CheckboxStory = ({
75
+ export const CheckboxStory = ({
68
76
  onChange,
69
77
  options,
70
78
  wide,
71
79
  bordered,
72
80
  classNames,
73
81
  inlineLayout,
82
+ fieldLegend,
74
83
  }: CheckboxProps<string>) => {
75
84
  const [checkedValues, setCheckedValues] = useState<string[]>([]);
76
85
 
77
86
  const handleOnChange = (newValue: string[]) => {
78
87
  setCheckedValues(newValue);
79
88
  onChange(newValue);
80
- }
89
+ };
81
90
 
82
91
  return (
83
- <Checkbox
92
+ <Checkbox
84
93
  wide={wide}
85
- options={options}
94
+ options={options}
86
95
  onChange={handleOnChange}
87
96
  value={checkedValues}
88
97
  bordered={bordered}
89
98
  classNames={classNames}
90
99
  inlineLayout={inlineLayout}
100
+ fieldLegend={fieldLegend}
91
101
  />
92
102
  );
93
- }
103
+ };
94
104
 
95
- export const CheckboxWithCustomWrapperStyles = ({ onChange }: CheckboxProps<string>) => {
105
+ export const CheckboxWithCustomWrapperStyles = ({
106
+ onChange,
107
+ }: CheckboxProps<string>) => {
96
108
  const [checkedValues, setCheckedValues] = useState<string[]>([]);
97
109
 
98
110
  const handleOnChange = (newValue: string[] = []) => {
99
111
  setCheckedValues(newValue);
100
112
  onChange(newValue);
101
- }
113
+ };
102
114
 
103
115
  return (
104
- <Checkbox
116
+ <Checkbox
105
117
  onChange={handleOnChange}
106
118
  value={checkedValues}
107
119
  options={{
108
120
  CAT1: 'Cat',
109
121
  DOG1: 'Dog',
110
- }}
111
- classNames={{ container: "p32 bg-primary-300 br24 bs-lg" }}
122
+ }}
123
+ classNames={{ container: 'p32 bg-primary-300 br24 bs-lg' }}
112
124
  />
113
125
  );
114
- }
126
+ };
115
127
 
116
- export const CheckboxWithCustomOptionStyles = ({ onChange }: CheckboxProps<string>) => {
128
+ export const CheckboxWithCustomOptionStyles = ({
129
+ onChange,
130
+ }: CheckboxProps<string>) => {
117
131
  const [checkedValues, setCheckedValues] = useState<string[]>([]);
118
132
 
119
133
  const handleOnChange = (newValue: string[] = []) => {
120
134
  setCheckedValues(newValue);
121
135
  onChange(newValue);
122
- }
136
+ };
123
137
 
124
138
  return (
125
- <Checkbox
139
+ <Checkbox
126
140
  onChange={handleOnChange}
127
141
  value={checkedValues}
128
142
  options={{
129
143
  CAT2: 'Cat',
130
144
  DOG2: 'Dog',
131
- }}
132
- classNames={{ option: "mb32 p24 bg-green-100 br12 bs-lg" }}
145
+ }}
146
+ classNames={{ option: 'mb32 p24 bg-green-100 br12 bs-lg' }}
133
147
  />
134
148
  );
135
- }
149
+ };
136
150
 
137
- export const CheckboxWithCustomLabelStyles = ({ onChange }: CheckboxProps<string>) => {
151
+ export const CheckboxWithCustomLabelStyles = ({
152
+ onChange,
153
+ }: CheckboxProps<string>) => {
138
154
  const [checkedValues, setCheckedValues] = useState<string[]>([]);
139
155
 
140
156
  const handleOnChange = (newValue: string[] = []) => {
141
157
  setCheckedValues(newValue);
142
158
  onChange(newValue);
143
- }
159
+ };
144
160
 
145
161
  return (
146
- <Checkbox
162
+ <Checkbox
147
163
  onChange={handleOnChange}
148
164
  value={checkedValues}
149
165
  options={{
150
166
  CAT3: 'Cat',
151
167
  DOG3: 'Dog',
152
- }}
153
- classNames={{ label: "bg-grey-900 tc-white" }}
168
+ }}
169
+ classNames={{ label: 'bg-grey-900 tc-white' }}
154
170
  />
155
171
  );
156
- }
172
+ };
157
173
 
158
- export const CheckboxWithInlineLayout = ({ onChange }: CheckboxProps<string>) => {
174
+ export const CheckboxWithInlineLayout = ({
175
+ onChange,
176
+ }: CheckboxProps<string>) => {
159
177
  const [checkedValues, setCheckedValues] = useState<string[]>([]);
160
178
 
161
179
  const handleOnChange = (newValue: string[] = []) => {
162
180
  setCheckedValues(newValue);
163
181
  onChange(newValue);
164
- }
182
+ };
165
183
 
166
184
  return (
167
- <Checkbox
185
+ <Checkbox
168
186
  onChange={handleOnChange}
169
187
  value={checkedValues}
170
188
  options={{
@@ -174,45 +192,50 @@ export const CheckboxWithInlineLayout = ({ onChange }: CheckboxProps<string>) =>
174
192
  RABBIT: 'Rabbit',
175
193
  RAT: 'Rat',
176
194
  ANOTHER: 'Other',
177
- }}
178
- classNames={{ option: "w30" }}
195
+ }}
196
+ classNames={{ option: 'w30' }}
179
197
  inlineLayout
180
198
  wide
181
199
  />
182
200
  );
183
- }
201
+ };
184
202
 
185
- export const CheckboxWithCustomLabel = ({ onChange, wide, classNames, inlineLayout }: CheckboxProps<string>) => {
203
+ export const CheckboxWithCustomLabel = ({
204
+ onChange,
205
+ wide,
206
+ classNames,
207
+ inlineLayout,
208
+ }: CheckboxProps<string>) => {
186
209
  const [checkedValues, setCheckedValues] = useState<string[]>([]);
187
210
 
188
211
  const handleOnChange = (newValue: string[] = []) => {
189
212
  setCheckedValues(newValue);
190
213
  onChange(newValue);
191
- }
214
+ };
192
215
 
193
216
  return (
194
- <Checkbox
217
+ <Checkbox
195
218
  options={{
196
219
  BIGDOG: {
197
- icon: () => <img src={images.bigDog} alt='' />,
220
+ icon: () => <img src={images.bigDog} alt="" />,
198
221
  title: 'Dog',
199
222
  },
200
- FISH:{
201
- icon: () => <img src={images.brokenAquarium} alt='' />,
223
+ FISH: {
224
+ icon: () => <img src={images.brokenAquarium} alt="" />,
202
225
  title: 'Fish',
203
226
  },
204
- OTHER:{
205
- icon: () => <img src={images.brokenGlass} alt='' />,
227
+ OTHER: {
228
+ icon: () => <img src={images.brokenGlass} alt="" />,
206
229
  title: 'Other',
207
- }
208
- }}
230
+ },
231
+ }}
209
232
  onChange={handleOnChange}
210
233
  value={checkedValues}
211
- classNames={{ option: "w30" }}
234
+ classNames={{ option: 'w30' }}
212
235
  inlineLayout
213
236
  />
214
237
  );
215
- }
238
+ };
216
239
 
217
240
  CheckboxStory.storyName = 'Checkbox';
218
241
 
@@ -20,6 +20,7 @@ export interface CheckboxProps<ValueType extends string> {
20
20
  label?: string;
21
21
  option?: string;
22
22
  };
23
+ fieldLegend?: string;
23
24
  }
24
25
 
25
26
  export const Checkbox = <ValueType extends string>({
@@ -30,6 +31,7 @@ export const Checkbox = <ValueType extends string>({
30
31
  inlineLayout = false,
31
32
  bordered = true,
32
33
  classNames: classNamesObj,
34
+ fieldLegend,
33
35
  }: CheckboxProps<ValueType> & {}) => {
34
36
  const hasNoneValue = Object.keys(options).includes('NONE');
35
37
 
@@ -71,8 +73,14 @@ export const Checkbox = <ValueType extends string>({
71
73
  return (label as CheckboxWithDescription).title !== undefined;
72
74
  };
73
75
 
76
+ const legend =
77
+ fieldLegend ??
78
+ (Object.keys(options).length > 1
79
+ ? 'Select one or more options'
80
+ : 'You may select this option');
81
+
74
82
  return (
75
- <div
83
+ <fieldset
76
84
  className={classNames(
77
85
  classNamesObj?.container,
78
86
  styles.container,
@@ -85,6 +93,7 @@ export const Checkbox = <ValueType extends string>({
85
93
  }
86
94
  )}
87
95
  >
96
+ <legend className="sr-only">{legend}</legend>
88
97
  {entries.map(([currentValue, label]) => {
89
98
  const checked = value?.includes(currentValue);
90
99
  const customIcon = (label as CheckboxWithDescription)?.icon;
@@ -129,6 +138,6 @@ export const Checkbox = <ValueType extends string>({
129
138
  </div>
130
139
  );
131
140
  })}
132
- </div>
141
+ </fieldset>
133
142
  );
134
143
  };
@@ -1,5 +1,9 @@
1
1
  .container {
2
2
  max-width: 100%;
3
+ border: 0;
4
+ margin: 0;
5
+ min-width: 0;
6
+ padding: 0.01em 0 0 0;
3
7
  }
4
8
 
5
9
  .narrow {
@@ -64,6 +64,8 @@ export const Input = React.forwardRef(
64
64
  )}
65
65
  placeholder={label || labelInsideInput ? placeholder : ' '}
66
66
  disabled={disabled}
67
+ aria-invalid={!!error}
68
+ aria-errormessage={error ? error : undefined}
67
69
  {...props}
68
70
  />
69
71
  {prefix && (
@@ -13,6 +13,14 @@ const story = {
13
13
  value: {
14
14
  description: 'Current checked values.',
15
15
  },
16
+ fieldLegend: {
17
+ description:
18
+ 'Property that describes the purpose of a group of radio buttons, read aloud by screen readers to provide context.',
19
+ },
20
+ groupName: {
21
+ description:
22
+ 'Property passed to each radio button. Informs the browser that the radio buttons belong to the same group, so only one can be selected',
23
+ },
16
24
  onChange: {
17
25
  description: 'Function called everytime a value changes.',
18
26
  action: true,
@@ -29,7 +37,8 @@ const story = {
29
37
  'Property that defines if options should show inline instead of block. Check inline radio options story for examples.',
30
38
  },
31
39
  inlineIcon: {
32
- description: 'Property that defines if options should show inline with icon',
40
+ description:
41
+ 'Property that defines if options should show inline with icon',
33
42
  },
34
43
  classNames: {
35
44
  description: 'ClassNames for custom styling',
@@ -57,6 +66,8 @@ const story = {
57
66
  description: 'No pets',
58
67
  },
59
68
  },
69
+ fieldLegend: 'Owned pets',
70
+ groupName: 'Pets',
60
71
  value: '',
61
72
  wide: false,
62
73
  classNames: {
@@ -68,7 +79,7 @@ const story = {
68
79
  inlineLayout: false,
69
80
  inlineIcon: false,
70
81
  disabled: false,
71
- }
82
+ },
72
83
  };
73
84
 
74
85
  export const RadioStory = ({
@@ -79,6 +90,8 @@ export const RadioStory = ({
79
90
  inlineLayout,
80
91
  bordered,
81
92
  disabled,
93
+ fieldLegend,
94
+ groupName,
82
95
  }: RadioProps<string>) => {
83
96
  const [checkedValues, setCheckedValues] = useState<string>();
84
97
 
@@ -97,6 +110,8 @@ export const RadioStory = ({
97
110
  inlineLayout={inlineLayout}
98
111
  bordered={bordered}
99
112
  disabled={disabled}
113
+ fieldLegend={fieldLegend}
114
+ groupName={groupName}
100
115
  />
101
116
  );
102
117
  };
@@ -2,6 +2,7 @@ import classNames from 'classnames';
2
2
  import { ReactNode } from 'react';
3
3
 
4
4
  import styles from './styles.module.scss';
5
+ import generateId from '../../../util/generateId';
5
6
  export interface RadioWithDescription {
6
7
  title: ReactNode;
7
8
  description?: string;
@@ -23,6 +24,8 @@ export interface RadioProps<ValueType extends string> {
23
24
  };
24
25
  bordered?: boolean;
25
26
  disabled?: boolean;
27
+ fieldLegend?: string;
28
+ groupName?: string;
26
29
  }
27
30
 
28
31
  export const Radio = <ValueType extends string>({
@@ -35,14 +38,18 @@ export const Radio = <ValueType extends string>({
35
38
  classNames: classNamesObj,
36
39
  bordered = true,
37
40
  disabled = false,
41
+ fieldLegend = 'Select an option',
42
+ groupName,
38
43
  }: RadioProps<ValueType>) => {
39
44
  const entries = Object.entries(options) as [
40
45
  ValueType,
41
46
  ReactNode | RadioWithDescription
42
47
  ][];
43
48
 
49
+ const name = groupName ?? generateId();
50
+
44
51
  return (
45
- <div
52
+ <fieldset
46
53
  className={classNames(
47
54
  classNamesObj?.container,
48
55
  styles.container,
@@ -56,6 +63,7 @@ export const Radio = <ValueType extends string>({
56
63
  }
57
64
  )}
58
65
  >
66
+ <legend className="sr-only">{fieldLegend}</legend>
59
67
  {entries.map(([currentValue, label]) => {
60
68
  const checked = value === currentValue;
61
69
  const customIcon = (label as RadioWithDescription)?.icon;
@@ -81,6 +89,7 @@ export const Radio = <ValueType extends string>({
81
89
  checked={checked}
82
90
  data-testid={`radio-input-${currentValue}`}
83
91
  disabled={disabled}
92
+ name={name}
84
93
  />
85
94
 
86
95
  <label
@@ -118,6 +127,6 @@ export const Radio = <ValueType extends string>({
118
127
  </div>
119
128
  );
120
129
  })}
121
- </div>
130
+ </fieldset>
122
131
  );
123
132
  };
@@ -1,5 +1,9 @@
1
1
  .container {
2
2
  max-width: 100%;
3
+ border: 0;
4
+ margin: 0;
5
+ min-width: 0;
6
+ padding: 0.01em 0 0 0;
3
7
  }
4
8
 
5
9
  .narrow {
@@ -8,4 +12,4 @@
8
12
 
9
13
  .wide {
10
14
  max-width: 736px;
11
- }
15
+ }
@@ -5,6 +5,7 @@ import styles from './style.module.scss';
5
5
  import { FileIcon, Trash2Icon, EyeVisionIcon } from '../../icon/icons';
6
6
  import { Color } from '../../../models/styles';
7
7
  import { UploadStatus, UploadedFile } from '../types';
8
+ import { Button } from '../../button';
8
9
 
9
10
  interface Props {
10
11
  uploadStatus: UploadStatus;
@@ -94,35 +95,40 @@ const UploadFileCell: React.FC<Props> = ({
94
95
  ) : (
95
96
  <>
96
97
  {isComplete && (
97
- <a
98
- className={styles['view-icon']}
98
+ <Button
99
+ as="a"
99
100
  href={previewUrl}
100
101
  target="_blank"
101
102
  rel="noopener noreferrer"
103
+ hideLabel
104
+ variant="filledWhite"
105
+ className={classnames('mr16', styles.button)}
106
+ leftIcon={
107
+ <EyeVisionIcon noMargin color={'grey-500'} size={24} />
108
+ }
102
109
  >
103
- <EyeVisionIcon
104
- color={'grey-500'}
105
- size={24}
106
- className={styles.icon}
107
- />
108
- </a>
110
+ Preview file
111
+ </Button>
109
112
  )}
110
113
 
111
114
  {onRemoveFile && (
112
- <button
113
- type="button"
115
+ <Button
114
116
  onClick={() => onRemoveFile(id)}
115
- className={classnames(styles['remove-icon'], {
116
- [styles.disabled]: uploading,
117
- })}
117
+ disabled={uploading}
118
118
  data-testid="remove-button"
119
+ className={styles.button}
120
+ leftIcon={
121
+ <Trash2Icon
122
+ color={hasError ? 'red-500' : 'grey-500'}
123
+ size={24}
124
+ noMargin
125
+ />
126
+ }
127
+ hideLabel
128
+ variant="filledWhite"
119
129
  >
120
- <Trash2Icon
121
- color={hasError ? 'red-500' : 'grey-500'}
122
- size={24}
123
- className={styles.icon}
124
- />
125
- </button>
130
+ Delete file
131
+ </Button>
126
132
  )}
127
133
  </>
128
134
  )}
@@ -27,10 +27,6 @@
27
27
  align-items: center;
28
28
  }
29
29
 
30
- .icon {
31
- margin: 0px;
32
- }
33
-
34
30
  .main-icon {
35
31
  margin-right: 16px;
36
32
  }
@@ -64,39 +60,25 @@
64
60
 
65
61
  .cell-right-section {
66
62
  display: flex;
67
- justify-content: flex-end;
68
- min-width: 32px;
69
- margin-left: 16px;
70
63
  }
71
64
 
72
65
  .cell-right-section-complete {
73
66
  min-width: 64px;
74
67
  }
75
68
 
76
- .view-icon,
77
- .remove-icon {
78
- cursor: pointer;
79
- background: none;
80
- border: none;
81
- padding: 0;
82
- margin: 0;
83
- color: inherit;
84
- text-align: inherit;
85
- outline: none;
86
- box-shadow: none;
87
- appearance: none;
88
- -webkit-appearance: none;
89
- -moz-appearance: none;
90
- }
69
+ .button {
70
+ width: 32px;
91
71
 
92
- .remove-icon {
93
- margin-left: 16px;
94
- }
72
+ div span span {
73
+ min-width: 24px !important;
74
+ height: 24px !important;
75
+ }
95
76
 
96
- .disabled {
97
- pointer-events: none;
98
- cursor: default;
99
- opacity: 0.3;
77
+ &:focus-visible {
78
+ outline: 2px solid $ds-grey-900;
79
+ border-radius: 2px;
80
+ outline-offset: 2px;
81
+ }
100
82
  }
101
83
 
102
84
  @keyframes appear-down {
@@ -10,6 +10,7 @@ import {
10
10
  formatAcceptFileList,
11
11
  getErrorMessage,
12
12
  getFormattedAcceptObject,
13
+ getStatusMessage,
13
14
  getUploadStatus,
14
15
  } from './utils';
15
16
 
@@ -48,6 +49,7 @@ const MultiDropzone = ({
48
49
  textOverrides,
49
50
  }: MultiDropzoneProps) => {
50
51
  const [errors, setErrors] = useState<ErrorMessage[]>([]);
52
+ const [statusMessage, setStatusMessage] = useState('');
51
53
  const formattedAccept = getFormattedAcceptObject(accept);
52
54
  const fileList = formatAcceptFileList(formattedAccept);
53
55
  const placeholder = getPlaceholder(textOverrides, accept, maxSize);
@@ -60,6 +62,15 @@ const MultiDropzone = ({
60
62
  (acceptedFiles: File[], filesRejected: FileRejection[]) => {
61
63
  onFileSelect(acceptedFiles);
62
64
 
65
+ const messageForScreenReader = getStatusMessage({
66
+ acceptedFiles,
67
+ filesRejected,
68
+ fileList,
69
+ maxSize,
70
+ textOverrides,
71
+ });
72
+ setStatusMessage(messageForScreenReader);
73
+
63
74
  setErrors((previousErrors) => [
64
75
  ...previousErrors,
65
76
  ...filesRejected.map(({ errors }) => ({
@@ -93,10 +104,10 @@ const MultiDropzone = ({
93
104
  )}
94
105
  {...getRootProps()}
95
106
  >
96
- <input
97
- data-testid="ds-drop-input"
98
- {...getInputProps()}
99
- />
107
+ <div className="sr-only" aria-live="polite" aria-atomic="true">
108
+ {statusMessage}
109
+ </div>
110
+ <input data-testid="ds-drop-input" {...getInputProps()} />
100
111
  <UploadCloudIcon
101
112
  className={isCondensed ? styles.img : ''}
102
113
  size={isCondensed ? 24 : 64}
@@ -151,7 +162,8 @@ const MultiDropzone = ({
151
162
 
152
163
  <AnimateHeight duration={300} height={isOverMaxFiles ? 'auto' : 0}>
153
164
  <p className="tc-red-500 mt16">
154
- {textOverrides?.tooManyFilesError || `You can upload maximum ${maxFiles} files.`}
165
+ {textOverrides?.tooManyFilesError ||
166
+ `You can upload maximum ${maxFiles} files.`}
155
167
  </p>
156
168
  </AnimateHeight>
157
169
  </div>