@transferwise/components 0.0.0-experimental-fdc11fa → 0.0.0-experimental-88ddab3

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 (67) hide show
  1. package/build/index.esm.js +658 -13
  2. package/build/index.esm.js.map +1 -1
  3. package/build/index.js +661 -13
  4. package/build/index.js.map +1 -1
  5. package/build/main.css +1 -1
  6. package/build/styles/inputs/Input.css +1 -1
  7. package/build/styles/inputs/InputGroup.css +1 -1
  8. package/build/styles/inputs/SelectInput.css +1 -0
  9. package/build/styles/inputs/TextArea.css +1 -1
  10. package/build/styles/main.css +1 -1
  11. package/build/types/common/hooks/useMedia.d.ts +2 -0
  12. package/build/types/common/hooks/useMedia.d.ts.map +1 -0
  13. package/build/types/common/hooks/useScreenSize.d.ts +3 -0
  14. package/build/types/common/hooks/useScreenSize.d.ts.map +1 -0
  15. package/build/types/common/preventScroll/PreventScroll.d.ts +2 -0
  16. package/build/types/common/preventScroll/PreventScroll.d.ts.map +1 -0
  17. package/build/types/dateLookup/dateTrigger/DateTrigger.messages.d.ts +7 -7
  18. package/build/types/dateLookup/dateTrigger/DateTrigger.messages.d.ts.map +1 -1
  19. package/build/types/index.d.ts +4 -0
  20. package/build/types/index.d.ts.map +1 -1
  21. package/build/types/inputs/Input.d.ts +1 -0
  22. package/build/types/inputs/Input.d.ts.map +1 -1
  23. package/build/types/inputs/SearchInput.d.ts +10 -0
  24. package/build/types/inputs/SearchInput.d.ts.map +1 -0
  25. package/build/types/inputs/SelectInput.d.ts +41 -0
  26. package/build/types/inputs/SelectInput.d.ts.map +1 -0
  27. package/build/types/inputs/_BottomSheet.d.ts +17 -0
  28. package/build/types/inputs/_BottomSheet.d.ts.map +1 -0
  29. package/build/types/inputs/_ButtonInput.d.ts +6 -0
  30. package/build/types/inputs/_ButtonInput.d.ts.map +1 -0
  31. package/build/types/inputs/_Popover.d.ts +18 -0
  32. package/build/types/inputs/_Popover.d.ts.map +1 -0
  33. package/build/types/inputs/_common.d.ts.map +1 -1
  34. package/build/types/utilities/wrapInFragment.d.ts +3 -0
  35. package/build/types/utilities/wrapInFragment.d.ts.map +1 -0
  36. package/package.json +13 -7
  37. package/src/common/hooks/useMedia.ts +15 -0
  38. package/src/common/hooks/useScreenSize.ts +7 -0
  39. package/src/common/preventScroll/PreventScroll.tsx +6 -0
  40. package/src/index.ts +8 -0
  41. package/src/inputs/Input.css +1 -1
  42. package/src/inputs/Input.less +14 -0
  43. package/src/inputs/Input.tsx +6 -2
  44. package/src/inputs/InputGroup.css +1 -1
  45. package/src/inputs/InputGroup.less +6 -1
  46. package/src/inputs/SearchInput.story.tsx +40 -0
  47. package/src/inputs/SearchInput.tsx +35 -0
  48. package/src/inputs/SelectInput.css +1 -0
  49. package/src/inputs/SelectInput.less +183 -0
  50. package/src/inputs/SelectInput.story.tsx +260 -0
  51. package/src/inputs/SelectInput.tsx +552 -0
  52. package/src/inputs/TextArea.css +1 -1
  53. package/src/inputs/TextArea.less +5 -0
  54. package/src/inputs/_BottomSheet.less +107 -0
  55. package/src/inputs/_BottomSheet.tsx +128 -0
  56. package/src/inputs/_ButtonInput.less +7 -0
  57. package/src/inputs/_ButtonInput.tsx +27 -0
  58. package/src/inputs/_Popover.less +38 -0
  59. package/src/inputs/_Popover.tsx +118 -0
  60. package/src/inputs/_common.less +0 -4
  61. package/src/inputs/_common.ts +0 -1
  62. package/src/main.css +1 -1
  63. package/src/main.less +4 -0
  64. package/src/select/searchBox/__snapshots__/SearchBox.spec.js.snap +1 -1
  65. package/src/ssr.spec.js +7 -0
  66. package/src/utilities/wrapInFragment.tsx +3 -0
  67. /package/src/dateLookup/dateTrigger/{DateTrigger.messages.js → DateTrigger.messages.ts} +0 -0
