From 7a7ceed359a23166ddad6eaf324e875c8f031d37 Mon Sep 17 00:00:00 2001 From: Aman Agarwal Date: Mon, 20 Jan 2025 13:04:16 +0530 Subject: [PATCH 1/8] fix: edit datasource for External saas plugins (#38672) --- .../ce/components/EnvConfigSection/index.tsx | 12 +- .../src/ce/constants/ReduxActionConstants.tsx | 2 + app/client/src/ce/constants/messages.ts | 1 + .../ExternalSaasConnection.tsx | 3 + .../ApiDatasourceFormsButtonConfig.ts | 2 +- .../ExternalSaasConnection.tsx | 3 + app/client/src/entities/Datasource/index.ts | 1 + app/client/src/entities/Plugin/index.ts | 1 + app/client/src/index.css | 4 + .../pages/Editor/DataSourceEditor/DBForm.tsx | 5 + .../DatasourceFormRenderer.tsx | 310 ++++++++++++++++++ .../DataSourceEditor/DatasourceSection.tsx | 310 +----------------- .../IntegrationEditor/APIOrSaasPlugins.tsx | 20 +- .../CreateNewDatasourceTab.tsx | 2 + .../pages/Editor/IntegrationEditor/index.css | 3 + .../src/pages/common/datasourceAuth/index.tsx | 19 +- .../entityReducers/datasourceReducer.ts | 6 + .../src/selectors/datasourceSelectors.ts | 3 + 18 files changed, 397 insertions(+), 310 deletions(-) create mode 100644 app/client/src/ce/pages/Editor/DataSourceEditor/ExternalSaasConnection.tsx create mode 100644 app/client/src/ee/pages/Editor/DataSourceEditor/ExternalSaasConnection.tsx create mode 100644 app/client/src/pages/Editor/DataSourceEditor/DatasourceFormRenderer.tsx create mode 100644 app/client/src/pages/Editor/IntegrationEditor/index.css diff --git a/app/client/src/ce/components/EnvConfigSection/index.tsx b/app/client/src/ce/components/EnvConfigSection/index.tsx index ef2e83ceaa55..176b59779eb8 100644 --- a/app/client/src/ce/components/EnvConfigSection/index.tsx +++ b/app/client/src/ce/components/EnvConfigSection/index.tsx @@ -1,5 +1,6 @@ +import React from "react"; import type { Datasource } from "entities/Datasource"; -import { renderDatasourceSection } from "pages/Editor/DataSourceEditor/DatasourceSection"; +import DatasourceFormRenderer from "pages/Editor/DataSourceEditor/DatasourceFormRenderer"; export interface Props { currentEnv: string; @@ -16,5 +17,12 @@ export function EnvConfigSection({ datasource, viewMode, }: Props) { - return renderDatasourceSection(config, currentEnv, datasource, viewMode); + return ( + + ); } diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 1c34ede4b084..bcb0b248ce1e 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -857,6 +857,8 @@ const DatasourceEditorActionTypes = { CREATE_DATASOURCE_INIT: "CREATE_DATASOURCE_INIT", CREATE_DATASOURCE_SUCCESS: "CREATE_DATASOURCE_SUCCESS", CREATE_DATASOURCE_FROM_FORM_INIT: "CREATE_DATASOURCE_FROM_FORM_INIT", + CREATE_DATASOURCE_FROM_FORM_TOGGLE_LOADING: + "CREATE_DATASOURCE_FROM_FORM_TOGGLE_LOADING", CREATE_TEMP_DATASOURCE_FROM_FORM_SUCCESS: "CREATE_TEMP_DATASOURCE_FROM_FORM_SUCCESS", UPDATE_DATASOURCE_INIT: "UPDATE_DATASOURCE_INIT", diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index 8113359f35d6..0171963ba53f 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -2017,6 +2017,7 @@ export const RECONNECT_BUTTON_TEXT = () => "Reconnect"; export const SAVE_BUTTON_TEXT = () => "Save"; export const TEST_BUTTON_TEXT = () => "Test configuration"; export const SAVE_AND_AUTHORIZE_BUTTON_TEXT = () => "Save & Authorize"; +export const CONNECT_DATASOURCE_BUTTON_TEXT = () => "Connect Datasource"; export const SAVE_AND_RE_AUTHORIZE_BUTTON_TEXT = () => "Save & Re-Authorize"; export const DISCARD_POPUP_DONT_SAVE_BUTTON_TEXT = () => "Don't save"; export const GSHEET_AUTHORISED_FILE_IDS_KEY = () => "userAuthorizedSheetIds"; diff --git a/app/client/src/ce/pages/Editor/DataSourceEditor/ExternalSaasConnection.tsx b/app/client/src/ce/pages/Editor/DataSourceEditor/ExternalSaasConnection.tsx new file mode 100644 index 000000000000..265850f4612c --- /dev/null +++ b/app/client/src/ce/pages/Editor/DataSourceEditor/ExternalSaasConnection.tsx @@ -0,0 +1,3 @@ +export default function ExternalSaasConnection() { + return null; +} diff --git a/app/client/src/constants/AppsmithActionConstants/formConfig/ApiDatasourceFormsButtonConfig.ts b/app/client/src/constants/AppsmithActionConstants/formConfig/ApiDatasourceFormsButtonConfig.ts index 1c929242817b..e93be3fb00d7 100644 --- a/app/client/src/constants/AppsmithActionConstants/formConfig/ApiDatasourceFormsButtonConfig.ts +++ b/app/client/src/constants/AppsmithActionConstants/formConfig/ApiDatasourceFormsButtonConfig.ts @@ -4,5 +4,5 @@ export default { SAAS: ["CANCEL", "SAVE_AND_AUTHORIZE"], REMOTE: ["CANCEL", "SAVE"], AI: ["TEST", "CANCEL", "SAVE"], - EXTERNAL_SAAS: ["CANCEL", "SAVE_AND_AUTHORIZE"], + EXTERNAL_SAAS: ["CANCEL", "CONNECT_DATASOURCE"], }; diff --git a/app/client/src/ee/pages/Editor/DataSourceEditor/ExternalSaasConnection.tsx b/app/client/src/ee/pages/Editor/DataSourceEditor/ExternalSaasConnection.tsx new file mode 100644 index 000000000000..51ff94ca7c76 --- /dev/null +++ b/app/client/src/ee/pages/Editor/DataSourceEditor/ExternalSaasConnection.tsx @@ -0,0 +1,3 @@ +export * from "ce/pages/Editor/DataSourceEditor/ExternalSaasConnection"; +import { default as CE_ExternalSaasConnection } from "ce/pages/Editor/DataSourceEditor/ExternalSaasConnection"; +export default CE_ExternalSaasConnection; diff --git a/app/client/src/entities/Datasource/index.ts b/app/client/src/entities/Datasource/index.ts index 8b9e03952045..222fafaa2529 100644 --- a/app/client/src/entities/Datasource/index.ts +++ b/app/client/src/entities/Datasource/index.ts @@ -211,6 +211,7 @@ export interface ExternalSaasDSAuthentication extends DatasourceAuthentication { integrationId: string; credentialId: string; integrationType: string; + providerData?: { key: string; value: string | boolean | number }[]; } export enum AuthenticationType { diff --git a/app/client/src/entities/Plugin/index.ts b/app/client/src/entities/Plugin/index.ts index e46a137b2a9d..1c807fea6f16 100644 --- a/app/client/src/entities/Plugin/index.ts +++ b/app/client/src/entities/Plugin/index.ts @@ -81,6 +81,7 @@ export interface Plugin { generateCRUDPageComponent?: string; // We need to know if the plugin requires a datasource (Eg Workflows plugin does not require a datasource to create queries) requiresDatasource: boolean; + pluginName?: string; } export interface DefaultPlugin { diff --git a/app/client/src/index.css b/app/client/src/index.css index 9758abf54e07..340e93a66541 100755 --- a/app/client/src/index.css +++ b/app/client/src/index.css @@ -190,6 +190,7 @@ div.bp3-popover-arrow { background-color: var(--ads-v2-color-bg) !important; border-radius: var(--ads-v2-border-radius) !important; } + .bp3-overlay-backdrop { background-color: rgba(16, 22, 26, 0.7) !important; } @@ -220,6 +221,7 @@ div.bp3-popover-arrow { .error-menuitem { color: var(--ads-v2-color-fg-error) !important; } + .error-menuitem > .ads-v2-menu__menu-item-children { color: var(--ads-v2-color-fg-error) !important; } @@ -228,9 +230,11 @@ div.bp3-popover-arrow { .error-menuitem:focus-visible { background-color: var(--ads-v2-color-red-50) !important; } + .error-menuitem:active:not([data-disabled]) { background-color: var(--ads-v2-color-red-100) !important; } + .menuitem-nohover:hover { background-color: transparent !important; cursor: default !important; diff --git a/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx index ab2649aa539c..934210d7c244 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx @@ -17,6 +17,7 @@ import { TEMP_DATASOURCE_ID } from "constants/Datasource"; import { DocsLink, openDoc } from "../../../constants/DocumentationLinks"; import { Callout } from "@appsmith/ads"; import store from "store"; +import ExternalSaasConnection from "ee/pages/Editor/DataSourceEditor/ExternalSaasConnection"; const { cloudHosting } = getAppsmithConfigs(); @@ -69,6 +70,10 @@ class DatasourceDBEditor extends JSONtoForm { return null; } + if (this.props.pluginType === PluginType.EXTERNAL_SAAS) { + return ; + } + return this.renderDataSourceConfigForm(formConfig); } diff --git a/app/client/src/pages/Editor/DataSourceEditor/DatasourceFormRenderer.tsx b/app/client/src/pages/Editor/DataSourceEditor/DatasourceFormRenderer.tsx new file mode 100644 index 000000000000..7058bad054d6 --- /dev/null +++ b/app/client/src/pages/Editor/DataSourceEditor/DatasourceFormRenderer.tsx @@ -0,0 +1,310 @@ +import React from "react"; +import { map, get, isArray } from "lodash"; +import { + formatFileSize, + isHidden, + isKVArray, +} from "components/formControls/utils"; +import log from "loglevel"; +import { ComparisonOperationsEnum } from "components/formControls/BaseControl"; +import { Text } from "@appsmith/ads"; +import { Table } from "@appsmith/ads-old"; +import type { FeatureFlags } from "ee/entities/FeatureFlag"; +import { RagDocuments } from "ee/components/formControls/RagDocuments"; +import type { Datasource } from "entities/Datasource"; +import styled from "styled-components"; + +const Key = styled.div` + color: var(--ads-v2-color-fg-muted); + font-size: 14px; + display: inline-block; +`; + +const Value = styled.div` + font-size: 14px; + font-weight: 500; + color: var(--ads-v2-color-fg); + display: inline-block; + margin-left: 5px; +`; + +const ValueWrapper = styled.div` + display: inline-block; + &:not(:first-child) { + margin-left: 10px; + } +`; + +const FieldWrapper = styled.div` + &:first-child { + margin-top: 9px; + } +`; + +const renderKVArray = ( + // TODO: Fix this the next time the file is edited + // eslint-disable-next-line @typescript-eslint/no-explicit-any + children: Array, + currentEnvironment: string, + datasource: Datasource, +) => { + try { + // setup config for each child + const firstConfigProperty = + `datasourceStorages.${currentEnvironment}.` + + children[0].configProperty || children[0].configProperty; + const configPropertyInfo = firstConfigProperty.split("[*]."); + const values = get(datasource, configPropertyInfo[0], null); + const renderValues: Array< + Array<{ + key: string; + // TODO: Fix this the next time the file is edited + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any; + label: string; + }> + > = children.reduce( + ( + acc, + { configProperty, label }: { configProperty: string; label: string }, + ) => { + const configPropertyKey = configProperty.split("[*].")[1]; + + // TODO: Fix this the next time the file is edited + // eslint-disable-next-line @typescript-eslint/no-explicit-any + values.forEach((value: any, index: number) => { + if (!acc[index]) { + acc[index] = []; + } + + acc[index].push({ + key: configPropertyKey, + label, + value: value[configPropertyKey], + }); + }); + + return acc; + }, + [], + ); + + return renderValues.map((renderValue, index: number) => ( + + {renderValue.map(({ key, label, value }) => ( + + {label}: + {value} + + ))} + + )); + } catch (e) { + return; + } +}; + +export interface DatasourceFormRendererProps { + // TODO: Fix this the next time the file is edited + // eslint-disable-next-line @typescript-eslint/no-explicit-any + section: any; + currentEnvironment: string; + datasource: Datasource; + viewMode: boolean | undefined; + featureFlags?: FeatureFlags; +} + +export default function DatasourceFormRenderer({ + currentEnvironment, + datasource, + featureFlags, + section, + viewMode, +}: DatasourceFormRendererProps) { + return ( + + {map(section.children, (section) => { + if ( + isHidden( + datasource.datasourceStorages[currentEnvironment], + section.hidden, + featureFlags, + viewMode, + ) + ) + return null; + + if ("children" in section) { + if (isKVArray(section.children)) { + return renderKVArray( + section.children, + currentEnvironment, + datasource, + ); + } + + return ( + + ); + } else { + try { + const { + configProperty, + controlType, + label, + subtitle = "", + } = section; + const customConfigProperty = + `datasourceStorages.${currentEnvironment}.` + configProperty; + const reactKey = datasource.id + "_" + label; + + if (controlType === "RAG_DOCUMENTS") { + return ( + + ); + } + + if (controlType === "FIXED_KEY_INPUT") { + return ( + + {configProperty.key}: {" "} + {configProperty.value} + + ); + } + + let value = get(datasource, customConfigProperty); + + if (controlType === "DROP_DOWN") { + if (Array.isArray(section.options)) { + const option = section.options.find( + // TODO: Fix this the next time the file is edited + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (el: any) => el.value === value, + ); + + if (option && option.label) { + value = option.label; + } + } + } + + if ( + !value && + !!viewMode && + (!section.hidden || + (!!section.hidden && + "comparison" in section.hidden && + section.hidden.comparison === + ComparisonOperationsEnum.VIEW_MODE)) + ) { + value = section.initialValue; + } + + if (controlType === "MULTIPLE_FILE_PICKER") { + if (value && Array.isArray(value) && value.length > 0) { + const isPlural = value.length > 1; + + return ( +
+ + {label}: {" "} + + {value.length} file{isPlural ? "s" : ""} uploaded + + +
+ formatFileSize(props.value), + }, + ]} + data={value} + /> + + {section.labelVisibleWithFiles && ( +
+ + {section.labelVisibleWithFiles} + +
+ )} + + ); + } else { + return ( +
+ + {label}: No Files Uploaded + + {subtitle} +
+ ); + } + } + + if (!value || (isArray(value) && value.length < 1)) { + return; + } + + if (isArray(value)) { + return ( + + {label}: + {value.map( + ( + // TODO: Fix this the next time the file is edited + // eslint-disable-next-line @typescript-eslint/no-explicit-any + { key, value }: { key: string; value: any }, + index: number, + ) => ( +
+
+ Key: + {key} +
+ + Value: + {value} + +
+ ), + )} +
+ ); + } + + return ( + + {label}: {value} + + ); + } catch (e) { + log.error(e); + } + } + })} + + ); +} diff --git a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx index 35e6e26bc0ee..afc5223922d5 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx @@ -1,14 +1,6 @@ import React from "react"; import type { Datasource } from "entities/Datasource"; -import { map, get, isArray } from "lodash"; import styled from "styled-components"; -import { - formatFileSize, - isHidden, - isKVArray, -} from "components/formControls/utils"; -import log from "loglevel"; -import { ComparisonOperationsEnum } from "components/formControls/BaseControl"; import type { AppState } from "ee/reducers"; import { connect } from "react-redux"; import { getPlugin } from "ee/selectors/entitiesSelector"; @@ -19,37 +11,8 @@ import { EnvConfigSection } from "ee/components/EnvConfigSection"; import { getCurrentEnvironmentId } from "ee/selectors/environmentSelectors"; import { isMultipleEnvEnabled } from "ee/utils/planHelpers"; import { selectFeatureFlags } from "ee/selectors/featureFlagsSelectors"; -import { Text } from "@appsmith/ads"; -import { Table } from "@appsmith/ads-old"; import type { FeatureFlags } from "ee/entities/FeatureFlag"; -import { RagDocuments } from "ee/components/formControls/RagDocuments"; - -const Key = styled.div` - color: var(--ads-v2-color-fg-muted); - font-size: 14px; - display: inline-block; -`; - -const Value = styled.div` - font-size: 14px; - font-weight: 500; - color: var(--ads-v2-color-fg); - display: inline-block; - margin-left: 5px; -`; - -const ValueWrapper = styled.div` - display: inline-block; - &:not(:first-child) { - margin-left: 10px; - } -`; - -const FieldWrapper = styled.div` - &:first-child { - margin-top: 9px; - } -`; +import DatasourceFormRenderer from "./DatasourceFormRenderer"; export const ViewModeWrapper = styled.div` display: flex; @@ -73,263 +36,6 @@ interface RenderDatasourceSectionProps { isEnvEnabled: boolean; featureFlags?: FeatureFlags; } -const renderKVArray = ( - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - children: Array, - currentEnvironment: string, - datasource: Datasource, -) => { - try { - // setup config for each child - const firstConfigProperty = - `datasourceStorages.${currentEnvironment}.` + - children[0].configProperty || children[0].configProperty; - const configPropertyInfo = firstConfigProperty.split("[*]."); - const values = get(datasource, configPropertyInfo[0], null); - const renderValues: Array< - Array<{ - key: string; - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value: any; - label: string; - }> - > = children.reduce( - ( - acc, - { configProperty, label }: { configProperty: string; label: string }, - ) => { - const configPropertyKey = configProperty.split("[*].")[1]; - - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - values.forEach((value: any, index: number) => { - if (!acc[index]) { - acc[index] = []; - } - - acc[index].push({ - key: configPropertyKey, - label, - value: value[configPropertyKey], - }); - }); - - return acc; - }, - [], - ); - - return renderValues.map((renderValue, index: number) => ( - - {renderValue.map(({ key, label, value }) => ( - - {label}: - {value} - - ))} - - )); - } catch (e) { - return; - } -}; - -export function renderDatasourceSection( - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - section: any, - currentEnvironment: string, - datasource: Datasource, - viewMode: boolean | undefined, - featureFlags?: FeatureFlags, -) { - return ( - - {map(section.children, (section) => { - if ( - isHidden( - datasource.datasourceStorages[currentEnvironment], - section.hidden, - featureFlags, - viewMode, - ) - ) - return null; - - if ("children" in section) { - if (isKVArray(section.children)) { - return renderKVArray( - section.children, - currentEnvironment, - datasource, - ); - } - - return renderDatasourceSection( - section, - currentEnvironment, - datasource, - viewMode, - featureFlags, - ); - } else { - try { - const { - configProperty, - controlType, - label, - subtitle = "", - } = section; - const customConfigProperty = - `datasourceStorages.${currentEnvironment}.` + configProperty; - const reactKey = datasource.id + "_" + label; - - if (controlType === "RAG_DOCUMENTS") { - return ( - - ); - } - - if (controlType === "FIXED_KEY_INPUT") { - return ( - - {configProperty.key}: {" "} - {configProperty.value} - - ); - } - - let value = get(datasource, customConfigProperty); - - if (controlType === "DROP_DOWN") { - if (Array.isArray(section.options)) { - const option = section.options.find( - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (el: any) => el.value === value, - ); - - if (option && option.label) { - value = option.label; - } - } - } - - if ( - !value && - !!viewMode && - (!section.hidden || - (!!section.hidden && - "comparison" in section.hidden && - section.hidden.comparison === - ComparisonOperationsEnum.VIEW_MODE)) - ) { - value = section.initialValue; - } - - if (controlType === "MULTIPLE_FILE_PICKER") { - if (value && Array.isArray(value) && value.length > 0) { - const isPlural = value.length > 1; - - return ( -
- - {label}: {" "} - - {value.length} file{isPlural ? "s" : ""} uploaded - - -
-
formatFileSize(props.value), - }, - ]} - data={value} - /> - - {section.labelVisibleWithFiles && ( -
- - {section.labelVisibleWithFiles} - -
- )} - - ); - } else { - return ( -
- - {label}: No Files Uploaded - - {subtitle} -
- ); - } - } - - if (!value || (isArray(value) && value.length < 1)) { - return; - } - - if (isArray(value)) { - return ( - - {label}: - {value.map( - ( - // TODO: Fix this the next time the file is edited - // eslint-disable-next-line @typescript-eslint/no-explicit-any - { key, value }: { key: string; value: any }, - index: number, - ) => ( -
-
- Key: - {key} -
- - Value: - {value} - -
- ), - )} -
- ); - } - - return ( - - {label}: {value} - - ); - } catch (e) { - log.error(e); - } - } - })} - - ); -} class RenderDatasourceInformation extends React.Component { render() { @@ -351,12 +57,14 @@ class RenderDatasourceInformation extends React.Component ); } diff --git a/app/client/src/pages/Editor/IntegrationEditor/APIOrSaasPlugins.tsx b/app/client/src/pages/Editor/IntegrationEditor/APIOrSaasPlugins.tsx index 29a51efe6070..17912c39fdfa 100644 --- a/app/client/src/pages/Editor/IntegrationEditor/APIOrSaasPlugins.tsx +++ b/app/client/src/pages/Editor/IntegrationEditor/APIOrSaasPlugins.tsx @@ -47,6 +47,7 @@ import { PREMIUM_INTEGRATIONS, type PremiumIntegration, } from "./PremiumDatasources/Constants"; +import { getDatasourcesLoadingState } from "selectors/datasourceSelectors"; interface CreateAPIOrSaasPluginsProps { location: { @@ -211,6 +212,8 @@ function APIOrSaasPlugins(props: CreateAPIOrSaasPluginsProps) { {plugins.map((p) => ( { + if (isCreating) return; + AnalyticsUtil.logEvent("CREATE_DATA_SOURCE_CLICK", { pluginName: p.name, pluginPackageName: p.packageName, @@ -222,6 +225,7 @@ function APIOrSaasPlugins(props: CreateAPIOrSaasPluginsProps) { icon={getAssetUrl(p.iconLocation)} key={p.id} name={p.name} + rightSibling={isCreating && } /> ))} @@ -275,7 +279,11 @@ function CreateAPIOrSaasPlugins(props: CreateAPIOrSaasPluginsProps) { const mapStateToProps = ( state: AppState, - props: { showSaasAPIs?: boolean; isPremiumDatasourcesViewEnabled: boolean }, + props: { + showSaasAPIs?: boolean; + isPremiumDatasourcesViewEnabled: boolean; + isCreating?: boolean; + }, ) => { const searchedPlugin = ( pluginSearchSelector(state, "search") || "" @@ -291,9 +299,12 @@ const mapStateToProps = ( p.type === PluginType.EXTERNAL_SAAS, ); - plugins = plugins.filter((p) => - p.name.toLocaleLowerCase().includes(searchedPlugin), - ); + plugins = plugins + .sort((a, b) => { + // Sort the AI plugins alphabetically + return a.name.localeCompare(b.name); + }) + .filter((p) => p.name.toLocaleLowerCase().includes(searchedPlugin)); let authApiPlugin = !props.showSaasAPIs ? allPlugins.find((p) => p.name === "REST API") @@ -329,6 +340,7 @@ const mapStateToProps = ( authApiPlugin, restAPIVisible, graphQLAPIVisible, + isCreating: props.isCreating || getDatasourcesLoadingState(state), }; }; diff --git a/app/client/src/pages/Editor/IntegrationEditor/CreateNewDatasourceTab.tsx b/app/client/src/pages/Editor/IntegrationEditor/CreateNewDatasourceTab.tsx index 90fa6a5a5f48..42339cdd41cf 100644 --- a/app/client/src/pages/Editor/IntegrationEditor/CreateNewDatasourceTab.tsx +++ b/app/client/src/pages/Editor/IntegrationEditor/CreateNewDatasourceTab.tsx @@ -36,6 +36,8 @@ import { StyledDivider } from "./IntegrationStyledComponents"; import CreateNewDatasourceHeader from "./CreateNewDatasourceHeader"; import EmptySearchedPlugins from "./EmptySearchedPlugins"; import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; +// This css file contains for the EXTERNAL_SAAS plugin modal +import "./index.css"; const NewIntegrationsContainer = styled.div<{ isOnboardingScreen?: boolean }>` ${thinScrollbar}; diff --git a/app/client/src/pages/Editor/IntegrationEditor/index.css b/app/client/src/pages/Editor/IntegrationEditor/index.css new file mode 100644 index 000000000000..714f31934fae --- /dev/null +++ b/app/client/src/pages/Editor/IntegrationEditor/index.css @@ -0,0 +1,3 @@ +#paragon-connect-frame { + pointer-events: auto; +} diff --git a/app/client/src/pages/common/datasourceAuth/index.tsx b/app/client/src/pages/common/datasourceAuth/index.tsx index 81b7fa685de3..4f96862f1188 100644 --- a/app/client/src/pages/common/datasourceAuth/index.tsx +++ b/app/client/src/pages/common/datasourceAuth/index.tsx @@ -17,6 +17,7 @@ import type { Datasource } from "entities/Datasource"; import { AuthType, AuthenticationStatus } from "entities/Datasource"; import { CANCEL, + CONNECT_DATASOURCE_BUTTON_TEXT, OAUTH_AUTHORIZATION_APPSMITH_ERROR, OAUTH_AUTHORIZATION_FAILED, SAVE_AND_AUTHORIZE_BUTTON_TEXT, @@ -36,7 +37,7 @@ import { INTEGRATION_TABS, SHOW_FILE_PICKER_KEY } from "constants/routes"; import { integrationEditorURL } from "ee/RouteBuilder"; import { getQueryParams } from "utils/URLUtils"; import type { AppsmithLocationState } from "utils/history"; -import type { PluginType } from "entities/Plugin"; +import { PluginType } from "entities/Plugin"; import { getCurrentEnvironmentDetails } from "ee/selectors/environmentSelectors"; import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; @@ -86,6 +87,7 @@ export enum DatasourceButtonTypeEnum { TEST = "TEST", CANCEL = "CANCEL", SAVE_AND_AUTHORIZE = "SAVE_AND_AUTHORIZE", + CONNECT_DATASOURCE = "CONNECT_DATASOURCE", } export const DatasourceButtonType: Record< @@ -96,6 +98,7 @@ export const DatasourceButtonType: Record< TEST: "TEST", CANCEL: "CANCEL", SAVE_AND_AUTHORIZE: "SAVE_AND_AUTHORIZE", + CONNECT_DATASOURCE: "CONNECT_DATASOURCE", }; export const ActionButton = styled(Button)<{ @@ -333,7 +336,7 @@ function DatasourceAuth({ updateDatasource( getSanitizedFormData(), currentEnvironment, - pluginType + pluginType && pluginType !== PluginType.EXTERNAL_SAAS ? redirectAuthorizationCode( parentEntityId, datasourceId, @@ -438,6 +441,18 @@ function DatasourceAuth({ {createMessage(SAVE_AND_AUTHORIZE_BUTTON_TEXT)} ), + [DatasourceButtonType.CONNECT_DATASOURCE]: ( + + ), }[buttonType]; }; diff --git a/app/client/src/reducers/entityReducers/datasourceReducer.ts b/app/client/src/reducers/entityReducers/datasourceReducer.ts index 67944ee64d15..1d20f11e5b94 100644 --- a/app/client/src/reducers/entityReducers/datasourceReducer.ts +++ b/app/client/src/reducers/entityReducers/datasourceReducer.ts @@ -126,6 +126,12 @@ const datasourceReducer = createReducer(initialState, { ) => { return { ...state, loading: true }; }, + [ReduxActionTypes.CREATE_DATASOURCE_FROM_FORM_TOGGLE_LOADING]: ( + state: DatasourceDataState, + action: ReduxAction<{ loading?: boolean }>, + ) => { + return { ...state, loading: !!action.payload.loading }; + }, [ReduxActionTypes.UPDATE_DATASOURCE_INIT]: (state: DatasourceDataState) => { return { ...state, loading: true }; }, diff --git a/app/client/src/selectors/datasourceSelectors.ts b/app/client/src/selectors/datasourceSelectors.ts index 5c067a4b50d1..1b4280617e78 100644 --- a/app/client/src/selectors/datasourceSelectors.ts +++ b/app/client/src/selectors/datasourceSelectors.ts @@ -38,3 +38,6 @@ export const getFirstDatasourceId = (state: AppState) => { export const getLoadingTokenForDatasourceId = (state: AppState) => state.entities.datasources.loadingTokenForDatasourceId; + +export const getDatasourcesLoadingState = (state: AppState) => + state.entities.datasources.loading; From 0b830a58b460279d09fe92f173a6af3af81cc989 Mon Sep 17 00:00:00 2001 From: Valera Melnikov Date: Mon, 20 Jan 2025 11:21:36 +0300 Subject: [PATCH 2/8] chore: Increase max-old-space-size to 8192 (#38768) ## Description > [!TIP] > _Add a TL;DR when the description is longer than 500 words or extremely technical (helps the content, marketing, and DevRel team)._ > > _Please also include relevant motivation and context. List any dependencies that are required for this change. Add links to Notion, Figma or any other documents that might be relevant to the PR._ Fixes #`Issue Number` _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="" ### :mag: Cypress test results > [!CAUTION] > If you modify the content in this section, you are likely to disrupt the CI result for your PR. ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No --- app/client/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/build.sh b/app/client/build.sh index 146c6d355f0c..16145f967673 100755 --- a/app/client/build.sh +++ b/app/client/build.sh @@ -18,6 +18,6 @@ export REACT_APP_SENTRY_RELEASE=$GIT_SHA export REACT_APP_CLIENT_LOG_LEVEL=ERROR # Disable CRA built-in ESLint checks since we have our own config and a separate step for this export DISABLE_ESLINT_PLUGIN=true -craco --max-old-space-size=7168 build --config craco.build.config.js +craco --max-old-space-size=8192 build --config craco.build.config.js echo "build finished" From 2ca5993b18b2f288c1082bb8c54574b7c21a2e66 Mon Sep 17 00:00:00 2001 From: albinAppsmith <87797149+albinAppsmith@users.noreply.github.com> Date: Mon, 20 Jan 2025 23:23:26 +0530 Subject: [PATCH 3/8] fix: Stopped calling usage pulse in air-gapped instance (#38749) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR added fix for not triggering usage pulse for air gapped instances Fixes https://github.com/appsmithorg/cloud-services/issues/1883 ## Automation /ok-to-test tags="@tag.Sanity" ### :mag: Cypress test results > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: > Commit: 2300d200cf4213edfc734c1a8b89b4ad797cdb64 > Cypress dashboard. > Tags: `@tag.Sanity` > Spec: >
Mon, 20 Jan 2025 12:38:11 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No ## Summary by CodeRabbit - **Bug Fixes** - Enhanced user activity tracking by introducing airgapped environment detection, preventing unnecessary tracking in restricted network settings. - **Tests** - Added a new test suite to verify the behavior of the user activity tracking method in airgapped conditions, ensuring correct functionality based on the airgapped status. --- app/client/src/usagePulse/index.ts | 6 ++++ app/client/src/usagePulse/usagePulse.test.ts | 30 ++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/app/client/src/usagePulse/index.ts b/app/client/src/usagePulse/index.ts index 0587889db13f..53edcb3b04ec 100644 --- a/app/client/src/usagePulse/index.ts +++ b/app/client/src/usagePulse/index.ts @@ -18,6 +18,7 @@ import { PULSE_INTERVAL as PULSE_INTERVAL_CE } from "ce/constants/UsagePulse"; import { PULSE_INTERVAL as PULSE_INTERVAL_EE } from "ee/constants/UsagePulse"; import store from "store"; import type { PageListReduxState } from "reducers/entityReducers/pageListReducer"; +import { isAirgapped } from "ee/utils/airgapHelpers"; class UsagePulse { static userAnonymousId: string | undefined; @@ -26,6 +27,7 @@ class UsagePulse { static isTelemetryEnabled: boolean; static isAnonymousUser: boolean; static isFreePlan: boolean; + static isAirgapped = isAirgapped(); /* * Function to check if the given URL is trakable or not. @@ -143,6 +145,10 @@ class UsagePulse { * registers listeners to wait for the user to go to a trackable url */ static async sendPulseAndScheduleNext() { + if (UsagePulse.isAirgapped) { + return; + } + UsagePulse.sendPulse(); UsagePulse.scheduleNextActivityListeners(); } diff --git a/app/client/src/usagePulse/usagePulse.test.ts b/app/client/src/usagePulse/usagePulse.test.ts index 9929128ea5df..2fa3c4507647 100644 --- a/app/client/src/usagePulse/usagePulse.test.ts +++ b/app/client/src/usagePulse/usagePulse.test.ts @@ -29,4 +29,34 @@ describe("Usage pulse", () => { }); }); }); + + describe("sendPulseAndScheduleNext", () => { + let sendPulseSpy: jest.SpyInstance; + let scheduleNextActivityListenersSpy: jest.SpyInstance; + + beforeEach(() => { + sendPulseSpy = jest + .spyOn(UsagePulse, "sendPulse") + .mockImplementation(() => {}); + scheduleNextActivityListenersSpy = jest + .spyOn(UsagePulse, "scheduleNextActivityListeners") + .mockImplementation(() => {}); + UsagePulse.isAirgapped = false; + }); + + it("should not send pulse or schedule next when airgapped", () => { + UsagePulse.isAirgapped = true; + UsagePulse.sendPulseAndScheduleNext(); + + expect(sendPulseSpy).not.toHaveBeenCalled(); + expect(scheduleNextActivityListenersSpy).not.toHaveBeenCalled(); + }); + + it("should send pulse and schedule next activity listeners when not airgapped", () => { + UsagePulse.sendPulseAndScheduleNext(); + + expect(sendPulseSpy).toHaveBeenCalledTimes(1); + expect(scheduleNextActivityListenersSpy).toHaveBeenCalledTimes(1); + }); + }); }); From d8de0f4c1161aa405710ce51dc2f062547be5e3e Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 10:39:30 +0530 Subject: [PATCH 4/8] fix: Update button property text (#38758) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: rahul.barwal@appsmith.com --- .../ClientSide/Widgets/Button/Button2_spec.ts | 2 +- .../Widgets/JSONForm/JSONForm_FormProperties_spec.js | 2 +- .../ListV2/Listv2_WithButtonGroupIconWidget_spec.ts | 2 +- .../dsl/src/migrate/helpers/widget-configs.json | 10 +++++----- .../src/widgets/ButtonGroupWidget/widget/index.tsx | 2 +- app/client/src/widgets/ButtonWidget/widget/index.tsx | 2 +- .../widgets/JSONFormWidget/widget/propertyConfig.ts | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/Button2_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/Button2_spec.ts index f3dc601cbcf1..9175742fd08f 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/Button2_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/Button2_spec.ts @@ -81,7 +81,7 @@ describe( "Form1", ]); // disable form validation - propPane.TogglePropertyState("Disabled invalid forms", "Off"); + propPane.TogglePropertyState("Disable when form is invalid", "Off"); // set invalid text agHelper.TypeText(clocators.inputField, fakerHelper.GetRandomNumber()); // assert submit button us enabled since disabled invalid form is off diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/JSONForm/JSONForm_FormProperties_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/JSONForm/JSONForm_FormProperties_spec.js index 9787c93addc4..a9d49d09d944 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/JSONForm/JSONForm_FormProperties_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/JSONForm/JSONForm_FormProperties_spec.js @@ -65,7 +65,7 @@ describe( deployMode.NavigateBacktoEditor(); }); - it("2. Disabled Invalid Forms - disables the submit button when form has invalid field(s)", () => { + it("2. Disable when form is invalid - disables the submit button when form has invalid field(s)", () => { EditorNavigation.SelectEntityByName("JSONForm1", EntityType.Widget); cy.get("button") diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_WithButtonGroupIconWidget_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_WithButtonGroupIconWidget_spec.ts index b545b0f4a687..11bd91d1f441 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_WithButtonGroupIconWidget_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/ListV2/Listv2_WithButtonGroupIconWidget_spec.ts @@ -27,7 +27,7 @@ describe( const generalProperties1 = ["visible", "disabled", "animateloading"]; const formSettingsProperties = [ - "disabledinvalidforms", + "disablewhenformisinvalid", "resetformonsuccess", ]; diff --git a/app/client/packages/dsl/src/migrate/helpers/widget-configs.json b/app/client/packages/dsl/src/migrate/helpers/widget-configs.json index ddd827e9bfb2..c33b3009ff6e 100644 --- a/app/client/packages/dsl/src/migrate/helpers/widget-configs.json +++ b/app/client/packages/dsl/src/migrate/helpers/widget-configs.json @@ -3530,7 +3530,7 @@ { "helpText": "Disabled if the form is invalid, if this widget exists directly within a Form widget.", "propertyName": "disabledWhenInvalid", - "label": "Disabled invalid forms", + "label": "Disable when form is invalid", "controlType": "SWITCH", "isJSConvertible": true, "isBindProperty": true, @@ -46238,7 +46238,7 @@ { "propertyName": "disabledWhenInvalid", "helpText": "Disables the submit button when the parent form has a required widget that is not filled", - "label": "Disabled invalid forms", + "label": "Disable when form is invalid", "controlType": "SWITCH", "isJSConvertible": true, "isBindProperty": true, @@ -54511,7 +54511,7 @@ { "helpText": "Disabled if the form is invalid, if this widget exists directly within a Form widget.", "propertyName": "disabledWhenInvalid", - "label": "Disabled invalid forms", + "label": "Disable when form is invalid", "controlType": "SWITCH", "isJSConvertible": true, "isBindProperty": true, @@ -60391,7 +60391,7 @@ { "helpText": "Disabled if the form is invalid, if this widget exists directly within a Form widget.", "propertyName": "disabledWhenInvalid", - "label": "Disabled invalid forms", + "label": "Disable when form is invalid", "controlType": "SWITCH", "isJSConvertible": true, "isBindProperty": true, @@ -60697,7 +60697,7 @@ { "helpText": "Disabled if the form is invalid, if this widget exists directly within a Form widget.", "propertyName": "disabledWhenInvalid", - "label": "Disabled invalid forms", + "label": "Disable when form is invalid", "controlType": "SWITCH", "isJSConvertible": true, "isBindProperty": true, diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx index b7f347a60bfe..4dd48779747c 100644 --- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx @@ -528,7 +528,7 @@ class ButtonGroupWidget extends BaseWidget< children: [ { propertyName: "disabledWhenInvalid", - label: "Disabled invalid forms", + label: "Disable when form is invalid", helpText: "Disables this button if the form is invalid, if this button exists directly within a Form widget", controlType: "SWITCH", diff --git a/app/client/src/widgets/ButtonWidget/widget/index.tsx b/app/client/src/widgets/ButtonWidget/widget/index.tsx index 42dc902d874a..41c57f66cf03 100644 --- a/app/client/src/widgets/ButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonWidget/widget/index.tsx @@ -271,7 +271,7 @@ class ButtonWidget extends BaseWidget { helpText: "Disabled if the form is invalid, if this widget exists directly within a Form widget.", propertyName: "disabledWhenInvalid", - label: "Disabled invalid forms", + label: "Disable when form is invalid", controlType: "SWITCH", isJSConvertible: true, isBindProperty: true, diff --git a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts index 436279dbd3cf..0f319f474396 100644 --- a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts @@ -360,7 +360,7 @@ export const contentConfig = [ propertyName: "disabledWhenInvalid", helpText: "Disables the submit button when the parent form has a required widget that is not filled", - label: "Disabled invalid forms", + label: "Disable when form is invalid", controlType: "SWITCH", isJSConvertible: true, isBindProperty: true, From 8e9db85eee9f9e95096fd71e3c34fc7ed7591b85 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 12:14:36 +0530 Subject: [PATCH 5/8] fix: Chart widget respects theme font (#38777) Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: rahul.barwal@appsmith.com --- .../component/EChartsConfigurationBuilder.ts | 109 +++++++++--------- .../widgets/ChartWidget/widget/index.test.ts | 19 ++- .../src/widgets/ChartWidget/widget/index.tsx | 8 +- 3 files changed, 78 insertions(+), 58 deletions(-) diff --git a/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.ts b/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.ts index 7559c8af54f1..b4c6d771733a 100644 --- a/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.ts +++ b/app/client/src/widgets/ChartWidget/component/EChartsConfigurationBuilder.ts @@ -9,9 +9,10 @@ import { import { Colors } from "constants/Colors"; import { EChartsLayoutBuilder } from "./LayoutBuilders/EChartsLayoutBuilder"; +import { objectKeys } from "@appsmith/utils"; export class EChartsConfigurationBuilder { - fontFamily: string = "Nunito Sans"; + fontFamily: string = ""; fontSize = 14; #seriesConfigurationForPieChart( @@ -59,58 +60,60 @@ export class EChartsConfigurationBuilder { */ const configs: unknown[] = []; - Object.keys(allSeriesData).forEach((seriesID, index) => { - const seriesData = allSeriesData[seriesID]; - let color = seriesData.color; - - if (index == 0 && (!color || color.length == 0)) { - color = props.primaryColor; - } - - let seriesName = messages.Undefined; - - if (seriesData.seriesName && seriesData.seriesName.length > 0) { - seriesName = seriesData.seriesName; - } - - let config: Record = { - label: { show: props.showDataPointLabel, position: "top" }, - name: seriesName, - itemStyle: { color: color }, - }; - - switch (props.chartType) { - case "BAR_CHART": - config = { ...config, type: "bar" }; - - // The series label should be on the right for bar chart - (config.label as Record).position = "right"; - break; - case "COLUMN_CHART": - config = { ...config, type: "bar" }; - break; - case "LINE_CHART": - config = { ...config, type: "line" }; - break; - case "AREA_CHART": - config = { - ...config, - type: "line", - areaStyle: {}, - }; - break; - case "PIE_CHART": - config = this.#seriesConfigurationForPieChart( - seriesID, - seriesData, - props.showDataPointLabel, - layoutConfig, - ); - break; - } - - configs.push(config); - }); + objectKeys(allSeriesData).forEach( + (seriesID: string | number, index: number) => { + const seriesData = allSeriesData[seriesID]; + let color = seriesData.color; + + if (index == 0 && (!color || color.length == 0)) { + color = props.primaryColor; + } + + let seriesName = messages.Undefined; + + if (seriesData.seriesName && seriesData.seriesName.length > 0) { + seriesName = seriesData.seriesName; + } + + let config: Record = { + label: { show: props.showDataPointLabel, position: "top" }, + name: seriesName, + itemStyle: { color: color }, + }; + + switch (props.chartType) { + case "BAR_CHART": + config = { ...config, type: "bar" }; + + // The series label should be on the right for bar chart + (config.label as Record).position = "right"; + break; + case "COLUMN_CHART": + config = { ...config, type: "bar" }; + break; + case "LINE_CHART": + config = { ...config, type: "line" }; + break; + case "AREA_CHART": + config = { + ...config, + type: "line", + areaStyle: {}, + }; + break; + case "PIE_CHART": + config = this.#seriesConfigurationForPieChart( + String(seriesID), + seriesData, + props.showDataPointLabel, + layoutConfig, + ); + break; + } + + configs.push(config); + }, + ); return configs; } diff --git a/app/client/src/widgets/ChartWidget/widget/index.test.ts b/app/client/src/widgets/ChartWidget/widget/index.test.ts index 040cc61a89e5..303d980aa440 100644 --- a/app/client/src/widgets/ChartWidget/widget/index.test.ts +++ b/app/client/src/widgets/ChartWidget/widget/index.test.ts @@ -54,7 +54,24 @@ describe("emptyChartData", () => { }; describe("font family", () => { - expect(ChartWidget.fontFamily).toEqual("Nunito Sans"); + it("uses theme font family with Nunito Sans fallback", () => { + const widget = new ChartWidget(defaultProps); + const view = widget.renderChartWithData(); + + expect(view.props.children.props.fontFamily).toEqual("fontfamily"); + + const propsWithoutFont: ChartWidgetProps = { + ...defaultProps, + fontFamily: undefined as unknown as string, + }; + const viewWithoutFont = new ChartWidget( + propsWithoutFont, + ).renderChartWithData(); + + expect(viewWithoutFont.props.children.props.fontFamily).toEqual( + "Nunito Sans", + ); + }); }); describe("when chart type is basic ECharts", () => { diff --git a/app/client/src/widgets/ChartWidget/widget/index.tsx b/app/client/src/widgets/ChartWidget/widget/index.tsx index 65498ad61110..eb15e6e65957 100644 --- a/app/client/src/widgets/ChartWidget/widget/index.tsx +++ b/app/client/src/widgets/ChartWidget/widget/index.tsx @@ -3,6 +3,7 @@ import type { WidgetProps, WidgetState } from "widgets/BaseWidget"; import BaseWidget from "widgets/BaseWidget"; import Skeleton from "components/utils/Skeleton"; import { retryPromise } from "utils/AppsmithUtils"; +import { objectKeys } from "@appsmith/utils"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { contentConfig, styleConfig } from "./propertyConfig"; import { @@ -44,9 +45,9 @@ const ChartComponent = lazy(async () => export const emptyChartData = (props: ChartWidgetProps) => { if (props.chartType == "CUSTOM_FUSION_CHART") { - return Object.keys(props.customFusionChartConfig).length == 0; + return objectKeys(props.customFusionChartConfig).length == 0; } else if (props.chartType == "CUSTOM_ECHART") { - return Object.keys(props.customEChartConfig).length == 0; + return objectKeys(props.customEChartConfig).length == 0; } else { const builder = new EChartsDatasetBuilder(props.chartType, props.chartData); @@ -65,7 +66,6 @@ export const emptyChartData = (props: ChartWidgetProps) => { class ChartWidget extends BaseWidget { static type = "CHART_WIDGET"; - static fontFamily: string = "Nunito Sans"; static getConfig() { return { @@ -257,7 +257,7 @@ class ChartWidget extends BaseWidget { customEChartConfig={this.props.customEChartConfig} customFusionChartConfig={this.props.customFusionChartConfig} dimensions={this.props} - fontFamily={ChartWidget.fontFamily} + fontFamily={this.props.fontFamily ?? "Nunito Sans"} hasOnDataPointClick={Boolean(this.props.onDataPointClick)} isLoading={this.props.isLoading} isVisible={this.props.isVisible} From c223c5070d89fad7be383d8a67060af6aba1862f Mon Sep 17 00:00:00 2001 From: Ankita Kinger Date: Tue, 21 Jan 2025 15:24:06 +0530 Subject: [PATCH 6/8] chore: Refactoring usage of `editorType` to `ideType` to remove code duplication (#38778) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Refactoring usage of `editorType` to `ideType` to remove code duplication Fixes [#38286](https://github.com/appsmithorg/appsmith/issues/38286) [#39289](https://github.com/appsmithorg/appsmith/issues/38289) ## Automation /ok-to-test tags="@tag.All" ### :mag: Cypress test results > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: > Commit: dfe8f5c6d14c7d5aa4c377ad67516e01c26e2e5b > Cypress dashboard. > Tags: `@tag.All` > Spec: >
Tue, 21 Jan 2025 06:50:04 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No ## Summary by CodeRabbit ## Release Notes - **New Features** - Introduced a new IDE type context to replace the previous editor type system - Enhanced type safety for IDE-related operations - **Improvements** - Refactored editor type handling across multiple components - Updated utility functions to work with new IDE type - Simplified permission and context management - **Code Quality** - Removed deprecated editor type hooks and constants - Standardized IDE type determination across the application - Improved import and module organization - **Performance** - Streamlined type checking and context retrieval mechanisms --- .../DatasourceTab/DatasourceInfo.tsx | 8 +- .../DatasourceTab/DatasourceTab.tsx | 8 +- .../src/ce/IDE/hooks/useParentEntityInfo.ts | 19 ++++ app/client/src/ce/actions/helpers.ts | 13 +-- .../IDE/hooks/useCreateActionsPermissions.ts | 20 ++++ .../src/ce/hooks/datasourceEditorHooks.tsx | 25 +---- app/client/src/ce/hooks/hooks.test.ts | 25 ----- app/client/src/ce/hooks/index.ts | 35 ------- app/client/src/ce/navigation/FocusSetters.ts | 2 +- .../Editor/IDE/EditorPane/JS/ListItem.tsx | 5 +- .../pages/Editor/IDE/EditorPane/JS/hooks.tsx | 2 +- .../JS/utils/getJSEntityItemUrl.test.ts | 21 ++++ .../EditorPane/JS/utils/getJSEntityItemUrl.ts | 12 +++ .../{utils.test.ts => utils/getJSUrl.test.ts} | 21 +--- .../JS/{utils.ts => utils/getJSUrl.ts} | 11 --- .../Editor/IDE/EditorPane/Query/ListItem.tsx | 5 +- .../Editor/IDE/EditorPane/Query/hooks.tsx | 2 +- .../Query/utils/getQueryEntityItemUrl.test.ts | 26 +++++ .../Query/utils/getQueryEntityItemUrl.ts | 15 +++ .../getQueryUrl.test.ts} | 28 +----- .../Query/{utils.ts => utils/getQueryUrl.ts} | 17 +--- .../Editor/gitSync/useReconnectModalData.ts | 4 +- .../src/ce/selectors/appIDESelectors.ts | 2 +- .../src/ce/selectors/entitiesSelector.ts | 10 +- .../permissionPageHelpers.tsx | 8 +- app/client/src/ce/utils/lintRulesHelpers.ts | 4 +- .../form/fields/DropdownWrapper.tsx | 2 +- .../src/ee/IDE/hooks/useParentEntityInfo.ts | 1 + .../IDE/hooks/useCreateActionsPermissions.ts | 1 + .../pages/Editor/IDE/EditorPane/JS/utils.ts | 1 - .../EditorPane/JS/utils/getJSEntityItemUrl.ts | 1 + .../IDE/EditorPane/JS/utils/getJSUrl.ts | 1 + .../Editor/IDE/EditorPane/Query/utils.ts | 1 - .../Query/utils/getQueryEntityItemUrl.ts | 1 + .../IDE/EditorPane/Query/utils/getQueryUrl.ts | 1 + .../CustomWidgetBuilder/useCustomBuilder.tsx | 8 +- .../Editor/DataSourceEditor/DSFormHeader.tsx | 10 +- .../pages/Editor/DataSourceEditor/hooks.ts | 6 +- .../DatasourceInfo/DatasourceStructure.tsx | 6 +- .../DatasourceViewModeSchema.tsx | 6 +- .../DatasourceInfo/GoogleSheetSchema.tsx | 6 +- .../HideGeneratePageButton.test.tsx | 3 +- .../Editor/Explorer/Actions/ActionEntity.tsx | 16 +-- .../src/pages/Editor/Explorer/Files/index.tsx | 2 - .../Explorer/JSActions/JSActionEntity.tsx | 19 ++-- .../pages/Editor/IDE/EditorPane/JS/List.tsx | 98 +++++++++---------- .../Editor/IDE/EditorPane/Query/List.tsx | 1 - .../pages/Editor/IDE/EditorTabs/constants.ts | 4 +- .../Editor/IDE/LeftPane/DataSidePane.test.tsx | 4 +- .../Editor/IDE/LeftPane/DataSidePane.tsx | 9 +- app/client/src/pages/Editor/IDE/hooks.ts | 9 +- .../IntegrationEditor/APIOrSaasPlugins.tsx | 21 ++-- .../DBOrMostPopularPlugins.tsx | 34 +++---- .../IntegrationEditor/DatasourceCard.tsx | 6 +- .../gitSync/ReconnectDatasourceModal.tsx | 4 +- app/client/src/plugins/Linting/constants.ts | 5 +- .../tests/generateLintingGlobalData.test.ts | 3 - .../plugins/Linting/utils/getLintingErrors.ts | 13 +-- app/client/src/sagas/IDESaga.tsx | 4 +- app/client/src/selectors/jsPaneSelectors.ts | 2 +- 60 files changed, 303 insertions(+), 354 deletions(-) create mode 100644 app/client/src/ce/IDE/hooks/useParentEntityInfo.ts create mode 100644 app/client/src/ce/entities/IDE/hooks/useCreateActionsPermissions.ts delete mode 100644 app/client/src/ce/hooks/hooks.test.ts create mode 100644 app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSEntityItemUrl.test.ts create mode 100644 app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSEntityItemUrl.ts rename app/client/src/ce/pages/Editor/IDE/EditorPane/JS/{utils.test.ts => utils/getJSUrl.test.ts} (77%) rename app/client/src/ce/pages/Editor/IDE/EditorPane/JS/{utils.ts => utils/getJSUrl.ts} (74%) create mode 100644 app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryEntityItemUrl.test.ts create mode 100644 app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryEntityItemUrl.ts rename app/client/src/ce/pages/Editor/IDE/EditorPane/Query/{utils.test.ts => utils/getQueryUrl.test.ts} (81%) rename app/client/src/ce/pages/Editor/IDE/EditorPane/Query/{utils.ts => utils/getQueryUrl.ts} (70%) create mode 100644 app/client/src/ee/IDE/hooks/useParentEntityInfo.ts create mode 100644 app/client/src/ee/entities/IDE/hooks/useCreateActionsPermissions.ts delete mode 100644 app/client/src/ee/pages/Editor/IDE/EditorPane/JS/utils.ts create mode 100644 app/client/src/ee/pages/Editor/IDE/EditorPane/JS/utils/getJSEntityItemUrl.ts create mode 100644 app/client/src/ee/pages/Editor/IDE/EditorPane/JS/utils/getJSUrl.ts delete mode 100644 app/client/src/ee/pages/Editor/IDE/EditorPane/Query/utils.ts create mode 100644 app/client/src/ee/pages/Editor/IDE/EditorPane/Query/utils/getQueryEntityItemUrl.ts create mode 100644 app/client/src/ee/pages/Editor/IDE/EditorPane/Query/utils/getQueryUrl.ts diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/DatasourceTab/DatasourceInfo.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/DatasourceTab/DatasourceInfo.tsx index 4c00cc7c3143..7b639a11a7e7 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/DatasourceTab/DatasourceInfo.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/DatasourceTab/DatasourceInfo.tsx @@ -8,8 +8,8 @@ import { datasourcesEditorIdURL } from "ee/RouteBuilder"; import { omit } from "lodash"; import { getQueryParams } from "utils/URLUtils"; import history from "utils/history"; -import { useEditorType } from "ee/hooks"; -import { useParentEntityInfo } from "ee/hooks/datasourceEditorHooks"; +import { useParentEntityInfo } from "ee/IDE/hooks/useParentEntityInfo"; +import { getIDETypeByUrl } from "ee/entities/IDE/utils"; import type { Plugin } from "entities/Plugin"; interface Props { @@ -25,8 +25,8 @@ const DatasourceInfo = ({ plugin, showEditButton, }: Props) => { - const editorType = useEditorType(location.pathname); - const { parentEntityId } = useParentEntityInfo(editorType); + const ideType = getIDETypeByUrl(location.pathname); + const { parentEntityId } = useParentEntityInfo(ideType); // eslint-disable-next-line react-perf/jsx-no-new-function-as-prop const editDatasource = () => { diff --git a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/DatasourceTab/DatasourceTab.tsx b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/DatasourceTab/DatasourceTab.tsx index 78964d811697..c31b4668ee70 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionResponse/components/DatasourceTab/DatasourceTab.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionResponse/components/DatasourceTab/DatasourceTab.tsx @@ -23,8 +23,7 @@ import { isEmpty, omit } from "lodash"; import { getQueryParams } from "utils/URLUtils"; import { TableColumns } from "./TableColumns"; import { BOTTOMBAR_HEIGHT } from "./constants"; -import { useEditorType } from "ee/hooks"; -import { useParentEntityInfo } from "ee/hooks/datasourceEditorHooks"; +import { useParentEntityInfo } from "ee/IDE/hooks/useParentEntityInfo"; import DatasourceInfo from "./DatasourceInfo"; import { getPlugin } from "ee/selectors/entitiesSelector"; import { @@ -34,6 +33,7 @@ import { } from "ee/utils/BusinessFeatures/permissionPageHelpers"; import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; +import { getIDETypeByUrl } from "ee/entities/IDE/utils"; interface Props { datasourceId: string; @@ -58,8 +58,8 @@ const DatasourceTab = (props: Props) => { const plugin = useSelector((state) => getPlugin(state, pluginId || "")); - const editorType = useEditorType(location.pathname); - const { parentEntityId } = useParentEntityInfo(editorType); + const ideType = getIDETypeByUrl(location.pathname); + const { parentEntityId } = useParentEntityInfo(ideType); const [selectedTable, setSelectedTable] = useState(); diff --git a/app/client/src/ce/IDE/hooks/useParentEntityInfo.ts b/app/client/src/ce/IDE/hooks/useParentEntityInfo.ts new file mode 100644 index 000000000000..47a277a4cca8 --- /dev/null +++ b/app/client/src/ce/IDE/hooks/useParentEntityInfo.ts @@ -0,0 +1,19 @@ +import { ActionParentEntityType } from "ee/entities/Engine/actionHelpers"; +import type { IDEType } from "ee/entities/IDE/constants"; +import { useSelector } from "react-redux"; +import { + getCurrentApplicationId, + getCurrentBasePageId, +} from "selectors/editorSelectors"; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const useParentEntityInfo = (ideType: IDEType) => { + const appId = useSelector(getCurrentApplicationId); + const basePageId = useSelector(getCurrentBasePageId); + + return { + editorId: appId || "", + parentEntityId: basePageId || "", + parentEntityType: ActionParentEntityType.PAGE, + }; +}; diff --git a/app/client/src/ce/actions/helpers.ts b/app/client/src/ce/actions/helpers.ts index 5c976888d3e9..f244ff7483d2 100644 --- a/app/client/src/ce/actions/helpers.ts +++ b/app/client/src/ce/actions/helpers.ts @@ -10,6 +10,7 @@ import { saveActionName, } from "actions/pluginActionActions"; import { saveJSObjectName } from "actions/jsActionActions"; +import { IDE_TYPE, type IDEType } from "ee/entities/IDE/constants"; export const createNewQueryBasedOnParentEntity = ( entityId: string, @@ -43,29 +44,29 @@ export const createNewJSCollectionBasedOnParentEntity = ( return createNewJSCollection(entityId, from); }; -export const saveActionNameBasedOnParentEntity = ( +export const saveActionNameBasedOnIdeType = ( id: string, name: string, // Used in EE // eslint-disable-next-line @typescript-eslint/no-unused-vars - parentEntityType: ActionParentEntityTypeInterface = ActionParentEntityType.PAGE, + ideType: IDEType = IDE_TYPE.App, ) => { return saveActionName({ id, name }); }; -export const saveJSObjectNameBasedOnParentEntity = ( +export const saveJSObjectNameBasedOnIdeType = ( id: string, name: string, // Used in EE // eslint-disable-next-line @typescript-eslint/no-unused-vars - parentEntityType: ActionParentEntityTypeInterface = ActionParentEntityType.PAGE, + ideType: IDEType = IDE_TYPE.App, ) => { return saveJSObjectName({ id, name }); }; -export const createNewApiActionBasedOnEditorType = ( +export const createNewApiActionBasedOnIdeType = ( // eslint-disable-next-line @typescript-eslint/no-unused-vars - editorType: string, + ideType: IDEType, // eslint-disable-next-line @typescript-eslint/no-unused-vars editorId: string, parentEntityId: string, diff --git a/app/client/src/ce/entities/IDE/hooks/useCreateActionsPermissions.ts b/app/client/src/ce/entities/IDE/hooks/useCreateActionsPermissions.ts new file mode 100644 index 000000000000..c984e29bc061 --- /dev/null +++ b/app/client/src/ce/entities/IDE/hooks/useCreateActionsPermissions.ts @@ -0,0 +1,20 @@ +import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; +import { IDE_TYPE, type IDEType } from "ee/entities/IDE/constants"; +import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; +import { useSelector } from "react-redux"; +import { getPagePermissions } from "selectors/editorSelectors"; +import { getHasCreateActionPermission } from "ee/utils/BusinessFeatures/permissionPageHelpers"; + +export const useCreateActionsPermissions = (ideType: IDEType) => { + const isFeatureEnabled = useFeatureFlag(FEATURE_FLAG.license_gac_enabled); + const pagePermissions = useSelector(getPagePermissions); + + switch (ideType) { + case IDE_TYPE.App: { + return getHasCreateActionPermission(isFeatureEnabled, pagePermissions); + } + default: { + return true; + } + } +}; diff --git a/app/client/src/ce/hooks/datasourceEditorHooks.tsx b/app/client/src/ce/hooks/datasourceEditorHooks.tsx index 5dde79d1f35d..db6730c1961d 100644 --- a/app/client/src/ce/hooks/datasourceEditorHooks.tsx +++ b/app/client/src/ce/hooks/datasourceEditorHooks.tsx @@ -2,7 +2,6 @@ import { GENERATE_NEW_PAGE_BUTTON_TEXT, createMessage, } from "ee/constants/messages"; -import { ActionParentEntityType } from "ee/entities/Engine/actionHelpers"; import { FEATURE_FLAG } from "ee/entities/FeatureFlag"; import type { AppState } from "ee/reducers"; import { getPlugin } from "ee/selectors/entitiesSelector"; @@ -18,17 +17,13 @@ import NewActionButton from "pages/Editor/DataSourceEditor/NewActionButton"; import { useShowPageGenerationOnHeader } from "pages/Editor/DataSourceEditor/hooks"; import React from "react"; import { useDispatch, useSelector } from "react-redux"; -import { - getCurrentApplicationId, - getCurrentBasePageId, - getPagePermissions, -} from "selectors/editorSelectors"; +import { getPagePermissions } from "selectors/editorSelectors"; import { getIsAnvilEnabledInCurrentApplication } from "layoutSystems/anvil/integrations/selectors"; import { isEnabledForPreviewData } from "utils/editorContextUtils"; import { useFeatureFlag } from "utils/hooks/useFeatureFlag"; -import { EditorNames } from "./"; import { getCurrentApplication } from "ee/selectors/applicationSelectors"; import { openGeneratePageModal } from "pages/Editor/GeneratePage/store/generatePageActions"; +import { IDE_TYPE, type IDEType } from "ee/entities/IDE/constants"; export interface HeaderActionProps { datasource: Datasource | ApiDatasourceForm | undefined; @@ -38,7 +33,7 @@ export interface HeaderActionProps { } export const useHeaderActions = ( - editorType: string, + ideType: IDEType, { datasource, isPluginAuthorized, @@ -77,7 +72,7 @@ export const useHeaderActions = ( ? false : !!isPluginAllowedToPreviewData; - if (editorType === EditorNames.APPLICATION) { + if (ideType === IDE_TYPE.App) { const canCreateDatasourceActions = hasCreateDSActionPermissionInApp({ isEnabled: isFeatureEnabled, dsPermissions: datasource?.userPermissions ?? [], @@ -141,15 +136,3 @@ export const useHeaderActions = ( return {}; }; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export const useParentEntityInfo = (editorType: string) => { - const appId = useSelector(getCurrentApplicationId); - const basePageId = useSelector(getCurrentBasePageId); - - return { - editorId: appId || "", - parentEntityId: basePageId || "", - parentEntityType: ActionParentEntityType.PAGE, - }; -}; diff --git a/app/client/src/ce/hooks/hooks.test.ts b/app/client/src/ce/hooks/hooks.test.ts deleted file mode 100644 index 8259537e9dd3..000000000000 --- a/app/client/src/ce/hooks/hooks.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useEditorType, EditorNames } from "./index"; -import { - BUILDER_VIEWER_PATH_PREFIX, - BUILDER_BASE_PATH_DEPRECATED, -} from "constants/routes"; - -describe("useEditorType", () => { - it('should return "app" for BUILDER_VIEWER_PATH_PREFIX', () => { - const result = useEditorType(BUILDER_VIEWER_PATH_PREFIX); - - expect(result).toBe(EditorNames.APPLICATION); - }); - - it('should return "app" for BUILDER_BASE_PATH_DEPRECATED', () => { - const result = useEditorType(BUILDER_BASE_PATH_DEPRECATED); - - expect(result).toBe(EditorNames.APPLICATION); - }); - - it('should default to "app" for unmatched paths', () => { - const result = useEditorType("/some-random-path"); - - expect(result).toBe(EditorNames.APPLICATION); - }); -}); diff --git a/app/client/src/ce/hooks/index.ts b/app/client/src/ce/hooks/index.ts index 07f4b12bcd7c..44bb64e4445e 100644 --- a/app/client/src/ce/hooks/index.ts +++ b/app/client/src/ce/hooks/index.ts @@ -1,39 +1,4 @@ -import { - BUILDER_BASE_PATH_DEPRECATED, - BUILDER_VIEWER_PATH_PREFIX, -} from "constants/routes"; import { useEffect, type RefObject } from "react"; -import { matchPath } from "react-router"; - -export const EditorNames = { - APPLICATION: "app", -}; - -export interface EditorType { - [key: string]: string; -} - -export const editorType: EditorType = { - [BUILDER_VIEWER_PATH_PREFIX]: EditorNames.APPLICATION, - [BUILDER_BASE_PATH_DEPRECATED]: EditorNames.APPLICATION, -}; - -// Utility function to get editor type based on path -// to be used in non react functions -export const getEditorType = (path: string) => { - const basePath = matchPath(path, { - path: [BUILDER_VIEWER_PATH_PREFIX, BUILDER_BASE_PATH_DEPRECATED], - }); - - return basePath - ? editorType[basePath.path] - : editorType[BUILDER_VIEWER_PATH_PREFIX]; -}; - -// custom hook to get editor type based on path -export const useEditorType = (path: string) => { - return getEditorType(path); -}; export function useOutsideClick( ref: RefObject, diff --git a/app/client/src/ce/navigation/FocusSetters.ts b/app/client/src/ce/navigation/FocusSetters.ts index 85a26812644e..4d5aa3197b08 100644 --- a/app/client/src/ce/navigation/FocusSetters.ts +++ b/app/client/src/ce/navigation/FocusSetters.ts @@ -7,7 +7,7 @@ import { import { PluginType } from "entities/Plugin"; import type { FocusEntityInfo } from "navigation/FocusEntity"; import { FocusEntity } from "navigation/FocusEntity"; -import { getQueryEntityItemUrl } from "../pages/Editor/IDE/EditorPane/Query/utils"; +import { getQueryEntityItemUrl } from "ee/pages/Editor/IDE/EditorPane/Query/utils/getQueryEntityItemUrl"; export function setSelectedDatasource(id?: string) { if (id) { diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/ListItem.tsx b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/ListItem.tsx index cf79cc6e9748..65db9a369bd6 100644 --- a/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/ListItem.tsx +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/ListItem.tsx @@ -1,6 +1,5 @@ import React from "react"; import ExplorerJSCollectionEntity from "pages/Editor/Explorer/JSActions/JSActionEntity"; -import type { ActionParentEntityTypeInterface } from "ee/entities/Engine/actionHelpers"; import { Flex } from "@appsmith/ads"; import type { EntityItem } from "ee/entities/IDE/constants"; @@ -8,11 +7,10 @@ export interface JSListItemProps { item: EntityItem; isActive: boolean; parentEntityId: string; - parentEntityType: ActionParentEntityTypeInterface; } export const JSListItem = (props: JSListItemProps) => { - const { isActive, item, parentEntityId, parentEntityType } = props; + const { isActive, item, parentEntityId } = props; return ( @@ -21,7 +19,6 @@ export const JSListItem = (props: JSListItemProps) => { isActive={isActive} key={item.key} parentEntityId={parentEntityId} - parentEntityType={parentEntityType} searchKeyword={""} step={1} /> diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/hooks.tsx b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/hooks.tsx index 34ce88dd556a..9f71fb80b129 100644 --- a/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/hooks.tsx +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/hooks.tsx @@ -12,7 +12,7 @@ import { ADD_PATH } from "ee/constants/routes/appRoutes"; import history from "utils/history"; import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity"; import { useModuleOptions } from "ee/utils/moduleInstanceHelpers"; -import { getJSUrl } from "ee/pages/Editor/IDE/EditorPane/JS/utils"; +import { getJSUrl } from "ee/pages/Editor/IDE/EditorPane/JS/utils/getJSUrl"; import { getIDEViewMode } from "selectors/ideSelectors"; import { EditorViewMode } from "ee/entities/IDE/constants"; import { setListViewActiveState } from "actions/ideActions"; diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSEntityItemUrl.test.ts b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSEntityItemUrl.test.ts new file mode 100644 index 000000000000..d05e384edd34 --- /dev/null +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSEntityItemUrl.test.ts @@ -0,0 +1,21 @@ +import { getJSEntityItemUrl } from "./getJSEntityItemUrl"; +import urlBuilder from "ee/entities/URLRedirect/URLAssembly"; +import { PluginType } from "entities/Plugin"; + +describe("getJSEntityItemUrl", () => { + urlBuilder.setCurrentBasePageId("0123456789abcdef00000000"); + it("returns a JS url", () => { + const url = getJSEntityItemUrl( + { + title: "TestTitle", + key: "abc", + type: PluginType.JS, + }, + "0123456789abcdef00000000", + ); + + expect(url).toEqual( + "/app/application/page-0123456789abcdef00000000/edit/jsObjects/abc", + ); + }); +}); diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSEntityItemUrl.ts b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSEntityItemUrl.ts new file mode 100644 index 000000000000..e394f9db47fd --- /dev/null +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSEntityItemUrl.ts @@ -0,0 +1,12 @@ +import type { EntityItem } from "ee/entities/IDE/constants"; +import { jsCollectionIdURL } from "ee/RouteBuilder"; + +export const getJSEntityItemUrl = ( + item: EntityItem, + basePageId: string, +): string => { + return jsCollectionIdURL({ + baseCollectionId: item.key, + basePageId, + }); +}; diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils.test.ts b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSUrl.test.ts similarity index 77% rename from app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils.test.ts rename to app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSUrl.test.ts index d12cadbbf429..d71b88d4f4bd 100644 --- a/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils.test.ts +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSUrl.test.ts @@ -1,28 +1,9 @@ -import { getJSEntityItemUrl, getJSUrl } from "./utils"; +import { getJSUrl } from "./getJSUrl"; import urlBuilder from "ee/entities/URLRedirect/URLAssembly"; -import { PluginType } from "entities/Plugin"; import type { FocusEntityInfo } from "navigation/FocusEntity"; import { FocusEntity } from "navigation/FocusEntity"; import { EditorState } from "ee/entities/IDE/constants"; -describe("getJSEntityItemUrl", () => { - urlBuilder.setCurrentBasePageId("0123456789abcdef00000000"); - it("returns a JS url", () => { - const url = getJSEntityItemUrl( - { - title: "TestTitle", - key: "abc", - type: PluginType.JS, - }, - "0123456789abcdef00000000", - ); - - expect(url).toEqual( - "/app/application/page-0123456789abcdef00000000/edit/jsObjects/abc", - ); - }); -}); - describe("getJSUrl", () => { urlBuilder.setCurrentBasePageId("0123456789abcdef00000000"); it("returns a JS collection url", () => { diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils.ts b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSUrl.ts similarity index 74% rename from app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils.ts rename to app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSUrl.ts index efc02a20c386..af7f33f6a406 100644 --- a/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils.ts +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/JS/utils/getJSUrl.ts @@ -1,4 +1,3 @@ -import type { EntityItem } from "ee/entities/IDE/constants"; import { jsCollectionAddURL, jsCollectionIdURL, @@ -6,16 +5,6 @@ import { } from "ee/RouteBuilder"; import { FocusEntity, type FocusEntityInfo } from "navigation/FocusEntity"; -export const getJSEntityItemUrl = ( - item: EntityItem, - basePageId: string, -): string => { - return jsCollectionIdURL({ - baseCollectionId: item.key, - basePageId, - }); -}; - export const getJSUrl = ( item: FocusEntityInfo, add: boolean = true, diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/ListItem.tsx b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/ListItem.tsx index 9cbf4f993aa8..90db39393a2a 100644 --- a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/ListItem.tsx +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/ListItem.tsx @@ -1,5 +1,4 @@ import React from "react"; -import type { ActionParentEntityTypeInterface } from "ee/entities/Engine/actionHelpers"; import ExplorerActionEntity from "pages/Editor/Explorer/Actions/ActionEntity"; import type { EntityItem } from "ee/entities/IDE/constants"; @@ -7,11 +6,10 @@ export interface QueryListItemProps { item: EntityItem; isActive: boolean; parentEntityId: string; - parentEntityType: ActionParentEntityTypeInterface; } export const QueryListItem = (props: QueryListItemProps) => { - const { isActive, item, parentEntityId, parentEntityType } = props; + const { isActive, item, parentEntityId } = props; return ( { isActive={isActive} key={item.key} parentEntityId={parentEntityId} - parentEntityType={parentEntityType} searchKeyword={""} step={1} type={item.type} diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/hooks.tsx b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/hooks.tsx index 1c850a191c4f..d7aacb7d649a 100644 --- a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/hooks.tsx +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/hooks.tsx @@ -15,7 +15,7 @@ import { getHasCreateActionPermission } from "ee/utils/BusinessFeatures/permissi import type { ActionOperation } from "components/editorComponents/GlobalSearch/utils"; import { SEARCH_ITEM_TYPES } from "components/editorComponents/GlobalSearch/utils"; import { createMessage, EDITOR_PANE_TEXTS } from "ee/constants/messages"; -import { getQueryUrl } from "ee/pages/Editor/IDE/EditorPane/Query/utils"; +import { getQueryUrl } from "ee/pages/Editor/IDE/EditorPane/Query/utils/getQueryUrl"; import { ADD_PATH, API_EDITOR_ID_PATH, diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryEntityItemUrl.test.ts b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryEntityItemUrl.test.ts new file mode 100644 index 000000000000..211808cffca0 --- /dev/null +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryEntityItemUrl.test.ts @@ -0,0 +1,26 @@ +import { getQueryEntityItemUrl } from "./getQueryEntityItemUrl"; +import { PluginType } from "entities/Plugin"; + +describe("getQueryEntityItemUrl", () => { + it("throws error if plugin type is not a query", () => { + const item = { + title: "testTitle", + type: PluginType.JS, + key: "abc", + }; + + expect(() => getQueryEntityItemUrl(item, "")).toThrow(); + }); + + it("returns url for a query type plugin", () => { + const item = { + title: "testTitle", + type: PluginType.INTERNAL, + key: "abc", + }; + + expect(getQueryEntityItemUrl(item, "0123456789abcdef00000000")).toEqual( + "/app/application/page-0123456789abcdef00000000/edit/queries/abc", + ); + }); +}); diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryEntityItemUrl.ts b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryEntityItemUrl.ts new file mode 100644 index 000000000000..b8b7ae4112de --- /dev/null +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryEntityItemUrl.ts @@ -0,0 +1,15 @@ +import type { EntityItem } from "ee/entities/IDE/constants"; +import { getActionConfig } from "pages/Editor/Explorer/Actions/helpers"; + +export const getQueryEntityItemUrl = ( + item: EntityItem, + basePageId: string, +): string => { + const config = getActionConfig(item.type); + + if (!config) { + throw Error(`Cannot find url of plugin type ${item.type}`); + } + + return config.getURL(basePageId, item.key, item.type); +}; diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils.test.ts b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryUrl.test.ts similarity index 81% rename from app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils.test.ts rename to app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryUrl.test.ts index f6e557398c62..aa5fee5925da 100644 --- a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils.test.ts +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryUrl.test.ts @@ -1,34 +1,10 @@ -import { getQueryEntityItemUrl, getQueryUrl } from "./utils"; +import { getQueryUrl } from "./getQueryUrl"; import type { FocusEntityInfo } from "navigation/FocusEntity"; import { FocusEntity } from "navigation/FocusEntity"; import { EditorState } from "ee/entities/IDE/constants"; -import { PluginPackageName, PluginType } from "entities/Plugin"; +import { PluginPackageName } from "entities/Plugin"; import urlBuilder from "ee/entities/URLRedirect/URLAssembly"; -describe("getQueryEntityItemUrl", () => { - it("throws error if plugin type is not a query", () => { - const item = { - title: "testTitle", - type: PluginType.JS, - key: "abc", - }; - - expect(() => getQueryEntityItemUrl(item, "")).toThrow(); - }); - - it("returns url for a query type plugin", () => { - const item = { - title: "testTitle", - type: PluginType.INTERNAL, - key: "abc", - }; - - expect(getQueryEntityItemUrl(item, "0123456789abcdef00000000")).toEqual( - "/app/application/page-0123456789abcdef00000000/edit/queries/abc", - ); - }); -}); - describe("getQueryUrl", () => { urlBuilder.setCurrentBasePageId("0123456789abcdef00000000"); it("gets the correct SAAS plugin url", () => { diff --git a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils.ts b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryUrl.ts similarity index 70% rename from app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils.ts rename to app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryUrl.ts index 6a732cee324b..f56cc625940b 100644 --- a/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils.ts +++ b/app/client/src/ce/pages/Editor/IDE/EditorPane/Query/utils/getQueryUrl.ts @@ -1,6 +1,3 @@ -import type { EntityItem } from "ee/entities/IDE/constants"; -import { getActionConfig } from "pages/Editor/Explorer/Actions/helpers"; -import type { FocusEntityInfo } from "navigation/FocusEntity"; import { apiEditorIdURL, queryAddURL, @@ -8,19 +5,7 @@ import { queryListURL, saasEditorApiIdURL, } from "ee/RouteBuilder"; - -export const getQueryEntityItemUrl = ( - item: EntityItem, - basePageId: string, -): string => { - const config = getActionConfig(item.type); - - if (!config) { - throw Error(`Cannot find url of plugin type ${item.type}`); - } - - return config.getURL(basePageId, item.key, item.type); -}; +import type { FocusEntityInfo } from "navigation/FocusEntity"; export const getQueryUrl = ( item: FocusEntityInfo, diff --git a/app/client/src/ce/pages/Editor/gitSync/useReconnectModalData.ts b/app/client/src/ce/pages/Editor/gitSync/useReconnectModalData.ts index 42e4c97d492b..b8c012ee97d1 100644 --- a/app/client/src/ce/pages/Editor/gitSync/useReconnectModalData.ts +++ b/app/client/src/ce/pages/Editor/gitSync/useReconnectModalData.ts @@ -1,10 +1,10 @@ +import { IDE_TYPE } from "ee/entities/IDE/constants"; import { builderURL } from "ee/RouteBuilder"; import { RECONNECT_MISSING_DATASOURCE_CREDENTIALS_DESCRIPTION, SKIP_TO_APPLICATION, createMessage, } from "ee/constants/messages"; -import { EditorNames } from "ee/hooks"; import { getApplicationByIdFromWorkspaces } from "ee/selectors/applicationSelectors"; import { useSelector } from "react-redux"; @@ -36,7 +36,7 @@ function useReconnectModalData({ appId, pageId }: UseReconnectModalDataProps) { editorURL, editorId: appId, parentEntityId: pageId, - editorType: EditorNames.APPLICATION, + ideType: IDE_TYPE.App, }; } diff --git a/app/client/src/ce/selectors/appIDESelectors.ts b/app/client/src/ce/selectors/appIDESelectors.ts index 79d4fd62558b..16fcca2e5e5f 100644 --- a/app/client/src/ce/selectors/appIDESelectors.ts +++ b/app/client/src/ce/selectors/appIDESelectors.ts @@ -9,7 +9,7 @@ import { getJSTabs, getQueryTabs } from "selectors/ideSelectors"; import type { AppState } from "ee/reducers"; import { identifyEntityFromPath } from "navigation/FocusEntity"; import { getCurrentBasePageId } from "selectors/editorSelectors"; -import { getQueryEntityItemUrl } from "ee/pages/Editor/IDE/EditorPane/Query/utils"; +import { getQueryEntityItemUrl } from "ee/pages/Editor/IDE/EditorPane/Query/utils/getQueryEntityItemUrl"; export type EditorSegmentList = Array<{ group: string | "NA"; diff --git a/app/client/src/ce/selectors/entitiesSelector.ts b/app/client/src/ce/selectors/entitiesSelector.ts index d81657d5d4a0..10b30bc32b90 100644 --- a/app/client/src/ce/selectors/entitiesSelector.ts +++ b/app/client/src/ce/selectors/entitiesSelector.ts @@ -64,6 +64,7 @@ import { EditorEntityTab, type EntityItem, type GenericEntityItem, + type IDEType, } from "ee/entities/IDE/constants"; import { ActionUrlIcon, @@ -1701,16 +1702,17 @@ export const getSelectedTableName = (state: AppState) => export const getDatasourceUsageCountForApp = createSelector( getActions, getDatasources, - (state: AppState, editorType: string) => editorType, - (actions, datasources, editorType) => { + (state: AppState, ideType: IDEType) => ideType, + (actions, datasources, ideType) => { const actionCount = countBy(actions, "config.datasource.id"); const actionDsMap: Record = {}; datasources.forEach((ds) => { - actionDsMap[ds.id] = `No queries in this ${editorType}`; + actionDsMap[ds.id] = `No queries in this ${ideType.toLowerCase()}`; }); Object.keys(actionCount).forEach((dsId) => { - actionDsMap[dsId] = `${actionCount[dsId]} queries in this ${editorType}`; + actionDsMap[dsId] = + `${actionCount[dsId]} queries in this ${ideType.toLowerCase()}`; }); return actionDsMap; diff --git a/app/client/src/ce/utils/BusinessFeatures/permissionPageHelpers.tsx b/app/client/src/ce/utils/BusinessFeatures/permissionPageHelpers.tsx index 8cd3dcb06c47..e882d308b893 100644 --- a/app/client/src/ce/utils/BusinessFeatures/permissionPageHelpers.tsx +++ b/app/client/src/ce/utils/BusinessFeatures/permissionPageHelpers.tsx @@ -43,7 +43,7 @@ import { hasExecuteActionPermission as hasExecuteActionPermission_EE } from "ee/ import { hasAuditLogsReadPermission as hasAuditLogsReadPermission_CE } from "ce/utils/permissionHelpers"; import { hasAuditLogsReadPermission as hasAuditLogsReadPermission_EE } from "ee/utils/permissionHelpers"; -import { EditorNames } from "ee/hooks"; +import { IDE_TYPE, type IDEType } from "ee/entities/IDE/constants"; export const getHasCreateWorkspacePermission = ( isEnabled: boolean, @@ -167,16 +167,16 @@ export const getHasAuditLogsReadPermission = ( export const hasCreateDSActionPermissionInApp = ({ dsPermissions, - editorType, + ideType, isEnabled, pagePermissions, }: { dsPermissions?: string[]; - editorType?: string; + ideType?: IDEType; isEnabled: boolean; pagePermissions?: string[]; }) => { - return !editorType || editorType === EditorNames.APPLICATION + return !ideType || ideType === IDE_TYPE.App ? getHasCreateDatasourceActionPermission(isEnabled, dsPermissions) && getHasCreateActionPermission(isEnabled, pagePermissions) : getHasCreateDatasourceActionPermission(isEnabled, dsPermissions); diff --git a/app/client/src/ce/utils/lintRulesHelpers.ts b/app/client/src/ce/utils/lintRulesHelpers.ts index fc857cad6b1e..fe0c07471d15 100644 --- a/app/client/src/ce/utils/lintRulesHelpers.ts +++ b/app/client/src/ce/utils/lintRulesHelpers.ts @@ -1,5 +1,7 @@ +import type { IDEType } from "ee/entities/IDE/constants"; + interface ContextType { - editorType: string; + ideType: IDEType; } /** diff --git a/app/client/src/components/editorComponents/form/fields/DropdownWrapper.tsx b/app/client/src/components/editorComponents/form/fields/DropdownWrapper.tsx index b728ad86e495..c42dd6c333a3 100644 --- a/app/client/src/components/editorComponents/form/fields/DropdownWrapper.tsx +++ b/app/client/src/components/editorComponents/form/fields/DropdownWrapper.tsx @@ -80,7 +80,7 @@ function DropdownWrapper(props: DropdownWrapperProps) { return (