@transferwise/components 0.0.0-experimental-e41fdbd → 0.0.0-experimental-f1f1846

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 (38) hide show
  1. package/build/main.css +209 -210
  2. package/build/styles/main.css +209 -210
  3. package/build/styles/uploadInput/UploadInput.css +13 -81
  4. package/build/styles/uploadInput/uploadButton/UploadButton.css +77 -31
  5. package/build/styles/uploadInput/uploadItem/UploadItem.css +130 -109
  6. package/build/types/uploadInput/UploadInput.d.ts.map +1 -1
  7. package/build/types/uploadInput/uploadButton/UploadButton.d.ts +5 -0
  8. package/build/types/uploadInput/uploadButton/UploadButton.d.ts.map +1 -1
  9. package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
  10. package/build/uploadInput/UploadInput.js +30 -20
  11. package/build/uploadInput/UploadInput.js.map +1 -1
  12. package/build/uploadInput/UploadInput.mjs +30 -20
  13. package/build/uploadInput/UploadInput.mjs.map +1 -1
  14. package/build/uploadInput/uploadButton/UploadButton.js +25 -32
  15. package/build/uploadInput/uploadButton/UploadButton.js.map +1 -1
  16. package/build/uploadInput/uploadButton/UploadButton.mjs +25 -32
  17. package/build/uploadInput/uploadButton/UploadButton.mjs.map +1 -1
  18. package/build/uploadInput/uploadItem/UploadItem.js +32 -38
  19. package/build/uploadInput/uploadItem/UploadItem.js.map +1 -1
  20. package/build/uploadInput/uploadItem/UploadItem.mjs +33 -39
  21. package/build/uploadInput/uploadItem/UploadItem.mjs.map +1 -1
  22. package/build/uploadInput/uploadItem/UploadItemLink.js +2 -1
  23. package/build/uploadInput/uploadItem/UploadItemLink.js.map +1 -1
  24. package/build/uploadInput/uploadItem/UploadItemLink.mjs +2 -1
  25. package/build/uploadInput/uploadItem/UploadItemLink.mjs.map +1 -1
  26. package/package.json +2 -2
  27. package/src/main.css +209 -210
  28. package/src/uploadInput/UploadInput.css +13 -81
  29. package/src/uploadInput/UploadInput.less +18 -80
  30. package/src/uploadInput/UploadInput.tests.story.tsx +5 -5
  31. package/src/uploadInput/UploadInput.tsx +43 -32
  32. package/src/uploadInput/uploadButton/UploadButton.css +77 -31
  33. package/src/uploadInput/uploadButton/UploadButton.less +77 -35
  34. package/src/uploadInput/uploadButton/UploadButton.tsx +37 -26
  35. package/src/uploadInput/uploadItem/UploadItem.css +130 -109
  36. package/src/uploadInput/uploadItem/UploadItem.less +130 -118
  37. package/src/uploadInput/uploadItem/UploadItem.tsx +26 -28
  38. package/src/uploadInput/uploadItem/UploadItemLink.tsx +3 -3
@@ -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
- }
25
-
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
- }
44
-
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;
6
+ --outerBorder: 1px solid var(--color-interactive-secondary);
7
+ border-radius: var(--radius-small);
64
8
 
