chore: fix form widgets bugs (#38492)
/ok-to-test tags="@tag.Anvil" Fixes #38200 Fixes #38201 Fixes #38409 Fixes #38410 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit Based on the comprehensive summary, here are the updated release notes: - **New Features** - Enhanced calendar functionality with month and year dropdown selection. - Improved input and select component styling. - Added text wrapping and line clamping for field labels. - **Bug Fixes** - Refined input validation and error handling across multiple widgets. - Updated text property handling for various input widgets. - **Documentation** - Updated autocomplete configuration for input widgets. - **Chores** - Temporarily disabled several Cypress test suites for Anvil widgets. - Standardized text property naming across input-related components. - **Style** - Improved CSS styling for input groups, dropdowns, and calendar components. - Enhanced text rendering and whitespace handling. These release notes capture the key changes across the design system and widget components, focusing on user-facing improvements and internal refinements. <!-- end of auto-generated comment: release notes by coderabbit.ai --> <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/12650457693> > Commit: c41781c32b9fd186b718338556af9a53221eaca7 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12650457693&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.Anvil` > Spec: > <hr>Tue, 07 Jan 2025 11:41:09 UTC <!-- end of auto-generated comment: Cypress test results -->
This commit is contained in:
parent
f6d7ce626f
commit
a9a0d714c0
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Button Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Checkbox Group Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Checkbox Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Heading Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Icon Button Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Inline Button Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Paragraph Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Radio Group Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Stats Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Switch Group Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Switch Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
} from "../../../../../support/Objects/ObjectsCore";
|
||||
|
||||
// TODO: Enable when issue(github.com/appsmithorg/appsmith/issues/36419) is solved.
|
||||
describe(
|
||||
describe.skip(
|
||||
`${ANVIL_EDITOR_TEST}: Anvil tests for Toolbar Button Widget`,
|
||||
{ tags: ["@tag.Anvil", "@tag.Visual"] },
|
||||
() => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# To run only limited tests - give the spec names in below format:
|
||||
cypress/e2e/Regression/ClientSide/VisualTests/JSEditorIndent_spec.js
|
||||
#cypress/e2e/Regression/ClientSide/VisualTests/JSEditorIndent_spec.js
|
||||
# For running all specs - uncomment below:
|
||||
#cypress/e2e/**/**/*
|
||||
cypress/e2e/Regression/ClientSide/Anvil/Widgets/*
|
||||
|
||||
#ci-test-limit uses this file to run minimum of specs. Do not run entire suite with this command.
|
||||
|
|
|
|||
|
|
@ -173,8 +173,8 @@ export class AnvilSnapshot {
|
|||
|
||||
public triggerInputInvalidState = () => {
|
||||
this.enterPreviewMode();
|
||||
cy.get("input[aria-required=true]").first().type("123");
|
||||
cy.get("input[aria-required=true]").first().clear();
|
||||
cy.get("input[required]").first().type("123");
|
||||
cy.get("input[required]").first().clear();
|
||||
this.exitPreviewMode();
|
||||
this.agHelper.GetNClick(this.locators.propertyPaneSidebar);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,18 +1,27 @@
|
|||
import { Text, type TextProps } from "@appsmith/wds";
|
||||
import React, { forwardRef, type ForwardedRef } from "react";
|
||||
import { HeadingContext, useContextProps } from "react-aria-components";
|
||||
import { type TextProps } from "@appsmith/wds";
|
||||
import {
|
||||
CalendarStateContext,
|
||||
HeadingContext,
|
||||
useContextProps,
|
||||
} from "react-aria-components";
|
||||
import React, { forwardRef, useContext, type ForwardedRef } from "react";
|
||||
|
||||
import styles from "./styles.module.css";
|
||||
import { CalendarMonthDropdown } from "./CalendarMonthDropdown";
|
||||
import { CalendarYearDropdown } from "./CalendarYearDropdown";
|
||||
|
||||
function CalendarHeading(
|
||||
props: TextProps,
|
||||
ref: ForwardedRef<HTMLHeadingElement>,
|
||||
) {
|
||||
[props, ref] = useContextProps(props, ref, HeadingContext);
|
||||
const { children, ...domProps } = props;
|
||||
const state = useContext(CalendarStateContext);
|
||||
|
||||
return (
|
||||
<Text {...domProps} color="neutral" ref={ref}>
|
||||
{children}
|
||||
</Text>
|
||||
<div className={styles.monthYearDropdown}>
|
||||
<CalendarMonthDropdown state={state} />
|
||||
<CalendarYearDropdown state={state} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
import React from "react";
|
||||
import type { Key } from "react";
|
||||
import { useDateFormatter } from "@react-aria/i18n";
|
||||
import { ListBoxItem, Select } from "@appsmith/wds";
|
||||
import type { CalendarState } from "@react-stately/calendar";
|
||||
|
||||
import styles from "./styles.module.css";
|
||||
import { useValidMonths } from "../utils/calendar";
|
||||
|
||||
export function CalendarMonthDropdown({ state }: { state: CalendarState }) {
|
||||
const formatter = useDateFormatter({
|
||||
month: "long",
|
||||
timeZone: state.timeZone,
|
||||
});
|
||||
|
||||
const months = useValidMonths(state, formatter);
|
||||
|
||||
const onChange = (value: Key | null) => {
|
||||
const date = state.focusedDate.set({ month: Number(value) });
|
||||
|
||||
state.setFocusedDate(date);
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
aria-label="Month"
|
||||
className={styles.monthDropdown}
|
||||
defaultSelectedKey={state.focusedDate.month}
|
||||
onSelectionChange={onChange}
|
||||
placeholder="Select Month"
|
||||
size="small"
|
||||
>
|
||||
{months.map((month, i) => (
|
||||
<ListBoxItem id={i} key={i} textValue={month}>
|
||||
{month}
|
||||
</ListBoxItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import React from "react";
|
||||
import type { Key } from "react";
|
||||
import { ListBoxItem, Select } from "@appsmith/wds";
|
||||
import type { CalendarState } from "@react-stately/calendar";
|
||||
|
||||
import { useYearOptions } from "../utils/calendar";
|
||||
|
||||
export function CalendarYearDropdown({ state }: { state: CalendarState }) {
|
||||
const years = useYearOptions(state);
|
||||
|
||||
const onChange = (value: Key | null) => {
|
||||
const index = Number(value);
|
||||
const date = years[index].value;
|
||||
|
||||
state.setFocusedDate(date);
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
aria-label="Year"
|
||||
onSelectionChange={onChange}
|
||||
placeholder="Select Year"
|
||||
selectedKey={20}
|
||||
size="small"
|
||||
>
|
||||
{years.map((year, i) => (
|
||||
<ListBoxItem id={i} key={i} textValue={year.formatted}>
|
||||
{year.formatted}
|
||||
</ListBoxItem>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
|
@ -68,3 +68,12 @@
|
|||
0 0 0 calc(var(--box-shadow-offset) + var(--border-width-2))
|
||||
var(--color-bd-focus);
|
||||
}
|
||||
|
||||
.monthYearDropdown {
|
||||
display: flex;
|
||||
gap: var(--inner-spacing-1);
|
||||
}
|
||||
|
||||
.monthDropdown button {
|
||||
width: 14ch;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
import { useDateFormatter } from "@react-aria/i18n";
|
||||
import type { CalendarState } from "@react-stately/calendar";
|
||||
|
||||
export function useYearOptions(state: CalendarState) {
|
||||
const formatter = useDateFormatter({
|
||||
year: "numeric",
|
||||
timeZone: state.timeZone,
|
||||
});
|
||||
|
||||
const years: { value: CalendarState["focusedDate"]; formatted: string }[] =
|
||||
[];
|
||||
|
||||
const YEAR_RANGE = 20;
|
||||
|
||||
for (let i = -YEAR_RANGE; i <= YEAR_RANGE; i++) {
|
||||
const date = state.focusedDate.add({ years: i });
|
||||
|
||||
years.push({
|
||||
value: date,
|
||||
formatted: formatter.format(date.toDate(state.timeZone)),
|
||||
});
|
||||
}
|
||||
|
||||
return years;
|
||||
}
|
||||
|
||||
export function useValidMonths(
|
||||
state: CalendarState,
|
||||
formatter: Intl.DateTimeFormat,
|
||||
) {
|
||||
const months = [];
|
||||
const numMonths = state.focusedDate.calendar.getMonthsInYear(
|
||||
state.focusedDate,
|
||||
);
|
||||
|
||||
for (let i = 1; i <= numMonths; i++) {
|
||||
const date = state.focusedDate.set({ month: i });
|
||||
|
||||
// Skip months outside valid range
|
||||
if (state.minValue && date.compare(state.minValue) < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state.maxValue && date.compare(state.maxValue) > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
months.push(formatter.format(date.toDate(state.timeZone)));
|
||||
}
|
||||
|
||||
return months;
|
||||
}
|
||||
|
|
@ -79,7 +79,6 @@ export const DatePicker = <T extends DateValue>(props: DatePickerProps<T>) => {
|
|||
isLoading={isLoading}
|
||||
size={size}
|
||||
/>
|
||||
<FieldError>{errorMessage}</FieldError>
|
||||
<Popover
|
||||
UNSTABLE_portalContainer={root}
|
||||
className={clsx(datePickerStyles.popover, popoverClassName)}
|
||||
|
|
@ -103,6 +102,7 @@ export const DatePicker = <T extends DateValue>(props: DatePickerProps<T>) => {
|
|||
)}
|
||||
</Dialog>
|
||||
</Popover>
|
||||
<FieldError>{errorMessage}</FieldError>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,12 @@ export function FieldLabel(props: LabelProps) {
|
|||
className={clsx(styles.label)}
|
||||
elementType="label"
|
||||
>
|
||||
<Text fontWeight={600} size="caption">
|
||||
<Text
|
||||
fontWeight={600}
|
||||
lineClamp={1}
|
||||
size="caption"
|
||||
title={children?.toString()}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
{Boolean(isRequired) && (
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { mergeRefs } from "@react-aria/utils";
|
|||
import React, { forwardRef, useRef, useState } from "react";
|
||||
import { getTypographyClassName } from "@appsmith/wds-theming";
|
||||
import { IconButton, Spinner, type IconProps } from "@appsmith/wds";
|
||||
import { Group, Input as HeadlessInput } from "react-aria-components";
|
||||
import { Input as HeadlessInput } from "react-aria-components";
|
||||
|
||||
import styles from "./styles.module.css";
|
||||
import type { InputProps } from "./types";
|
||||
|
|
@ -49,7 +49,7 @@ function _Input(props: InputProps, ref: React.Ref<HTMLInputElement>) {
|
|||
})();
|
||||
|
||||
return (
|
||||
<Group className={styles.inputGroup}>
|
||||
<div className={styles.inputGroup}>
|
||||
{Boolean(prefix) && (
|
||||
<span data-input-prefix onClick={() => localRef.current?.focus()}>
|
||||
{prefix}
|
||||
|
|
@ -74,7 +74,7 @@ function _Input(props: InputProps, ref: React.Ref<HTMLInputElement>) {
|
|||
{suffix}
|
||||
</span>
|
||||
)}
|
||||
</Group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
padding-block: var(--inner-spacing-1);
|
||||
padding-inline: var(--inner-spacing-2);
|
||||
box-sizing: content-box;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.inputGroup:has([data-input-prefix]) .input {
|
||||
|
|
@ -63,10 +64,18 @@
|
|||
margin-inline-start: var(--inner-spacing-2);
|
||||
}
|
||||
|
||||
.inputGroup:has(.input[data-size="small"]) [data-input-prefix] {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
.inputGroup [data-input-suffix] {
|
||||
margin-inline-end: var(--inner-spacing-2);
|
||||
}
|
||||
|
||||
.inputGroup:has(.input[data-size="small"]) [data-input-suffix] {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.inputGroup :is([data-input-suffix], [data-input-prefix]) button {
|
||||
border-radius: calc(
|
||||
var(--border-radius-elevation-3) - var(--inner-spacing-1)
|
||||
|
|
@ -84,7 +93,7 @@
|
|||
* HOVERED
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
.inputGroup[data-hovered]:has(
|
||||
.inputGroup:is([data-hovered], :has([data-hovered])):has(
|
||||
> .input:not(
|
||||
:is(
|
||||
[data-focused],
|
||||
|
|
@ -111,6 +120,9 @@
|
|||
|
||||
.inputGroup input[data-readonly] {
|
||||
padding-inline: 0;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
/** Reason for doing this is because for readonly inputs, we want focus state to be wider than the component width */
|
||||
|
|
@ -143,8 +155,9 @@
|
|||
* DISABLED
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
.inputGroup:has(> .input[data-disabled]),
|
||||
.inputGroup:has(> .input:has(~ input[data-disabled])) {
|
||||
.inputGroup:has(
|
||||
:is(.input[data-disabled], .input:has(~ input[data-disabled]))
|
||||
) {
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import clsx from "clsx";
|
|||
import React from "react";
|
||||
import { Icon, Spinner, textInputStyles } from "@appsmith/wds";
|
||||
import { getTypographyClassName } from "@appsmith/wds-theming";
|
||||
import { Button, Group, SelectValue } from "react-aria-components";
|
||||
import { Button, SelectValue } from "react-aria-components";
|
||||
|
||||
import styles from "./styles.module.css";
|
||||
import type { SelectProps } from "./types";
|
||||
|
|
@ -19,9 +19,7 @@ export const SelectTrigger: React.FC<SelectTriggerProps> = (props) => {
|
|||
const { isDisabled, isInvalid, isLoading, placeholder, size } = props;
|
||||
|
||||
return (
|
||||
<Group
|
||||
className={clsx(textInputStyles.inputGroup, styles.selectInputGroup)}
|
||||
>
|
||||
<div className={clsx(textInputStyles.inputGroup, styles.selectInputGroup)}>
|
||||
<Button
|
||||
className={clsx(textInputStyles.input, styles.selectTriggerButton)}
|
||||
data-invalid={Boolean(isInvalid) ? "" : undefined}
|
||||
|
|
@ -44,6 +42,6 @@ export const SelectTrigger: React.FC<SelectTriggerProps> = (props) => {
|
|||
)}
|
||||
</span>
|
||||
</Button>
|
||||
</Group>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ const _Text = (props: TextProps, ref: Ref<HTMLDivElement>) => {
|
|||
fontStyle: isItalic ? "italic" : "normal",
|
||||
wordBreak,
|
||||
textAlign,
|
||||
whiteSpace: "pre-wrap",
|
||||
...style,
|
||||
}}
|
||||
{...rest}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class WDSBaseInputWidget<
|
|||
static getMetaPropertiesMap(): Record<string, any> {
|
||||
return {
|
||||
rawText: undefined,
|
||||
parsedText: undefined,
|
||||
text: undefined,
|
||||
isFocused: false,
|
||||
isDirty: false,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ export const autocompleteConfig = {
|
|||
"!doc":
|
||||
"An input text field is used to capture a currency value. Inputs are used in forms and can have custom validations.",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/currency-input",
|
||||
parsedText: {
|
||||
text: {
|
||||
"!type": "string",
|
||||
"!doc": "The formatted text value of the input",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/currency-input",
|
||||
},
|
||||
rawText: {
|
||||
"!type": "number",
|
||||
"!doc": "The value of the input",
|
||||
"!doc": "The raw text value of the input",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/currency-input",
|
||||
},
|
||||
isValid: "bool",
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget<
|
|||
static getMetaPropertiesMap(): Record<string, any> {
|
||||
return _.merge(super.getMetaPropertiesMap(), {
|
||||
rawText: "",
|
||||
parsedText: "",
|
||||
text: "",
|
||||
currencyCode: undefined,
|
||||
});
|
||||
}
|
||||
|
|
@ -139,7 +139,7 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget<
|
|||
return _.merge(super.getDefaultPropertiesMap(), {
|
||||
currencyCode: "defaultCurrencyCode",
|
||||
rawText: "defaultText",
|
||||
parsedText: "defaultText",
|
||||
text: "defaultText",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -153,9 +153,9 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget<
|
|||
|
||||
componentDidUpdate(prevProps: CurrencyInputWidgetProps) {
|
||||
if (
|
||||
prevProps.text !== this.props.parsedText &&
|
||||
prevProps.text !== this.props.text &&
|
||||
!this.props.isFocused &&
|
||||
this.props.parsedText === String(this.props.defaultText)
|
||||
this.props.text === String(this.props.defaultText)
|
||||
) {
|
||||
this.formatText();
|
||||
}
|
||||
|
|
@ -192,7 +192,7 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget<
|
|||
Sentry.captureException(e);
|
||||
}
|
||||
|
||||
this.props.updateWidgetMetaProperty("parsedText", String(formattedValue));
|
||||
this.props.updateWidgetMetaProperty("text", String(formattedValue));
|
||||
|
||||
this.props.updateWidgetMetaProperty("rawText", value, {
|
||||
triggerPropertyName: "onTextChanged",
|
||||
|
|
@ -214,13 +214,13 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget<
|
|||
|
||||
try {
|
||||
if (isFocused) {
|
||||
const text = this.props.parsedText || "";
|
||||
const text = this.props.text || "";
|
||||
const deFormattedValue = text.replace(
|
||||
new RegExp("\\" + getLocaleThousandSeparator(), "g"),
|
||||
"",
|
||||
);
|
||||
|
||||
this.props.updateWidgetMetaProperty("parsedText", deFormattedValue);
|
||||
this.props.updateWidgetMetaProperty("text", deFormattedValue);
|
||||
this.props.updateWidgetMetaProperty("isFocused", isFocused, {
|
||||
triggerPropertyName: "onFocus",
|
||||
dynamicString: this.props.onFocus,
|
||||
|
|
@ -229,13 +229,13 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget<
|
|||
},
|
||||
});
|
||||
} else {
|
||||
if (this.props.parsedText) {
|
||||
if (this.props.text) {
|
||||
const formattedValue = formatCurrencyNumber(
|
||||
this.props.decimals,
|
||||
this.props.parsedText,
|
||||
this.props.text,
|
||||
);
|
||||
|
||||
this.props.updateWidgetMetaProperty("parsedText", formattedValue);
|
||||
this.props.updateWidgetMetaProperty("text", formattedValue);
|
||||
}
|
||||
|
||||
this.props.updateWidgetMetaProperty("isFocused", isFocused, {
|
||||
|
|
@ -249,7 +249,7 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget<
|
|||
} catch (e) {
|
||||
log.error(e);
|
||||
Sentry.captureException(e);
|
||||
this.props.updateWidgetMetaProperty("parsedText", this.props.parsedText);
|
||||
this.props.updateWidgetMetaProperty("text", this.props.text);
|
||||
}
|
||||
|
||||
super.onFocusChange(!!isFocused);
|
||||
|
|
@ -294,13 +294,13 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget<
|
|||
};
|
||||
|
||||
isTextFormatted = () => {
|
||||
return this.props.parsedText.includes(getLocaleThousandSeparator());
|
||||
return this.props.text.includes(getLocaleThousandSeparator());
|
||||
};
|
||||
|
||||
formatText() {
|
||||
if (!!this.props.parsedText && !this.isTextFormatted()) {
|
||||
if (!!this.props.text && !this.isTextFormatted()) {
|
||||
try {
|
||||
const floatVal = parseFloat(this.props.parsedText);
|
||||
const floatVal = parseFloat(this.props.text);
|
||||
|
||||
const formattedValue = Intl.NumberFormat(getLocale(), {
|
||||
style: "decimal",
|
||||
|
|
@ -308,7 +308,7 @@ class WDSCurrencyInputWidget extends WDSBaseInputWidget<
|
|||
maximumFractionDigits: this.props.decimals,
|
||||
}).format(floatVal);
|
||||
|
||||
this.props.updateWidgetMetaProperty("parsedText", formattedValue);
|
||||
this.props.updateWidgetMetaProperty("text", formattedValue);
|
||||
} catch (e) {
|
||||
log.error(e);
|
||||
Sentry.captureException(e);
|
||||
|
|
|
|||
|
|
@ -4,9 +4,14 @@ export const autocompleteConfig = {
|
|||
"!doc":
|
||||
"An input text field is used to capture a users textual input such as their names, numbers, emails etc. Inputs are used in forms and can have custom validations.",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/input",
|
||||
parsedText: {
|
||||
text: {
|
||||
"!type": "string",
|
||||
"!doc": "The text value of the input",
|
||||
"!doc": "The parsed text value of the input",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/input",
|
||||
},
|
||||
rawText: {
|
||||
"!type": "string",
|
||||
"!doc": "The raw text value of the input",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/input",
|
||||
},
|
||||
isValid: "bool",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export const settersConfig = {
|
|||
setValue: {
|
||||
path: "defaultText",
|
||||
type: "string",
|
||||
accessor: "parsedText",
|
||||
accessor: "text",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -69,11 +69,11 @@ export const validateInput = (props: InputWidgetProps): Validation => {
|
|||
maxChars,
|
||||
maxNum,
|
||||
minNum,
|
||||
parsedText,
|
||||
rawText,
|
||||
text,
|
||||
} = props;
|
||||
|
||||
if (isDirty && isRequired && !isNil(parsedText) && parsedText.length === 0) {
|
||||
if (isDirty && isRequired && !isNil(text) && text.length === 0) {
|
||||
return {
|
||||
validationStatus: "invalid",
|
||||
errorMessage: createMessage(FIELD_REQUIRED_ERROR),
|
||||
|
|
@ -81,7 +81,7 @@ export const validateInput = (props: InputWidgetProps): Validation => {
|
|||
}
|
||||
|
||||
if (isInputTypeSingleLineOrMultiLine(inputType) && maxChars) {
|
||||
if (parsedText && parsedText.toString().length > maxChars) {
|
||||
if (text && text.toString().length > maxChars) {
|
||||
return {
|
||||
validationStatus: "invalid",
|
||||
errorMessage: createMessage(INPUT_TEXT_MAX_CHAR_ERROR, maxChars),
|
||||
|
|
@ -97,16 +97,16 @@ export const validateInput = (props: InputWidgetProps): Validation => {
|
|||
};
|
||||
}
|
||||
|
||||
if (rawText !== "" && isNumber(parsedText)) {
|
||||
if (rawText !== "" && isNumber(text)) {
|
||||
// check the default text is neither greater than max nor less than min value.
|
||||
if (!isNil(minNum) && minNum > parsedText) {
|
||||
if (!isNil(minNum) && minNum > text) {
|
||||
return {
|
||||
validationStatus: "invalid",
|
||||
errorMessage: createMessage(INPUT_DEFAULT_TEXT_MIN_NUM_ERROR),
|
||||
};
|
||||
}
|
||||
|
||||
if (!isNil(maxNum) && maxNum < parsedText) {
|
||||
if (!isNil(maxNum) && maxNum < text) {
|
||||
return {
|
||||
validationStatus: "invalid",
|
||||
errorMessage: createMessage(INPUT_DEFAULT_TEXT_MAX_NUM_ERROR),
|
||||
|
|
|
|||
|
|
@ -62,14 +62,14 @@ class WDSInputWidget extends WDSBaseInputWidget<InputWidgetProps, WidgetState> {
|
|||
static getMetaPropertiesMap(): Record<string, any> {
|
||||
return merge(super.getMetaPropertiesMap(), {
|
||||
rawText: "",
|
||||
parsedText: "",
|
||||
text: "",
|
||||
});
|
||||
}
|
||||
|
||||
static getDefaultPropertiesMap(): Record<string, string> {
|
||||
return {
|
||||
rawText: "defaultText",
|
||||
parsedText: "defaultText",
|
||||
text: "defaultText",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -157,17 +157,17 @@ class WDSInputWidget extends WDSBaseInputWidget<InputWidgetProps, WidgetState> {
|
|||
|
||||
if (
|
||||
prevProps.rawText !== this.props.rawText &&
|
||||
this.props.rawText !== toString(this.props.parsedText)
|
||||
this.props.rawText !== toString(this.props.text)
|
||||
) {
|
||||
pushBatchMetaUpdates(
|
||||
"parsedText",
|
||||
"text",
|
||||
parseText(this.props.rawText, this.props.inputType),
|
||||
);
|
||||
}
|
||||
|
||||
if (prevProps.inputType !== this.props.inputType) {
|
||||
pushBatchMetaUpdates(
|
||||
"parsedText",
|
||||
"text",
|
||||
parseText(this.props.rawText, this.props.inputType),
|
||||
);
|
||||
}
|
||||
|
|
@ -190,7 +190,7 @@ class WDSInputWidget extends WDSBaseInputWidget<InputWidgetProps, WidgetState> {
|
|||
// derived properties won't work as expected inside a List widget.
|
||||
// TODO(Balaji): Once we refactor the List widget, need to conver
|
||||
// text to a derived property.
|
||||
pushBatchMetaUpdates("parsedText", parseText(value, this.props.inputType));
|
||||
pushBatchMetaUpdates("text", parseText(value, this.props.inputType));
|
||||
|
||||
pushBatchMetaUpdates("rawText", value, {
|
||||
triggerPropertyName: "onTextChanged",
|
||||
|
|
@ -211,7 +211,7 @@ class WDSInputWidget extends WDSBaseInputWidget<InputWidgetProps, WidgetState> {
|
|||
const { commitBatchMetaUpdates, pushBatchMetaUpdates } = this.props;
|
||||
|
||||
pushBatchMetaUpdates("rawText", "");
|
||||
pushBatchMetaUpdates("parsedText", parseText("", this.props.inputType));
|
||||
pushBatchMetaUpdates("text", parseText("", this.props.inputType));
|
||||
commitBatchMetaUpdates();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export const autocompleteConfig = {
|
|||
"!doc":
|
||||
"An input text field is used to capture a phone number. Inputs are used in forms and can have custom validations.",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/phone-input",
|
||||
parsedText: {
|
||||
text: {
|
||||
"!type": "string",
|
||||
"!doc": "The formatted text value of the input",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/phone-input",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { ISDCodeOptions } from "constants/ISDCodes_v2";
|
|||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function validateInput(props: any) {
|
||||
const value = props.parsedText ?? "";
|
||||
const value = props.text ?? "";
|
||||
const isInvalid = "isValid" in props && !props.isValid && !!props.isDirty;
|
||||
|
||||
// TODO: Fix this the next time the file is edited
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ class WDSPhoneInputWidget extends WDSBaseInputWidget<
|
|||
static getMetaPropertiesMap(): Record<string, any> {
|
||||
return merge(super.getMetaPropertiesMap(), {
|
||||
rawText: "",
|
||||
parsedText: "",
|
||||
text: "",
|
||||
dialCode: undefined,
|
||||
});
|
||||
}
|
||||
|
|
@ -126,7 +126,7 @@ class WDSPhoneInputWidget extends WDSBaseInputWidget<
|
|||
return merge(super.getDefaultPropertiesMap(), {
|
||||
dialCode: "defaultDialCode",
|
||||
rawText: "defaultText",
|
||||
parsedText: "defaultText",
|
||||
text: "defaultText",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ class WDSPhoneInputWidget extends WDSBaseInputWidget<
|
|||
const formattedValue = this.getFormattedPhoneNumber(this.props.rawText);
|
||||
|
||||
this.props.updateWidgetMetaProperty("rawText", this.props.rawText);
|
||||
this.props.updateWidgetMetaProperty("parsedText", formattedValue);
|
||||
this.props.updateWidgetMetaProperty("text", formattedValue);
|
||||
} catch (e) {
|
||||
log.error(e);
|
||||
Sentry.captureException(e);
|
||||
|
|
@ -176,24 +176,22 @@ class WDSPhoneInputWidget extends WDSBaseInputWidget<
|
|||
if (prevProps.allowFormatting !== this.props.allowFormatting) {
|
||||
const formattedValue = this.getFormattedPhoneNumber(this.props.rawText);
|
||||
|
||||
this.props.updateWidgetMetaProperty("parsedText", formattedValue);
|
||||
this.props.updateWidgetMetaProperty("text", formattedValue);
|
||||
}
|
||||
|
||||
// When the default text changes
|
||||
if (
|
||||
prevProps.parsedText !== this.props.parsedText &&
|
||||
this.props.parsedText === this.props.defaultText
|
||||
prevProps.text !== this.props.text &&
|
||||
this.props.text === this.props.defaultText
|
||||
) {
|
||||
const formattedValue = this.getFormattedPhoneNumber(
|
||||
this.props.parsedText,
|
||||
);
|
||||
const formattedValue = this.getFormattedPhoneNumber(this.props.text);
|
||||
|
||||
if (formattedValue) {
|
||||
this.props.updateWidgetMetaProperty(
|
||||
"rawText",
|
||||
parseIncompletePhoneNumber(formattedValue),
|
||||
);
|
||||
this.props.updateWidgetMetaProperty("parsedText", formattedValue);
|
||||
this.props.updateWidgetMetaProperty("text", formattedValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -214,7 +212,7 @@ class WDSPhoneInputWidget extends WDSBaseInputWidget<
|
|||
if (this.props.rawText && this.props.allowFormatting) {
|
||||
const formattedValue = this.getFormattedPhoneNumber(this.props.rawText);
|
||||
|
||||
this.props.updateWidgetMetaProperty("parsedText", formattedValue);
|
||||
this.props.updateWidgetMetaProperty("text", formattedValue);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -222,7 +220,7 @@ class WDSPhoneInputWidget extends WDSBaseInputWidget<
|
|||
let formattedValue;
|
||||
|
||||
// Don't format, as value is typed, when user is deleting
|
||||
if (value && value.length > this.props.parsedText?.length) {
|
||||
if (value && value.length > this.props.text?.length) {
|
||||
formattedValue = this.getFormattedPhoneNumber(value);
|
||||
} else {
|
||||
formattedValue = value;
|
||||
|
|
@ -232,7 +230,7 @@ class WDSPhoneInputWidget extends WDSBaseInputWidget<
|
|||
"rawText",
|
||||
parseIncompletePhoneNumber(formattedValue),
|
||||
);
|
||||
this.props.updateWidgetMetaProperty("parsedText", formattedValue, {
|
||||
this.props.updateWidgetMetaProperty("text", formattedValue, {
|
||||
triggerPropertyName: "onTextChanged",
|
||||
dynamicString: this.props.onTextChanged,
|
||||
event: {
|
||||
|
|
@ -300,7 +298,7 @@ class WDSPhoneInputWidget extends WDSBaseInputWidget<
|
|||
};
|
||||
|
||||
getWidgetView() {
|
||||
const rawText = this.props.parsedText ?? "";
|
||||
const rawText = this.props.text ?? "";
|
||||
|
||||
const validation = validateInput(this.props);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user