@transferwise/components 0.0.0-experimental-47816e0 → 0.0.0-experimental-13147b1

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 +6 -1
  8. package/build/types/uploadInput/uploadButton/UploadButton.d.ts.map +1 -1
  9. package/build/uploadInput/UploadInput.js +26 -16
  10. package/build/uploadInput/UploadInput.js.map +1 -1
  11. package/build/uploadInput/UploadInput.mjs +26 -16
  12. package/build/uploadInput/UploadInput.mjs.map +1 -1
  13. package/build/uploadInput/uploadButton/UploadButton.js +24 -31
  14. package/build/uploadInput/uploadButton/UploadButton.js.map +1 -1
  15. package/build/uploadInput/uploadButton/UploadButton.mjs +24 -31
  16. package/build/uploadInput/uploadButton/UploadButton.mjs.map +1 -1
  17. package/build/uploadInput/uploadItem/UploadItem.js +30 -37
  18. package/build/uploadInput/uploadItem/UploadItem.js.map +1 -1
  19. package/build/uploadInput/uploadItem/UploadItem.mjs +31 -38
  20. package/build/uploadInput/uploadItem/UploadItem.mjs.map +1 -1
  21. package/build/uploadInput/uploadItem/UploadItemLink.js +2 -1
  22. package/build/uploadInput/uploadItem/UploadItemLink.js.map +1 -1
  23. package/build/uploadInput/uploadItem/UploadItemLink.mjs +2 -1
  24. package/build/uploadInput/uploadItem/UploadItemLink.mjs.map +1 -1
  25. package/package.json +3 -3
  26. package/src/main.css +210 -210
  27. package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.story.tsx +8 -3
  28. package/src/uploadInput/UploadInput.css +13 -81
  29. package/src/uploadInput/UploadInput.less +17 -79
  30. package/src/uploadInput/UploadInput.tests.story.tsx +8 -3
  31. package/src/uploadInput/UploadInput.tsx +38 -28
  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 +36 -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 +27 -27
  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
- }
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' },
@@ -307,35 +307,45 @@ const UploadInput = ({
307
307
  className={clsx('np-upload-input', className, { disabled })}
308
308
  {...inputAttributes}
309
309
  >
310
- {uploadedFiles.map((file) => (
311
- <UploadItem
312
- key={file.id}
313
- file={file}
314
- singleFileUpload={!multiple}
315
- canDelete={
316
- (!!onDeleteFile || file.status === Status.FAILED) &&
317
- (!file.status || !PROGRESS_STATUSES.has(file.status))
318
- }
319
- onDelete={
320
- file.status === Status.FAILED
321
- ? () => removeFile(file)
322
- : () => setMarkedFileForDelete(file)
323
- }
324
- onDownload={onDownload}
325
- />
326
- ))}
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>
327
334
  {(multiple || (!multiple && !uploadedFiles.length)) && (
328
- <UploadButton
329
- id={id}
330
- uploadButtonTitle={uploadButtonTitle}
331
- disabled={areMaximumFilesUploadedAlready() || disabled}
332
- multiple={multiple}
333
- fileTypes={fileTypes}
334
- sizeLimit={sizeLimit}
335
- description={description}
336
- maxFiles={maxFiles}
337
- onChange={addFiles}
338
- />
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>
339
349
  )}
340
350
  </div>
341
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
+ }
@@ -17,6 +17,12 @@ export type UploadButtonProps = {
17
17
  */
18
18
  disabled?: boolean;
19
19
 
20
+ /**
21
+ * Should be true, if the UploadInput has at least 1
22
+ * file (valid or invalid) listed.
23
+ */
24
+ withEntries?: boolean;
25
+
20
26
  /**
21
27
  * Allow multiple file uploads
22
28
  */
@@ -72,6 +78,7 @@ const onDragOver = (event: DragEvent): void => {
72
78
  const DEFAULT_FILE_INPUT_ID = 'np-upload-button';
73
79
  const UploadButton = ({
74
80
  disabled,
81
+ withEntries,
75
82
  multiple,
76
83
  description,
77
84
  fileTypes = imageFileTypes,
@@ -169,7 +176,7 @@ const UploadButton = ({
169
176
 
170
177
  function renderDescription() {
171
178
  return (
172
- <Body className={clsx({ 'text-primary': !disabled })}>
179
+ <Body className="np-upload-input__text">
173
180
  {getDescription()}
174
181
  {maxFiles && (
175
182
  <>
@@ -189,49 +196,53 @@ const UploadButton = ({
189
196
  }
190
197
 
191
198
  return (
192
- <div
193
- className={clsx('np-upload-button-container', 'droppable', {
194
- 'droppable-dropping': isDropping,
195
- })}
199
+ <label
200
+ className={clsx(
201
+ 'np-upload-input__upload-button',
202
+ `np-upload-input__upload-button--${disabled ? 'disabled' : 'enabled'}`,
203
+ `np-upload-input__upload-button--${withEntries ? 'with' : 'without'}-entries`,
204
+ {
205
+ 'is-dropping': isDropping,
206
+ },
207
+ )}
208
+ htmlFor={id}
196
209
  {...(!disabled && { onDragEnter, onDragLeave, onDrop, onDragOver })}
197
210
  >
211
+ <span className="np-upload-input__icon">
212
+ <UploadIcon size={24} className="text-link" />
213
+ </span>
214
+ <div className="np-upload-input__item-content" data-testid={TEST_IDS.mediaBody}>
215
+ <Body type={Typography.BODY_LARGE_BOLD} className="np-upload-input__title">
216
+ {renderButtonTitle()}
217
+ </Body>
218
+ {renderDescription()}
219
+ </div>
198
220
  <input
199
221
  ref={inputReference}
200
- id={id}
222
+ className="np-upload-input__upload-button-input sr-only"
201
223
  type="file"
224
+ id={id}
202
225
  {...getAcceptedTypes()}
203
226
  {...(multiple && { multiple: true })}
204
- className="tw-droppable-input"
205
227
  disabled={disabled}
206
228
  name="file-upload"
207
229
  data-testid={TEST_IDS.uploadInput}
208
230
  onChange={filesSelected}
209
231
  />
210
- {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
211
- <label htmlFor={id} className={clsx('btn', 'np-upload-button')}>
212
- <div className="media">
213
- <div className="np-upload-icon media-middle media-left">
214
- <UploadIcon size={24} className="text-link" />
215
- </div>
216
- <div className="media-body text-xs-left" data-testid={TEST_IDS.mediaBody}>
217
- <Body type={Typography.BODY_LARGE_BOLD} className="d-block">
218
- {renderButtonTitle()}
219
- </Body>
220
- {renderDescription()}
221
- </div>
222
- </div>
223
- </label>
224
-
225
- {/* Drop area overlay */}
226
232
  {isDropping && (
227
233
  <div
228
- className={clsx('droppable-card', 'droppable-dropping-card', 'droppable-card-content')}
234
+ className={clsx(
235
+ 'np-upload-input__drop-file-overlay',
236
+ 'droppable-card',
237
+ 'droppable-dropping-card',
238
+ 'droppable-card-content',
239
+ )}
229
240
  >
230
241
  <PlusIcon className="m-x-1" size={24} />
231
242
  <div>{formatMessage(MESSAGES.dropFile)}</div>
232
243
  </div>
233
244
  )}
234
- </div>
245
+ </label>
235
246
  );
236
247
  };
237
248