chore: AppIDE new Entity Explorer changes (#39557)
## Description Just UI changes from #39093 Fixes #39556 ## 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/13673781581> > Commit: b78e21d50dce1e6af78e40237a9a0bced5b35bc7 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=13673781581&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.IDE` > Spec: > <hr>Wed, 05 Mar 2025 10:57:58 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** - Enhanced editable text components now support name normalization with dynamic ellipsis and improved tooltip handling. - Entity and widget list views now display names directly with updated expand/collapse icons and refined selection behavior. - Context menus reflect conditional interactivity by disabling options based on permissions and item types. - New identifiers for list items and context menus improve testing capabilities. - **Refactor** - Consolidated property handling and class name assignments across components for consistent behavior. - Streamlined data structures and conditional rendering in entity explorers and context menus to enhance clarity and maintainability. - Updated components to use direct property access for improved performance and readability. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
44de3c82b0
commit
defc0a93f3
|
|
@ -119,7 +119,7 @@ function ListItem(props: ListItemProps) {
|
|||
|
||||
return (
|
||||
<StyledListItem
|
||||
className={clsx(ListItemClassName, props.className)}
|
||||
className={clsx(ListItemClassName, props.className, "t--ide-list-item")}
|
||||
data-disabled={props.isDisabled || false}
|
||||
data-isblockdescription={isBlockDescription}
|
||||
data-rightcontrolvisibility={rightControlVisibility}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
import React, { useMemo } from "react";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import { Spinner, Tooltip } from "../..";
|
||||
import { Spinner } from "../../Spinner";
|
||||
import { Tooltip } from "../../Tooltip";
|
||||
import { useEditableText } from "../../__hooks__";
|
||||
|
||||
import * as Styled from "./EditableEntityName.styles";
|
||||
|
||||
import type { EditableEntityNameProps } from "./EditableEntityName.types";
|
||||
import clsx from "clsx";
|
||||
|
||||
export const isEllipsisActive = (element: HTMLElement | null) => {
|
||||
return element && element.clientWidth < element.scrollWidth;
|
||||
};
|
||||
|
||||
export const EditableEntityName = (props: EditableEntityNameProps) => {
|
||||
const {
|
||||
|
|
@ -16,13 +22,17 @@ export const EditableEntityName = (props: EditableEntityNameProps) => {
|
|||
isFixedWidth,
|
||||
isLoading,
|
||||
name,
|
||||
normalizeName = false,
|
||||
onExitEditing,
|
||||
onNameSave,
|
||||
showEllipsis = false,
|
||||
size = "small",
|
||||
validateName,
|
||||
} = props;
|
||||
|
||||
const inEditMode = canEdit ? isEditing : false;
|
||||
const [showTooltip, setShowTooltip] = useState(false);
|
||||
const longNameRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const [
|
||||
inputRef,
|
||||
|
|
@ -36,6 +46,7 @@ export const EditableEntityName = (props: EditableEntityNameProps) => {
|
|||
onExitEditing,
|
||||
validateName,
|
||||
onNameSave,
|
||||
normalizeName,
|
||||
);
|
||||
|
||||
// When in loading state, start icon becomes the loading icon
|
||||
|
|
@ -58,30 +69,54 @@ export const EditableEntityName = (props: EditableEntityNameProps) => {
|
|||
paddingTop: "4px",
|
||||
paddingBottom: "4px",
|
||||
top: "-5px",
|
||||
placeholder: "Name",
|
||||
},
|
||||
}),
|
||||
[handleKeyUp, handleTitleChange, inputTestId],
|
||||
);
|
||||
|
||||
useEffect(
|
||||
function handleShowTooltipOnEllipsis() {
|
||||
if (showEllipsis) {
|
||||
setShowTooltip(!!isEllipsisActive(longNameRef.current));
|
||||
}
|
||||
},
|
||||
[editableName, showEllipsis],
|
||||
);
|
||||
|
||||
return (
|
||||
<Styled.Root data-size={size}>
|
||||
{startIcon}
|
||||
<Tooltip
|
||||
content={validationError}
|
||||
placement="bottom"
|
||||
visible={Boolean(validationError)}
|
||||
content={name}
|
||||
isDisabled={!showTooltip}
|
||||
key="entity-name"
|
||||
mouseEnterDelay={1}
|
||||
placement="topLeft"
|
||||
showArrow={false}
|
||||
>
|
||||
<Styled.Text
|
||||
aria-invalid={Boolean(validationError)}
|
||||
data-isediting={inEditMode}
|
||||
data-isfixedwidth={isFixedWidth}
|
||||
inputProps={inputProps}
|
||||
inputRef={inputRef}
|
||||
isEditable={inEditMode}
|
||||
kind={size === "small" ? "body-s" : "body-m"}
|
||||
<Tooltip
|
||||
content={validationError}
|
||||
isDisabled={false}
|
||||
mouseEnterDelay={0}
|
||||
placement="bottom"
|
||||
showArrow
|
||||
visible={Boolean(validationError)}
|
||||
>
|
||||
{editableName}
|
||||
</Styled.Text>
|
||||
<Styled.Text
|
||||
aria-invalid={Boolean(validationError)}
|
||||
className={clsx("t--entity-name", { editing: inEditMode })}
|
||||
data-isediting={inEditMode}
|
||||
data-isfixedwidth={isFixedWidth}
|
||||
inputProps={inputProps}
|
||||
inputRef={inputRef}
|
||||
isEditable={inEditMode}
|
||||
kind={size === "small" ? "body-s" : "body-m"}
|
||||
ref={showEllipsis ? longNameRef : null}
|
||||
>
|
||||
{editableName}
|
||||
</Styled.Text>
|
||||
</Tooltip>
|
||||
</Tooltip>
|
||||
</Styled.Root>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,4 +23,8 @@ export interface EditableEntityNameProps {
|
|||
onNameSave: (name: string) => void;
|
||||
/** Function to validate the name. */
|
||||
validateName: (name: string) => string | null;
|
||||
/** Whether a name should be normalized on renaming */
|
||||
normalizeName?: boolean;
|
||||
/** Used for showing ellipsis for longer names */
|
||||
showEllipsis?: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
import * as Styled from "./EntityContextMenu.styles";
|
||||
|
||||
interface Props {
|
||||
dataTestid?: string;
|
||||
dataTestId?: string;
|
||||
children?: React.ReactNode[] | React.ReactNode;
|
||||
tooltipContent?: React.ReactNode;
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ interface Props {
|
|||
export const EntityContextMenu = (props: Props) => {
|
||||
const {
|
||||
children,
|
||||
dataTestid = DEFAULT_DATA_TEST_ID,
|
||||
dataTestId = DEFAULT_DATA_TEST_ID,
|
||||
tooltipContent = DEFAULT_TOOLTIP_CONTENT,
|
||||
} = props;
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ export const EntityContextMenu = (props: Props) => {
|
|||
>
|
||||
<Button
|
||||
className={EntityClassNames.CONTEXT_MENU}
|
||||
data-testid={dataTestid}
|
||||
data-testid={dataTestId}
|
||||
isIconButton
|
||||
kind="tertiary"
|
||||
startIcon="more-2-fill"
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ const EntityGroup = <T,>({
|
|||
group.renderList(item)
|
||||
) : (
|
||||
<ListItem
|
||||
dataTestId={`entity-group-item-${(item as ListItemProps)?.title}`}
|
||||
key={(item as ListItemProps)?.title || `item-${index}`}
|
||||
{...(item as ListItemProps)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export const EntityItem = (props: EntityItemProps) => {
|
|||
canEdit,
|
||||
isEditing,
|
||||
isLoading,
|
||||
normalizeName = false,
|
||||
onEditComplete,
|
||||
onNameSave,
|
||||
validateName,
|
||||
|
|
@ -34,8 +35,10 @@ export const EntityItem = (props: EntityItemProps) => {
|
|||
isFixedWidth
|
||||
isLoading={isLoading}
|
||||
name={props.title}
|
||||
normalizeName={normalizeName}
|
||||
onExitEditing={onEditComplete}
|
||||
onNameSave={onNameSave}
|
||||
showEllipsis
|
||||
size="medium"
|
||||
validateName={validateName}
|
||||
/>
|
||||
|
|
@ -44,6 +47,7 @@ export const EntityItem = (props: EntityItemProps) => {
|
|||
canEdit,
|
||||
isEditing,
|
||||
isLoading,
|
||||
normalizeName,
|
||||
onEditComplete,
|
||||
onNameSave,
|
||||
props.title,
|
||||
|
|
@ -65,7 +69,7 @@ export const EntityItem = (props: EntityItemProps) => {
|
|||
{...rest}
|
||||
className={clx("t--entity-item", props.className)}
|
||||
customTitleComponent={customTitle}
|
||||
data-testid={`t--entity-item-${props.title}`}
|
||||
dataTestId={`t--entity-item-${props.title}`}
|
||||
id={"entity-" + props.id}
|
||||
onDoubleClick={doubleClickOverride}
|
||||
rightControl={rightControl}
|
||||
|
|
|
|||
|
|
@ -21,5 +21,7 @@ export interface EntityItemProps
|
|||
onNameSave: (newName: string) => void;
|
||||
// Provide a function validate the new name
|
||||
validateName: (newName: string) => string | null;
|
||||
// Whether entity name should be normalized on renaming
|
||||
normalizeName?: boolean;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,36 +28,31 @@ const nameEditorConfig = {
|
|||
validateName: () => null,
|
||||
};
|
||||
|
||||
const names = {
|
||||
"1": "Parent 1",
|
||||
"1.1": "Child 1.1",
|
||||
"1.1.1": "Child 1.1.1",
|
||||
"1.1.2": "Child 1.1.2",
|
||||
"1.2": "Child 1.2",
|
||||
"2": "Parent 2",
|
||||
};
|
||||
|
||||
const Tree: EntityListTreeProps["items"] = [
|
||||
{
|
||||
id: "1",
|
||||
isExpanded: true,
|
||||
isSelected: false,
|
||||
name: "Parent 1",
|
||||
children: [
|
||||
{
|
||||
id: "1.1",
|
||||
isExpanded: false,
|
||||
isSelected: true,
|
||||
name: "Child 1.1",
|
||||
children: [
|
||||
{
|
||||
id: "1.1.1",
|
||||
isExpanded: false,
|
||||
isSelected: false,
|
||||
name: "Child 1.1.1",
|
||||
},
|
||||
{
|
||||
id: "1.1.2",
|
||||
isDisabled: true,
|
||||
isExpanded: false,
|
||||
isSelected: false,
|
||||
name: "Child 1.1.2",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -65,6 +60,7 @@ const Tree: EntityListTreeProps["items"] = [
|
|||
id: "1.2",
|
||||
isExpanded: false,
|
||||
isSelected: false,
|
||||
name: "Child 1.2",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -72,6 +68,7 @@ const Tree: EntityListTreeProps["items"] = [
|
|||
id: "2",
|
||||
isExpanded: false,
|
||||
isSelected: false,
|
||||
name: "Parent 2",
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -111,7 +108,7 @@ const EntityItemComponent = (props: { item: EntityListTreeItem }) => {
|
|||
onClick={noop}
|
||||
onDoubleClick={() => onItemEdit(item.id)}
|
||||
startIcon={<Icon name="apps-line" />}
|
||||
title={names[item.id as keyof typeof names] || item.id}
|
||||
title={item.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,18 +8,8 @@ import type {
|
|||
|
||||
const mockOnItemExpand = jest.fn();
|
||||
|
||||
const name = {
|
||||
"1": "Parent 1",
|
||||
"1.1": "Child 1.1",
|
||||
"1.1.1": "Child 1.1.1",
|
||||
"1.1.2": "Child 1.1.2",
|
||||
"1.2": "Child 1.2",
|
||||
"2": "No Children Parent",
|
||||
"1-1": "Child",
|
||||
};
|
||||
|
||||
const ItemComponent = ({ item }: { item: EntityListTreeItem }) => {
|
||||
return <div>{name[item.id as keyof typeof name] || item.id}</div>;
|
||||
return <div>{item.name}</div>;
|
||||
};
|
||||
|
||||
const defaultProps: EntityListTreeProps = {
|
||||
|
|
@ -30,12 +20,14 @@ const defaultProps: EntityListTreeProps = {
|
|||
isExpanded: false,
|
||||
isSelected: false,
|
||||
isDisabled: false,
|
||||
name: "Parent 1",
|
||||
children: [
|
||||
{
|
||||
id: "1-1",
|
||||
isExpanded: false,
|
||||
isSelected: false,
|
||||
isDisabled: false,
|
||||
name: "Child 1.1",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
|
|
@ -52,7 +44,7 @@ describe("EntityListTree", () => {
|
|||
|
||||
it("calls onItemExpand when expand icon is clicked", () => {
|
||||
render(<EntityListTree {...defaultProps} />);
|
||||
const expandIcon = screen.getByTestId("t--entity-item-expand-icon");
|
||||
const expandIcon = screen.getByTestId("t--entity-collapse-toggle");
|
||||
|
||||
fireEvent.click(expandIcon);
|
||||
expect(mockOnItemExpand).toHaveBeenCalledWith("1");
|
||||
|
|
@ -67,13 +59,14 @@ describe("EntityListTree", () => {
|
|||
isExpanded: false,
|
||||
isSelected: false,
|
||||
isDisabled: false,
|
||||
name: "No Children Parent",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
render(<EntityListTree {...props} />);
|
||||
const expandIcon = screen.queryByTestId("t--entity-item-expand-icon");
|
||||
const expandIcon = screen.queryByTestId("t--entity-collapse-toggle");
|
||||
|
||||
expect(
|
||||
screen.getByRole("treeitem", { name: "No Children Parent" }),
|
||||
|
|
@ -90,12 +83,14 @@ describe("EntityListTree", () => {
|
|||
isExpanded: true,
|
||||
isSelected: false,
|
||||
isDisabled: false,
|
||||
name: "Parent 1",
|
||||
children: [
|
||||
{
|
||||
id: "1-1",
|
||||
isExpanded: false,
|
||||
isSelected: false,
|
||||
isDisabled: false,
|
||||
name: "Child 1.1",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
|
|
@ -105,12 +100,14 @@ describe("EntityListTree", () => {
|
|||
|
||||
render(<EntityListTree {...props} />);
|
||||
|
||||
expect(screen.getByRole("treeitem", { name: "Child" })).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("treeitem", { name: "Child 1.1" }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render nested EntityListTree when item is not expanded", () => {
|
||||
render(<EntityListTree {...defaultProps} />);
|
||||
|
||||
expect(screen.queryByRole("treeitem", { name: "Child" })).toBeNull();
|
||||
expect(screen.queryByRole("treeitem", { name: "Child 1.1" })).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -49,8 +49,11 @@ export function EntityListTree(props: EntityListTreeProps) {
|
|||
>
|
||||
{item.children && item.children.length ? (
|
||||
<CollapseWrapper
|
||||
data-icon={
|
||||
item.isExpanded ? "arrow-down-s-line" : "arrow-right-s-line"
|
||||
}
|
||||
data-itemid={item.id}
|
||||
data-testid="t--entity-item-expand-icon"
|
||||
data-testid="t--entity-collapse-toggle"
|
||||
onClick={handleOnExpandClick}
|
||||
>
|
||||
<Icon
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ export interface EntityListTreeItem {
|
|||
isSelected: boolean;
|
||||
isDisabled?: boolean;
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface EntityListTreeProps {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export function useEditableText(
|
|||
exitEditing: () => void,
|
||||
validateName: (name: string) => string | null,
|
||||
onNameSave: (name: string) => void,
|
||||
normalizeName?: boolean,
|
||||
): [
|
||||
RefObject<HTMLInputElement>,
|
||||
string,
|
||||
|
|
@ -87,7 +88,9 @@ export function useEditableText(
|
|||
|
||||
const handleTitleChange = useEventCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
const value = normaliseName(e.target.value);
|
||||
const value = normalizeName
|
||||
? normaliseName(e.target.value)
|
||||
: e.target.value;
|
||||
|
||||
setEditableName(value);
|
||||
validate(value);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import {
|
|||
import { EntityClassNames } from "pages/Editor/Explorer/Entity";
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode[] | React.ReactNode;
|
||||
children: React.ReactNode[] | React.ReactNode;
|
||||
dataTestId?: string;
|
||||
}
|
||||
|
||||
const EntityContextMenu = (props: Props) => {
|
||||
|
|
@ -26,7 +27,7 @@ const EntityContextMenu = (props: Props) => {
|
|||
>
|
||||
<Button
|
||||
className={EntityClassNames.CONTEXT_MENU}
|
||||
data-testid="t--more-action-trigger"
|
||||
data-testid={props.dataTestId}
|
||||
isIconButton
|
||||
kind="tertiary"
|
||||
startIcon="more-2-fill"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export const Copy = ({ action, disabled }: Props) => {
|
|||
|
||||
return (
|
||||
<MenuSub>
|
||||
<MenuSubTrigger startIcon="duplicate">
|
||||
<MenuSubTrigger disabled={disabled} startIcon="duplicate">
|
||||
{createMessage(CONTEXT_COPY)}
|
||||
</MenuSubTrigger>
|
||||
<MenuSubContent style={{ maxHeight: "350px" }} width="220px">
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export const Move = ({ action, disabled }: Props) => {
|
|||
|
||||
return (
|
||||
<MenuSub>
|
||||
<MenuSubTrigger startIcon="swap-horizontal">
|
||||
<MenuSubTrigger disabled={disabled} startIcon="swap-horizontal">
|
||||
{createMessage(CONTEXT_MOVE)}
|
||||
</MenuSubTrigger>
|
||||
<MenuSubContent style={{ maxHeight: "350px" }} width="220px">
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { jsCollectionIdURL } from "ee/RouteBuilder";
|
|||
import { JsFileIconV2 } from "pages/Editor/Explorer/ExplorerIcons";
|
||||
import { AppJSContextMenuItems } from "./AppJSContextMenuItems";
|
||||
import type { EntityItem as EntityItemProps } from "ee/IDE/Interfaces/EntityItem";
|
||||
import clsx from "clsx";
|
||||
|
||||
export const JSEntityItem = ({ item }: { item: EntityItemProps }) => {
|
||||
const jsAction = useSelector((state: AppState) =>
|
||||
|
|
@ -37,14 +38,17 @@ export const JSEntityItem = ({ item }: { item: EntityItemProps }) => {
|
|||
entityName: item.title,
|
||||
});
|
||||
const dispatch = useDispatch();
|
||||
const contextMenu = useMemo(
|
||||
() => (
|
||||
const contextMenu = useMemo(() => {
|
||||
if (Boolean(jsAction.isMainJSCollection)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EntityContextMenu>
|
||||
<AppJSContextMenuItems jsAction={jsAction} />
|
||||
</EntityContextMenu>
|
||||
),
|
||||
[jsAction],
|
||||
);
|
||||
);
|
||||
}, [jsAction]);
|
||||
|
||||
const jsActionPermissions = jsAction.userPermissions || [];
|
||||
|
||||
|
|
@ -55,6 +59,11 @@ export const JSEntityItem = ({ item }: { item: EntityItemProps }) => {
|
|||
jsActionPermissions,
|
||||
);
|
||||
|
||||
const canEdit = useMemo(
|
||||
() => canManageJSAction && !Boolean(jsAction?.isMainJSCollection),
|
||||
[canManageJSAction, jsAction?.isMainJSCollection],
|
||||
);
|
||||
|
||||
const navigateToUrl = jsCollectionIdURL({
|
||||
baseParentEntityId: parentEntityId,
|
||||
baseCollectionId: jsAction.baseId,
|
||||
|
|
@ -73,11 +82,11 @@ export const JSEntityItem = ({ item }: { item: EntityItemProps }) => {
|
|||
invokedBy: NavigationMethod.EntityExplorer,
|
||||
});
|
||||
}
|
||||
}, [parentEntityId, jsAction.baseId, jsAction.name, location.pathname]);
|
||||
}, [jsAction.baseId, jsAction.name, location.pathname, navigateToUrl]);
|
||||
|
||||
const nameEditorConfig = useMemo(() => {
|
||||
return {
|
||||
canEdit: canManageJSAction && !Boolean(jsAction.isMainJSCollection),
|
||||
canEdit,
|
||||
isEditing: editingEntity === jsAction.id,
|
||||
isLoading: updatingEntity === jsAction.id,
|
||||
onEditComplete: exitEditMode,
|
||||
|
|
@ -86,23 +95,23 @@ export const JSEntityItem = ({ item }: { item: EntityItemProps }) => {
|
|||
validateName: (newName: string) => validateName(newName),
|
||||
};
|
||||
}, [
|
||||
canManageJSAction,
|
||||
canEdit,
|
||||
editingEntity,
|
||||
exitEditMode,
|
||||
ideType,
|
||||
item.title,
|
||||
jsAction.id,
|
||||
jsAction.isMainJSCollection,
|
||||
dispatch,
|
||||
updatingEntity,
|
||||
exitEditMode,
|
||||
dispatch,
|
||||
ideType,
|
||||
validateName,
|
||||
]);
|
||||
|
||||
return (
|
||||
<EntityItem
|
||||
className="t--jsaction"
|
||||
className={clsx("t--jsaction", {
|
||||
editable: canEdit,
|
||||
})}
|
||||
id={jsAction.id}
|
||||
isSelected={activeActionBaseId === jsAction.id}
|
||||
isSelected={activeActionBaseId === item.key}
|
||||
key={jsAction.id}
|
||||
nameEditorConfig={nameEditorConfig}
|
||||
onClick={navigateToJSCollection}
|
||||
|
|
|
|||
|
|
@ -50,14 +50,14 @@ export const QueryEntityItem = ({ item }: { item: EntityItemProps }) => {
|
|||
const dispatch = useDispatch();
|
||||
const contextMenu = useMemo(
|
||||
() => (
|
||||
<EntityContextMenu>
|
||||
<EntityContextMenu dataTestId="t--entity-context-menu-trigger">
|
||||
<AppQueryContextMenuItems action={action} />
|
||||
</EntityContextMenu>
|
||||
),
|
||||
[action],
|
||||
);
|
||||
|
||||
const actionPermissions = action.userPermissions || [];
|
||||
const actionPermissions = action?.userPermissions || [];
|
||||
|
||||
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
|
||||
|
||||
|
|
@ -74,6 +74,8 @@ export const QueryEntityItem = ({ item }: { item: EntityItemProps }) => {
|
|||
pluginGroups[action.pluginId],
|
||||
);
|
||||
|
||||
const icon = config?.getIcon(action, pluginGroups[action.pluginId]);
|
||||
|
||||
const switchToAction = useCallback(() => {
|
||||
url && history.push(url, { invokedBy: NavigationMethod.EntityExplorer });
|
||||
AnalyticsUtil.logEvent("ENTITY_EXPLORER_CLICK", {
|
||||
|
|
@ -104,25 +106,26 @@ export const QueryEntityItem = ({ item }: { item: EntityItemProps }) => {
|
|||
}, [
|
||||
canManageAction,
|
||||
editingEntity,
|
||||
exitEditMode,
|
||||
ideType,
|
||||
item.title,
|
||||
action.id,
|
||||
updatingEntity,
|
||||
exitEditMode,
|
||||
dispatch,
|
||||
ideType,
|
||||
validateName,
|
||||
]);
|
||||
|
||||
return (
|
||||
<EntityItem
|
||||
className="action t--action-entity"
|
||||
id={action.id}
|
||||
isSelected={activeActionBaseId === action.id}
|
||||
isSelected={activeActionBaseId === item.key}
|
||||
key={action.id}
|
||||
nameEditorConfig={nameEditorConfig}
|
||||
onClick={switchToAction}
|
||||
onDoubleClick={() => enterEditMode(action.id)}
|
||||
rightControl={contextMenu}
|
||||
rightControlVisibility="hover"
|
||||
startIcon={item.icon}
|
||||
startIcon={icon}
|
||||
title={item.title}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export const UIEntityListTree = () => {
|
|||
|
||||
const items = enhanceItemsTree(widgets?.children || [], (widget) => ({
|
||||
id: widget.widgetId,
|
||||
name: widget.widgetName,
|
||||
isSelected: selectedWidgets.includes(widget.widgetId),
|
||||
isExpanded: expandedWidgets.includes(widget.widgetId),
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import {
|
|||
} from "ee/constants/messages";
|
||||
import { useDeleteWidget } from "./hooks/useDeleteWidget";
|
||||
import { InspectStateMenuItem } from "components/editorComponents/Debugger/StateInspector/CTAs";
|
||||
import { EntityClassNames } from "pages/Editor/Explorer/Entity";
|
||||
import clsx from "clsx";
|
||||
|
||||
export const WidgetContextMenu = (props: {
|
||||
widgetId: string;
|
||||
|
|
@ -86,6 +88,7 @@ export const WidgetContextMenu = (props: {
|
|||
editWidgetName,
|
||||
showBinding,
|
||||
widget?.isDeletable,
|
||||
widgetId,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
|
@ -98,7 +101,16 @@ export const WidgetContextMenu = (props: {
|
|||
startIcon="more-2-fill"
|
||||
/>
|
||||
</MenuTrigger>
|
||||
<MenuContent align="start" key={widgetId} side="right" width="300px">
|
||||
<MenuContent
|
||||
align="start"
|
||||
className={clsx(
|
||||
"t--entity-context-menu",
|
||||
EntityClassNames.CONTEXT_MENU_CONTENT,
|
||||
)}
|
||||
key={widgetId}
|
||||
side="right"
|
||||
width="300px"
|
||||
>
|
||||
{menuContent}
|
||||
</MenuContent>
|
||||
</Menu>
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@ import { getPagePermissions } from "selectors/editorSelectors";
|
|||
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||
import { useSwitchToWidget } from "./hooks/useSwitchToWidget";
|
||||
import { WidgetTypeIcon } from "./WidgetTypeIcon";
|
||||
import type { WidgetProps } from "widgets/BaseWidget";
|
||||
|
||||
export const WidgetTreeItem = ({ item }: { item: EntityListTreeItem }) => {
|
||||
const widget = useSelector(getWidgetByID(item.id));
|
||||
const widget: WidgetProps | undefined = useSelector(getWidgetByID(item.id));
|
||||
const switchToWidget = useSwitchToWidget();
|
||||
|
||||
const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
|
||||
|
|
@ -36,15 +37,15 @@ export const WidgetTreeItem = ({ item }: { item: EntityListTreeItem }) => {
|
|||
useNameEditorState();
|
||||
|
||||
const validateName = useValidateEntityName({
|
||||
entityName: widget.widgetName,
|
||||
entityId: widget.widgetId,
|
||||
entityName: item.name,
|
||||
entityId: item.id,
|
||||
});
|
||||
|
||||
const isLoading = updatingEntity === widget.widgetId;
|
||||
const isEditing = editingEntity === widget.widgetId;
|
||||
const isLoading = updatingEntity === item.id;
|
||||
const isEditing = editingEntity === item.id;
|
||||
const onNameSave = useCallback(
|
||||
(newName: string) => handleNameSave(widget.widgetId, newName),
|
||||
[handleNameSave, widget.widgetId],
|
||||
(newName: string) => handleNameSave(item.id, newName),
|
||||
[handleNameSave, item.id],
|
||||
);
|
||||
|
||||
const nameEditorConfig = useMemo(
|
||||
|
|
@ -67,8 +68,8 @@ export const WidgetTreeItem = ({ item }: { item: EntityListTreeItem }) => {
|
|||
);
|
||||
|
||||
const startIcon = useMemo(
|
||||
() => <WidgetTypeIcon type={widget.type} />,
|
||||
[widget.type],
|
||||
() => <WidgetTypeIcon type={widget?.type} />,
|
||||
[widget?.type],
|
||||
);
|
||||
|
||||
const onClick = useCallback(
|
||||
|
|
@ -77,20 +78,19 @@ export const WidgetTreeItem = ({ item }: { item: EntityListTreeItem }) => {
|
|||
);
|
||||
|
||||
const onDoubleClick = useCallback(
|
||||
() => enterEditMode(widget.widgetId),
|
||||
[enterEditMode, widget.widgetId],
|
||||
() => enterEditMode(item.id),
|
||||
[enterEditMode, item.id],
|
||||
);
|
||||
|
||||
const rightControl = useMemo(
|
||||
() => (
|
||||
<WidgetContextMenu
|
||||
canManagePages={canManagePages}
|
||||
widgetId={widget.widgetId}
|
||||
/>
|
||||
<WidgetContextMenu canManagePages={canManagePages} widgetId={item.id} />
|
||||
),
|
||||
[canManagePages, widget.widgetId],
|
||||
[canManagePages, item.id],
|
||||
);
|
||||
|
||||
if (!widget) return null;
|
||||
|
||||
return (
|
||||
<EntityItem
|
||||
id={item.id}
|
||||
|
|
@ -101,7 +101,7 @@ export const WidgetTreeItem = ({ item }: { item: EntityListTreeItem }) => {
|
|||
rightControl={rightControl}
|
||||
rightControlVisibility="hover"
|
||||
startIcon={startIcon}
|
||||
title={widget.widgetName}
|
||||
title={item.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ const Wrapper = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
const EditableWrapper = styled.div`
|
||||
const EditableWrapper = styled.span`
|
||||
overflow: hidden;
|
||||
margin: 0 ${(props) => props.theme.spaces[1]}px;
|
||||
padding: ${(props) => props.theme.spaces[3] + 1}px 0;
|
||||
|
|
|
|||
|
|
@ -28,8 +28,12 @@ export function AppJSEditorContextMenu({
|
|||
jsCollection?.userPermissions || [],
|
||||
);
|
||||
|
||||
if (Boolean(jsCollection?.isMainJSCollection)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EntityContextMenu>
|
||||
<EntityContextMenu dataTestId="t--more-action-trigger">
|
||||
<RenameMenuItem
|
||||
disabled={!isChangePermitted}
|
||||
entityId={jsCollection.id}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export const Copy = ({ disabled, jsAction }: Props) => {
|
|||
|
||||
return (
|
||||
<MenuSub>
|
||||
<MenuSubTrigger startIcon="duplicate">
|
||||
<MenuSubTrigger disabled={disabled} startIcon="duplicate">
|
||||
{createMessage(CONTEXT_COPY)}
|
||||
</MenuSubTrigger>
|
||||
<MenuSubContent style={{ maxHeight: "350px" }} width="220px">
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export const Move = ({ disabled, jsAction }: Props) => {
|
|||
|
||||
return (
|
||||
<MenuSub>
|
||||
<MenuSubTrigger startIcon="swap-horizontal">
|
||||
<MenuSubTrigger disabled={disabled} startIcon="swap-horizontal">
|
||||
{createMessage(CONTEXT_MOVE)}
|
||||
</MenuSubTrigger>
|
||||
<MenuSubContent style={{ maxHeight: "350px" }} width="220px">
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user