Skip to content

Commit

Permalink
chore: Handling the updation of action name in the plugin action tool…
Browse files Browse the repository at this point in the history
…bar (#36560)

## Description

Handling the updation of action name in the plugin action toolbar in the
new modularised flow.

Fixes [#36498](#36498)

## Automation

/ok-to-test tags="@tag.All"

### 🔍 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/11071023786>
> Commit: 73647e5
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11071023786&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Fri, 27 Sep 2024 14:15:24 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**
- Introduced a new component for editing plugin action names, enhancing
user experience in managing plugin actions.
- Added optional icon size property to the editable text component for
improved customization.
- Enhanced the `CommonEditorForm` and `QueryEditorHeader` components to
display plugin-specific information and saving status.

- **Bug Fixes**
- Streamlined action dispatching logic, improving reliability in saving
actions.

- **Documentation**
- Updated interfaces and prop types for better clarity and type safety
in the codebase.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
ankitakinger authored Sep 27, 2024
1 parent b1ed82d commit 5995e42
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 93 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React from "react";
import { useSelector } from "react-redux";
import ActionNameEditor from "components/editorComponents/ActionNameEditor";
import { usePluginActionContext } from "PluginActionEditor/PluginActionContext";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { getHasManageActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
import { PluginType } from "entities/Action";
import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import styled from "styled-components";
import { getSavingStatusForActionName } from "selectors/actionSelectors";
import { getAssetUrl } from "ee/utils/airgapHelpers";
import { ActionUrlIcon } from "pages/Editor/Explorer/ExplorerIcons";

export interface SaveActionNameParams {
id: string;
name: string;
}

export interface PluginActionNameEditorProps {
saveActionName: (
params: SaveActionNameParams,
) => ReduxAction<SaveActionNameParams>;
}

const ActionNameEditorWrapper = styled.div`
& .ads-v2-box {
gap: var(--ads-v2-spaces-2);
}
&& .t--action-name-edit-field {
font-size: 12px;
.bp3-editable-text-content {
height: unset !important;
line-height: unset !important;
}
}
& .t--plugin-icon-box {
height: 12px;
width: 12px;
img {
width: 12px;
height: auto;
}
}
`;

const PluginActionNameEditor = (props: PluginActionNameEditorProps) => {
const { action, plugin } = usePluginActionContext();

const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled);
const isChangePermitted = getHasManageActionPermission(
isFeatureEnabled,
action?.userPermissions,
);

const saveStatus = useSelector((state) =>
getSavingStatusForActionName(state, action?.id || ""),
);

const iconUrl = getAssetUrl(plugin?.iconLocation) || "";
const icon = ActionUrlIcon(iconUrl);

return (
<ActionNameEditorWrapper>
<ActionNameEditor
actionConfig={action}
disabled={!isChangePermitted}
enableFontStyling={plugin?.type === PluginType.API}
icon={icon}
saveActionName={props.saveActionName}
saveStatus={saveStatus}
/>
</ActionNameEditorWrapper>
);
};

export default PluginActionNameEditor;
5 changes: 5 additions & 0 deletions app/client/src/PluginActionEditor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ export {
export { default as PluginActionToolbar } from "./components/PluginActionToolbar";
export { default as PluginActionForm } from "./components/PluginActionForm";
export { default as PluginActionResponse } from "./components/PluginActionResponse";
export type {
SaveActionNameParams,
PluginActionNameEditorProps,
} from "./components/PluginActionNameEditor";
export { default as PluginActionNameEditor } from "./components/PluginActionNameEditor";
71 changes: 27 additions & 44 deletions app/client/src/components/editorComponents/ActionNameEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,27 @@
import React, { memo } from "react";
import { useSelector } from "react-redux";

import { useParams } from "react-router-dom";
import EditableText, {
EditInteractionKind,
} from "components/editorComponents/EditableText";
import { removeSpecialChars } from "utils/helpers";
import type { AppState } from "ee/reducers";

import { saveActionName } from "actions/pluginActionActions";
import { Flex } from "@appsmith/ads";
import { getActionByBaseId, getPlugin } from "ee/selectors/entitiesSelector";
import NameEditorComponent, {
IconBox,
IconWrapper,
NameWrapper,
} from "components/utils/NameEditorComponent";
import {
ACTION_ID_NOT_FOUND_IN_URL,
ACTION_NAME_PLACEHOLDER,
createMessage,
} from "ee/constants/messages";
import { getAssetUrl } from "ee/utils/airgapHelpers";
import { getSavingStatusForActionName } from "selectors/actionSelectors";
import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import type { SaveActionNameParams } from "PluginActionEditor";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";
import type { Action } from "entities/Action";
import type { ModuleInstance } from "ee/constants/ModuleInstanceConstants";

interface SaveActionNameParams {
id: string;
name: string;
}
interface ActionNameEditorProps {
/*
This prop checks if page is API Pane or Query Pane or Curl Pane
Expand All @@ -38,38 +31,34 @@ interface ActionNameEditorProps {
*/
enableFontStyling?: boolean;
disabled?: boolean;
saveActionName?: (
saveActionName: (
params: SaveActionNameParams,
) => ReduxAction<SaveActionNameParams>;
actionConfig?: Action | ModuleInstance;
icon?: JSX.Element;
saveStatus: { isSaving: boolean; error: boolean };
}

function ActionNameEditor(props: ActionNameEditorProps) {
const params = useParams<{ baseApiId?: string; baseQueryId?: string }>();

const currentActionConfig = useSelector((state: AppState) =>
getActionByBaseId(state, params.baseApiId || params.baseQueryId || ""),
);

const currentPlugin = useSelector((state: AppState) =>
getPlugin(state, currentActionConfig?.pluginId || ""),
);
const {
actionConfig,
disabled = false,
enableFontStyling = false,
icon = "",
saveActionName,
saveStatus,
} = props;

const saveStatus = useSelector((state) =>
getSavingStatusForActionName(state, currentActionConfig?.id || ""),
const isActionRedesignEnabled = useFeatureFlag(
FEATURE_FLAG.release_actions_redesign_enabled,
);

return (
<NameEditorComponent
/**
* This component is used by module editor in EE which uses a different
* action to save the name of an action. The current callers of this component
* pass the existing saveAction action but as fallback the saveActionName is used here
* as a guard.
*/
dispatchAction={props.saveActionName || saveActionName}
id={currentActionConfig?.id}
id={actionConfig?.id}
idUndefinedErrorMessage={ACTION_ID_NOT_FOUND_IN_URL}
name={currentActionConfig?.name}
name={actionConfig?.name}
onSaveName={saveActionName}
saveStatus={saveStatus}
>
{({
Expand All @@ -85,28 +74,22 @@ function ActionNameEditor(props: ActionNameEditorProps) {
isNew: boolean;
saveStatus: { isSaving: boolean; error: boolean };
}) => (
<NameWrapper enableFontStyling={props.enableFontStyling}>
<NameWrapper enableFontStyling={enableFontStyling}>
<Flex
alignItems="center"
gap="spaces-3"
overflow="hidden"
width="100%"
>
{currentPlugin && (
<IconBox>
<IconWrapper
alt={currentPlugin.name}
src={getAssetUrl(currentPlugin?.iconLocation)}
/>
</IconBox>
)}
{icon && <IconBox className="t--plugin-icon-box">{icon}</IconBox>}
<EditableText
className="t--action-name-edit-field"
defaultValue={currentActionConfig ? currentActionConfig.name : ""}
disabled={props.disabled}
defaultValue={actionConfig ? actionConfig.name : ""}
disabled={disabled}
editInteractionKind={EditInteractionKind.SINGLE}
errorTooltipClass="t--action-name-edit-error"
forceDefault={forceUpdate}
iconSize={isActionRedesignEnabled ? "sm" : "md"}
isEditingDefault={isNew}
isInvalid={isInvalidNameForEntity}
onTextChanged={handleNameChange}
Expand Down
12 changes: 10 additions & 2 deletions app/client/src/components/editorComponents/EditableText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import {
} from "@blueprintjs/core";
import styled from "styled-components";
import _ from "lodash";
import { Button, Spinner, toast, Tooltip } from "@appsmith/ads";
import {
Button,
Spinner,
toast,
Tooltip,
type ButtonSizes,
} from "@appsmith/ads";
import { INVALID_NAME_ERROR, createMessage } from "ee/constants/messages";

export enum EditInteractionKind {
Expand Down Expand Up @@ -39,6 +45,7 @@ interface EditableTextProps {
minLines?: number;
customErrorTooltip?: string;
useFullWidth?: boolean;
iconSize?: ButtonSizes;
}

// using the !important keyword here is mandatory because a style is being applied to that element using the style attribute
Expand Down Expand Up @@ -129,6 +136,7 @@ export function EditableText(props: EditableTextProps) {
errorTooltipClass,
forceDefault,
hideEditIcon,
iconSize = "md",
isEditingDefault,
isInvalid,
maxLength,
Expand Down Expand Up @@ -275,7 +283,7 @@ export function EditableText(props: EditableTextProps) {
className="t--action-name-edit-icon"
isIconButton
kind="tertiary"
size="md"
size={iconSize}
startIcon="pencil-line"
/>
))}
Expand Down
14 changes: 8 additions & 6 deletions app/client/src/components/utils/NameEditorComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
} from "ee/constants/messages";
import styled from "styled-components";
import { Classes } from "@blueprintjs/core";
import type { SaveActionNameParams } from "PluginActionEditor";
import type { ReduxAction } from "ee/constants/ReduxActionConstants";

export const NameWrapper = styled.div<{ enableFontStyling?: boolean }>`
min-width: 50%;
Expand Down Expand Up @@ -71,9 +73,9 @@ interface NameEditorProps {
children: (params: any) => JSX.Element;
id?: string;
name?: string;
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
dispatchAction: (a: any) => any;
onSaveName: (
params: SaveActionNameParams,
) => ReduxAction<SaveActionNameParams>;
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
suffixErrorMessage?: (params?: any) => string;
Expand All @@ -90,10 +92,10 @@ interface NameEditorProps {

function NameEditor(props: NameEditorProps) {
const {
dispatchAction,
id: entityId,
idUndefinedErrorMessage,
name: entityName,
onSaveName,
saveStatus,
suffixErrorMessage = ACTION_NAME_CONFLICT_ERROR,
} = props;
Expand Down Expand Up @@ -131,8 +133,8 @@ function NameEditor(props: NameEditorProps) {

const handleNameChange = useCallback(
(name: string) => {
if (name !== entityName && !isInvalidNameForEntity(name)) {
dispatch(dispatchAction({ id: entityId, name }));
if (name !== entityName && !isInvalidNameForEntity(name) && entityId) {
dispatch(onSaveName({ id: entityId, name }));
}
},
[dispatch, isInvalidNameForEntity, entityId, entityName],
Expand Down
8 changes: 2 additions & 6 deletions app/client/src/pages/Editor/APIEditor/ApiEditorContext.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import type { ReduxAction } from "ee/constants/ReduxActionConstants";
import type { PaginationField } from "api/ActionAPI";
import React, { createContext, useMemo } from "react";

interface SaveActionNameParams {
id: string;
name: string;
}
import type { SaveActionNameParams } from "PluginActionEditor";

interface ApiEditorContextContextProps {
moreActionsMenu?: React.ReactNode;
Expand All @@ -15,7 +11,7 @@ interface ApiEditorContextContextProps {
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
settingsConfig: any;
saveActionName?: (
saveActionName: (
params: SaveActionNameParams,
) => ReduxAction<SaveActionNameParams>;
closeEditorLink?: React.ReactNode;
Expand Down
18 changes: 18 additions & 0 deletions app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ import {
InfoFields,
RequestTabs,
} from "PluginActionEditor/components/PluginActionForm/components/CommonEditorForm";
import { getSavingStatusForActionName } from "selectors/actionSelectors";
import { getAssetUrl } from "ee/utils/airgapHelpers";
import { ActionUrlIcon } from "../Explorer/ExplorerIcons";

const Form = styled.form`
position: relative;
Expand Down Expand Up @@ -245,6 +248,18 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) {
currentActionConfig?.userPermissions,
);

const currentPlugin = useSelector((state: AppState) =>
getPlugin(state, currentActionConfig?.pluginId || ""),
);

const saveStatus = useSelector((state) =>
getSavingStatusForActionName(state, currentActionConfig?.id || ""),
);

const iconUrl = getAssetUrl(currentPlugin?.iconLocation) || "";

const icon = ActionUrlIcon(iconUrl);

const plugin = useSelector((state: AppState) =>
getPlugin(state, pluginId ?? ""),
);
Expand Down Expand Up @@ -281,9 +296,12 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) {
<FormRow className="form-row-header">
<NameWrapper className="t--nameOfApi">
<ActionNameEditor
actionConfig={currentActionConfig}
disabled={!isChangePermitted}
enableFontStyling
icon={icon}
saveActionName={saveActionName}
saveStatus={saveStatus}
/>
</NameWrapper>
<ActionButtons className="t--formActionButtons">
Expand Down
Loading

0 comments on commit 5995e42

Please sign in to comment.