@@ -0,0 +1,183 @@
1
+ @import (reference) "../../node_modules/@transferwise/neptune-css/src/less/ring.less";
2
+
3
+ .np-select-input-placeholder {
4
+ overflow: hidden;
5
+ text-overflow: ellipsis;
6
+ white-space: nowrap;
7
+ color: var(--color-content-tertiary);
8
+ }
9
+
10
+ .np-select-input-options-container {
11
+ display: flex;
12
+ height: 100%;
13
+ flex-direction: column;
14
+
15
+ &:focus {
16
+ outline: none;
17
+ }
18
+
19
+ @media (--screen-sm) {
20
+ & {
21
+ max-height: 28rem /* 448px */;
22
+ }
23
+ }
24
+ }
25
+
26
+ .np-select-input-query-container {
27
+ display: flex;
28
+ flex-direction: column;
29
+ padding: var(--size-8);
30
+ padding-top: 0px;
31
+
32
+ @media (--screen-sm) {
33
+ & {
34
+ padding-top: var(--size-8);
35
+ }
36
+ }
37
+ }
38
+
39
+ .np-select-input-listbox-container {
40
+ position: relative;
41
+ height: var(--initial-height);
42
+ scroll-padding-top: var(--size-8);
43
+ scroll-padding-bottom: var(--size-8);
44
+ overflow-y: auto;
45
+
46
+ @media (--screen-sm) {
47
+ & {
48
+ height: auto;
49
+ }
50
+ }
51
+
52
+ &--has-group {
53
+ scroll-padding-top: var(--size-32);
54
+ }
55
+ }
56
+
57
+ .np-select-input-listbox {
58
+ padding: var(--size-8);
59
+
60
+ &:focus {
61
+ outline: none;
62
+ }
63
+ }
64
+
65
+ .np-select-input-separator-item {
66
+ margin: var(--size-8);
67
+ border-top-width: 1px;
68
+ }
69
+
70
+ .np-select-input-group-item {
71
+ &--without-needle:first-child {
72
+ margin-top: calc(-1 * var(--size-8));
73
+ }
74
+ }
75
+
76
+ .np-select-input-group-item-header {
77
+ position: sticky;
78
+ top: 0px;
79
+ z-index: 10;
80
+ background-color: var(--color-background-elevated);
81
+ padding: var(--size-8) var(--size-16) var(--size-4);
82
+ color: var(--color-content-secondary);
83
+ }
84
+
85
+ .np-select-input-option-container {
86
+ display: flex;
87
+ align-items: center;
88
+ column-gap: var(--size-8);
89
+ border-radius: var(--radius-small);
90
+ padding: var(--size-12) var(--size-16);
91
+ color: var(--color-content-primary);
92
+
93
+ &--active {
94
+ background-color: var(--color-background-screen-hover);
95
+ }
96
+
97
+ &--disabled {
98
+ opacity: 0.45;
99
+ }
100
+ }
101
+
102
+ .np-select-input-option-check {
103
+ &--not-selected {
104
+ visibility: hidden;
105
+ }
106
+ }
107
+
108
+ .np-select-input-option {
109
+ flex: 1;
110
+ }
111
+
112
+ .np-select-input-option-content-container {
113
+ display: flex;
114
+ align-items: center;
115
+ column-gap: var(--size-8);
116
+ color: var(--color-content-primary);
117
+ }
118
+
119
+ .np-select-input-option-content-icon {
120
+ display: flex;
121
+
122
+ &--not-compact {
123
+ align-self: flex-start;
124
+ }
125
+ }
126
+
127
+ .np-select-input-option-content-text {
128
+ display: flex;
129
+ flex: 1;
130
+ flex-direction: column;
131
+ overflow: hidden;
132
+ }
133
+
134
+ .np-select-input-option-content-text-secondary {
135
+ color: var(--color-content-secondary);
136
+ }
137
+
138
+ .np-select-input-option-content-text-compact {
139
+ overflow: hidden;
140
+ text-overflow: ellipsis;
141
+ white-space: nowrap;
142
+ }
143
+
144
+ .np-select-input-option-content-text-line-1 {
145
+ > :not([hidden]) ~ :not([hidden]) {
146
+ margin-right: var(--size-8);
147
+ margin-left: var(--size-8);
148
+ }
149
+ }
150
+
151
+ .np-select-input-addon-container {
152
+ pointer-events: none;
153
+ display: inline-flex;
154
+ align-items: center;
155
+ }
156
+
157
+ .np-select-input-addon {
158
+ border-width: 0;
159
+ background: none;
160
+
161
+ display: inline-flex;
162
+ height: var(--size-32);
163
+ width: var(--size-32);
164
+ align-items: center;
165
+ justify-content: center;
166
+ border-radius: 0.125rem /* 2px */; /* TODO: Tokenize */
167
+
168
+ &--interactive {
169
+ pointer-events: auto;
170
+ color: var(--color-interactive-secondary);
171
+
172
+ &:hover {
173
+ color: var(--color-interactive-secondary-hover);
174
+ }
175
+
176
+ .focus-ring();
177
+ }
178
+ }
179
+
180
+ .np-select-input-addon-separator {
181
+ height: var(--size-24);
182
+ border-inline-start-width: 1px;
183
+ }
@@ -0,0 +1,260 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import { Calendar } from '@transferwise/icons';
3
+ import { Flag } from '@wise/art';
4
+ import { useState } from 'react';
5
+
6
+ import { getMonthNames } from '../common/dateUtils';
7
+
8
+ import { SelectInput, type SelectInputItem, SelectInputOptionContent } from './SelectInput';
9
+
10
+ export default {
11
+ component: SelectInput,
12
+ } satisfies Meta<typeof SelectInput>;
13
+
14
+ interface TestMonth {
15
+ id: number;
16
+ name: string;
17
+ unavailable: boolean;
18
+ }
19
+
20
+ const testMonths: TestMonth[] = getMonthNames('en-US').map((name, index) => ({
21
+ id: index + 1,
22
+ name,
23
+ unavailable: index % 6 === 2,
24
+ }));
25
+
26
+ const testQuarters = [
27
+ testMonths.slice(0, 3),
28
+ testMonths.slice(3, 6),
29
+ testMonths.slice(6, 9),
30
+ testMonths.slice(9, 12),
31
+ ] as const;
32
+
33
+ export const Basic: StoryObj<{
34
+ filterable: boolean;
35
+ filterPlaceholder: string;
36
+ clearable: boolean;
37
+ invalid: boolean;
38
+ disabled: boolean;
39
+ onChange: (value: TestMonth | null) => void;
40
+ onClear: () => void;
41
+ }> = {
42
+ render: function Story({
43
+ filterable,
44
+ filterPlaceholder,
45
+ clearable,
46
+ disabled,
47
+ onChange,
48
+ onClear,
49
+ }) {
50
+ const [selectedMonth, setSelectedMonth] = useState<TestMonth | null>(null);
51
+
52
+ return (
53
+ <div className="d-flex flex-column">
54
+ {/* TODO:
55
+ <Field
56
+ label="Label"
57
+ hint="Information message."
58
+ error={invalid ? "Error message." : undefined}
59
+ >
60
+ */}
61
+ <SelectInput
62
+ placeholder="Month"
63
+ items={testQuarters
64
+ .flatMap<SelectInputItem<TestMonth>>((quarterMonths, index) => [
65
+ {
66
+ type: 'group',
67
+ label: `Quarter #${index + 1}`,
68
+ options: quarterMonths.map((month) => ({
69
+ type: 'option',
70
+ value: month,
71
+ filterMatchers: [month.name],
72
+ disabled: month.unavailable,
73
+ })),
74
+ },
75
+ { type: 'separator' },
76
+ ])
77
+ .slice(0, -1)}
78
+ value={selectedMonth}
79
+ renderValue={(month, compact) => (
80
+ <SelectInputOptionContent
81
+ title={month.name}
82
+ note="Note"
83
+ description={compact ? undefined : `Month #${month.id}`}
84
+ icon={<Calendar size={24} />}
85
+ />
86
+ )}
87
+ filterable={filterable}
88
+ filterPlaceholder={filterPlaceholder}
89
+ disabled={disabled}
90
+ onChange={(month) => {
91
+ setSelectedMonth(month);
92
+ onChange(month);
93
+ }}
94
+ onClear={
95
+ clearable
96
+ ? () => {
97
+ setSelectedMonth(null);
98
+ onClear();
99
+ }
100
+ : undefined
101
+ }
102
+ />
103
+ </div>
104
+ );
105
+ },
106
+ args: {
107
+ filterable: true,
108
+ filterPlaceholder: 'Type a month’s name',
109
+ clearable: true,
110
+ invalid: false,
111
+ disabled: false,
112
+ },
113
+ argTypes: {
114
+ onChange: {
115
+ action: 'changed',
116
+ },
117
+ onClear: {
118
+ action: 'cleared',
119
+ },
120
+ },
121
+ };
122
+
123
+ interface Month {
124
+ id: number;
125
+ name: string;
126
+ }
127
+
128
+ const months: Month[] = getMonthNames('en-US').map((name, index) => ({
129
+ id: index + 1,
130
+ name,
131
+ }));
132
+
133
+ export const Months: StoryObj<{
134
+ onChange: (value: Month | null) => void;
135
+ onClear: () => void;
136
+ }> = {
137
+ render: function Story({ onChange, onClear }) {
138
+ const [selectedMonth, setSelectedMonth] = useState<Month | null>(null);
139
+
140
+ return (
141
+ <div className="d-flex flex-column">
142
+ <SelectInput
143
+ placeholder="Month"
144
+ items={months.map((month) => ({ type: 'option', value: month }))}
145
+ value={selectedMonth}
146
+ renderValue={(month) => <SelectInputOptionContent title={month.name} />}
147
+ onChange={(month) => {
148
+ setSelectedMonth(month);
149
+ onChange(month);
150
+ }}
151
+ onClear={() => {
152
+ setSelectedMonth(null);
153
+ onClear();
154
+ }}
155
+ />
156
+ </div>
157
+ );
158
+ },
159
+ argTypes: {
160
+ onChange: {
161
+ action: 'changed',
162
+ },
163
+ onClear: {
164
+ action: 'cleared',
165
+ },
166
+ },
167
+ };
168
+
169
+ interface Currency {
170
+ code: string;
171
+ name: string;
172
+ countries?: string[];
173
+ }
174
+
175
+ const popularCurrencies: Currency[] = [
176
+ {
177
+ code: 'USD',
178
+ name: 'United States dollar',
179
+ countries: ['Hong Kong', 'Saudi Arabia'],
180
+ },
181
+ {
182
+ code: 'EUR',
183
+
184
+ name: 'Euro',
185
+ countries: ['Spain', 'Germany', 'France', 'Austria', 'Estonia'],
186
+ },
187
+ {
188
+ code: 'GBP',
189
+ name: 'British pound',
190
+ countries: ['England', 'Scotland', 'Wales'],
191
+ },
192
+ ];
193
+
194
+ const otherCurrencies: Currency[] = [
195
+ {
196
+ code: 'CAD',
197
+ name: 'Canadian dollar',
198
+ countries: ['Canada'],
199
+ },
200
+ {
201
+ code: 'AUD',
202
+ name: 'Australian dollar',
203
+ },
204
+ ];
205
+
206
+ const allCurrencies: Currency[] = [...popularCurrencies, ...otherCurrencies].sort((a, b) =>
207
+ a.code.localeCompare(b.code),
208
+ );
209
+
210
+ export const Currencies: StoryObj<{
211
+ onChange: (value: Currency) => void;
212
+ }> = {
213
+ render: function Story({ onChange }) {
214
+ const [selectedCurrency, setSelectedCurrency] = useState<Currency>(popularCurrencies[0]);
215
+
216
+ return (
217
+ <div className="d-flex flex-column">
218
+ <SelectInput
219
+ items={[
220
+ {
221
+ type: 'group',
222
+ label: 'Popular currencies',
223
+ options: popularCurrencies.map((currency) => ({
224
+ type: 'option',
225
+ value: currency,
226
+ })),
227
+ },
228
+ {
229
+ type: 'group',
230
+ label: 'All currencies',
231
+ options: allCurrencies.map((currency) => ({
232
+ type: 'option',
233
+ value: currency,
234
+ })),
235
+ },
236
+ ]}
237
+ value={selectedCurrency}
238
+ renderValue={(currency) => (
239
+ <SelectInputOptionContent
240
+ title={currency.code}
241
+ note={currency.name}
242
+ icon={<Flag code={currency.code} intrinsicSize={24} />}
243
+ />
244
+ )}
245
+ filterable
246
+ filterPlaceholder="Type a currency / country"
247
+ onChange={(currency) => {
248
+ setSelectedCurrency(currency);
249
+ onChange(currency);
250
+ }}
251
+ />
252
+ </div>
253
+ );
254
+ },
255
+ argTypes: {
256
+ onChange: {
257
+ action: 'changed',
258
+ },
259
+ },
260
+ };