Skip to content

Commit bb4993c

Browse files
authored
Merge pull request #638 from AOEpeople/feature/272351_money-input
Add automated money formatting
2 parents acaee3c + f09d13f commit bb4993c

File tree

2 files changed

+98
-16
lines changed

2 files changed

+98
-16
lines changed

src/Resources/src/components/misc/MoneyInput.vue

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,57 @@
11
<template>
22
<input
3-
v-model="value"
4-
type="number"
3+
v-model="displayValue"
4+
type="text"
5+
inputmode="decimal"
56
required
6-
min="0.00"
7-
step=".01"
87
class="h-[46px] rounded-full border-2 border-solid border-[#CAD6E1] bg-white text-center"
9-
@focus="(e) => selectAllAndPlaceCursor(e.target as HTMLInputElement)"
8+
@input="onInput"
9+
@focus="onFocus"
10+
@blur="onBlur"
1011
/>
1112
</template>
1213

1314
<script setup lang="ts">
14-
import { computed } from 'vue';
15+
import { computed, ref } from 'vue';
1516
1617
const props = defineProps<{
1718
modelValue: number;
1819
}>();
1920
2021
const emit = defineEmits(['update:modelValue']);
22+
const isFocused = ref(false);
23+
const rawValue = ref(props.modelValue.toString());
24+
const displayValue = computed(() => (isFocused.value ? rawValue.value : formatMoney(props.modelValue)));
2125
22-
const value = computed({
23-
get() {
24-
return props.modelValue;
25-
},
26-
set(value) {
27-
emit('update:modelValue', parseFloat(value.toFixed(2)));
26+
function onInput(event: Event) {
27+
const value = (event.target as HTMLInputElement).value;
28+
rawValue.value = value;
29+
const parsed = parseFloat(value.replace(',', '.'));
30+
if (isNaN(parsed) || parsed < 0) {
31+
return;
2832
}
29-
});
33+
emit('update:modelValue', round(parsed));
34+
}
35+
36+
function onFocus() {
37+
isFocused.value = true;
38+
rawValue.value = props.modelValue.toString();
39+
}
40+
41+
function onBlur() {
42+
isFocused.value = false;
43+
rawValue.value = formatMoney(props.modelValue);
44+
}
45+
46+
function round(value: number) {
47+
return Math.round(value * 100) / 100;
48+
}
3049
31-
function selectAllAndPlaceCursor(element: HTMLInputElement) {
32-
element.select();
33-
element.focus();
50+
function formatMoney(value: number) {
51+
return value.toLocaleString('de-DE', {
52+
minimumFractionDigits: 2,
53+
maximumFractionDigits: 2
54+
});
3455
}
3556
</script>
3657

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { describe, it, expect} from 'vitest';
2+
import MoneyInput from '../../../src/components/misc/MoneyInput.vue';
3+
import {mount} from '@vue/test-utils';
4+
describe('Test MoneyInput', () => {
5+
it('should input number and convert to money', async () => {
6+
const wrapper = mount(MoneyInput, {
7+
props: {
8+
modelValue: 1234.567,
9+
},
10+
});
11+
12+
const input = wrapper.get('input');
13+
14+
expect((input.element as HTMLInputElement).value).toBe('1.234,57');
15+
16+
await input.trigger('focus');
17+
expect((input.element as HTMLInputElement).value).toBe('1234.567');
18+
19+
await input.trigger('blur');
20+
expect((input.element as HTMLInputElement).value).toBe('1.234,57');
21+
});
22+
23+
it('should input number with 30 cents and convert to money', async () => {
24+
const wrapper = mount(MoneyInput, {
25+
props: {
26+
modelValue: 12.3,
27+
},
28+
});
29+
30+
const input = wrapper.get('input');
31+
await input.setValue('12,3');
32+
33+
expect((input.element as HTMLInputElement).value).toBe('12,3');
34+
35+
await input.trigger('focus');
36+
expect((input.element as HTMLInputElement).value).toBe('12.3');
37+
38+
await input.trigger('blur');
39+
expect((input.element as HTMLInputElement).value).toBe('12,30');
40+
});
41+
42+
it('should input negative number and not convert to money', async () => {
43+
const wrapper = mount(MoneyInput, {
44+
props: {
45+
modelValue: 1234.5,
46+
},
47+
});
48+
49+
const input = wrapper.get('input');
50+
await input.setValue('-1234.5');
51+
expect(wrapper.emitted("update:modelValue")).toBeFalsy();
52+
53+
expect((input.element as HTMLInputElement).value).toBe('-1234.5');
54+
55+
await input.trigger('focus');
56+
expect((input.element as HTMLInputElement).value).toBe('1234.5');
57+
58+
await input.trigger('blur');
59+
expect((input.element as HTMLInputElement).value).toBe('1.234,50');
60+
});
61+
});

0 commit comments

Comments
 (0)