chore: add wds datepicker widget (#37711)
 /ok-to-test tags="@tag.Anvil" <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes - **New Features** - Introduced the WDS Date Picker Widget, enhancing date selection capabilities within the UI. - Added configuration options for widget size, visibility, and autocomplete behavior. - Implemented comprehensive validation for date input, ensuring accurate user selections. - Expanded widget collection to include the new WDS Date Picker Widget. - Introduced new constants for date format options, facilitating diverse formatting choices. - Added support for a new "Date" input type, enhancing input widget configurability. - **Documentation** - Updated property pane configurations to include detailed settings for date format, validation, and event handling. - **Bug Fixes** - Improved handling of derived properties to ensure proper context during widget interactions. These updates collectively improve user experience and flexibility in date selection within the application. <!-- 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/12083004714> > Commit: b17348e03db911501970d2c8a59c4fea30a175e1 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12083004714&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.Anvil` > Spec: > <hr>Fri, 29 Nov 2024 10:44:39 UTC <!-- end of auto-generated comment: Cypress test results --> --------- Co-authored-by: Vadim Vaitenko <vadim@appsmith.com>
This commit is contained in:
parent
8a369e1096
commit
63eec76635
|
|
@ -61,6 +61,10 @@
|
|||
}
|
||||
|
||||
.calendar tbody [role="button"][data-focus-visible] {
|
||||
outline: var(--border-width-2) solid var(--color-bd-accent);
|
||||
outline-offset: var(--border-width-2);
|
||||
--box-shadow-offset: 2px;
|
||||
|
||||
box-shadow:
|
||||
0 0 0 var(--box-shadow-offset) var(--color-bg),
|
||||
0 0 0 calc(var(--box-shadow-offset) + var(--border-width-2))
|
||||
var(--color-bd-focus);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ export const INPUT_TYPES = {
|
|||
PASSWORD: "PASSWORD",
|
||||
PHONE_NUMBER: "PHONE_NUMBER",
|
||||
MULTI_LINE_TEXT: "MULTI_LINE_TEXT",
|
||||
DATE: "DATE",
|
||||
} as const;
|
||||
|
||||
export const INPUT_TYPE_TO_WIDGET_TYPE_MAP = {
|
||||
|
|
@ -16,4 +17,5 @@ export const INPUT_TYPE_TO_WIDGET_TYPE_MAP = {
|
|||
[INPUT_TYPES.MULTI_LINE_TEXT]: "WDS_MULTILINE_INPUT_WIDGET",
|
||||
[INPUT_TYPES.CURRENCY]: "WDS_CURRENCY_INPUT_WIDGET",
|
||||
[INPUT_TYPES.PHONE_NUMBER]: "WDS_PHONE_INPUT_WIDGET",
|
||||
[INPUT_TYPES.DATE]: "WDS_DATEPICKER_WIDGET",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { WDSBaseInputWidget } from "modules/ui-builder/ui/wds/WDSBaseInputWidget";
|
||||
import {
|
||||
INPUT_TYPES,
|
||||
WDSBaseInputWidget,
|
||||
} from "modules/ui-builder/ui/wds/WDSBaseInputWidget";
|
||||
import { ResponsiveBehavior } from "layoutSystems/common/utils/constants";
|
||||
import type { WidgetDefaultProps } from "WidgetProvider/constants";
|
||||
|
||||
|
|
@ -14,4 +17,5 @@ export const defaultsConfig = {
|
|||
showStepArrows: false,
|
||||
label: "Current Price",
|
||||
responsiveBehavior: ResponsiveBehavior.Fill,
|
||||
inputType: INPUT_TYPES.CURRENCY,
|
||||
} as WidgetDefaultProps;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import type { AnvilConfig } from "WidgetProvider/constants";
|
||||
|
||||
export const anvilConfig: AnvilConfig = {
|
||||
isLargeWidget: false,
|
||||
widgetSize: {
|
||||
minWidth: {
|
||||
base: "100%",
|
||||
"180px": "sizing-30",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import { DefaultAutocompleteDefinitions } from "widgets/WidgetUtils";
|
||||
|
||||
export const autocompleteConfig = {
|
||||
"!doc":
|
||||
"Datepicker is used to capture the date and time from a user. It can be used to filter data base on the input date range as well as to capture personal information such as date of birth",
|
||||
"!url": "https://docs.appsmith.com/widget-reference/datepicker",
|
||||
isVisible: DefaultAutocompleteDefinitions.isVisible,
|
||||
selectedDate: "string",
|
||||
formattedDate: "string",
|
||||
isDisabled: "bool",
|
||||
};
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import { ResponsiveBehavior } from "layoutSystems/common/utils/constants";
|
||||
import type { WidgetDefaultProps } from "WidgetProvider/constants";
|
||||
import { INPUT_TYPES } from "modules/ui-builder/ui/wds/WDSBaseInputWidget";
|
||||
|
||||
export const defaultsConfig = {
|
||||
animateLoading: true,
|
||||
label: "Label",
|
||||
dateFormat: "YYYY-MM-DD HH:mm",
|
||||
defaultOptionValue: "",
|
||||
isRequired: false,
|
||||
isDisabled: false,
|
||||
isVisible: true,
|
||||
isInline: false,
|
||||
widgetName: "DatePicker",
|
||||
widgetType: "WDS_DATE_PICKER",
|
||||
version: 1,
|
||||
timePrecision: "day",
|
||||
responsiveBehavior: ResponsiveBehavior.Fill,
|
||||
inputType: INPUT_TYPES.DATE,
|
||||
} as unknown as WidgetDefaultProps;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
export * from "./propertyPaneConfig";
|
||||
export { metaConfig } from "./metaConfig";
|
||||
export { anvilConfig } from "./anvilConfig";
|
||||
export { defaultsConfig } from "./defaultsConfig";
|
||||
export { settersConfig } from "./settersConfig";
|
||||
export { methodsConfig } from "./methodsConfig";
|
||||
export { autocompleteConfig } from "./autocompleteConfig";
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { WIDGET_TAGS } from "constants/WidgetConstants";
|
||||
|
||||
export const metaConfig = {
|
||||
name: "DatePicker",
|
||||
tags: [WIDGET_TAGS.INPUTS],
|
||||
needsMeta: true,
|
||||
searchTags: [
|
||||
"datepicker",
|
||||
"appointment",
|
||||
"calendar",
|
||||
"date",
|
||||
"day",
|
||||
"hour",
|
||||
"meeting",
|
||||
"moment",
|
||||
"schedule",
|
||||
"time",
|
||||
"week",
|
||||
"year",
|
||||
],
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import { DatePickerIcon, DatePickerThumbnail } from "appsmith-icons";
|
||||
|
||||
export const methodsConfig = {
|
||||
IconCmp: DatePickerIcon,
|
||||
ThumbnailCmp: DatePickerThumbnail,
|
||||
};
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
import { ValidationTypes } from "constants/WidgetValidation";
|
||||
import { DATE_FORMAT_OPTIONS } from "../../constants";
|
||||
|
||||
import { propertyPaneContentConfig as WdsInputWidgetPropertyPaneContentConfig } from "modules/ui-builder/ui/wds/WDSInputWidget/config/propertyPaneConfig/contentConfig";
|
||||
import type { PropertyPaneConfig } from "constants/PropertyControlConstants";
|
||||
|
||||
const inputTypeSectionConfig = WdsInputWidgetPropertyPaneContentConfig.find(
|
||||
(config) => config.sectionName === "Type",
|
||||
);
|
||||
|
||||
export const propertyPaneContentConfig = [
|
||||
inputTypeSectionConfig,
|
||||
{
|
||||
sectionName: "Data",
|
||||
children: [
|
||||
{
|
||||
helpText: "Sets the format of the selected date",
|
||||
propertyName: "dateFormat",
|
||||
label: "Date format",
|
||||
controlType: "DROP_DOWN",
|
||||
isJSConvertible: true,
|
||||
optionWidth: "340px",
|
||||
options: DATE_FORMAT_OPTIONS,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.TEXT },
|
||||
hideSubText: true,
|
||||
},
|
||||
{
|
||||
propertyName: "defaultDate",
|
||||
label: "Default Date",
|
||||
helpText:
|
||||
"Sets the default date of the widget. The date is updated if the default date changes",
|
||||
controlType: "DATE_PICKER",
|
||||
placeholderText: "Enter Default Date",
|
||||
useValidationMessage: true,
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.DATE_ISO_STRING },
|
||||
},
|
||||
{
|
||||
propertyName: "timePrecision",
|
||||
label: "Time Precision",
|
||||
controlType: "DROP_DOWN",
|
||||
helpText: "Sets the time precision or hides the time picker.",
|
||||
defaultValue: "day",
|
||||
options: [
|
||||
{
|
||||
label: "Day",
|
||||
value: "day",
|
||||
},
|
||||
{
|
||||
label: "Hour",
|
||||
value: "hour",
|
||||
},
|
||||
{
|
||||
label: "Minute",
|
||||
value: "minute",
|
||||
},
|
||||
{
|
||||
label: "Second",
|
||||
value: "second",
|
||||
},
|
||||
],
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: {
|
||||
type: ValidationTypes.TEXT,
|
||||
params: {
|
||||
allowedValues: ["day", "hour", "minute", "second"],
|
||||
default: "day",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionName: "Label",
|
||||
children: [
|
||||
{
|
||||
helpText: "Sets the label text of the date picker widget",
|
||||
propertyName: "label",
|
||||
label: "Text",
|
||||
controlType: "INPUT_TEXT",
|
||||
placeholderText: "Label",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.TEXT },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionName: "Validations",
|
||||
children: [
|
||||
{
|
||||
propertyName: "isRequired",
|
||||
label: "Required",
|
||||
helpText: "Makes input to the widget mandatory",
|
||||
controlType: "SWITCH",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.BOOLEAN },
|
||||
},
|
||||
{
|
||||
propertyName: "minDate",
|
||||
label: "Minimum Date",
|
||||
helpText: "Sets the minimum date that can be selected",
|
||||
controlType: "DATE_PICKER",
|
||||
placeholderText: "Enter Minimum Date",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.DATE_ISO_STRING },
|
||||
},
|
||||
{
|
||||
propertyName: "maxDate",
|
||||
label: "Maximum Date",
|
||||
helpText: "Sets the maximum date that can be selected",
|
||||
controlType: "DATE_PICKER",
|
||||
placeholderText: "Enter Maximum Date",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.DATE_ISO_STRING },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionName: "General",
|
||||
children: [
|
||||
{
|
||||
helpText: "Shows help text or details about the current input",
|
||||
propertyName: "labelTooltip",
|
||||
label: "Tooltip",
|
||||
controlType: "INPUT_TEXT",
|
||||
placeholderText: "",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.TEXT },
|
||||
},
|
||||
{
|
||||
helpText: "Controls the visibility of the widget",
|
||||
propertyName: "isVisible",
|
||||
label: "Visible",
|
||||
controlType: "SWITCH",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.BOOLEAN },
|
||||
},
|
||||
{
|
||||
propertyName: "isDisabled",
|
||||
label: "Disabled",
|
||||
helpText: "Disables input to this widget",
|
||||
controlType: "SWITCH",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.BOOLEAN },
|
||||
},
|
||||
{
|
||||
propertyName: "animateLoading",
|
||||
label: "Animate loading",
|
||||
controlType: "SWITCH",
|
||||
helpText: "Controls the loading of the widget",
|
||||
defaultValue: true,
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.BOOLEAN },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionName: "Events",
|
||||
children: [
|
||||
{
|
||||
propertyName: "onDateSelected",
|
||||
label: "onDateSelected",
|
||||
helpText: "when a date is selected in the calendar",
|
||||
controlType: "ACTION_SELECTOR",
|
||||
isJSConvertible: true,
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
] as PropertyPaneConfig[];
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { propertyPaneContentConfig } from "./contentConfig";
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
export const settersConfig = {
|
||||
__setters: {
|
||||
setVisibility: {
|
||||
path: "isVisible",
|
||||
type: "boolean",
|
||||
},
|
||||
setDisabled: {
|
||||
path: "isDisabled",
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
import moment from "moment";
|
||||
import { SubTextPosition } from "components/constants";
|
||||
|
||||
export const DATE_FORMAT_OPTIONS = [
|
||||
{
|
||||
label: moment().format("YYYY-MM-DDTHH:mm:ss.sssZ"),
|
||||
subText: "ISO 8601",
|
||||
value: "YYYY-MM-DDTHH:mm:ss.sssZ",
|
||||
},
|
||||
{
|
||||
label: moment().format("LLL"),
|
||||
subText: "LLL",
|
||||
value: "LLL",
|
||||
},
|
||||
{
|
||||
label: moment().format("LL"),
|
||||
subText: "LL",
|
||||
value: "LL",
|
||||
},
|
||||
{
|
||||
label: moment().format("YYYY-MM-DD HH:mm"),
|
||||
subText: "YYYY-MM-DD HH:mm",
|
||||
value: "YYYY-MM-DD HH:mm",
|
||||
},
|
||||
{
|
||||
label: moment().format("YYYY-MM-DDTHH:mm:ss"),
|
||||
subText: "YYYY-MM-DDTHH:mm:ss",
|
||||
value: "YYYY-MM-DDTHH:mm:ss",
|
||||
},
|
||||
{
|
||||
label: moment().format("YYYY-MM-DD hh:mm:ss A"),
|
||||
subText: "YYYY-MM-DD hh:mm:ss A",
|
||||
value: "YYYY-MM-DD hh:mm:ss A",
|
||||
},
|
||||
{
|
||||
label: moment().format("DD/MM/YYYY HH:mm"),
|
||||
subText: "DD/MM/YYYY HH:mm",
|
||||
value: "DD/MM/YYYY HH:mm",
|
||||
},
|
||||
{
|
||||
label: moment().format("D MMMM, YYYY"),
|
||||
subText: "D MMMM, YYYY",
|
||||
value: "D MMMM, YYYY",
|
||||
},
|
||||
{
|
||||
label: moment().format("H:mm A D MMMM, YYYY"),
|
||||
subText: "H:mm A D MMMM, YYYY",
|
||||
value: "H:mm A D MMMM, YYYY",
|
||||
},
|
||||
{
|
||||
label: moment().format("YYYY-MM-DD"),
|
||||
subText: "YYYY-MM-DD",
|
||||
value: "YYYY-MM-DD",
|
||||
},
|
||||
{
|
||||
label: moment().format("MM-DD-YYYY"),
|
||||
subText: "MM-DD-YYYY",
|
||||
value: "MM-DD-YYYY",
|
||||
},
|
||||
{
|
||||
label: moment().format("DD-MM-YYYY"),
|
||||
subText: "DD-MM-YYYY",
|
||||
value: "DD-MM-YYYY",
|
||||
},
|
||||
{
|
||||
label: moment().format("MM/DD/YYYY"),
|
||||
subText: "MM/DD/YYYY",
|
||||
value: "MM/DD/YYYY",
|
||||
},
|
||||
{
|
||||
label: moment().format("DD/MM/YYYY"),
|
||||
subText: "DD/MM/YYYY",
|
||||
value: "DD/MM/YYYY",
|
||||
},
|
||||
{
|
||||
label: moment().format("DD/MM/YY"),
|
||||
subText: "DD/MM/YY",
|
||||
value: "DD/MM/YY",
|
||||
},
|
||||
{
|
||||
label: moment().format("MM/DD/YY"),
|
||||
subText: "MM/DD/YY",
|
||||
value: "MM/DD/YY",
|
||||
},
|
||||
].map((x) => ({
|
||||
...x,
|
||||
subTextPosition: SubTextPosition.BOTTOM,
|
||||
}));
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import { WDSDatePickerWidget } from "./widget";
|
||||
|
||||
export { WDSDatePickerWidget };
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
export default {
|
||||
isValid: (props, moment) => {
|
||||
const parsedMinDate = new Date(props.minDate);
|
||||
const parsedMaxDate = new Date(props.maxDate);
|
||||
const parsedSelectedDate = props.selectedDate
|
||||
? moment(new Date(props.selectedDate))
|
||||
: null;
|
||||
|
||||
// only do validation when the date is dirty
|
||||
if (!props.isDirty) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!parsedSelectedDate && !props.isRequired) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!parsedSelectedDate && props.isRequired) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (props.minDate && props.maxDate) {
|
||||
return parsedSelectedDate.isBetween(parsedMinDate, parsedMaxDate);
|
||||
}
|
||||
|
||||
if (props.minDate) {
|
||||
return parsedSelectedDate.isAfter(parsedMinDate);
|
||||
}
|
||||
|
||||
if (props.maxDate) {
|
||||
return parsedSelectedDate.isBefore(parsedMaxDate);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
import moment from "moment";
|
||||
import derived from "./derived";
|
||||
|
||||
describe("isValid function", () => {
|
||||
const mockMoment = (date: string) => moment(date);
|
||||
|
||||
it("should return true when isDirty is false", () => {
|
||||
const props = { isDirty: false };
|
||||
|
||||
expect(derived.isValid(props, mockMoment)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true when selectedDate is null and not required", () => {
|
||||
const props = { isDirty: true, isRequired: false, selectedDate: null };
|
||||
|
||||
expect(derived.isValid(props, mockMoment)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false when selectedDate is null and required", () => {
|
||||
const props = { isDirty: true, isRequired: true, selectedDate: null };
|
||||
|
||||
expect(derived.isValid(props, mockMoment)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true when selectedDate is between minDate and maxDate", () => {
|
||||
const props = {
|
||||
isDirty: true,
|
||||
minDate: "2023-01-01",
|
||||
maxDate: "2023-12-31",
|
||||
selectedDate: "2023-06-15",
|
||||
};
|
||||
|
||||
expect(derived.isValid(props, mockMoment)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false when selectedDate is before minDate", () => {
|
||||
const props = {
|
||||
isDirty: true,
|
||||
minDate: "2023-01-01",
|
||||
selectedDate: "2022-12-31",
|
||||
};
|
||||
|
||||
expect(derived.isValid(props, mockMoment)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false when selectedDate is after maxDate", () => {
|
||||
const props = {
|
||||
isDirty: true,
|
||||
maxDate: "2023-12-31",
|
||||
selectedDate: "2024-01-01",
|
||||
};
|
||||
|
||||
expect(derived.isValid(props, mockMoment)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true when selectedDate is valid and no min/max dates are set", () => {
|
||||
const props = {
|
||||
isDirty: true,
|
||||
selectedDate: "2023-06-15",
|
||||
};
|
||||
|
||||
expect(derived.isValid(props, mockMoment)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import type { WDSDatePickerWidgetProps } from "./types";
|
||||
|
||||
export function validateInput(props: WDSDatePickerWidgetProps) {
|
||||
if (props.isValid === false) {
|
||||
return {
|
||||
validationStatus: "invalid",
|
||||
errorMessage: "Please select a valid date",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
validationStatus: "valid",
|
||||
errorMessage: "",
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
import React from "react";
|
||||
import moment from "moment";
|
||||
import BaseWidget from "widgets/BaseWidget";
|
||||
import type { WidgetState } from "widgets/BaseWidget";
|
||||
import type {
|
||||
AnvilConfig,
|
||||
AutocompletionDefinitions,
|
||||
} from "WidgetProvider/constants";
|
||||
import { parseDateTime } from "@internationalized/date";
|
||||
import { DatePicker, type DateValue } from "@appsmith/wds";
|
||||
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
||||
|
||||
import * as config from "../config";
|
||||
import { validateInput } from "./helpers";
|
||||
import derivedPropertyFns from "./derived";
|
||||
import type { WDSDatePickerWidgetProps } from "./types";
|
||||
import { parseDerivedProperties } from "widgets/WidgetUtils";
|
||||
|
||||
class WDSDatePickerWidget extends BaseWidget<
|
||||
WDSDatePickerWidgetProps,
|
||||
WidgetState
|
||||
> {
|
||||
static type = "WDS_DATEPICKER_WIDGET";
|
||||
|
||||
static getConfig() {
|
||||
return config.metaConfig;
|
||||
}
|
||||
|
||||
static getDefaults() {
|
||||
return config.defaultsConfig;
|
||||
}
|
||||
|
||||
static getMethods() {
|
||||
return config.methodsConfig;
|
||||
}
|
||||
|
||||
static getAnvilConfig(): AnvilConfig | null {
|
||||
return config.anvilConfig;
|
||||
}
|
||||
|
||||
static getAutocompleteDefinitions(): AutocompletionDefinitions {
|
||||
return config.autocompleteConfig;
|
||||
}
|
||||
|
||||
static getPropertyPaneContentConfig() {
|
||||
return config.propertyPaneContentConfig;
|
||||
}
|
||||
|
||||
static getPropertyPaneStyleConfig() {
|
||||
return [];
|
||||
}
|
||||
|
||||
static getDerivedPropertiesMap() {
|
||||
const parsedDerivedProperties = parseDerivedProperties(derivedPropertyFns);
|
||||
|
||||
return {
|
||||
isValid: `{{(() => {${parsedDerivedProperties.isValid}})()}}`,
|
||||
selectedDate: `{{ this.value ? moment(this.value).toISOString() : "" }}`,
|
||||
formattedDate: `{{ this.value ? moment(this.value).format(this.dateFormat) : "" }}`,
|
||||
};
|
||||
}
|
||||
|
||||
static getDefaultPropertiesMap(): Record<string, string> {
|
||||
return {
|
||||
value: "defaultDate",
|
||||
};
|
||||
}
|
||||
|
||||
static getMetaPropertiesMap() {
|
||||
return {
|
||||
value: undefined,
|
||||
isDirty: false,
|
||||
};
|
||||
}
|
||||
|
||||
static getStylesheetConfig() {
|
||||
return {};
|
||||
}
|
||||
|
||||
static getSetterConfig() {
|
||||
return config.settersConfig;
|
||||
}
|
||||
|
||||
static getDependencyMap() {
|
||||
return {};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: WDSDatePickerWidgetProps): void {
|
||||
if (!this.shouldResetDirtyState(prevProps)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.resetDirtyState();
|
||||
}
|
||||
|
||||
handleDateChange = (date: DateValue) => {
|
||||
if (!this.props.isDirty) {
|
||||
this.props.updateWidgetMetaProperty("isDirty", true);
|
||||
}
|
||||
|
||||
this.props.updateWidgetMetaProperty("value", date.toString(), {
|
||||
triggerPropertyName: "onDateSelected",
|
||||
dynamicString: this.props.onDateSelected,
|
||||
event: {
|
||||
type: EventType.ON_DATE_SELECTED,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
private shouldResetDirtyState(prevProps: WDSDatePickerWidgetProps): boolean {
|
||||
const { defaultDate, isDirty } = this.props;
|
||||
const hasDefaultDateChanged = defaultDate !== prevProps.defaultDate;
|
||||
|
||||
return hasDefaultDateChanged && isDirty;
|
||||
}
|
||||
|
||||
private resetDirtyState() {
|
||||
this.props.updateWidgetMetaProperty("isDirty", false);
|
||||
}
|
||||
|
||||
private parseDate(date: string | undefined) {
|
||||
return date
|
||||
? parseDateTime(moment(date).format("YYYY-MM-DDTHH:mm:ss"))
|
||||
: undefined;
|
||||
}
|
||||
|
||||
getWidgetView() {
|
||||
const { label, labelTooltip, maxDate, minDate, value, ...rest } =
|
||||
this.props;
|
||||
const { errorMessage, validationStatus } = validateInput(this.props);
|
||||
|
||||
return (
|
||||
<DatePicker
|
||||
contextualHelp={labelTooltip}
|
||||
errorMessage={errorMessage}
|
||||
granularity={this.props.timePrecision}
|
||||
isInvalid={validationStatus === "invalid"}
|
||||
label={label}
|
||||
maxValue={this.parseDate(maxDate)}
|
||||
minValue={this.parseDate(minDate)}
|
||||
onChange={this.handleDateChange}
|
||||
value={this.parseDate(value)}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { WDSDatePickerWidget };
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import type { WidgetProps } from "widgets/BaseWidget";
|
||||
|
||||
export interface WDSDatePickerWidgetProps extends WidgetProps {
|
||||
selectedDate: string;
|
||||
defaultDate: string;
|
||||
onDateSelected: string;
|
||||
isRequired?: boolean;
|
||||
isDisabled?: boolean;
|
||||
label: string;
|
||||
labelTooltip?: string;
|
||||
}
|
||||
|
|
@ -48,6 +48,10 @@ export const propertyPaneContentConfig = [
|
|||
label: "Currency",
|
||||
value: "CURRENCY",
|
||||
},
|
||||
{
|
||||
label: "Date",
|
||||
value: "DATE",
|
||||
},
|
||||
],
|
||||
isBindProperty: false,
|
||||
isTriggerProperty: false,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { WDSBaseInputWidget } from "modules/ui-builder/ui/wds/WDSBaseInputWidget";
|
||||
import {
|
||||
INPUT_TYPES,
|
||||
WDSBaseInputWidget,
|
||||
} from "modules/ui-builder/ui/wds/WDSBaseInputWidget";
|
||||
import { ResponsiveBehavior } from "layoutSystems/common/utils/constants";
|
||||
import type { WidgetDefaultProps } from "WidgetProvider/constants";
|
||||
|
||||
|
|
@ -13,4 +16,5 @@ export const defaultsConfig = {
|
|||
allowFormatting: true,
|
||||
responsiveBehavior: ResponsiveBehavior.Fill,
|
||||
label: "Phone number",
|
||||
inputType: INPUT_TYPES.PHONE_NUMBER,
|
||||
} as WidgetDefaultProps;
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ export const WDS_V2_WIDGET_MAP = {
|
|||
MULTILINE_INPUT_WIDGET: "WDS_MULTILINE_INPUT_WIDGET",
|
||||
WDS_SELECT_WIDGET: "WDS_SELECT_WIDGET",
|
||||
WDS_COMBOBOX_WIDGET: "WDS_COMBOBOX_WIDGET",
|
||||
WDS_DATEPICKER_WIDGET: "WDS_DATEPICKER_WIDGET",
|
||||
|
||||
// Anvil layout widgets
|
||||
ZONE_WIDGET: anvilWidgets.ZONE_WIDGET,
|
||||
|
|
|
|||
|
|
@ -989,3 +989,48 @@ export const checkForOnClick = (e: React.MouseEvent<HTMLElement>) => {
|
|||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the derived properties from the given property functions. Used in getDerivedPropertiesMap
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* {
|
||||
* isValidDate: (props, moment, _) => {
|
||||
* return props.value === 1;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* It will return
|
||||
* ```js
|
||||
* {
|
||||
* isValidDate: "{{ this.value === 1 }}"
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Main rule to remember is don't deconstruct the props like `const { value } = props;` in the derived property function.
|
||||
* Directly access props like `props.value`
|
||||
*/
|
||||
export function parseDerivedProperties(propertyFns: Record<string, unknown>) {
|
||||
const derivedProperties: Record<string, string> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(propertyFns)) {
|
||||
if (typeof value === "function") {
|
||||
const functionString = value.toString();
|
||||
const functionBody = functionString.match(/(?<=\{)(.|\n)*(?=\})/)?.[0];
|
||||
|
||||
if (functionBody) {
|
||||
const paramMatch = functionString.match(/\((.*?),/);
|
||||
const propsParam = paramMatch ? paramMatch[1].trim() : "props";
|
||||
|
||||
const modifiedBody = functionBody
|
||||
.trim()
|
||||
.replace(new RegExp(`${propsParam}\\.`, "g"), "this.");
|
||||
|
||||
derivedProperties[key] = modifiedBody;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return derivedProperties;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ import { WDSNumberInputWidget } from "modules/ui-builder/ui/wds/WDSNumberInputWi
|
|||
import { WDSMultilineInputWidget } from "modules/ui-builder/ui/wds/WDSMultilineInputWidget";
|
||||
import { WDSSelectWidget } from "modules/ui-builder/ui/wds/WDSSelectWidget";
|
||||
import { EEWDSWidgets } from "ee/modules/ui-builder/ui/wds";
|
||||
import { WDSDatePickerWidget } from "modules/ui-builder/ui/wds/WDSDatePickerWidget";
|
||||
|
||||
const LegacyWidgets = [
|
||||
CanvasWidget,
|
||||
|
|
@ -185,6 +186,7 @@ const WDSWidgets = [
|
|||
WDSNumberInputWidget,
|
||||
WDSMultilineInputWidget,
|
||||
WDSSelectWidget,
|
||||
WDSDatePickerWidget,
|
||||
];
|
||||
|
||||
const Widgets = [
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user