fix: kebab menu rename jumps to wrong tab (#38346)
## Description Fixes an issue when renaming from context menu in JS objects, cursor always jumps to the first tab. Fixes #38207 ## Automation /ok-to-test tags="@tag.IDE" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/12480124658> > Commit: 221119c1487842f0e58340eb3ce2b1ecd740d56d > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=12480124658&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.IDE` > Spec: > <hr>Tue, 24 Dec 2024 10:31:34 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Improved context menu options with enhanced performance and clarity. - **Bug Fixes** - Added checks for the availability of the CodeMirror instance to prevent errors. - **Refactor** - Simplified state management by replacing `useState` with `useBoolean`. - Optimized context menu option generation using `useMemo`. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
ecf9934859
commit
c0d393a8f7
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { useCallback, useState } from "react";
|
import React, { useCallback, useMemo } from "react";
|
||||||
|
import { useBoolean } from "usehooks-ts";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
moveJSCollectionRequest,
|
moveJSCollectionRequest,
|
||||||
|
|
@ -34,6 +35,7 @@ import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||||
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
|
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
|
||||||
import type { JSCollection } from "entities/JSCollection";
|
import type { JSCollection } from "entities/JSCollection";
|
||||||
import { setRenameEntity } from "actions/ideActions";
|
import { setRenameEntity } from "actions/ideActions";
|
||||||
|
import type CodeMirror from "codemirror";
|
||||||
|
|
||||||
interface AppJSEditorContextMenuProps {
|
interface AppJSEditorContextMenuProps {
|
||||||
pageId: string;
|
pageId: string;
|
||||||
|
|
@ -46,7 +48,11 @@ export function AppJSEditorContextMenu({
|
||||||
jsCollection,
|
jsCollection,
|
||||||
pageId,
|
pageId,
|
||||||
}: AppJSEditorContextMenuProps) {
|
}: AppJSEditorContextMenuProps) {
|
||||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
const {
|
||||||
|
setFalse: cancelConfirmDelete,
|
||||||
|
setValue: setConfirmDelete,
|
||||||
|
value: confirmDelete,
|
||||||
|
} = useBoolean(false);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
|
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
|
||||||
const isDeletePermitted = getHasDeleteActionPermission(
|
const isDeletePermitted = getHasDeleteActionPermission(
|
||||||
|
|
@ -63,7 +69,7 @@ export function AppJSEditorContextMenu({
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
dispatch(setRenameEntity(jsCollection.id));
|
dispatch(setRenameEntity(jsCollection.id));
|
||||||
}, 100);
|
}, 100);
|
||||||
}, []);
|
}, [dispatch, jsCollection.id]);
|
||||||
|
|
||||||
const copyJSCollectionToPage = useCallback(
|
const copyJSCollectionToPage = useCallback(
|
||||||
(actionId: string, actionName: string, pageId: string) => {
|
(actionId: string, actionName: string, pageId: string) => {
|
||||||
|
|
@ -95,113 +101,130 @@ export function AppJSEditorContextMenu({
|
||||||
dispatch(deleteJSCollection({ id: actionId, name: actionName }));
|
dispatch(deleteJSCollection({ id: actionId, name: actionName }));
|
||||||
setConfirmDelete(false);
|
setConfirmDelete(false);
|
||||||
},
|
},
|
||||||
[dispatch],
|
[dispatch, setConfirmDelete],
|
||||||
);
|
);
|
||||||
|
|
||||||
const confirmDeletion = (value: boolean, event?: Event) => {
|
|
||||||
event?.preventDefault?.();
|
|
||||||
setConfirmDelete(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const menuPages = useSelector(getPageListAsOptions, equal);
|
const menuPages = useSelector(getPageListAsOptions, equal);
|
||||||
|
|
||||||
const renameOption = {
|
const options = useMemo(() => {
|
||||||
icon: "input-cursor-move" as IconName,
|
const confirmDeletion = (value: boolean, event?: Event) => {
|
||||||
value: "rename",
|
event?.preventDefault?.();
|
||||||
onSelect: renameJS,
|
setConfirmDelete(value);
|
||||||
label: createMessage(CONTEXT_RENAME),
|
};
|
||||||
disabled: !isChangePermitted,
|
|
||||||
};
|
|
||||||
|
|
||||||
const copyOption = {
|
const renameOption = {
|
||||||
icon: "duplicate" as IconName,
|
icon: "input-cursor-move" as IconName,
|
||||||
value: "copy",
|
value: "rename",
|
||||||
onSelect: noop,
|
onSelect: renameJS,
|
||||||
label: createMessage(CONTEXT_COPY),
|
label: createMessage(CONTEXT_RENAME),
|
||||||
children: menuPages.map((page) => {
|
disabled: !isChangePermitted,
|
||||||
return {
|
};
|
||||||
...page,
|
|
||||||
onSelect: () =>
|
|
||||||
copyJSCollectionToPage(jsCollection.id, jsCollection.name, page.id),
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const moveOption = {
|
const copyOption = {
|
||||||
icon: "swap-horizontal" as IconName,
|
icon: "duplicate" as IconName,
|
||||||
value: "move",
|
value: "copy",
|
||||||
onSelect: noop,
|
onSelect: noop,
|
||||||
label: createMessage(CONTEXT_MOVE),
|
label: createMessage(CONTEXT_COPY),
|
||||||
children:
|
children: menuPages.map((page) => {
|
||||||
menuPages.length > 1
|
return {
|
||||||
? menuPages
|
...page,
|
||||||
.filter((page) => page.id !== pageId) // Remove current page from the list
|
onSelect: () =>
|
||||||
.map((page) => {
|
copyJSCollectionToPage(jsCollection.id, jsCollection.name, page.id),
|
||||||
return {
|
};
|
||||||
...page,
|
}),
|
||||||
onSelect: () =>
|
};
|
||||||
moveJSCollectionToPage(
|
|
||||||
jsCollection.id,
|
|
||||||
jsCollection.name,
|
|
||||||
page.id,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
: [{ value: "No Pages", onSelect: noop, label: "No Pages" }],
|
|
||||||
};
|
|
||||||
|
|
||||||
const prettifyOptions = {
|
const moveOption = {
|
||||||
value: "prettify",
|
icon: "swap-horizontal" as IconName,
|
||||||
icon: "code" as IconName,
|
value: "move",
|
||||||
subText: prettifyCodeKeyboardShortCut,
|
onSelect: noop,
|
||||||
onSelect: () => {
|
label: createMessage(CONTEXT_MOVE),
|
||||||
/*
|
children:
|
||||||
PS: Please do not remove ts-ignore from here, TS keeps suggesting that
|
menuPages.length > 1
|
||||||
the object is null, but that is not the case, and we need an
|
? menuPages
|
||||||
instance of the editor to pass to autoIndentCode function
|
.filter((page) => page.id !== pageId) // Remove current page from the list
|
||||||
*/
|
.map((page) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
return {
|
||||||
// @ts-ignore
|
...page,
|
||||||
const editor = document.querySelector(".CodeMirror").CodeMirror;
|
onSelect: () =>
|
||||||
|
moveJSCollectionToPage(
|
||||||
|
jsCollection.id,
|
||||||
|
jsCollection.name,
|
||||||
|
page.id,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [{ value: "No Pages", onSelect: noop, label: "No Pages" }],
|
||||||
|
};
|
||||||
|
|
||||||
autoIndentCode(editor);
|
const prettifyOptions = {
|
||||||
dispatch(updateJSCollectionBody(editor.getValue(), jsCollection.id));
|
value: "prettify",
|
||||||
AnalyticsUtil.logEvent("PRETTIFY_CODE_MANUAL_TRIGGER");
|
icon: "code" as IconName,
|
||||||
},
|
subText: prettifyCodeKeyboardShortCut,
|
||||||
label: "Prettify code",
|
onSelect: () => {
|
||||||
};
|
const editorElement = document.querySelector(".CodeMirror");
|
||||||
|
|
||||||
const deleteOption = {
|
if (
|
||||||
confirmDelete: confirmDelete,
|
editorElement &&
|
||||||
icon: "delete-bin-line" as IconName,
|
"CodeMirror" in editorElement &&
|
||||||
value: "delete",
|
editorElement.CodeMirror
|
||||||
onSelect: (event?: Event): void => {
|
) {
|
||||||
confirmDelete
|
const editor = editorElement.CodeMirror as CodeMirror.Editor;
|
||||||
? deleteJSCollectionFromPage(jsCollection.id, jsCollection.name)
|
|
||||||
: confirmDeletion(true, event);
|
|
||||||
},
|
|
||||||
label: confirmDelete
|
|
||||||
? createMessage(CONFIRM_CONTEXT_DELETE)
|
|
||||||
: createMessage(CONTEXT_DELETE),
|
|
||||||
className: "t--apiFormDeleteBtn error-menuitem",
|
|
||||||
};
|
|
||||||
|
|
||||||
const options: ContextMenuOption[] = [renameOption];
|
autoIndentCode(editor);
|
||||||
|
dispatch(updateJSCollectionBody(editor.getValue(), jsCollection.id));
|
||||||
|
AnalyticsUtil.logEvent("PRETTIFY_CODE_MANUAL_TRIGGER");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label: "Prettify code",
|
||||||
|
};
|
||||||
|
|
||||||
if (isChangePermitted) {
|
const deleteOption = {
|
||||||
options.push(copyOption);
|
confirmDelete: confirmDelete,
|
||||||
options.push(moveOption);
|
icon: "delete-bin-line" as IconName,
|
||||||
options.push(prettifyOptions);
|
value: "delete",
|
||||||
}
|
onSelect: (event?: Event): void => {
|
||||||
|
confirmDelete
|
||||||
|
? deleteJSCollectionFromPage(jsCollection.id, jsCollection.name)
|
||||||
|
: confirmDeletion(true, event);
|
||||||
|
},
|
||||||
|
label: confirmDelete
|
||||||
|
? createMessage(CONFIRM_CONTEXT_DELETE)
|
||||||
|
: createMessage(CONTEXT_DELETE),
|
||||||
|
className: "t--apiFormDeleteBtn error-menuitem",
|
||||||
|
};
|
||||||
|
|
||||||
if (isDeletePermitted) options.push(deleteOption);
|
const options: ContextMenuOption[] = [renameOption];
|
||||||
|
|
||||||
|
if (isChangePermitted) {
|
||||||
|
options.push(copyOption);
|
||||||
|
options.push(moveOption);
|
||||||
|
options.push(prettifyOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDeletePermitted) options.push(deleteOption);
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}, [
|
||||||
|
confirmDelete,
|
||||||
|
copyJSCollectionToPage,
|
||||||
|
deleteJSCollectionFromPage,
|
||||||
|
dispatch,
|
||||||
|
isChangePermitted,
|
||||||
|
isDeletePermitted,
|
||||||
|
jsCollection.id,
|
||||||
|
jsCollection.name,
|
||||||
|
menuPages,
|
||||||
|
moveJSCollectionToPage,
|
||||||
|
pageId,
|
||||||
|
renameJS,
|
||||||
|
setConfirmDelete,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<JSEditorContextMenu
|
<JSEditorContextMenu
|
||||||
className="t--more-action-menu"
|
className="t--more-action-menu"
|
||||||
onMenuClose={() => {
|
onMenuClose={cancelConfirmDelete}
|
||||||
setConfirmDelete(false);
|
|
||||||
}}
|
|
||||||
options={options}
|
options={options}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user