Skip to content

Commit e3c06b5

Browse files
committed
[FIX] Fix localization part 3
Before this commit : Issue in the input of a "value in list" DV 1/2 was automaticcaly transformed into 1/2/2026 and no change was possible After this commit: The input is stocked in a state and is only transformed when the user confirms his input Task: 6089166
1 parent 874348d commit e3c06b5

5 files changed

Lines changed: 83 additions & 48 deletions

File tree

src/components/side_panel/data_validation/dv_criterion_form/dv_input/dv_input.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, useEffect, useRef, useState } from "@odoo/owl";
1+
import { Component, onWillUpdateProps, useEffect, useRef, useState } from "@odoo/owl";
22
import { canonicalizeContent, localizeContent } from "../../../../../helpers/locale";
33
import { dataValidationEvaluatorRegistry } from "../../../../../registries/data_validation_registry";
44
import { _t } from "../../../../../translation";
@@ -57,10 +57,16 @@ export class DataValidationInput extends Component<Props, SpreadsheetChildEnv> {
5757
},
5858
() => [this.props.focused, this.inputRef.el]
5959
);
60+
onWillUpdateProps((nextProps: Props) => {
61+
if (nextProps.value !== this.props.value) {
62+
this.state.textInput = localizeContent(nextProps.value, this.env.model.getters.getLocale());
63+
}
64+
});
6065
}
6166

6267
state = useState({
6368
shouldDisplayError: !!this.props.value, // Don't display error if user inputted nothing yet
69+
textInput: localizeContent(this.props.value, this.env.model.getters.getLocale()),
6470
});
6571

6672
get placeholder(): string {
@@ -80,16 +86,14 @@ export class DataValidationInput extends Component<Props, SpreadsheetChildEnv> {
8086
return evaluator.allowedValues ?? "any";
8187
}
8288

83-
get localeInputValue(): string {
84-
const locale = this.env.model.getters.getLocale();
85-
return localizeContent(this.props.value, locale);
86-
}
87-
8889
onInputValueChanged(ev: Event) {
8990
this.state.shouldDisplayError = true;
90-
const value = (ev.target as HTMLInputElement).value;
91+
this.state.textInput = (ev.target as HTMLInputElement).value;
92+
}
93+
94+
onInputValueConfirmed() {
9195
const locale = this.env.model.getters.getLocale();
92-
const canonicalizedValue = canonicalizeContent(value, locale);
96+
const canonicalizedValue = canonicalizeContent(this.state.textInput, locale);
9397
this.props.onValueChanged(canonicalizedValue);
9498
}
9599

@@ -115,7 +119,7 @@ export class DataValidationInput extends Component<Props, SpreadsheetChildEnv> {
115119
}
116120
return this.env.model.getters.getDataValidationInvalidCriterionValueMessage(
117121
this.props.criterionType,
118-
canonicalizeContent(this.props.value, this.env.model.getters.getLocale())
122+
canonicalizeContent(this.state.textInput, this.env.model.getters.getLocale())
119123
);
120124
}
121125
}

src/components/side_panel/data_validation/dv_criterion_form/dv_input/dv_input.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
type="text"
77
t-ref="input"
88
t-on-input="onInputValueChanged"
9-
t-att-value="this.localeInputValue"
9+
t-on-change="onInputValueConfirmed"
10+
t-att-value="this.state.textInput"
1011
class="o-input"
1112
t-att-class="{
1213
'o-invalid border-danger position-relative': errorMessage,

src/registries/auto_completes/data_validation_auto_complete.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CellPosition, CellValue, Getters } from "../..";
22
import { positions } from "../../helpers";
3-
import { canonicalizeContent, localizeContent } from "../../helpers/locale";
3+
import { localizeContent } from "../../helpers/locale";
44
import { autoCompleteProviders } from "./auto_complete_registry";
55

