@transferwise/components 0.0.0-experimental-1ce161b → 0.0.0-experimental-1fdcf13

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 +210 -210
  2. package/build/styles/main.css +210 -210
  3. package/build/styles/uploadInput/UploadInput.css +13 -81
  4. package/build/styles/uploadInput/uploadButton/UploadButton.css +78 -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.map +1 -1
  8. package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
  9. package/build/types/uploadInput/uploadItem/UploadItemLink.d.ts.map +1 -1
  10. package/build/uploadInput/UploadInput.js +28 -20
  11. package/build/uploadInput/UploadInput.js.map +1 -1
  12. package/build/uploadInput/UploadInput.mjs +28 -20
  13. package/build/uploadInput/UploadInput.mjs.map +1 -1
  14. package/build/uploadInput/uploadButton/UploadButton.js +23 -31
  15. package/build/uploadInput/uploadButton/UploadButton.js.map +1 -1
  16. package/build/uploadInput/uploadButton/UploadButton.mjs +23 -31
  17. package/build/uploadInput/uploadButton/UploadButton.mjs.map +1 -1
  18. package/build/uploadInput/uploadItem/UploadItem.js +33 -36
  19. package/build/uploadInput/uploadItem/UploadItem.js.map +1 -1
  20. package/build/uploadInput/uploadItem/UploadItem.mjs +33 -36
  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 +3 -3
  27. package/src/main.css +210 -210
  28. package/src/uploadInput/UploadInput.css +13 -81
  29. package/src/uploadInput/UploadInput.less +17 -79
  30. package/src/uploadInput/UploadInput.tests.story.tsx +7 -2
  31. package/src/uploadInput/UploadInput.tsx +40 -32
  32. package/src/uploadInput/uploadButton/UploadButton.css +78 -31
  33. package/src/uploadInput/uploadButton/UploadButton.less +78 -35
  34. package/src/uploadInput/uploadButton/UploadButton.tsx +28 -25
  35. package/src/uploadInput/uploadItem/UploadItem.css +130 -109
  36. package/src/uploadInput/uploadItem/UploadItem.less +129 -118
  37. package/src/uploadInput/uploadItem/UploadItem.tsx +24 -25
  38. package/src/uploadInput/uploadItem/UploadItemLink.tsx +10 -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
- }
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
  }
@@ -31,17 +31,22 @@ const files = [
31
31
  },
32
32
  {
33
33
  id: 3,
34
+ filename: 'purchase-receipt-2.pdf',
35
+ url: 'https://wise.com/public-resources/assets/logos/wise/brand_logo_inverse.svg',
36
+ },
37
+ {
38
+ id: 4,
34
39
  filename: 'receipt failed.png',
35
40
  status: Status.FAILED,
36
41
  },
37
42
  {
38
- id: 4,
43
+ id: 5,
39
44
  filename: 'receipt failed With error string.png',
40
45
  status: Status.FAILED,
41
46
  error: 'Something went wrong',
42
47
  },
43
48
  {
44
- id: 5,
49
+ id: 6,
45
50
  filename: 'receipt failed With error object.png',
46
51
  status: Status.FAILED,
47
52
  error: { message: 'Something went wrong' },
@@ -345,39 +345,47 @@ 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
+ id={id}
379
+ uploadButtonTitle={uploadButtonTitle}
380
+ disabled={areMaximumFilesUploadedAlready() || disabled}
381
+ multiple={multiple}
382
+ fileTypes={fileTypes}
383
+ sizeLimit={sizeLimit}
384
+ description={description}
385
+ maxFiles={maxFiles}
386
+ onChange={addFiles}
387
+ />
388
+ </div>
381
389
  )}
382
390
  </div>
383
391
  <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
+ }
@@ -188,7 +188,7 @@ const UploadButton = forwardRef<HTMLInputElement | null, UploadButtonProps>(
188
188
 
189
189
  function renderDescription() {
190
190
  return (
191
- <Body className={clsx({ 'text-primary': !disabled })}>
191
+ <Body className="np-upload-input__text">
192
192
  {getDescription()}
193
193
  {maxFiles && (
194
194
  <>
@@ -208,49 +208,52 @@ const UploadButton = forwardRef<HTMLInputElement | null, UploadButtonProps>(
208
208
  }
209
209
 
210
210
  return (
211
- <div
212
- className={clsx('np-upload-button-container', 'droppable', {
213
- 'droppable-dropping': isDropping,
214
- })}
211
+ <label
212
+ className={clsx(
213
+ 'np-upload-input__upload-button',
214
+ `np-upload-input__upload-button--${disabled ? 'disabled' : 'enabled'}`,
215
+ {
216
+ 'is-dropping': isDropping,
217
+ },
218
+ )}
219
+ htmlFor={id}
215
220
  {...(!disabled && { onDragEnter, onDragLeave, onDrop, onDragOver })}
216
221
  >
222
+ <span className="np-upload-input__icon">
223
+ <UploadIcon size={24} className="text-link" />
224
+ </span>
225
+ <div className="np-upload-input__item-content" data-testid={TEST_IDS.mediaBody}>
226
+ <Body type={Typography.BODY_LARGE_BOLD} className="np-upload-input__title">
227
+ {renderButtonTitle()}
228
+ </Body>
229
+ {renderDescription()}
230
+ </div>
217
231
  <input
218
232
  ref={inputRef}
219
- id={id}
233
+ className="np-upload-input__upload-button-input sr-only"
220
234
  type="file"
235
+ id={id}
221
236
  {...getAcceptedTypes()}
222
237
  {...(multiple && { multiple: true })}
223
- className="tw-droppable-input"
224
238
  disabled={disabled}
225
239
  name="file-upload"
226
240
  data-testid={TEST_IDS.uploadInput}
227
241
  onChange={filesSelected}
228
242
  />
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
243
  {isDropping && (
246
244
  <div
247
- className={clsx('droppable-card', 'droppable-dropping-card', 'droppable-card-content')}
245
+ className={clsx(
246
+ 'np-upload-input__drop-file-overlay',
247
+ 'droppable-card',
248
+ 'droppable-dropping-card',
249
+ 'droppable-card-content',
250
+ )}
248
251
  >
249
252
  <PlusIcon className="m-x-1" size={24} />
250
253
  <div>{formatMessage(MESSAGES.dropFile)}</div>
251
254
  </div>
252
255
  )}
253
- </div>
256
+ </label>
254
257
  );
255
258
  },
256
259
  );