diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/inputs/update-event-type.input.ts b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/inputs/update-event-type.input.ts index cf9c3c901c..1f1f5c50a7 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/inputs/update-event-type.input.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/inputs/update-event-type.input.ts @@ -177,6 +177,10 @@ export class BookingField_2024_04_15 { @ValidateNested({ each: true }) @Type(() => Source) sources?: Source[]; + + @IsOptional() + @IsBoolean() + disableOnPrefill?: boolean; } export class RecurringEvent_2024_04_15 { diff --git a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/outputs/get-event-type-public.output.ts b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/outputs/get-event-type-public.output.ts index 887e1f6bef..7d71d04965 100644 --- a/apps/api/v2/src/ee/event-types/event-types_2024_04_15/outputs/get-event-type-public.output.ts +++ b/apps/api/v2/src/ee/event-types/event-types_2024_04_15/outputs/get-event-type-public.output.ts @@ -87,6 +87,10 @@ class BookingField { @Type(() => Source) @IsOptional() sources?: Source[]; + + @IsOptional() + @IsBoolean() + disableOnPrefill?: boolean; } class Organization { diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index b04c5ea5ea..b30e514580 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -2561,5 +2561,6 @@ "number_of_options":"{{count}} options", "reschedule_with_same_round_robin_host_title": "Reschedule with same Round-Robin host", "reschedule_with_same_round_robin_host_description": "Rescheduled events will be assigned to the same host as initially scheduled", + "disable_input_if_prefilled": "Disable input if the URL identifier is prefilled", "ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑" } diff --git a/packages/app-store/routing-forms/components/FormInputFields.tsx b/packages/app-store/routing-forms/components/FormInputFields.tsx index 4a15f6550f..ad87020c33 100644 --- a/packages/app-store/routing-forms/components/FormInputFields.tsx +++ b/packages/app-store/routing-forms/components/FormInputFields.tsx @@ -47,7 +47,7 @@ export default function FormInputFields(props: Props) { ({ + __esModule: true, + default: () => { + return function MockSelect({ + options, + onChange, + value, + isMulti, + }: { + options: { value: string; label: string }[]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onChange: (value: any) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any; + isMulti: boolean; + }) { + return ( + + ); + }; + }, +})); + +describe("Select Widgets", () => { + describe("SelectWidget", () => { + const listValues = [ + { title: "Option 1", value: "1" }, + { title: "Option 2", value: "2" }, + { title: "Option 3", value: "3" }, + ]; + + it("should handle render the value in option correctly", () => { + const setValue = vi.fn(); + render(); + + const select = screen.getByTestId("mock-select"); + expect(select).toBeInTheDocument(); + expect(screen.getAllByRole("option")).toHaveLength(3); + expect(setValue).not.toHaveBeenCalled(); + }); + + it("should handle a value that is not in the list and reset the value to empty string", () => { + const setValue = vi.fn(); + render(); + + const select = screen.getByTestId("mock-select"); + expect(select).toBeInTheDocument(); + expect(screen.getAllByRole("option")).toHaveLength(3); + expect(setValue).toHaveBeenCalledWith(""); + }); + }); + + describe("MultiSelectWidget", () => { + const listValues = [ + { title: "Option 1", value: "1" }, + { title: "Option 2", value: "2" }, + { title: "Option 3", value: "3" }, + ]; + + it("renders options correctly", () => { + const setValue = vi.fn(); + + render(); + + const select = screen.getByTestId("mock-select"); + expect(select).toBeInTheDocument(); + expect(screen.getAllByRole("option")).toHaveLength(3); + expect(setValue).not.toHaveBeenCalled(); + }); + + it("sets value to empty array when no options match", () => { + const setValue = vi.fn(); + render(); + + expect(setValue).toHaveBeenCalledWith([]); + }); + }); +}); diff --git a/packages/app-store/routing-forms/components/react-awesome-query-builder/widgets.tsx b/packages/app-store/routing-forms/components/react-awesome-query-builder/widgets.tsx index 5065df70c7..0d7220b3b2 100644 --- a/packages/app-store/routing-forms/components/react-awesome-query-builder/widgets.tsx +++ b/packages/app-store/routing-forms/components/react-awesome-query-builder/widgets.tsx @@ -150,8 +150,6 @@ const MultiSelectWidget = ({ value, ...remainingProps }: SelectLikeComponentPropsRAQB) => { - //TODO: Use Select here. - //TODO: Let's set listValue itself as label and value instead of using title. if (!listValues) { return null; } @@ -164,6 +162,14 @@ const MultiSelectWidget = ({ const optionsFromList = selectItems.filter((item) => value?.includes(item.value)); + // If no value could be found in the list, then we set the value to undefined. + // This is to update the value back to the source that we couldn't set it. This is important otherwise the outside party thinks that the value is set but it is not. + // Do it only when it is not already empty, this is to avoid infinite state updates + // NOTE: value is some times sent as undefined even though the type will tell you that it can't be + if (optionsFromList.length === 0 && value?.length) { + setValue([]); + } + return ( = { props.setValue(val); }} {...props} + disabled={props.readOnly} /> ); }, @@ -364,9 +365,10 @@ export const Components: Record = { }, radio: { propsType: propsTypes.radio, - factory: ({ setValue, name, value, options }) => { + factory: ({ setValue, name, value, options, readOnly }) => { return ( { setValue(e); diff --git a/packages/features/form-builder/FormBuilder.tsx b/packages/features/form-builder/FormBuilder.tsx index 8031cb107f..eb138b7f65 100644 --- a/packages/features/form-builder/FormBuilder.tsx +++ b/packages/features/form-builder/FormBuilder.tsx @@ -11,6 +11,7 @@ import { Badge, BooleanToggleGroupField, Button, + CheckboxField, Dialog, DialogClose, DialogContent, @@ -491,6 +492,10 @@ function FieldEditDialog({ } label={t("identifier")} /> + + +