66
autoCompleteProviders.add("dataValidation", {
@@ -22,8 +22,8 @@ autoCompleteProviders.add("dataValidation", {
2222
},
2323
selectProposal(tokenAtCursor, value) {
2424
const locale = this.getters.getLocale();
25-
const canonicalizedValue = canonicalizeContent(value, locale);
26-
this.composer.setCurrentContent(canonicalizedValue);
25+
const localizedValue = localizeContent(value, locale) ?? "";
26+
this.composer.setCurrentContent(localizedValue);
2727
this.composer.stopEdition();
2828
},
2929
});

src/registries/data_validation_registry.ts

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
IsValueInRangeCriterion,
4747
Locale,
4848
TextContainsCriterion,
49+
TextIsCriterion,
4950
TextNotContainsCriterion,
5051
UID,
5152
} from "../types";
@@ -105,9 +106,7 @@ dataValidationEvaluatorRegistry.add("textContains", {
105106
name: _t("Text contains"),
106107
getPreview: (criterion: TextContainsCriterion, getters: Getters) => {
107108
const locale = getters.getLocale();
108-
const localizedValue = criterion.values[0]
109-
? localizeContent(criterion.values[0]?.toString(), locale)
110-
: "";
109+
const localizedValue = criterion.values[0] ? localizeContent(criterion.values[0], locale) : "";
111110
return _t('Text contains "%s"', localizedValue);
112111
},
113112
});
@@ -127,31 +126,27 @@ dataValidationEvaluatorRegistry.add("textNotContains", {
127126
name: _t("Text does not contains"),
128127
getPreview: (criterion: TextNotContainsCriterion, getters: Getters) => {
129128
const locale = getters.getLocale();
130-
const localizedValue = criterion.values[0]
131-
? localizeContent(criterion.values[0]?.toString(), locale)
132-
: "";
129+
const localizedValue = criterion.values[0] ? localizeContent(criterion.values[0], locale) : "";
133130
return _t('Text does not contain "%s"', localizedValue);
134131
},
135132
});
136133

