@transferwise/components 0.0.0-experimental-438bbba → 0.0.0-experimental-cf33ac7

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 (61) hide show
  1. package/build/card/Card.js.map +1 -1
  2. package/build/card/Card.mjs.map +1 -1
  3. package/build/circularButton/CircularButton.js.map +1 -1
  4. package/build/circularButton/CircularButton.mjs.map +1 -1
  5. package/build/common/locale/index.js.map +1 -1
  6. package/build/common/locale/index.mjs.map +1 -1
  7. package/build/dateLookup/tableLink/TableLink.js.map +1 -1
  8. package/build/dateLookup/tableLink/TableLink.mjs.map +1 -1
  9. package/build/instructionsList/InstructionsList.js.map +1 -1
  10. package/build/instructionsList/InstructionsList.mjs.map +1 -1
  11. package/build/main.css +210 -210
  12. package/build/styles/main.css +210 -210
  13. package/build/styles/uploadInput/UploadInput.css +13 -81
  14. package/build/styles/uploadInput/uploadButton/UploadButton.css +78 -31
  15. package/build/styles/uploadInput/uploadItem/UploadItem.css +130 -109
  16. package/build/types/card/Card.d.ts.map +1 -1
  17. package/build/types/circularButton/CircularButton.d.ts.map +1 -1
  18. package/build/types/instructionsList/InstructionsList.d.ts.map +1 -1
  19. package/build/types/uploadInput/UploadInput.d.ts.map +1 -1
  20. package/build/types/uploadInput/uploadButton/UploadButton.d.ts +6 -1
  21. package/build/types/uploadInput/uploadButton/UploadButton.d.ts.map +1 -1
  22. package/build/types/uploadInput/uploadItem/UploadItem.d.ts +1 -5
  23. package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
  24. package/build/types/uploadInput/uploadItem/UploadItemLink.d.ts +5 -5
  25. package/build/types/uploadInput/uploadItem/UploadItemLink.d.ts.map +1 -1
  26. package/build/uploadInput/UploadInput.js +28 -38
  27. package/build/uploadInput/UploadInput.js.map +1 -1
  28. package/build/uploadInput/UploadInput.mjs +29 -39
  29. package/build/uploadInput/UploadInput.mjs.map +1 -1
  30. package/build/uploadInput/uploadButton/UploadButton.js +31 -38
  31. package/build/uploadInput/uploadButton/UploadButton.js.map +1 -1
  32. package/build/uploadInput/uploadButton/UploadButton.mjs +32 -39
  33. package/build/uploadInput/uploadButton/UploadButton.mjs.map +1 -1
  34. package/build/uploadInput/uploadItem/UploadItem.js +33 -56
  35. package/build/uploadInput/uploadItem/UploadItem.js.map +1 -1
  36. package/build/uploadInput/uploadItem/UploadItem.mjs +34 -57
  37. package/build/uploadInput/uploadItem/UploadItem.mjs.map +1 -1
  38. package/build/uploadInput/uploadItem/UploadItemLink.js +5 -7
  39. package/build/uploadInput/uploadItem/UploadItemLink.js.map +1 -1
  40. package/build/uploadInput/uploadItem/UploadItemLink.mjs +5 -7
  41. package/build/uploadInput/uploadItem/UploadItemLink.mjs.map +1 -1
  42. package/package.json +3 -3
  43. package/src/card/Card.spec.tsx +5 -4
  44. package/src/card/Card.story.tsx +6 -4
  45. package/src/card/Card.tsx +2 -3
  46. package/src/circularButton/CircularButton.tsx +1 -1
  47. package/src/common/locale/index.ts +1 -1
  48. package/src/dateLookup/tableLink/TableLink.tsx +15 -15
  49. package/src/instructionsList/InstructionsList.tsx +4 -1
  50. package/src/main.css +210 -210
  51. package/src/uploadInput/UploadInput.css +13 -81
  52. package/src/uploadInput/UploadInput.less +17 -79
  53. package/src/uploadInput/UploadInput.tests.story.tsx +8 -3
  54. package/src/uploadInput/UploadInput.tsx +41 -68
  55. package/src/uploadInput/uploadButton/UploadButton.css +78 -31
  56. package/src/uploadInput/uploadButton/UploadButton.less +78 -35
  57. package/src/uploadInput/uploadButton/UploadButton.tsx +153 -147
  58. package/src/uploadInput/uploadItem/UploadItem.css +130 -109
  59. package/src/uploadInput/uploadItem/UploadItem.less +129 -118
  60. package/src/uploadInput/uploadItem/UploadItem.tsx +123 -146
  61. package/src/uploadInput/uploadItem/UploadItemLink.tsx +25 -23
