fix(dossier): improuve decimal field formatting

This commit is contained in:
Paul Chavard 2023-10-16 10:58:11 +02:00
parent dbb68f29da
commit 63303e51f8
4 changed files with 57 additions and 49 deletions

View file

@ -1 +1 @@
= @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: @champ.describedby_id }, required: @champ.required?, pattern: "-?[0-9]+([\.,][0-9]{1,3})?", inputmode: :decimal, data: { controller: 'format decimal-number-input', format: :decimal })) = @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: @champ.describedby_id }, required: @champ.required?, pattern: "-?[0-9]+([\.,][0-9]{1,3})?", inputmode: :decimal, data: { controller: 'format', format: :decimal }))

View file

@ -1,35 +0,0 @@
import { ApplicationController } from './application_controller';
export class DecimalNumberInputController extends ApplicationController {
connect() {
const value = this.inputElement.value;
if (value) {
this.formatValue(value);
}
}
formatValue(value: string) {
const number = parseFloat(value);
if (isNaN(number)) {
return;
}
this.inputElement.value = number.toLocaleString();
this.emitInputEvent(); // trigger format controller
}
private get inputElement(): HTMLInputElement {
return this.element as HTMLInputElement;
}
private emitInputEvent() {
const event = new InputEvent('input', {
bubbles: true,
cancelable: true
});
this.inputElement.dispatchEvent(event);
}
}

View file

@ -7,25 +7,29 @@ export class FormatController extends ApplicationController {
case 'list': case 'list':
this.on('change', (event) => { this.on('change', (event) => {
const target = event.target as HTMLInputElement; const target = event.target as HTMLInputElement;
target.value = this.formatList(target.value); const value = this.formatList(target.value);
replaceValue(target, value);
}); });
break; break;
case 'iban': case 'iban':
this.on('input', (event) => { this.on('input', (event) => {
const target = event.target as HTMLInputElement; const target = event.target as HTMLInputElement;
target.value = this.formatIBAN(target.value); const value = this.formatIBAN(target.value);
replaceValue(target, value);
}); });
break; break;
case 'integer': case 'integer':
this.on('input', (event) => { this.on('input', (event) => {
const target = event.target as HTMLInputElement; const target = event.target as HTMLInputElement;
target.value = this.formatInteger(target.value); const value = this.formatInteger(target.value);
replaceValue(target, value);
}); });
break; break;
case 'decimal': case 'decimal':
this.on('input', (event) => { this.on('input', (event) => {
const target = event.target as HTMLInputElement; const target = event.target as HTMLInputElement;
target.value = this.formatDecimal(target.value); const value = this.formatDecimal(target.value);
replaceValue(target, value);
}); });
break; break;
} }
@ -48,12 +52,34 @@ export class FormatController extends ApplicationController {
} }
private formatDecimal(value: string) { private formatDecimal(value: string) {
// Le séparateur de décimales est toujours après le séparateur de milliers (un point ou une virgule). const decimalSeparator = getDecimalSeparator(value);
// S'il n'y a qu'un seul séparateur, on considère que c'est celui des décimales. const number =
// S'il n'y en a pas, ça n'a pas d'effet. decimalSeparator == ','
const decimalSeparator = ? value.replace(/\./g, '').replace(/,/g, '.')
value.lastIndexOf(',') > value.lastIndexOf('.') ? ',' : '.'; : value.replace(/,/g, '');
return number.replace(new RegExp(`[^-?\\d.]`, 'g'), '');
}
}
return value.replace(new RegExp(`[^-?\\d${decimalSeparator}]`, 'g'), ''); function replaceValue(target: HTMLInputElement, value: string) {
const delta = target.value.length - value.length;
const start = target.selectionStart;
const end = target.selectionStart;
const dir = target.selectionDirection;
target.value = value;
target.selectionStart = start ? start - delta : 0;
target.selectionEnd = end ? end - delta : 0;
target.selectionDirection = dir;
} }
function getDecimalSeparator(value: string) {
if (value.indexOf('.') != -1 && value.indexOf(',') != -1) {
if (value.lastIndexOf('.') < value.lastIndexOf(',')) {
return ',';
}
return '.';
} else if (value.indexOf(',') != -1) {
return ',';
}
return (1.1).toLocaleString().indexOf('.') != -1 ? '.' : ',';
} }

View file

@ -195,15 +195,20 @@ describe 'The user' do
champ_value_for('nombre décimal') == '123456.78' champ_value_for('nombre décimal') == '123456.78'
} }
fill_in('nombre décimal', with: '1,234.56') champ_past_value_for('nombre décimal', '1,234.56')
wait_until { wait_until {
champ_value_for('nombre décimal') == '1234.56' champ_value_for('nombre décimal') == '1234.56'
} }
fill_in('nombre décimal', with: '-1,234.56') champ_past_value_for('nombre décimal', '-1,234.56')
wait_until { wait_until {
champ_value_for('nombre décimal') == '-1234.56' champ_value_for('nombre décimal') == '-1234.56'
} }
champ_past_value_for('nombre décimal', '1.234,56')
wait_until {
champ_value_for('nombre décimal') == '1234.56'
}
end end
scenario 'extends dossier experation date more than one time, ', js: true, retry: 3 do scenario 'extends dossier experation date more than one time, ', js: true, retry: 3 do
@ -584,6 +589,18 @@ describe 'The user' do
champ_for(libelle).value champ_for(libelle).value
end end
def champ_id_for(libelle)
champ_for(libelle).input_id
end
def champ_past_value_for(libelle, value)
execute_script("{
let target = document.querySelector('##{champ_id_for(libelle)}');
target.value = \"#{value}\";
target.dispatchEvent(new CustomEvent('input', { bubbles: true }));
}")
end
def champ_for(libelle) def champ_for(libelle)
champs = user_dossier.reload.champs_public champs = user_dossier.reload.champs_public
champs.find { |c| c.libelle == libelle } champs.find { |c| c.libelle == libelle }