137134
dataValidationEvaluatorRegistry.add("textIs", {
138135
type: "textIs",
139-
isValueValid: (value: CellValue, criterion: TextContainsCriterion) => {
136+
isValueValid: (value: CellValue, criterion: TextIsCriterion) => {
140137
const strValue = String(value);
141138
return strValue.toLowerCase() === criterion.values[0].toLowerCase();
142139
},
143-
getErrorString: (criterion: TextContainsCriterion) => {
140+
getErrorString: (criterion: TextIsCriterion) => {
144141
return _t('The value must be exactly "%s"', criterion.values[0]);
145142
},
146143
isCriterionValueValid: (value: string) => !!value,
147144
criterionValueErrorString: DVTerms.CriterionError.notEmptyValue,
148145
numberOfValues: () => 1,
149146
name: _t("Text is exactly"),
150-
getPreview: (criterion: TextContainsCriterion, getters: Getters) => {
147+
getPreview: (criterion: TextIsCriterion, getters: Getters) => {
151148
const locale = getters.getLocale();
152-
const localizedValue = criterion.values[0]
153-
? localizeContent(criterion.values[0]?.toString(), locale)
154-
: "";
149+
const localizedValue = criterion.values[0] ? localizeContent(criterion.values[0], locale) : "";
155150
return _t('Text is exactly "%s"', localizedValue);
156151
},
157152
});
@@ -441,7 +436,7 @@ dataValidationEvaluatorRegistry.add("isEqual", {
441436
},
442437
getErrorString: (criterion: IsEqualCriterion, getters: Getters) => {
443438
const locale = getters.getLocale();
444-
const values = getNumberCriterionlocalizedValues(criterion, locale);
439+
const values = getCriterionLocalizedValues(criterion, locale);
445440
return _t("The value must be equal to %s", values[0]);
446441
},
447442
isCriterionValueValid: (value) => checkValueIsNumber(value),
@@ -450,7 +445,7 @@ dataValidationEvaluatorRegistry.add("isEqual", {
450445
name: _t("Is equal to"),
451446
getPreview: (criterion: IsEqualCriterion, getters: Getters) => {
452447
const locale = getters.getLocale();
453-
const values = getNumberCriterionlocalizedValues(criterion, locale);
448+
const values = getCriterionLocalizedValues(criterion, locale);
454449
return _t("Value is equal to %s", values[0]);
455450
},
456451
});
@@ -470,7 +465,7 @@ dataValidationEvaluatorRegistry.add("isNotEqual", {
470465
},
471466
getErrorString: (criterion: IsNotEqualCriterion, getters: Getters) => {
472467
const locale = getters.getLocale();
473-
const values = getNumberCriterionlocalizedValues(criterion, locale);
468+
const values = getCriterionLocalizedValues(criterion, locale);
474469
return _t("The value must not be equal to %s", values[0]);
475470
},
476471
isCriterionValueValid: (value) => checkValueIsNumber(value),
@@ -479,7 +474,7 @@ dataValidationEvaluatorRegistry.add("isNotEqual", {
479474
name: _t("Is not equal to"),
480475
getPreview: (criterion: IsEqualCriterion, getters: Getters) => {
481476
const locale = getters.getLocale();
482-
const values = getNumberCriterionlocalizedValues(criterion, locale);
477+
const values = getCriterionLocalizedValues(criterion, locale);
483478
return _t("Value is not equal to %s", values[0]);
484479
},
485480
});
@@ -499,7 +494,7 @@ dataValidationEvaluatorRegistry.add("isGreaterThan", {
499494
},
500495
getErrorString: (criterion: IsGreaterThanCriterion, getters: Getters) => {
501496
const locale = getters.getLocale();
502-
const values = getNumberCriterionlocalizedValues(criterion, locale);
497+
const values = getCriterionLocalizedValues(criterion, locale);
503498
return _t("The value must be greater than %s", values[0]);
504499
},
505500
isCriterionValueValid: (value) => checkValueIsNumber(value),
@@ -508,7 +503,7 @@ dataValidationEvaluatorRegistry.add("isGreaterThan", {
508503
name: _t("Is greater than"),
509504
getPreview: (criterion: IsGreaterThanCriterion, getters: Getters) => {
510505
const locale = getters.getLocale();
511-
const values = getNumberCriterionlocalizedValues(criterion, locale);
506+
const values = getCriterionLocalizedValues(criterion, locale);
512507
return _t("Value is greater than %s", values[0]);
513508
},
514509
});
@@ -528,7 +523,7 @@ dataValidationEvaluatorRegistry.add("isGreaterOrEqualTo", {
528523
},
529524
getErrorString: (criterion: IsGreaterOrEqualToCriterion, getters: Getters) => {
530525
const locale = getters.getLocale();
531-
const values = getNumberCriterionlocalizedValues(criterion, locale);
526+
const values = getCriterionLocalizedValues(criterion, locale);
532527
return _t("The value must be greater or equal to %s", values[0]);
533528
},
534529
isCriterionValueValid: (value) => checkValueIsNumber(value),
@@ -537,7 +532,7 @@ dataValidationEvaluatorRegistry.add("isGreaterOrEqualTo", {
537532
name: _t("Is greater or equal to"),
538533
getPreview: (criterion: IsGreaterOrEqualToCriterion, getters: Getters) => {
539534
const locale = getters.getLocale();
540-
const values = getNumberCriterionlocalizedValues(criterion, locale);
535+
const values = getCriterionLocalizedValues(criterion, locale);
541536
return _t("Value is greater or equal to %s", values[0]);
542537
},
543538
});
@@ -557,7 +552,7 @@ dataValidationEvaluatorRegistry.add("isLessThan", {
557552
},
558553
getErrorString: (criterion: IsLessThanCriterion, getters: Getters) => {
559554
const locale = getters.getLocale();
560-
const values = getNumberCriterionlocalizedValues(criterion, locale);
555+
const values = getCriterionLocalizedValues(criterion, locale);
561556
return _t("The value must be less than %s", values[0]);
562557
},
563558
isCriterionValueValid: (value) => checkValueIsNumber(value),
@@ -566,7 +561,7 @@ dataValidationEvaluatorRegistry.add("isLessThan", {
566561
name: _t("Is less than"),
567562
getPreview: (criterion: IsLessThanCriterion, getters: Getters) => {
568563
const locale = getters.getLocale();
569-
const values = getNumberCriterionlocalizedValues(criterion, locale);
564+
const values = getCriterionLocalizedValues(criterion, locale);
570565
return _t("Value is less than %s", values[0]);
571566
},
572567
});
@@ -586,7 +581,7 @@ dataValidationEvaluatorRegistry.add("isLessOrEqualTo", {
586581
},
587582
getErrorString: (criterion: IsLessOrEqualToCriterion, getters: Getters) => {
588583
const locale = getters.getLocale();
589-
const values = getNumberCriterionlocalizedValues(criterion, locale);
584+
const values = getCriterionLocalizedValues(criterion, locale);
590585
return _t("The value must be less or equal to %s", values[0]);
591586
},
592587
isCriterionValueValid: (value) => checkValueIsNumber(value),
@@ -595,7 +590,7 @@ dataValidationEvaluatorRegistry.add("isLessOrEqualTo", {
595590
name: _t("Is less or equal to"),
596591
getPreview: (criterion: IsLessOrEqualToCriterion, getters: Getters) => {
597592
const locale = getters.getLocale();
598-
const values = getNumberCriterionlocalizedValues(criterion, locale);
593+
const values = getCriterionLocalizedValues(criterion, locale);
599594
return _t("Value is less or equal to %s", values[0]);
600595
},
601596
});
@@ -615,7 +610,7 @@ dataValidationEvaluatorRegistry.add("isBetween", {
615610
},
616611
getErrorString: (criterion: IsBetweenCriterion, getters: Getters) => {
617612
const locale = getters.getLocale();
618-
const values = getNumberCriterionlocalizedValues(criterion, locale);
613+
const values = getCriterionLocalizedValues(criterion, locale);
619614
return _t("The value must be between %s and %s", values[0], values[1]);
620615
},
621616
isCriterionValueValid: (value) => checkValueIsNumber(value),
@@ -624,7 +619,7 @@ dataValidationEvaluatorRegistry.add("isBetween", {
624619
name: _t("Is between"),
625620
getPreview: (criterion: IsBetweenCriterion, getters: Getters) => {
626621
const locale = getters.getLocale();
627-
const values = getNumberCriterionlocalizedValues(criterion, locale);
622+
const values = getCriterionLocalizedValues(criterion, locale);
628623
return _t("Value is between %s and %s", values[0], values[1]);
629624
},
630625
});
@@ -644,7 +639,7 @@ dataValidationEvaluatorRegistry.add("isNotBetween", {
644639
},
645640
getErrorString: (criterion: IsNotBetweenCriterion, getters: Getters) => {
646641
const locale = getters.getLocale();
647-
const values = getNumberCriterionlocalizedValues(criterion, locale);
642+
const values = getCriterionLocalizedValues(criterion, locale);
648643
return _t("The value must not be between %s and %s", values[0], values[1]);
649644
},
650645
isCriterionValueValid: (value) => checkValueIsNumber(value),
@@ -653,7 +648,7 @@ dataValidationEvaluatorRegistry.add("isNotBetween", {
653648
name: _t("Is not between"),
654649
getPreview: (criterion: IsNotBetweenCriterion, getters: Getters) => {
655650
const locale = getters.getLocale();
656-
const values = getNumberCriterionlocalizedValues(criterion, locale);
651+
const values = getCriterionLocalizedValues(criterion, locale);
657652
return _t("Value is not between %s and %s", values[0], values[1]);
658653
},
659654
});
@@ -681,7 +676,7 @@ dataValidationEvaluatorRegistry.add("isValueInList", {
681676
},
682677
getErrorString: (criterion: IsValueInListCriterion, getters: Getters) => {
683678
const locale = getters.getLocale();
684-
const values = getNumberCriterionlocalizedValues(criterion, locale);
679+
const values = getCriterionLocalizedValues(criterion, locale);
685680
const separator = `${locale.formulaArgSeparator || ","} `;
686681
return _t("The value must be one of: %s", values.join(separator));
687682
},
@@ -692,7 +687,7 @@ dataValidationEvaluatorRegistry.add("isValueInList", {
692687
name: _t("Value in list"),
693688
getPreview: (criterion: IsValueInListCriterion, getters: Getters) => {
694689
const locale = getters.getLocale();
695-
const values = getNumberCriterionlocalizedValues(criterion, locale);
690+
const values = getCriterionLocalizedValues(criterion, locale);
696691
const separator = `${locale.formulaArgSeparator || ","} `;
697692
return _t("Value one of: %s", values.join(separator));
698693
},
@@ -744,10 +739,7 @@ dataValidationEvaluatorRegistry.add("customFormula", {
744739
getPreview: (criterion) => _t("Custom formula %s", criterion.values[0]),
745740
});
746741

747-
function getNumberCriterionlocalizedValues(
748-
criterion: DataValidationCriterion,
749-
locale: Locale
750-
): string[] {
742+
function getCriterionLocalizedValues(criterion: DataValidationCriterion, locale: Locale): string[] {
751743
return criterion.values.map((value) =>
752744
value !== undefined ? localizeContent(value, locale) : CellErrorType.InvalidReference
753745
);

tests/conditional_formatting/conditional_formatting_panel_component.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ import { ComposerFocusStore } from "../../src/components/composer/composer_focus
44
import { ConditionalFormattingPanel } from "../../src/components/side_panel/conditional_formatting/conditional_formatting";
55
import { toHex, toZone } from "../../src/helpers";
66
import { ConditionalFormatPlugin } from "../../src/plugins/core/conditional_format";
7-
import { CellIsRule, CommandResult, SpreadsheetChildEnv, UID } from "../../src/types";
7+
import {
8+
CellIsRule,
9+
ColorScaleRule,
10+
CommandResult,
11+
IconSetRule,
12+
SpreadsheetChildEnv,
13+
UID,
14+
} from "../../src/types";
815
import {
916
activateSheet,
1017
copy,
@@ -1505,6 +1512,37 @@ describe("UI of conditional formats", () => {
15051512
expect(description?.textContent).toContain("01/05/2012");
15061513
});
15071514

1515+
test("color-scale threshold value is canonicalized when sending to the model", async () => {
1516+
updateLocale(model, FR_LOCALE);
1517+
await click(fixture, selectors.buttonAdd);
1518+
await click(fixture.querySelectorAll(selectors.cfTabSelector)[1]);
1519+
1520+
await setInputValueAndTrigger(selectors.colorScaleEditor.minType, "number");
1521+
setInputValueAndTrigger(selectors.colorScaleEditor.minValue, "1,5");
1522+
1523+
await click(fixture, selectors.buttonSave);
1524+
const sheetId = model.getters.getActiveSheetId();
1525+
const lastCf = model.getters.getConditionalFormats(sheetId).at(-1)!;
1526+
expect((lastCf.rule as ColorScaleRule).minimum.value).toBe("1.5");
1527+
});
1528+
1529+
test("icon-set inflection point value is canonicalized when sending to the model", async () => {
1530+
updateLocale(model, FR_LOCALE);
1531+
await click(fixture, selectors.buttonAdd);
1532+
await click(fixture.querySelectorAll(selectors.cfTabSelector)[2]);
1533+
1534+
const rows = document.querySelectorAll(selectors.ruleEditor.editor.iconSetRule.rows);
1535+
const typeinflectionLower = rows[2].querySelectorAll("select")[1];
1536+
const inputinflectionLower = rows[2].querySelectorAll("input")[0];
1537+
await setInputValueAndTrigger(typeinflectionLower, "number");
1538+
await setInputValueAndTrigger(inputinflectionLower, "1,5");
1539+
1540+
await click(fixture, selectors.buttonSave);
1541+
const sheetId = model.getters.getActiveSheetId();
1542+
const lastCf = model.getters.getConditionalFormats(sheetId).at(-1)!;
1543+
expect((lastCf.rule as IconSetRule).lowerInflectionPoint.value).toBe("1.5");
1544+
});
1545+
15081546
test("Can create a data bar rule", async () => {
15091547
await click(fixture, selectors.buttonAdd);
15101548

0 commit comments

Comments
 (0)