Fix infinite renders on event-type edit page (#2820)

This commit is contained in:
Hariom Balhara 2022-05-23 23:29:09 +05:30 committed by GitHub
parent 67770bf878
commit f0a36f8194
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 54 deletions

View File

@ -8,6 +8,8 @@ Fixes # (issue)
Loom Video: https://www.loom.com/ Loom Video: https://www.loom.com/
--> -->
**Environment**: Staging(main branch) / Production
## Type of change ## Type of change
<!-- Please delete options that are not relevant. --> <!-- Please delete options that are not relevant. -->

View File

@ -1,57 +1,31 @@
import { CheckIcon, XIcon } from "@heroicons/react/outline"; import { CheckIcon, XIcon } from "@heroicons/react/outline";
import React, { useEffect, useState } from "react"; import React from "react";
import { MultiValue } from "react-select"; import { Props } from "react-select";
import { useLocale } from "@lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import Avatar from "@components/ui/Avatar"; import Avatar from "@components/ui/Avatar";
import Select from "@components/ui/form/Select"; import Select from "@components/ui/form/Select";
type CheckedSelectValue = { type CheckedSelectOption = {
avatar: string; avatar: string;
label: string; label: string;
value: string; value: string;
disabled?: boolean; disabled?: boolean;
}[];
export type CheckedSelectProps = {
defaultValue?: CheckedSelectValue;
placeholder?: string;
name?: string;
options: CheckedSelectValue;
onChange: (options: CheckedSelectValue) => void;
disabled: boolean;
}; };
export const CheckedSelect = (props: CheckedSelectProps) => { export const CheckedSelect = ({
const { onChange } = props; options = [],
const [selectedOptions, setSelectedOptions] = useState<CheckedSelectValue>(props.defaultValue || []); value = [],
...props
}: Omit<Props<CheckedSelectOption, true>, "value" | "onChange"> & {
value?: readonly CheckedSelectOption[];
onChange: (value: readonly CheckedSelectOption[]) => void;
}) => {
const { t } = useLocale(); const { t } = useLocale();
useEffect(() => {
onChange(selectedOptions);
}, [onChange, selectedOptions]);
const options = props.options.map((option) => ({
...option,
disabled: !!selectedOptions.find((selectedOption) => selectedOption.value === option.value),
}));
const removeOption = (value: string) =>
setSelectedOptions(selectedOptions.filter((option) => option.value !== value));
const changeHandler = (selections: MultiValue<CheckedSelectValue[number]>) =>
selections.forEach((selected) => {
if (selectedOptions.find((option) => option.value === selected.value)) {
removeOption(selected.value);
return;
}
setSelectedOptions(selectedOptions.concat(selected));
});
return ( return (
<> <>
<Select<CheckedSelectValue[number], true> <Select
styles={{ styles={{
option: (styles, { isDisabled }) => ({ option: (styles, { isDisabled }) => ({
...styles, ...styles,
@ -73,11 +47,11 @@ export const CheckedSelect = (props: CheckedSelectProps) => {
</div> </div>
)} )}
options={options} options={options}
value={value}
isMulti isMulti
// value={props.placeholder || t("select")} {...props}
onChange={changeHandler}
/> />
{selectedOptions.map((option) => ( {value.map((option) => (
<div key={option.value} className="border-1 border p-2 font-medium"> <div key={option.value} className="border-1 border p-2 font-medium">
<Avatar <Avatar
className="inline h-6 w-6 rounded-full ltr:mr-2 rtl:ml-2" className="inline h-6 w-6 rounded-full ltr:mr-2 rtl:ml-2"
@ -86,7 +60,7 @@ export const CheckedSelect = (props: CheckedSelectProps) => {
/> />
{option.label} {option.label}
<XIcon <XIcon
onClick={() => changeHandler([option])} onClick={() => props.onChange(value.filter((item) => item.value !== option.value))}
className="float-right mt-0.5 h-5 w-5 cursor-pointer text-neutral-500" className="float-right mt-0.5 h-5 w-5 cursor-pointer text-neutral-500"
/> />
</div> </div>
@ -95,6 +69,4 @@ export const CheckedSelect = (props: CheckedSelectProps) => {
); );
}; };
CheckedSelect.displayName = "CheckedSelect";
export default CheckedSelect; export default CheckedSelect;

View File

@ -148,6 +148,7 @@ const SuccessRedirectEdit = <T extends UseFormReturn<FormValues>>({
const { t } = useLocale(); const { t } = useLocale();
const proUpgradeRequired = !isSuccessRedirectAvailable(eventType); const proUpgradeRequired = !isSuccessRedirectAvailable(eventType);
const [modalOpen, setModalOpen] = useState(false); const [modalOpen, setModalOpen] = useState(false);
return ( return (
<> <>
<hr className="border-neutral-200" /> <hr className="border-neutral-200" />
@ -233,6 +234,7 @@ const AvailabilitySelect = ({
const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => { const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
const { t } = useLocale(); const { t } = useLocale();
const PERIOD_TYPES = [ const PERIOD_TYPES = [
{ {
type: "ROLLING" as const, type: "ROLLING" as const,
@ -1189,16 +1191,19 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
name="users" name="users"
control={formMethods.control} control={formMethods.control}
defaultValue={eventType.users.map((user) => user.id.toString())} defaultValue={eventType.users.map((user) => user.id.toString())}
render={() => ( render={({ field: { onChange, value } }) => (
<CheckedSelect <CheckedSelect
disabled={false} isDisabled={false}
onChange={(options) => { onChange={(options) => onChange(options.map((user) => user.value))}
formMethods.setValue( value={value
"users", .map(
options.map((user) => user.value) (userId) =>
); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
}} teamMembers
defaultValue={eventType.users.map(mapUserToValue)} .map(mapUserToValue)
.find((member) => member.value === userId)!
)
.filter(Boolean)}
options={teamMembers.map(mapUserToValue)} options={teamMembers.map(mapUserToValue)}
placeholder={t("add_attendees")} placeholder={t("add_attendees")}
/> />