@@ -3,90 +3,28 @@
3
3
  @import (reference) './uploadItem/UploadItem.less';
4
4
 
5
5
  .np-upload-input {
6
- &.form-control {
7
- height: auto;
8
- padding: initial;
9
- }
10
-
11
- & > div {
12
- &:first-child,
13
- &:first-child .np-upload-item--single-file,
14
- &:first-child .np-upload-item--link {
15
- border-top-left-radius: var(--radius-small);
16
- border-top-right-radius: var(--radius-small);
17
- }
18
-
19
- &:last-child {
20
- border-bottom-left-radius: var(--radius-small);
21
- border-bottom-right-radius: var(--radius-small);
22
- }
23
- }
24
- }
6
+ --outerBorder: 1px solid var(--color-interactive-secondary);
7
+ border-radius: var(--radius-small);
25
8
 
26
- .np-theme-personal {
27
- .np-upload-input {
28
- &.disabled .btn {
29
- cursor: inherit;
30
- }
31
-
32
- .np-upload-icon {
33
- color: var(--color-interactive-primary);
34
- }
35
-
36
- .media-body {
37
- padding-right: var(--size-32);
38
- color: var(--color-content-link);
39
- white-space: break-spaces;
40
-
41
- @media (--screen-400-zoom) {
42
- padding-right: var(--size-64);
43
- }
9
+ .np-upload-input__section {
10
+ margin: 0;
44
11
 
45
- .np-text-body-large-bold {
46
- text-decoration: underline;
47
- .link-underline();
48
- }
49
-
50
- .np-text-body-default,
51
- .np-upload-description,
52
- .text-positive {
53
- color: var(--color-content-secondary) !important;
54
- }
55
-
56
- .text-negative {
57
- color: var(--color-sentiment-negative) !important;
58
- }
59
- }
60
-
61
- &-errors {
62
- list-style: none;
63
- padding-left: 0;
64
-
65
- li {
66
- position: relative;
67
- padding-left: var(--size-16);
68
-
69
- @media (--screen-400-zoom) {
70
- padding-left: var(--size-32);
71
- }
72
-
73
- &:before {
74
- content: '•';
75
- position: absolute;
76
- display: block;
77
- left: 0;
78
- }
12
+ // only 1 file allowed and already uploaded (no UploadButton shown)
13
+ &:only-child {
14
+ .np-upload-input__item {
15
+ border-radius: var(--radius-small);
16
+ border-bottom: var(--outerBorder);
79
17
  }
80
18
  }
81
19
 
82
- .status-circle {
83
- width: var(--size-x-small);
84
- height: var(--size-x-small);
85
-
86
- @media (--screen-400-zoom) {
87
- width: var(--size-large);
88
- height: var(--size-large);
89
- }
20
+ // if the last item in the file list is interactive (not an error)
21
+ // and the user is hovering over it, then we need to adjust the
22
+ // bottom separator, which happens to be in .np-upload-input__section--uploader
23
+ &:has(.np-upload-input__item:last-child.is-interactive:hover)
24
+ + .np-upload-input__section--uploader .np-upload-input__upload-button::before {
25
+ left: 0;
26
+ width: 100%;
27
+ top: 0;
90
28
  }
91
29
  }
92
30
  }
@@ -26,18 +26,23 @@ const files = [
26
26
  url: 'https://wise.com/public-resources/assets/logos/wise/brand_logo_inverse.svg',
27
27
  },
28
28
  {
29
- id: 2,
29
+ id: 3,
30
+ filename: 'purchase-receipt-2.pdf',
31
+ url: 'https://wise.com/public-resources/assets/logos/wise/brand_logo_inverse.svg',
32
+ },
33
+ {
34
+ id: 4,
30
35
  filename: 'receipt failed.png',
31
36
  status: Status.FAILED,
32
37
  },
33
38
  {
34
- id: 3,
39
+ id: 5,
35
40
  filename: 'receipt failed With error string.png',
36
41
  status: Status.FAILED,
37
42
  error: 'Something went wrong',
38
43
  },
39
44
  {
40
- id: 4,
45
+ id: 6,
41
46
  filename: 'receipt failed With error object.png',
42
47
  status: Status.FAILED,
43
48
  error: { message: 'Something went wrong' },
@@ -1,5 +1,5 @@
1
1
  import { clsx } from 'clsx';
2
- import { useEffect, useRef, useState, useCallback } from 'react';
2
+ import { useEffect, useRef, useState } from 'react';
3
3
  import { useIntl } from 'react-intl';
4
4
 
5
5
  import Button from '../button';
@@ -101,10 +101,6 @@ export type UploadInputProps = {
101
101
  Pick<UploadItemProps, 'onDownload'> &
102
102
  CommonProps;
103
103
 
104
- interface UploadItemRef extends HTMLDivElement {
105
- focus: () => void;
106
- }
107
-
108
104
  function generateFileId(file: File) {
109
105
  const { name, size } = file;
110
106
  const uploadTimeStamp = new Date().getTime();
@@ -137,8 +133,6 @@ const UploadInput = ({
137
133
  const [markedFileForDelete, setMarkedFileForDelete] = useState<UploadedFile | null>(null);
138
134
  const [mounted, setMounted] = useState(false);
139
135
  const { formatMessage } = useIntl();
140
- const itemRefs = useRef<(HTMLDivElement | null)[]>([]);
141
- const uploadInputRef = useRef<HTMLInputElement | null>(null);
142
136
 
143
137
  const PROGRESS_STATUSES = new Set([Status.PENDING, Status.PROCESSING]);
144
138
 
@@ -146,9 +140,7 @@ const UploadInput = ({
146
140
  multiple || files.length === 0 ? files : [files[0]],
147
141
  );
148
142
 
149
- const uploadedFilesListReference = useRef([
150
- ...(multiple || files.length === 0 ? files : [files[0]]),
151
- ]);
143
+ const uploadedFilesListReference = useRef(multiple || files.length === 0 ? files : [files[0]]);
152
144
 
153
145
  function addFileToList(recentUploadedFile: UploadedFile) {
154
146
  function addToList(listToAddTo: readonly UploadedFile[]) {
@@ -182,46 +174,21 @@ const UploadInput = ({
182
174
  uploadedFilesListReference.current = updateListItem(uploadedFilesListReference.current);
183
175
  };
184
176
 
185
- const manageFocusAfterRemoval = useCallback(
186
- (file: UploadedFile) => {
187
- const index = uploadedFiles.findIndex((f) => f.id === file.id);
188
- const nextFocusIndex =
189
- uploadedFiles.length > 1
190
- ? index === uploadedFiles.length - 1
191
- ? index - 1
192
- : index + 1
193
- : -1;
194
-
195
- if (nextFocusIndex >= 0 && itemRefs.current[nextFocusIndex]) {
196
- itemRefs.current[nextFocusIndex].focus();
197
- } else if (uploadInputRef.current) {
198
- uploadInputRef.current.focus();
199
- }
200
- },
201
- [uploadedFiles, itemRefs, uploadInputRef],
202
- );
203
-
204
177
  const removeFile = (file: UploadedFile) => {
205
178
  const { id, status } = file;
206
179
 
207
180
  if (status === Status.FAILED) {
208
181
  // If removing a failed upload, we're just updating the view
209
182
  removeFileFromList(file);
210
- manageFocusAfterRemoval(file);
211
183
  } else if (onDeleteFile && id) {
212
184
  // Set status to PROCESSING
213
185
  modifyFileInList(file, { status: Status.PROCESSING, error: undefined });
214
186
 
215
187
  // Notify host app about deletion
216
188
  onDeleteFile(id)
217
- .then(() => {
218
- removeFileFromList(file);
219
- })
189
+ .then(() => removeFileFromList(file))
220
190
  .catch((error) => {
221
191
  modifyFileInList(file, { error: error as UploadError });
222
- })
223
- .finally(() => {
224
- manageFocusAfterRemoval(file);
225
192
  });
226
193
  }
227
194
  };
@@ -340,39 +307,45 @@ const UploadInput = ({
340
307
  className={clsx('np-upload-input', className, { disabled })}
341
308
  {...inputAttributes}
342
309
  >
343
- {uploadedFiles.map((file, index) => (
344
- <UploadItem
345
- key={file.id}
346
- ref={(el: UploadItemRef | null) => {
347
- itemRefs.current[index] = el;
348
- }}
349
- file={file}
350
- singleFileUpload={!multiple}
351
- canDelete={
352
- (!!onDeleteFile || file.status === Status.FAILED) &&
353
- (!file.status || !PROGRESS_STATUSES.has(file.status))
354
- }
355
- onDelete={
356
- file.status === Status.FAILED
357
- ? () => removeFile(file)
358
- : () => setMarkedFileForDelete(file)
359
- }
360
- onDownload={onDownload}
361
- />
362
- ))}
310
+ <div
311
+ className="np-upload-input__section"
312
+ aria-live="polite"
313
+ aria-relevant="all"
314
+ role="region"
315
+ >
316
+ {uploadedFiles.map((file) => (
317
+ <UploadItem
318
+ key={file.id}
319
+ file={file}
320
+ singleFileUpload={!multiple}
321
+ canDelete={
322
+ (!!onDeleteFile || file.status === Status.FAILED) &&
323
+ (!file.status || !PROGRESS_STATUSES.has(file.status))
324
+ }
325
+ onDelete={
326
+ file.status === Status.FAILED
327
+ ? () => removeFile(file)
328
+ : () => setMarkedFileForDelete(file)
329
+ }
330
+ onDownload={onDownload}
331
+ />
332
+ ))}
333
+ </div>
363
334
  {(multiple || (!multiple && !uploadedFiles.length)) && (
364
- <UploadButton
365
- ref={uploadInputRef}
366
- id={id}
367
- uploadButtonTitle={uploadButtonTitle}
368
- disabled={areMaximumFilesUploadedAlready() || disabled}
369
- multiple={multiple}
370
- fileTypes={fileTypes}
371
- sizeLimit={sizeLimit}
372
- description={description}
373
- maxFiles={maxFiles}
374
- onChange={addFiles}
375
- />
335
+ <div className="np-upload-input__section np-upload-input__section--uploader">
336
+ <UploadButton
337
+ id={id}
338
+ uploadButtonTitle={uploadButtonTitle}
339
+ disabled={areMaximumFilesUploadedAlready() || disabled}
340
+ multiple={multiple}
341
+ fileTypes={fileTypes}
342
+ sizeLimit={sizeLimit}
343
+ description={description}
344
+ maxFiles={maxFiles}
345
+ withEntries={Boolean(uploadedFiles.length)}
346
+ onChange={addFiles}
347
+ />
348
+ </div>
376
349
  )}
377
350
  </div>
378
351
  <Modal
@@ -1,44 +1,91 @@
1
- .np-upload-button-container {
2
- border-style: solid;
3
- }
4
- .np-upload-button-container .droppable-card-content {
1
+ .np-upload-input__upload-button {
2
+ position: relative;
3
+ padding: 16px;
4
+ padding: var(--size-16);
5
5
  display: flex;
6
+ align-items: center;
7
+ margin: 0;
8
+ border-bottom-left-radius: 10px;
9
+ border-bottom-left-radius: var(--radius-small);
10
+ border-bottom-right-radius: 10px;
11
+ border-bottom-right-radius: var(--radius-small);
12
+ border: var(--outerBorder);
13
+ cursor: pointer;
14
+ }
15
+ .np-upload-input__upload-button .np-upload-input__title {
16
+ color: var(--color-content-link);
17
+ -webkit-text-decoration: underline;
18
+ text-decoration: underline;
19
+ text-underline-offset: 0.3em;
20
+ }
21
+ .np-upload-input__upload-button .np-upload-input__title + .np-upload-input__text {
22
+ margin-top: 4px;
23
+ margin-top: var(--size-4);
24
+ line-height: 22px;
6
25
  }
7
- .np-upload-button-container.droppable-dropping {
8
- border-color: #c9cbce !important;
9
- border-color: var(--color-interactive-secondary) !important;
26
+ .np-upload-input__upload-button .np-upload-input__icon {
27
+ padding-right: 16px;
28
+ padding-right: var(--size-16);
29
+ color: var(--color-interactive-primary);
10
30
  }
11
- .np-upload-button-container.droppable-dropping:before {
12
- z-index: 2;
31
+ .np-upload-input__upload-button.is-dropping .np-upload-input__icon,
32
+ .np-upload-input__upload-button.is-dropping .np-upload-input__item-content {
33
+ display: none;
13
34
  }
14
- .np-upload-button-container input[type="file"] {
35
+ .np-upload-input__upload-button:focus-within,
36
+ .np-upload-input__upload-button:focus-visible {
37
+ outline: var(--ring-outline-color) solid 3px;
38
+ outline-offset: -3px;
39
+ }
40
+ .np-upload-input__upload-button-input {
41
+ position: absolute;
15
42
  opacity: 0;
16
43
  z-index: -1;
17
- position: absolute;
18
44
  }
19
- .np-upload-button-container .np-upload-button {
20
- border: none;
45
+ .np-upload-input__upload-button-input:focus {
46
+ outline: none;
21
47
  }
22
- .np-upload-button {
23
- width: 100%;
24
- border-top: 1px solid transparent;
25
- padding: 16px;
26
- padding: var(--padding-small);
27
- border-radius: 0;
48
+ .np-upload-input__upload-button .np-upload-input__drop-file-overlay {
49
+ display: flex;
50
+ flex: 1;
51
+ padding: 13px 0 !important;
52
+ background-color: transparent;
53
+ transition: transform 0.3s ease;
54
+ position: relative;
28
55
  }
29
- label.np-upload-button:not(.disabled):hover,
30
- label.np-upload-button:not(.disabled):active {
31
- background-color: var(--color-background-screen-hover);
56
+ .np-upload-input__upload-button--with-entries {
57
+ border-top-width: 0;
32
58
  }
33
- .disabled label.np-upload-button:not(.disabled):hover,
34
- .disabled label.np-upload-button:not(.disabled):active {
35
- background-color: transparent;
59
+ .np-upload-input__upload-button--with-entries:before {
60
+ display: block;
61
+ position: absolute;
62
+ height: 1px;
63
+ background-color: rgba(0,0,0,0.10196);
64
+ background-color: var(--color-border-neutral);
65
+ content: " ";
66
+ left: 16px;
67
+ left: var(--size-16);
68
+ width: calc(100% - 2 * 16px);
69
+ width: calc(100% - 2 * var(--size-16));
70
+ top: 0;
71
+ }
72
+ .np-upload-input__upload-button--without-entries {
73
+ border-top: var(--outerBorder);
74
+ border-radius: 10px;
75
+ border-radius: var(--radius-small);
36
76
  }
37
- .np-upload-button .media {
38
- align-items: flex-start;
77
+ .np-upload-input__upload-button--enabled.is-dropping,
78
+ .np-upload-input__upload-button--enabled:hover,
79
+ .np-upload-input__upload-button--enabled:active {
80
+ background: rgba(134,167,189,0.10196);
81
+ background: var(--color-background-neutral);
82
+ }
83
+ .np-upload-input__upload-button--enabled.is-dropping:before,
84
+ .np-upload-input__upload-button--enabled:hover:before,
85
+ .np-upload-input__upload-button--enabled:active:before {
86
+ width: 100%;
87
+ left: 0;
39
88
  }
40
- @media (max-width: 320px) {
41
- .np-upload-icon {
42
- padding-left: 0;
43
- }
89
+ .np-upload-input__upload-button--disabled {
90
+ cursor: inherit;
44
91
  }
@@ -1,55 +1,98 @@
1
- .np-upload-button-container {
2
- border-style: solid;
1
+ .np-upload-input__upload-button {
2
+ position: relative;
3
+ padding: var(--size-16);
4
+ display: flex;
5
+ align-items: center;
6
+ margin: 0;
7
+ border-bottom-left-radius: var(--radius-small);
8
+ border-bottom-right-radius: var(--radius-small);
9
+ border: var(--outerBorder);
10
+ cursor: pointer;
3
11
 
4
- .droppable-card-content {
5
- display: flex;
12
+ .np-upload-input__title {
13
+ color: var(--color-content-link);
14
+ text-decoration: underline;
15
+ text-underline-offset: 0.3em;
16
+
17
+ & + .np-upload-input__text {
18
+ margin-top: var(--size-4);
19
+ line-height: 22px;
20
+ }
6
21
  }
7
22
 
8
- &.droppable-dropping {
9
- border-color: var(--color-interactive-secondary) !important;
23
+ .np-upload-input__icon {
24
+ padding-right: var(--size-16);
25
+ color: var(--color-interactive-primary);
26
+ }
10
27
 
11
- &:before {
12
- z-index: 2;
28
+ &.is-dropping {
29
+ .np-upload-input__icon,
30
+ .np-upload-input__item-content {
31
+ display: none;
13
32
  }
14
33
  }
15
34
 
16
- input[type="file"] {
35
+ &:focus-within,
36
+ &:focus-visible {
37
+ outline: var(--ring-outline-color) solid 3px;
38
+ outline-offset: -3px;
39
+ }
40
+
41
+ &-input {
42
+ position: absolute;
17
43
  opacity: 0;
18
44
  z-index: -1;
19
- position: absolute;
45
+
46
+ &:focus {
47
+ outline: none;
48
+ }
20
49
  }
21
50
 
22
- .np-upload-button {
23
- border: none;
51
+ .np-upload-input__drop-file-overlay {
52
+ display: flex;
53
+ flex: 1;
54
+ padding: 13px 0 !important;
55
+ background-color: transparent;
56
+ transition: transform 0.3s ease;
57
+ position: relative;
24
58
  }
25
59
  }
26
60
 
27
- .np-upload-button {
28
- width: 100%;
29
- border-top: 1px solid transparent;
30
- padding: var(--padding-small);
31
- border-radius: 0;
32
-
33
- label& {
34
- &:not(.disabled) {
35
- &:hover,
36
- &:active {
37
- background-color: var(--color-background-screen-hover);
38
-
39
- .disabled & {
40
- background-color: transparent;
41
- }
42
- }
43
- }
44
- }
61
+ // at least 1 file uploaded or 1 error visible
62
+ .np-upload-input__upload-button--with-entries {
63
+ border-top-width: 0;
45
64
 
46
- .media {
47
- align-items: flex-start;
65
+ &:before {
66
+ display: block;
67
+ position: absolute;
68
+ height: 1px;
69
+ background-color: var(--color-border-neutral);
70
+ content: " ";
71
+ left: var(--size-16);
72
+ width: calc(100% - 2 * var(--size-16));
73
+ top: 0;
48
74
  }
49
75
  }
50
76
 
51
- .np-upload-icon{
52
- @media (max-width: 320px) {
53
- padding-left: 0;
77
+ // no files uploaded (only button shown)
78
+ .np-upload-input__upload-button--without-entries {
79
+ border-top: var(--outerBorder);
80
+ border-radius: var(--radius-small);
81
+ }
82
+
83
+ .np-upload-input__upload-button--enabled {
84
+ &.is-dropping,
85
+ &:hover,
86
+ &:active {
87
+ background: var(--color-background-neutral);
88
+
89
+ &:before {
90
+ width: 100%;
91
+ left: 0;
92
+ }
54
93
  }
55
94
  }
95
+
96
+ .np-upload-input__upload-button--disabled {
97
+ cursor: inherit;
98
+ }