feat:allow local decimal sep. curr. & input widget (#16380)
* feat:allow local decimal sep. curr. & input widget * test: fix the test for input widget * refactor: code refinement * refactor: rename the suggestion key * fix: changed the regex to replaceAll function * fix: address review callouts * refactor: new default and min max validation * fix: directly converting default value to current locale * fix: converting value to locale directly inside onStep * fix: add fraction during formatting * revert: input widget locale changes * fix: restrict getting of current locale for currency widget * fix: introduced shouldUseLocale prop Co-authored-by: balajisoundar <balaji@appsmith.com> Co-authored-by: keyurparalkar <keyur@appsmith.com> Co-authored-by: Aishwarya UR <aishwarya@appsmith.com>
This commit is contained in:
parent
6ea44271c3
commit
e8219284ea
|
|
@ -560,6 +560,11 @@ export const entityDefinitions = {
|
|||
"!doc": "The text value of the input",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/input",
|
||||
},
|
||||
inputText: {
|
||||
"!type": "string",
|
||||
"!doc": "The unformatted text value of the input",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/input",
|
||||
},
|
||||
isValid: "bool",
|
||||
isVisible: isVisible,
|
||||
isDisabled: "bool",
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import { InputType } from "widgets/InputWidget/constants";
|
|||
import { getBaseWidgetClassName } from "constants/componentClassNameConstants";
|
||||
import { LabelPosition } from "components/constants";
|
||||
import { lightenColor } from "widgets/WidgetUtils";
|
||||
import { getLocale } from "utils/helpers";
|
||||
|
||||
/**
|
||||
* All design system component specific logic goes here.
|
||||
|
|
@ -499,6 +500,8 @@ class BaseInputComponent extends React.Component<
|
|||
};
|
||||
|
||||
private numericInputComponent = () => {
|
||||
// Get current locale only for the currency widget.
|
||||
const locale = this.props.shouldUseLocale ? getLocale() : undefined;
|
||||
const leftIcon = this.getLeftIcon();
|
||||
const conditionalProps: Record<string, number> = {};
|
||||
|
||||
|
|
@ -523,6 +526,7 @@ class BaseInputComponent extends React.Component<
|
|||
}}
|
||||
intent={this.props.intent}
|
||||
leftIcon={leftIcon}
|
||||
locale={locale}
|
||||
majorStepSize={null}
|
||||
minorStepSize={null}
|
||||
onBlur={() => this.setFocusState(false)}
|
||||
|
|
@ -760,6 +764,7 @@ export interface BaseInputComponentProps extends ComponentProps {
|
|||
boxShadow?: string;
|
||||
accentColor?: string;
|
||||
errorTooltipBoundary?: string;
|
||||
shouldUseLocale?: boolean;
|
||||
}
|
||||
|
||||
export default BaseInputComponent;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ class CurrencyInputComponent extends React.Component<
|
|||
onStep={this.props.onStep}
|
||||
onValueChange={this.props.onValueChange}
|
||||
placeholder={this.props.placeholder}
|
||||
shouldUseLocale
|
||||
showError={this.props.showError}
|
||||
stepSize={1}
|
||||
tooltip={this.props.tooltip}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import {
|
||||
getLocaleDecimalSeperator,
|
||||
getLocaleThousandSeparator,
|
||||
} from "widgets/WidgetUtils";
|
||||
import {
|
||||
countryToFlag,
|
||||
formatCurrencyNumber,
|
||||
getLocaleThousandSeparator,
|
||||
getLocaleDecimalSeperator,
|
||||
limitDecimalValue,
|
||||
parseLocaleFormattedStringToNumber,
|
||||
} from "./utilities";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { getLocale } from "utils/helpers";
|
||||
import {
|
||||
getLocaleDecimalSeperator,
|
||||
getLocaleThousandSeparator,
|
||||
} from "widgets/WidgetUtils";
|
||||
|
||||
export const countryToFlag = (isoCode: string) => {
|
||||
return typeof String.fromCodePoint !== "undefined"
|
||||
|
|
@ -66,15 +70,3 @@ export function parseLocaleFormattedStringToNumber(currencyString = "") {
|
|||
.replace(new RegExp("\\" + getLocaleDecimalSeperator()), "."),
|
||||
);
|
||||
}
|
||||
|
||||
export function getLocaleDecimalSeperator() {
|
||||
return Intl.NumberFormat(getLocale())
|
||||
.format(1.1)
|
||||
.replace(/\p{Number}/gu, "");
|
||||
}
|
||||
|
||||
export function getLocaleThousandSeparator() {
|
||||
return Intl.NumberFormat(getLocale())
|
||||
.format(11111)
|
||||
.replace(/\p{Number}/gu, "");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,12 +27,14 @@ import * as Sentry from "@sentry/react";
|
|||
import log from "loglevel";
|
||||
import {
|
||||
formatCurrencyNumber,
|
||||
getLocaleDecimalSeperator,
|
||||
getLocaleThousandSeparator,
|
||||
limitDecimalValue,
|
||||
} from "../component/utilities";
|
||||
import { mergeWidgetConfig } from "utils/helpers";
|
||||
import { getLocale, mergeWidgetConfig } from "utils/helpers";
|
||||
import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
|
||||
import {
|
||||
getLocaleDecimalSeperator,
|
||||
getLocaleThousandSeparator,
|
||||
} from "widgets/WidgetUtils";
|
||||
|
||||
export function defaultValueValidation(
|
||||
value: any,
|
||||
|
|
@ -40,7 +42,18 @@ export function defaultValueValidation(
|
|||
_?: any,
|
||||
): ValidationResponse {
|
||||
const NUMBER_ERROR_MESSAGE = "This value must be number";
|
||||
const DECIMAL_SEPARATOR_ERROR_MESSAGE =
|
||||
"Please use . as the decimal separator for default values.";
|
||||
const EMPTY_ERROR_MESSAGE = "";
|
||||
const localeLang = navigator.languages?.[0] || "en-US";
|
||||
|
||||
function getLocaleDecimalSeperator() {
|
||||
return Intl.NumberFormat(localeLang)
|
||||
.format(1.1)
|
||||
.replace(/\p{Number}/gu, "");
|
||||
}
|
||||
const decimalSeperator = getLocaleDecimalSeperator();
|
||||
const defaultDecimalSeperator = ".";
|
||||
if (_.isObject(value)) {
|
||||
return {
|
||||
isValid: false,
|
||||
|
|
@ -64,8 +77,20 @@ export function defaultValueValidation(
|
|||
* When parsed value is not a finite numer
|
||||
*/
|
||||
isValid = false;
|
||||
messages = [NUMBER_ERROR_MESSAGE];
|
||||
parsed = undefined;
|
||||
|
||||
/**
|
||||
* Check whether value contains the locale decimal separator apart from "."
|
||||
* We only allow "." as a decimal separator inside default value
|
||||
*/
|
||||
if (
|
||||
String(value).indexOf(defaultDecimalSeperator) === -1 &&
|
||||
String(value).indexOf(decimalSeperator) > 0
|
||||
) {
|
||||
messages = [DECIMAL_SEPARATOR_ERROR_MESSAGE];
|
||||
} else {
|
||||
messages = [NUMBER_ERROR_MESSAGE];
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* When parsed value is a Number
|
||||
|
|
@ -262,10 +287,17 @@ class CurrencyInputWidget extends BaseInputWidget<
|
|||
formatText() {
|
||||
if (!!this.props.text) {
|
||||
try {
|
||||
const formattedValue = formatCurrencyNumber(
|
||||
this.props.decimals,
|
||||
this.props.text,
|
||||
);
|
||||
/**
|
||||
* Since we are restricting default value to only have "." decimal separator,
|
||||
* hence we directly convert it to the current locale
|
||||
*/
|
||||
const floatVal = parseFloat(this.props.text);
|
||||
|
||||
const formattedValue = Intl.NumberFormat(getLocale(), {
|
||||
style: "decimal",
|
||||
minimumFractionDigits: this.props.decimals,
|
||||
maximumFractionDigits: this.props.decimals,
|
||||
}).format(floatVal);
|
||||
this.props.updateWidgetMetaProperty("text", formattedValue);
|
||||
} catch (e) {
|
||||
log.error(e);
|
||||
|
|
@ -347,10 +379,9 @@ class CurrencyInputWidget extends BaseInputWidget<
|
|||
|
||||
onStep = (direction: number) => {
|
||||
const value = Number(this.props.value) + direction;
|
||||
const formattedValue = formatCurrencyNumber(
|
||||
this.props.decimals,
|
||||
String(value),
|
||||
);
|
||||
|
||||
// Since value is always going to be a number therefore, directly converting it to the current locale
|
||||
const formattedValue = Intl.NumberFormat(getLocale()).format(value);
|
||||
if (!this.props.isDirty) {
|
||||
this.props.updateWidgetMetaProperty("isDirty", true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,13 +14,11 @@ import CurrencyTypeDropdown, {
|
|||
import FormContext from "../FormContext";
|
||||
import { BaseFieldComponentProps } from "../constants";
|
||||
import { RenderModes } from "constants/WidgetConstants";
|
||||
import {
|
||||
getLocaleDecimalSeperator,
|
||||
limitDecimalValue,
|
||||
} from "widgets/CurrencyInputWidget/component/utilities";
|
||||
import { limitDecimalValue } from "widgets/CurrencyInputWidget/component/utilities";
|
||||
import derived from "widgets/CurrencyInputWidget/widget/derived";
|
||||
import { isEmpty } from "../helper";
|
||||
import { BASE_LABEL_TEXT_SIZE } from "../component/FieldLabel";
|
||||
import { getLocaleDecimalSeperator } from "widgets/WidgetUtils";
|
||||
|
||||
type CurrencyInputComponentProps = BaseInputComponentProps & {
|
||||
currencyCountryCode: string;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import { rgbaMigrationConstantV56 } from "./constants";
|
|||
import { DynamicPath } from "utils/DynamicBindingUtils";
|
||||
import { isArray } from "lodash";
|
||||
import { PropertyHookUpdates } from "constants/PropertyControlConstants";
|
||||
import { getLocale } from "utils/helpers";
|
||||
|
||||
const punycode = require("punycode/");
|
||||
|
||||
|
|
@ -692,6 +693,18 @@ export function composePropertyUpdateHook(
|
|||
};
|
||||
}
|
||||
|
||||
export function getLocaleDecimalSeperator() {
|
||||
return Intl.NumberFormat(getLocale())
|
||||
.format(1.1)
|
||||
.replace(/\p{Number}/gu, "");
|
||||
}
|
||||
|
||||
export function getLocaleThousandSeparator() {
|
||||
return Intl.NumberFormat(getLocale())
|
||||
.format(11111)
|
||||
.replace(/\p{Number}/gu, "");
|
||||
}
|
||||
|
||||
interface DropdownOption {
|
||||
label: string;
|
||||
value: string | number;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user