@canutin/svelte-currency-input 0.3.1 → 0.5.0

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/README.md CHANGED
@@ -58,15 +58,17 @@ This is more or less what `<CurrencyInput />` looks like under the hood:
58
58
 
59
59
  ## API
60
60
 
61
- | Option | Type | Default | Description |
62
- | ----------------- | --------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
63
- | value | `number` | `undefined` | Initial value. If left `undefined` a formatted value of `0` is visible as a placeholder |
64
- | locale | `string` | `en-US` | Overrides default locale. [Examples](https://gist.github.com/ncreated/9934896) |
65
- | currency | `string` | `USD` | Overrides default currency. [Examples](https://github.com/datasets/currency-codes/blob/master/data/codes-all.csv) |
66
- | name | `string` | `total` | Applies the name to the [input fields](#how-it-works) for _unformatted_ (e.g `[name=total]`) and _formatted_ (e.g. `[name=formatted-total]`) values |
67
- | required | `boolean` | `false` | Marks the inputs as required |
68
- | disabled | `boolean` | `false` | Marks the inputs as disabled |
69
- | isNegativeAllowed | `boolean` | `true` | If `false`, forces formatting only to positive values and ignores `--positive` and `--negative` styling modifiers |
61
+ | Option | Type | Default | Description |
62
+ | ----------------- | --------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
63
+ | value | `number` | `undefined` | Initial value. If left `undefined` a formatted value of `0` is visible as a placeholder |
64
+ | locale | `string` | `en-US` | Overrides default locale. [Examples](https://gist.github.com/ncreated/9934896) |
65
+ | currency | `string` | `USD` | Overrides default currency. [Examples](https://github.com/datasets/currency-codes/blob/master/data/codes-all.csv) |
66
+ | name | `string` | `total` | Applies the name to the [input fields](#how-it-works) for _unformatted_ (e.g `[name=total]`) and _formatted_ (e.g. `[name=formatted-total]`) values |
67
+ | required | `boolean` | `false` | Marks the inputs as required |
68
+ | disabled | `boolean` | `false` | Marks the inputs as disabled |
69
+ | placeholder | `number` `null` | `0` | Overrides the default placeholder. Setting the value to a `number` will display it as formatted. Setting it to `null` will not show a placeholder |
70
+ | isNegativeAllowed | `boolean` | `true` | If `false`, forces formatting only to positive values and ignores `--positive` and `--negative` styling modifiers |
71
+ | fractionDigits | `number` | `2` | Sets `maximumFractionDigits` in [`Intl.NumberFormat()` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#minimumfractiondigits) used for formatting the currency. Supported digits: `0` to `20` |
70
72
 
71
73
  ## Styling
72
74
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canutin/svelte-currency-input",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "exports": {
5
5
  ".": "./index.js"
6
6
  },
@@ -3,6 +3,7 @@
3
3
  const DEFAULT_CURRENCY = 'USD';
4
4
  const DEFAULT_NAME = 'total';
5
5
  const DEFAULT_VALUE = 0;
6
+ const DEFAULT_FRACTION_DIGITS = 2;
6
7
 
7
8
  export let value: number = DEFAULT_VALUE;
8
9
  export let locale: string = DEFAULT_LOCALE;
@@ -10,7 +11,9 @@
10
11
  export let name: string = DEFAULT_NAME;
11
12
  export let required: boolean = false;
12
13
  export let disabled: boolean = false;
14
+ export let placeholder: number | null = DEFAULT_VALUE;
13
15
  export let isNegativeAllowed: boolean = true;
16
+ export let fractionDigits: number = DEFAULT_FRACTION_DIGITS;
14
17
 
15
18
  // Formats value as: e.g. $1,523.00 | -$1,523.00
16
19
  const formatCurrency = (
@@ -36,16 +39,18 @@
36
39
  if (!isDeletion && !isModifier && !isArrowKey && isInvalidCharacter) event.preventDefault();
37
40
  };
38
41
 
42
+ const currencyDecimal = new Intl.NumberFormat(locale).format(1.1).charAt(1); // '.' or ','
43
+ const isDecimalComma = currencyDecimal === ','; // Remove currency formatting from `formattedValue` so we can assign it to `value`
44
+ const currencySymbol = formatCurrency(0, 0)
45
+ .replace('0', '') // e.g. '$0' > '$'
46
+ .replace(/\u00A0/, ''); // e.g '0 €' > '€'
47
+
39
48
  // Updates `value` by stripping away the currency formatting
40
49
  const setUnformattedValue = (event: KeyboardEvent) => {
41
50
  // Don't format if the user is typing a `currencyDecimal` point
42
- const currencyDecimal = new Intl.NumberFormat(locale).format(1.1).charAt(1); // '.' or ','
43
51
  if (event.key === currencyDecimal) return;
44
52
 
45
- // If `formattedValue` is ['$', '-$', "-"] we don't need to continue
46
- const currencySymbol = formatCurrency(0, 0)
47
- .replace('0', '') // e.g. '$0' > '$'
48
- .replace(/\u00A0/, ''); // e.g '0 €' > '€'
53
+ // Don't format if `formattedValue` is ['$', '-$', "-"]
49
54
  const ignoreSymbols = [currencySymbol, `-${currencySymbol}`, '-'];
50
55
  const strippedUnformattedValue = formattedValue.replace(' ', '');
51
56
  if (ignoreSymbols.includes(strippedUnformattedValue)) return;
@@ -63,7 +68,6 @@
63
68
  value = 0;
64
69
  } else {
65
70
  // The order of the following operations is *critical*
66
- const isDecimalComma = currencyDecimal === ','; // Remove currency formatting from `formattedValue` so we can assign it to `value`
67
71
  unformattedValue = unformattedValue.replace(isDecimalComma ? /\./g : /\,/g, ''); // Remove all group symbols
68
72
  if (isDecimalComma) unformattedValue = unformattedValue.replace(',', '.'); // If the decimal point is a comma, replace it with a period
69
73
  value = parseFloat(unformattedValue);
@@ -71,15 +75,15 @@
71
75
  };
72
76
 
73
77
  const setFormattedValue = () => {
74
- formattedValue = isZero ? '' : formatCurrency(value, 2, 0);
78
+ formattedValue = isZero ? '' : formatCurrency(value, fractionDigits, 0);
75
79
  };
76
80
 
77
81
  let formattedValue = '';
82
+ let formattedPlaceholder =
83
+ placeholder !== null ? formatCurrency(placeholder, fractionDigits, fractionDigits) : '';
78
84
  $: isZero = value === 0;
79
85
  $: isNegative = value < 0;
80
86
  $: value, setFormattedValue();
81
-
82
- const placeholder = formatCurrency(DEFAULT_VALUE, 2, 2); // e.g. '$0.00'
83
87
  </script>
84
88
 
85
89
  <div class="currencyInput">
@@ -95,7 +99,7 @@
95
99
  inputmode="numeric"
96
100
  name={`formatted-${name}`}
97
101
  required={required && !isZero}
98
- {placeholder}
102
+ placeholder={formattedPlaceholder}
99
103
  {disabled}
100
104
  bind:value={formattedValue}
101
105
  on:keydown={handleKeyDown}
@@ -18,13 +18,19 @@
18
18
  <div class="demoForm__container">
19
19
  <CurrencyInput name="total" value={-42069.69} />
20
20
  <CurrencyInput name="rent" />
21
- <CurrencyInput name="cashflow" value={5678.9} />
22
- <CurrencyInput name="balance" value={1234.56} isNegativeAllowed={false} />
21
+ <CurrencyInput name="balance" value={1234.56} isNegativeAllowed={false} placeholder={null} />
22
+ <CurrencyInput name="btc" value={0.87654321} fractionDigits={8} />
23
23
 
24
24
  <CurrencyInput name="amount" value={5678.9} {locale} {currency} />
25
- <CurrencyInput name="deficit" value={1234.56} isNegativeAllowed={false} {locale} {currency} />
26
25
  <CurrencyInput name="loss" value={97532.95} disabled={true} {locale} {currency} />
27
26
  <CurrencyInput name="cost" value={-42069.69} {locale} {currency} />
27
+ <CurrencyInput
28
+ name="deficit"
29
+ placeholder={1234.56}
30
+ isNegativeAllowed={false}
31
+ {locale}
32
+ {currency}
33
+ />
28
34
  </div>
29
35
 
30
36
  <nav class="demoForm__output">
@@ -1,4 +1,9 @@
1
- import { expect, test } from '@playwright/test';
1
+ import { expect, test, type Page } from '@playwright/test';
2
+
3
+ const isMacOs = process.platform === 'darwin';
4
+ const selectAll = async (page: Page) => {
5
+ isMacOs ? await page.keyboard.press('Meta+A') : await page.keyboard.press('Control+A');
6
+ };
2
7
 
3
8
  test.describe('CurrencyInput', () => {
4
9
  test('Default behavior is correct', async ({ page }) => {
@@ -71,16 +76,16 @@ test.describe('CurrencyInput', () => {
71
76
  'formatted-total': '-$42,069.69',
72
77
  rent: '0',
73
78
  'formatted-rent': '',
74
- cashflow: '5678.9',
75
- 'formatted-cashflow': '$5,678.9',
76
79
  balance: '1234.56',
77
80
  'formatted-balance': '$1,234.56',
81
+ btc: '0.87654321',
82
+ 'formatted-btc': '$0.87654321',
78
83
  amount: '5678.9',
79
84
  'formatted-amount': '€ 5.678,9',
80
- deficit: '1234.56',
81
- 'formatted-deficit': '€ 1.234,56',
82
85
  cost: '-42069.69',
83
- 'formatted-cost': '€ -42.069,69'
86
+ 'formatted-cost': '€ -42.069,69',
87
+ deficit: '0',
88
+ 'formatted-deficit': ''
84
89
  },
85
90
  null,
86
91
  2
@@ -139,7 +144,6 @@ test.describe('CurrencyInput', () => {
139
144
  test("Incorrect characters can't be entered", async ({ page }) => {
140
145
  await page.goto('/');
141
146
 
142
- const isMacOs = process.platform === 'darwin';
143
147
  const rentUnformattedInput = page.locator('.currencyInput__unformatted[name=rent]');
144
148
  const rentFormattedInput = page.locator('.currencyInput__formatted[name="formatted-rent"]');
145
149
 
@@ -162,10 +166,8 @@ test.describe('CurrencyInput', () => {
162
166
  await expect(rentFormattedInput).toHaveValue('$420.69');
163
167
  await expect(rentUnformattedInput).toHaveValue('420.69');
164
168
 
165
- // Select all
166
- isMacOs ? await page.keyboard.press('Meta+A') : await page.keyboard.press('Control+A');
167
-
168
169
  // Check "Backspace" works
170
+ await selectAll(page);
169
171
  await page.keyboard.press('Backspace');
170
172
  await expect(rentUnformattedInput).toHaveValue('0');
171
173
  await expect(rentFormattedInput).toHaveValue('');
@@ -175,15 +177,54 @@ test.describe('CurrencyInput', () => {
175
177
  await expect(rentFormattedInput).toHaveValue('-$420.69');
176
178
  await expect(rentUnformattedInput).toHaveValue('-420.69');
177
179
 
178
- // Select all
179
- isMacOs ? await page.keyboard.press('Meta+A') : await page.keyboard.press('Control+A');
180
-
181
180
  // Check "Delete" also works
181
+ await selectAll(page);
182
182
  await page.keyboard.press('Delete');
183
183
  await expect(rentUnformattedInput).toHaveValue('0');
184
184
  await expect(rentFormattedInput).toHaveValue('');
185
185
  });
186
186
 
187
+ test('Placeholders can be overriden', async ({ page }) => {
188
+ await page.goto('/');
189
+
190
+ // Default placeholder
191
+ const rentFormattedInput = page.locator('.currencyInput__formatted[name="formatted-rent"]');
192
+ await expect(rentFormattedInput).toHaveAttribute('placeholder', '$0.00');
193
+
194
+ // Null placeholder
195
+ const balanceFormattedInput = page.locator(
196
+ '.currencyInput__formatted[name="formatted-balance"]'
197
+ );
198
+ await expect(balanceFormattedInput).toHaveAttribute('placeholder', '');
199
+
200
+ // Overriden placeholder
201
+ const deficitFormattedInput = page.locator(
202
+ '.currencyInput__formatted[name="formatted-deficit"]'
203
+ );
204
+ await expect(deficitFormattedInput).toHaveAttribute('placeholder', '€ 1.234,56'); // The space is `%A0`, not `%20`
205
+ });
206
+
207
+ test('Fraction digits can be overriden', async ({ page }) => {
208
+ await page.goto('/');
209
+
210
+ const btcUnformattedInput = page.locator('.currencyInput__unformatted[name=btc]');
211
+ const btcFormattedInput = page.locator('.currencyInput__formatted[name="formatted-btc"]');
212
+
213
+ await expect(btcUnformattedInput).toHaveValue('0.87654321');
214
+ await expect(btcFormattedInput).toHaveValue('$0.87654321');
215
+ await expect(btcFormattedInput).toHaveAttribute('placeholder', '$0.00000000');
216
+
217
+ await btcFormattedInput.focus();
218
+ await selectAll(page);
219
+ await page.keyboard.press('Backspace');
220
+ await expect(btcUnformattedInput).toHaveValue('0');
221
+ await expect(btcFormattedInput).toHaveValue('');
222
+
223
+ await page.keyboard.type('-0.987654321');
224
+ await expect(btcUnformattedInput).toHaveValue('-0.987654321');
225
+ await expect(btcFormattedInput).toHaveValue('-$0.98765432');
226
+ });
227
+
187
228
  test.skip('Updating chained inputs have the correct behavior', async () => {
188
229
  // TODO
189
230
  });