65
- li {
66
- position: relative;
67
- padding-left: var(--size-16);
9
+ .np-upload-input__section {
10
+ margin: 0;
68
11
 
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):not(:has(.np-upload-input__item-button: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
+ }
@@ -25,23 +25,23 @@ const files = [
25
25
  filename: 'purchase-receipt-1.pdf',
26
26
  },
27
27
  {
28
- id: 6,
29
- filename: 'purchase-receipt-1.pdf',
28
+ id: 3,
29
+ filename: 'purchase-receipt-2.pdf',
30
30
  url: 'https://wise.com/public-resources/assets/logos/wise/brand_logo_inverse.svg',
31
31
  },
32
32
  {
33
- id: 3,
33
+ id: 4,
34
34
  filename: 'receipt failed.png',
35
35
  status: Status.FAILED,
36
36
  },
37
37
  {
38
- id: 4,
38
+ id: 5,
39
39
  filename: 'receipt failed With error string.png',
40
40
  status: Status.FAILED,
41
41
  error: 'Something went wrong',
42
42
  },
43
43
  {
44
- id: 5,
44
+ id: 6,
45
45
  filename: 'receipt failed With error object.png',
46
46
  status: Status.FAILED,
47
47
  error: { message: 'Something went wrong' },
@@ -345,39 +345,50 @@ const UploadInput = ({
345
345
  className={clsx('np-upload-input', className, { disabled })}
346
346
  {...inputAttributes}
347
347
  >
348
- {uploadedFiles.map((file, index) => (
349
- <UploadItem
350
- key={file.id}
351
- ref={(el: UploadItemRef | null) => {
352
- itemRefs.current[index] = el;
353
- }}
354
- file={file}
355
- singleFileUpload={!multiple}
356
- canDelete={
357
- (!!onDeleteFile || file.status === Status.FAILED) &&
358
- (!file.status || !PROGRESS_STATUSES.has(file.status))
359
- }
360
- onDelete={
361
- file.status === Status.FAILED
362
- ? () => removeFile(file)
363
- : () => setMarkedFileForDelete(file)
364
- }
365
- onDownload={onDownload}
366
- />
367
- ))}
348
+ <div
349
+ className="np-upload-input__section"
350
+ aria-live="polite"
351
+ aria-relevant="all"
352
+ role="region"
353
+ >
354
+ {uploadedFiles.map((file, index) => (
355
+ <UploadItem
356
+ key={file.id}
357
+ ref={(el: UploadItemRef | null) => {
358
+ itemRefs.current[index] = el;
359
+ }}
360
+ file={file}
361
+ singleFileUpload={!multiple}
362
+ canDelete={
363
+ (!!onDeleteFile || file.status === Status.FAILED) &&
364
+ (!file.status || !PROGRESS_STATUSES.has(file.status))
365
+ }
366
+ onDelete={
367
+ file.status === Status.FAILED
368
+ ? () => removeFile(file)
369
+ : () => setMarkedFileForDelete(file)
370
+ }
371
+ onDownload={onDownload}
372
+ />
373
+ ))}
374
+ </div>
368
375
  {(multiple || (!multiple && !uploadedFiles.length)) && (
369
- <UploadButton
370
- ref={uploadInputRef}
371
- id={id}
372
- uploadButtonTitle={uploadButtonTitle}
373
- disabled={areMaximumFilesUploadedAlready() || disabled}
374
- multiple={multiple}
375
- fileTypes={fileTypes}
376
- sizeLimit={sizeLimit}
377
- description={description}
378
- maxFiles={maxFiles}
379
- onChange={addFiles}
380
- />
376
+ <div className="np-upload-input__section np-upload-input__section--uploader">
377
+ <UploadButton
378
+ ref={uploadInputRef}
379
+ id={id}
380
+ uploadButtonTitle={uploadButtonTitle}
381
+ disabled={areMaximumFilesUploadedAlready() || disabled}
382
+ multiple={multiple}
383
+ fileTypes={fileTypes}
384
+ sizeLimit={sizeLimit}
385
+ description={description}
386
+ maxFiles={maxFiles}
387
+ withEntries={Boolean(uploadedFiles.length)}
388
+ onChange={addFiles}
389
+
390
+ />
391
+ </div>
381
392
  )}
382
393
  </div>
383
394
  <Modal
@@ -1,44 +1,90 @@
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);
6
24
  }
7
- .np-upload-button-container.droppable-dropping {
8
- border-color: #c9cbce !important;
9
- border-color: var(--color-interactive-secondary) !important;
25
+ .np-upload-input__upload-button .np-upload-input__icon {
26
+ padding-right: 16px;
27
+ padding-right: var(--size-16);
28
+ color: var(--color-interactive-primary);
10
29
  }
11
- .np-upload-button-container.droppable-dropping:before {
12
- z-index: 2;
30
+ .np-upload-input__upload-button.is-dropping .np-upload-input__icon,
31
+ .np-upload-input__upload-button.is-dropping .np-upload-input__item-content {
32
+ display: none;
13
33
  }
14
- .np-upload-button-container input[type="file"] {
34
+ .np-upload-input__upload-button:focus-within,
35
+ .np-upload-input__upload-button:focus-visible {
36
+ outline: var(--ring-outline-color) solid 3px;
37
+ outline-offset: -3px;
38
+ }
39
+ .np-upload-input__upload-button-input {
40
+ position: absolute;
15
41
  opacity: 0;
16
42
  z-index: -1;
17
- position: absolute;
18
43
  }
19
- .np-upload-button-container .np-upload-button {
20
- border: none;
44
+ .np-upload-input__upload-button-input:focus {
45
+ outline: none;
21
46
  }
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;
47
+ .np-upload-input__upload-button .np-upload-input__drop-file-overlay {
48
+ display: flex;
49
+ flex: 1;
50
+ padding: 13px 0 !important;
51
+ background-color: transparent;
52
+ transition: transform 0.3s ease;
53
+ position: relative;
28
54
  }
29
- label.np-upload-button:not(.disabled):hover,
30
- label.np-upload-button:not(.disabled):active {
31
- background-color: var(--color-background-screen-hover);
55
+ .np-upload-input__upload-button--with-entries {
56
+ border-top-width: 0;
32
57
  }
33
- .disabled label.np-upload-button:not(.disabled):hover,
34
- .disabled label.np-upload-button:not(.disabled):active {
35
- background-color: transparent;
58
+ .np-upload-input__upload-button--with-entries:before {
59
+ display: block;
60
+ position: absolute;
61
+ height: 1px;
62
+ background-color: rgba(0,0,0,0.10196);
63
+ background-color: var(--color-border-neutral);
64
+ content: " ";
65
+ left: 16px;
66
+ left: var(--size-16);
67
+ width: calc(100% - 2 * 16px);
68
+ width: calc(100% - 2 * var(--size-16));
69
+ top: 0;
70
+ }
71
+ .np-upload-input__upload-button--without-entries {
72
+ border-top: var(--outerBorder);
73
+ border-radius: 10px;
74
+ border-radius: var(--radius-small);
36
75
  }
37
- .np-upload-button .media {
38
- align-items: flex-start;
76
+ .np-upload-input__upload-button--enabled.is-dropping,
77
+ .np-upload-input__upload-button--enabled:hover,
78
+ .np-upload-input__upload-button--enabled:active {
79
+ background: rgba(134,167,189,0.10196);
80
+ background: var(--color-background-neutral);
81
+ }
82
+ .np-upload-input__upload-button--enabled.is-dropping:before,
83
+ .np-upload-input__upload-button--enabled:hover:before,
84
+ .np-upload-input__upload-button--enabled:active:before {
85
+ width: 100%;
86
+ left: 0;
39
87
  }
40
- @media (max-width: 320px) {
41
- .np-upload-icon {
42
- padding-left: 0;
43
- }
88
+ .np-upload-input__upload-button--disabled {
89
+ cursor: inherit;
44
90
  }
@@ -1,55 +1,97 @@
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
+ }
6
20
  }
7
21
 
8
- &.droppable-dropping {
9
- border-color: var(--color-interactive-secondary) !important;
22
+ .np-upload-input__icon {
23
+ padding-right: var(--size-16);
24
+ color: var(--color-interactive-primary);
25
+ }
10
26
 
11
- &:before {
12
- z-index: 2;
27
+ &.is-dropping {
28
+ .np-upload-input__icon,
29
+ .np-upload-input__item-content {
30
+ display: none;
13
31
  }
14
32
  }
15
33
 
16
- input[type="file"] {
34
+ &:focus-within,
35
+ &:focus-visible {
36
+ outline: var(--ring-outline-color) solid 3px;
37
+ outline-offset: -3px;
38
+ }
39
+
40
+ &-input {
41
+ position: absolute;
17
42
  opacity: 0;
18
43
  z-index: -1;
19
- position: absolute;
44
+
45
+ &:focus {
46
+ outline: none;
47
+ }
20
48
  }
21
49
 
22
- .np-upload-button {
23
- border: none;
50
+ .np-upload-input__drop-file-overlay {
51
+ display: flex;
52
+ flex: 1;
53
+ padding: 13px 0 !important;
54
+ background-color: transparent;
55
+ transition: transform 0.3s ease;
56
+ position: relative;
24
57
  }
25
58
  }
26
59
 
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
- }
60
+ // at least 1 file uploaded or 1 error visible
61
+ .np-upload-input__upload-button--with-entries {
62
+ border-top-width: 0;
45
63
 
46
- .media {
47
- align-items: flex-start;
64
+ &:before {
65
+ display: block;
66
+ position: absolute;
67
+ height: 1px;
68
+ background-color: var(--color-border-neutral);
69
+ content: " ";
70
+ left: var(--size-16);
71
+ width: calc(100% - 2 * var(--size-16));
72
+ top: 0;
48
73
  }
49
74
  }
50
75
 
51
- .np-upload-icon{
52
- @media (max-width: 320px) {
53
- padding-left: 0;
76
+ // no files uploaded (only button shown)
77
+ .np-upload-input__upload-button--without-entries {
78
+ border-top: var(--outerBorder);
79
+ border-radius: var(--radius-small);
80
+ }
81
+
82
+ .np-upload-input__upload-button--enabled {
83
+ &.is-dropping,
84
+ &:hover,
85
+ &:active {
86
+ background: var(--color-background-neutral);
87
+
88
+ &:before {
89
+ width: 100%;
90
+ left: 0;
91
+ }
54
92
  }
55
93
  }
94
+
95
+ .np-upload-input__upload-button--disabled {
96
+ cursor: inherit;
97
+ }
@@ -25,6 +25,12 @@ export type UploadButtonProps = {
25
25
  */
26
26
  disabled?: boolean;
27
27
 
28
+ /**
29
+ * Should be true, if the UploadInput has at least 1
30
+ * file (valid or invalid) listed.
31
+ */
32
+ withEntries?: boolean;
33
+
28
34
  /**
29
35
  * Allow multiple file uploads
30
36
  */
@@ -82,6 +88,7 @@ const UploadButton = forwardRef<HTMLInputElement | null, UploadButtonProps>(
82
88
  (
83
89
  {
84
90
  disabled,
91
+ withEntries,
85
92
  multiple,
86
93
  description,
87
94
  fileTypes = imageFileTypes,
@@ -188,7 +195,7 @@ const UploadButton = forwardRef<HTMLInputElement | null, UploadButtonProps>(
188
195
 
189
196
  function renderDescription() {
190
197
  return (
191
- <Body className={clsx({ 'text-primary': !disabled })}>
198
+ <Body className="np-upload-input__text">
192
199
  {getDescription()}
193
200
  {maxFiles && (
194
201
  <>
@@ -208,49 +215,53 @@ const UploadButton = forwardRef<HTMLInputElement | null, UploadButtonProps>(
208
215
  }
209
216
 
210
217
  return (
211
- <div
212
- className={clsx('np-upload-button-container', 'droppable', {
213
- 'droppable-dropping': isDropping,
214
- })}
218
+ <label
219
+ className={clsx(
220
+ 'np-upload-input__upload-button',
221
+ `np-upload-input__upload-button--${disabled ? 'disabled' : 'enabled'}`,
222
+ `np-upload-input__upload-button--${withEntries ? 'with-entries' : 'without-entries'}`,
223
+ {
224
+ 'is-dropping': isDropping,
225
+ },
226
+ )}
227
+ htmlFor={id}
215
228
  {...(!disabled && { onDragEnter, onDragLeave, onDrop, onDragOver })}
216
229
  >
230
+ <span className="np-upload-input__icon">
231
+ <UploadIcon size={24} className="text-link" />
232
+ </span>
233
+ <div className="np-upload-input__item-content" data-testid={TEST_IDS.mediaBody}>
234
+ <Body type={Typography.BODY_LARGE_BOLD} className="np-upload-input__title">
235
+ {renderButtonTitle()}
236
+ </Body>
237
+ {renderDescription()}
238
+ </div>
217
239
  <input
218
- ref={inputRef}
219
- id={id}
240
+ className="np-upload-input__upload-button-input sr-only"
220
241
  type="file"
242
+ id={id}
221
243
  {...getAcceptedTypes()}
222
244
  {...(multiple && { multiple: true })}
223
- className="tw-droppable-input"
245
+ ref={inputRef}
224
246
  disabled={disabled}
225
247
  name="file-upload"
226
248
  data-testid={TEST_IDS.uploadInput}
227
249
  onChange={filesSelected}
228
250
  />
229
- {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
230
- <label htmlFor={id} className={clsx('btn', 'np-upload-button')}>
231
- <div className="media">
232
- <div className="np-upload-icon media-middle media-left">
233
- <UploadIcon size={24} className="text-link" />
234
- </div>
235
- <div className="media-body text-xs-left" data-testid={TEST_IDS.mediaBody}>
236
- <Body type={Typography.BODY_LARGE_BOLD} className="d-block">
237
- {renderButtonTitle()}
238
- </Body>
239
- {renderDescription()}
240
- </div>
241
- </div>
242
- </label>
243
-
244
- {/* Drop area overlay */}
245
251
  {isDropping && (
246
252
  <div
247
- className={clsx('droppable-card', 'droppable-dropping-card', 'droppable-card-content')}
253
+ className={clsx(
254
+ 'np-upload-input__drop-file-overlay',
255
+ 'droppable-card',
256
+ 'droppable-dropping-card',
257
+ 'droppable-card-content',
258
+ )}
248
259
  >
249
260
  <PlusIcon className="m-x-1" size={24} />
250
261
  <div>{formatMessage(MESSAGES.dropFile)}</div>
251
262
  </div>
252
263
  )}
253
- </div>
264
+ </label>
254
265
  );
255
266
  },
256
267
  );