Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions packages/ui/src/dropdowns/custom-search-select.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import React, { useRef, useState } from "react";
import { usePopper } from "react-popper";
import { Combobox } from "@headlessui/react";
import { Check, ChevronDown, Info, Search } from "lucide-react";
import React, { useRef, useState } from "react";
import { createPortal } from "react-dom";
// plane helpers
import { usePopper } from "react-popper";
// plane imports
import { useOutsideClickDetector } from "@plane/hooks";
// hooks
import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down";
// helpers
// local imports
import { cn } from "../../helpers";
// types
import { ICustomSearchSelectProps } from "./helper";
// local components
import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down";
import { Tooltip } from "../tooltip";
import { ICustomSearchSelectProps } from "./helper";

export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
const {
Expand All @@ -36,6 +33,7 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
optionsClassName = "",
value,
tabIndex,
noResultsMessage = "No matches found",
} = props;
const [query, setQuery] = useState("");

Expand Down Expand Up @@ -201,7 +199,7 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
</Combobox.Option>
))
) : (
<p className="text-custom-text-400 italic py-1 px-1.5">No matches found</p>
<p className="text-custom-text-400 italic py-1 px-1.5">{noResultsMessage}</p>
)
) : (
<p className="text-custom-text-400 italic py-1 px-1.5">Loading...</p>
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/dropdowns/helper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ interface CustomSearchSelectProps {
footerOption?: JSX.Element;
onChange: any;
onClose?: () => void;
noResultsMessage?: string;
options:
| {
value: any;
Expand Down
1 change: 1 addition & 0 deletions web/ce/components/issues/issue-modal/template-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type TWorkItemTemplateSelect = {
placeholder?: string;
renderChevron?: boolean;
dropDownContainerClassName?: string;
handleModalClose: () => void;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Consider making this property optional to maintain backward compatibility.

Adding a required property handleModalClose to an existing type could break existing implementations. If this component is already used elsewhere in the codebase, consider making this property optional with a default behavior.


🏁 Script executed:

#!/bin/bash
# Check for existing usages of the WorkItemTemplateSelect component
echo "Searching for usages of WorkItemTemplateSelect component..."
rg "WorkItemTemplateSelect" --type tsx --type jsx

Length of output: 206


Action Required: Verify the Optionality of the handleModalClose Property

  • The addition of the required handleModalClose property in web/ce/components/issues/issue-modal/template-select.tsx may break components that rely on a default behavior.
  • Our initial search encountered file type issues, so we recommend re-running the search (as done in the updated script above) to check if there are any usages of the WorkItemTemplateSelect component.
  • If further verification confirms that the component is used without explicitly providing a handleModalClose prop, adjust the interface to make this property optional and implement a suitable default behavior.

🏁 Script executed:

#!/bin/bash
# Updated search for usages of WorkItemTemplateSelect component using explicit file globs for tsx and jsx files
echo "Searching for usages of WorkItemTemplateSelect in .tsx and .jsx files..."
rg "WorkItemTemplateSelect" -g "*.tsx" -g "*.jsx"

Length of output: 605


Action Required: Make handleModalClose Optional to Maintain Backward Compatibility

  • Our updated search confirms that the WorkItemTemplateSelect component is used in web/core/components/issues/issue-modal/form.tsx without an explicit handleModalClose prop.
  • Keeping handleModalClose as a required property risks breaking existing implementations.
  • Please update the type in web/ce/components/issues/issue-modal/template-select.tsx by marking handleModalClose as optional (for example, change it from
      handleModalClose: () => void;
    to
      handleModalClose?: () => void;
    ) and implement a default behavior if none is provided.

handleFormChange?: () => void;
};

Expand Down
47 changes: 31 additions & 16 deletions web/core/components/issues/issue-modal/draft-issue-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,34 @@ export const DraftIssueLayout: React.FC<DraftIssueProps> = observer((props) => {
const { createIssue } = useWorkspaceDraftIssues();
const { t } = useTranslation();

const sanitizeChanges = (): Partial<TIssue> => {
const sanitizedChanges = { ...changesMade };
Object.entries(sanitizedChanges).forEach(([key, value]) => {
const issueKey = key as keyof TIssue;
if (value === null || value === undefined || value === "") delete sanitizedChanges[issueKey];
if (typeof value === "object" && isEmpty(value)) delete sanitizedChanges[issueKey];
if (Array.isArray(value) && value.length === 0) delete sanitizedChanges[issueKey];
if (issueKey === "project_id") delete sanitizedChanges.project_id;
if (issueKey === "priority" && value && value === "none") delete sanitizedChanges.priority;
if (
issueKey === "description_html" &&
changesMade?.description_html &&
isEmptyHtmlString(changesMade.description_html, ["img"])
)
delete sanitizedChanges.description_html;
});
return sanitizedChanges;
};

const handleClose = () => {
// If the user is updating an existing work item, we don't need to show the discard modal
if (data?.id) {
onClose();
setIssueDiscardModal(false);
} else {
if (changesMade) {
Object.entries(changesMade).forEach(([key, value]) => {
const issueKey = key as keyof TIssue;
if (value === null || value === undefined || value === "") delete changesMade[issueKey];
if (typeof value === "object" && isEmpty(value)) delete changesMade[issueKey];
if (Array.isArray(value) && value.length === 0) delete changesMade[issueKey];
if (issueKey === "project_id") delete changesMade.project_id;
if (issueKey === "priority" && value && value === "none") delete changesMade.priority;
if (
issueKey === "description_html" &&
changesMade.description_html &&
isEmptyHtmlString(changesMade.description_html, ["img"])
)
delete changesMade.description_html;
});
if (isEmpty(changesMade)) {
const sanitizedChanges = sanitizeChanges();
if (isEmpty(sanitizedChanges)) {
onClose();
setIssueDiscardModal(false);
} else setIssueDiscardModal(true);
Expand Down Expand Up @@ -119,6 +126,14 @@ export const DraftIssueLayout: React.FC<DraftIssueProps> = observer((props) => {
}
};

const handleDraftAndClose = () => {
const sanitizedChanges = sanitizeChanges();
if (!data?.id && !isEmpty(sanitizedChanges)) {
handleCreateDraftIssue();
}
onClose();
};

return (
<>
<ConfirmIssueDiscard
Expand All @@ -131,7 +146,7 @@ export const DraftIssueLayout: React.FC<DraftIssueProps> = observer((props) => {
onClose();
}}
/>
<IssueFormRoot {...props} onClose={handleClose} />
<IssueFormRoot {...props} onClose={handleClose} handleDraftAndClose={handleDraftAndClose} />
</>
);
});
33 changes: 25 additions & 8 deletions web/core/components/issues/issue-modal/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export interface IssueFormProps {
};
isDuplicateModalOpen: boolean;
handleDuplicateIssueModal: (isOpen: boolean) => void;
handleDraftAndClose?: () => void;
isProjectSelectionDisabled?: boolean;
storeType: EIssuesStoreType;
}
Expand All @@ -86,6 +87,7 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
},
isDuplicateModalOpen,
handleDuplicateIssueModal,
handleDraftAndClose,
isProjectSelectionDisabled = false,
storeType,
} = props;
Expand Down Expand Up @@ -235,14 +237,22 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
await onSubmit(submitData, is_draft_issue)
.then(() => {
setGptAssistantModal(false);
reset({
...DEFAULT_WORK_ITEM_FORM_VALUES,
...(isCreateMoreToggleEnabled ? { ...data } : {}),
project_id: getValues<"project_id">("project_id"),
type_id: getValues<"type_id">("type_id"),
description_html: data?.description_html ?? "<p></p>",
});
editorRef?.current?.clearEditor();
if (isCreateMoreToggleEnabled && workItemTemplateId) {
handleTemplateChange({
workspaceSlug: workspaceSlug?.toString(),
reset,
editorRef,
});
} else {
reset({
...DEFAULT_WORK_ITEM_FORM_VALUES,
...(isCreateMoreToggleEnabled ? { ...data } : {}),
project_id: getValues<"project_id">("project_id"),
type_id: getValues<"type_id">("type_id"),
description_html: data?.description_html ?? "<p></p>",
});
editorRef?.current?.clearEditor();
}
})
.catch((error) => {
console.error(error);
Expand Down Expand Up @@ -389,6 +399,13 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
<WorkItemTemplateSelect
projectId={projectId}
typeId={watch("type_id")}
handleModalClose={() => {
if (handleDraftAndClose) {
handleDraftAndClose();
} else {
onClose();
}
}}
handleFormChange={handleFormChange}
renderChevron
/>
Expand Down