@commercetools-uikit/localized-money-input 0.0.0-canary-2021830134526

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 commercetools GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,314 @@
1
+ # LocalizedMoneyInput
2
+
3
+ ## Description
4
+
5
+ A controlled input component for localized money values with validation
6
+ states.
7
+
8
+ ## Usage
9
+
10
+ ```js
11
+ import LocalizedMoneyInput from '@commercetools-uikit/localized-money-input';
12
+
13
+ <LocalizedMoneyInput
14
+ value={{
15
+ USD: { currencyCode: 'USD', amount: '12.22' },
16
+ EUR: { currencyCode: 'EUR', amount: '41.44' },
17
+ }}
18
+ onChange={(event) => alert(event.target.name, event.target.value)}
19
+ />;
20
+ ```
21
+
22
+ ## Properties
23
+
24
+ | Props | Type | Required | Values | Default | Description |
25
+ | ------------------------------- | ---------------- | :------: | ----------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
26
+ | `id` | `string` | - | - | - | Used as prefix of HTML `id` property. Each input field id will have the currency as a suffix (`${idPrefix}.${lang}`), e.g. `foo.en` |
27
+ | `name` | `string` | - | - | - | Used as HTML `name` property for each input field. Each input field name will have the currency as a suffix (`${namePrefix}.${lang}`), e.g. `foo.en` |
28
+ | `value` | `object` | ✅ | - | - | Values to use. Keyed by currency, the values are the money values, e.g. `{ USD: {currencyCode: 'USD', amount: '12.22'}, EUR: {currencyCode: 'EUR', amount: '41.44'} }` |
29
+ | `onChange` | `function` | ✅ | - | - | Gets called when any input is changed. Is called with the change event of the changed input. |
30
+ | `selectedCurrency` | `string` | ✅ | - | - | Specifies which currency will be shown in case the `LocalizedMoneyInput` is collapsed. |
31
+ | `onBlur` | `function` | - | - | - | Called when any field is blurred. Is called with the `event` of that field. |
32
+ | `onFocus` | `function` | - | - | - | Called when any field is focussed. Is called with the `event` of that field. |
33
+ | `hideCurrencyExpansionControls` | `bool` | - | - | `false` | Will hide the currency toggle controls when set to `true`. It always shows all currencies instead. |
34
+ | `defaultExpandCurrencies` | `bool` | - | - | `false` | Controls whether one or all currencies are visible by default. Pass `true` to show all currencies by default. |
35
+ | `isDisabled` | `bool` | - | - | `false` | Disables all input fields. |
36
+ | `isReadOnly` | `bool` | - | - | `false` | Makes all input fields readonly. |
37
+ | `placeholder` | `object` | - | - | - | Placeholders for each currency. Object of the same shape as `value`. |
38
+ | `horizontalConstraint` | `object` | - | `m`, `l`, `xl`, `scale` | `scale` | Horizontal size limit of the input fields. |
39
+ | `hasError` | `bool` | - | - | - | Will apply the error state to each input without showing any error message. |
40
+ | `hasWarning` | `bool` | - | - | - | Indicates the input field has a warning |
41
+ | `errors` | `objectOf(node)` | - | - | - | Used to show errors underneath the inputs of specific currencies. Pass an object whose key is a currency and whose value is the error to show for that key. |
42
+ | `warnings` | `objectOf(node)` | - | - | - | Used to show warnings underneath the inputs of specific currencies. Pass an object whose key is a currency and whose value is the warning to show for that key. |
43
+
44
+ The component forwards all `data` attribute props. It further adds a `-${currency}` suffix to the values of the `data-test` and `data-track-component` attributes, e.g `data-test="foo"` will get added to the input for `en` as `data-test="foo-en"`.
45
+
46
+ Main Functions and use cases are:
47
+
48
+ - Receiving localized input from user
49
+
50
+ ## Static Properties
51
+
52
+ ### `LocalizedMoneyInput.convertToMoneyValue`
53
+
54
+ The `convertToMoneyValue` function will turn a LocalizedMoneyInput value into a [`MoneyValue`](https://docs.commercetools.com/http-api-types#money) the API can handle. It automatically converts to `centPrecision` or `highPrecision` types when the number of supplied fraction digits exceeds the number of fraction digits used by the currency.
55
+ If you want to forbid `highPrecision`, then the form's validation needs to add an error when it sees a `highPrecision` price. See example below.
56
+
57
+ Here are examples of `centPrecision` and `highPrecision` prices.
58
+
59
+ ```js
60
+ // 42.00 €
61
+ {
62
+ "type": "centPrecision",
63
+ "currencyCode": "EUR",
64
+ "centAmount": 4200,
65
+ "fractionDigits": 2
66
+ }
67
+ ```
68
+
69
+ ```js
70
+ // 0.0123456 €
71
+ {
72
+ "type": "highPrecision",
73
+ "currencyCode": "EUR",
74
+ "centAmount": 1,
75
+ "preciseAmount": 123456,
76
+ "fractionDigits": 7
77
+ }
78
+ ```
79
+
80
+ ### `LocalizedMoneyInput.parseMoneyValue`
81
+
82
+ The `parseMoneyValue` function will turn a [`MoneyValue`](https://docs.commercetools.com/http-api-types#money) into a value the LocalizedMoneyInput component can handle `({ amount, currencyCode })`.
83
+
84
+ ### `LocalizedMoneyInput.getEmptyCurrencies`
85
+
86
+ Returns array of the empty currencies
87
+
88
+ ```js
89
+ LocalizedMoneyInput.getEmptyCurrencies({});
90
+ // -> []
91
+ ```
92
+
93
+ ```js
94
+ LocalizedMoneyInput.getEmptyCurrencies({
95
+ USD: { currencyCode: 'USD', amount: '' },
96
+ EUR: { currencyCode: 'EUR', amount: '' },
97
+ });
98
+ // -> ['USD', 'EUR']
99
+ ```
100
+
101
+ ```js
102
+ LocalizedMoneyInput.getEmptyCurrencies({
103
+ USD: { currencyCode: 'USD', amount: '12.43' },
104
+ EUR: { currencyCode: 'EUR', amount: '' },
105
+ });
106
+ // -> ['EUR']
107
+ ```
108
+
109
+ ### `LocalizedMoneyInput.getHighPrecisionCurrencies`
110
+
111
+ Returns array of the currencies that have high precision amount
112
+
113
+ ```js
114
+ LocalizedMoneyInput.getHighPrecisionCurrencies({});
115
+ // -> []
116
+ ```
117
+
118
+ ```js
119
+ LocalizedMoneyInput.getHighPrecisionCurrencies({
120
+ USD: {
121
+ currencyCode: 'USD',
122
+ amount: '12.2221',
123
+ },
124
+ EUR: {
125
+ currencyCode: 'EUR',
126
+ amount: '9.9999',
127
+ },
128
+ });
129
+ // -> ['USD', 'EUR']
130
+ ```
131
+
132
+ ```js
133
+ LocalizedMoneyInput.getHighPrecisionCurrencies({
134
+ USD: {
135
+ currencyCode: 'USD',
136
+ amount: '12.43',
137
+ },
138
+ EUR: {
139
+ currencyCode: 'EUR',
140
+ amount: '0.00001',
141
+ },
142
+ });
143
+ // -> ['EUR']
144
+ ```
145
+
146
+ ### `LocalizedMoneyInput.convertToMoneyValues`
147
+
148
+ The `convertToMoneyValues` function will turn a LocalizedMoneyInput value into array of [`MoneyValue`](https://docs.commercetools.com/http-api-types#money) the API can handle. It automatically converts to `centPrecision` or `highPrecision` types when the number of supplied fraction digits exceeds the number of fraction digits used by the currency.
149
+ If you want to forbid `highPrecision`, then the form's validation needs to add an error when it sees a `highPrecision` price. See example below.
150
+
151
+ Here are examples of `centPrecision` and `highPrecision` prices.
152
+
153
+ ```js
154
+ // 42.00 €
155
+ [
156
+ {
157
+ type: 'centPrecision',
158
+ currencyCode: 'EUR',
159
+ centAmount: 4200,
160
+ fractionDigits: 2,
161
+ },
162
+ ];
163
+ ```
164
+
165
+ ```js
166
+ // 0.0123456 €
167
+ [
168
+ {
169
+ type: 'highPrecision',
170
+ currencyCode: 'EUR',
171
+ centAmount: 1,
172
+ preciseAmount: 123456,
173
+ fractionDigits: 7,
174
+ },
175
+ ];
176
+ ```
177
+
178
+ ### `LocalizedMoneyInput.parseMoneyValues`
179
+
180
+ The `parseMoneyValues` function will turn a [`MoneyValue`](https://docs.commercetools.com/http-api-types#money) into a value the LocalizedMoneyInput component can handle `({ [currencyCode]: {currencyCode, amount} })`.
181
+
182
+ ### `LocalizedMoneyInput.getEmptyCurrencies`
183
+
184
+ The `getEmptyCurrencies` function will return array of currencies that don't have amount .
185
+
186
+ ```js
187
+ LocalizedMoneyInput.getEmptyCurrencies({
188
+ EUR: { currencyCode: 'EUR', amount: '' },
189
+ USD: { currencyCode: 'USD', amount: '12.77' },
190
+ }); // -> ['EUR']
191
+
192
+ LocalizedMoneyInput.getEmptyCurrencies({
193
+ EUR: { currencyCode: 'EUR', amount: '12.77' },
194
+ }); // -> []
195
+ ```
196
+
197
+ ## Example
198
+
199
+ Here's an example of how `LocalizedMoneyInput` would be used inside a form.
200
+
201
+ ```jsx
202
+ import { IntlProvider } from 'react-intl';
203
+ import { Formik } from 'formik';
204
+ import omitEmpty from 'omit-empty-es';
205
+ import { ErrorMessage } from '@commercetools-uikit/messages';
206
+ import LocalizedMoneyInput from '@commercetools-uikit/localized-money-input';
207
+
208
+ // the existing document, e.g. from the database
209
+ const doc = {
210
+ prices: [
211
+ {
212
+ currencyCode: 'EUR',
213
+ centAmount: 1200,
214
+ },
215
+ {
216
+ currencyCode: 'AMD',
217
+ centAmount: 3300,
218
+ },
219
+ ],
220
+ };
221
+
222
+ // A function to convert a document to form values.
223
+ const docToFormValues = (doc) => ({
224
+ // The parseMoneyValue function will turn a MoneyValue into a
225
+ // value the LocalizedMoneyInput component can handle ({currency: amount})
226
+ prices: LocalizedMoneyInput.parseMoneyValues(doc.prices),
227
+ });
228
+
229
+ // a function to convert form values back to a document
230
+ const formValuesToDoc = (formValues) => ({
231
+ // The convertToMoneyValue function will convert a LocalizedMoneyInput
232
+ // value into a value the API can handle
233
+ // It automatically converts to centPrecision or highPrecision
234
+ // depending on the number of supplied fraction digits and the
235
+ // used currency code.
236
+ // If you want to forbid highPrecision, then the form's validation
237
+ // needs to add an error when it sees a highPrecision price.
238
+ // See example below
239
+ prices: LocalizedMoneyInput.convertToMoneyValues(formValues.prices),
240
+ });
241
+
242
+ const validate = (formValues) => {
243
+ const errors = { prices: {} };
244
+ Object.keys(formValues.prices).forEach((currency) => {
245
+ errors.prices[currency] = {};
246
+ });
247
+ const emptyCurrencies = LocalizedMoneyInput.getEmptyCurrencies(
248
+ formValues.prices
249
+ );
250
+ // ['EUR', 'USD']
251
+ // This form doesn't accept high precision prices
252
+ const highPrecisionCurrencies =
253
+ LocalizedMoneyInput.getHighPrecisionCurrencies(formValues.prices);
254
+
255
+ // ['CAD', 'USD']
256
+ emptyCurrencies.forEach((emptyCurrency) => {
257
+ errors.prices[emptyCurrency].missing = true;
258
+ });
259
+ highPrecisionCurrencies.forEach((highPrecisionCurrency) => {
260
+ errors.prices[highPrecisionCurrency].highPrecision = true;
261
+ });
262
+
263
+ return omitEmpty(errors);
264
+ };
265
+ const initialValues = docToFormValues(doc);
266
+ const renderErrors = (errors) => {
267
+ Object.keys(errors.prices).reduce((currency) => {
268
+ const error =
269
+ (errors.prices[currency] && errors.prices[currency].missing && (
270
+ <ErrorMessage>This field is required!</ErrorMessage>
271
+ )) ||
272
+ (errors.prices[currency] && errors.prices[currency].highPrecision && (
273
+ <ErrorMessage>High precision prices not supported here!</ErrorMessage>
274
+ ));
275
+ return {
276
+ [currency]: touched.prices[currency] && error,
277
+ };
278
+ });
279
+ };
280
+
281
+ return (
282
+ <Formik
283
+ initialValues={initialValues}
284
+ validate={validate}
285
+ onSubmit={(formValues) => {
286
+ // doc will contain "prices" holding a MoneyValue,
287
+ // ready to be consumed by the API
288
+ const nextDoc = formValuesToDoc(formValues);
289
+ console.log(nextDoc);
290
+ }}
291
+ render={({
292
+ values,
293
+ errors,
294
+ handleChange,
295
+ handleBlur,
296
+ handleSubmit,
297
+ isSubmitting,
298
+ }) => (
299
+ <form onSubmit={handleSubmit}>
300
+ <LocalizedMoneyInput
301
+ value={values.prices}
302
+ onChange={handleChange}
303
+ onBlur={handleBlur}
304
+ isDisabled={isSubmitting}
305
+ errors={renderErrors(errors)}
306
+ horizontalConstraint={10}
307
+ />
308
+
309
+ <button type="submit">Submit</button>
310
+ </form>
311
+ )}
312
+ />
313
+ );
314
+ ```