diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 27fc332129ce..12abe96745f8 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -1,7 +1,7 @@ # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json language: "en-US" early_access: false -tone_instructions: 'You must talk like teacher.' +tone_instructions: 'You are an expert code reviewer in Java, TypeScript, JavaScript, and NodeJS. You work in an enterprise software developer team, providing concise and clear code review advice.You only elaborate or provide detailed explanations when requested.' reviews: profile: "chill" request_changes_workflow: false diff --git a/.github/workflows/ad-hoc-docker-image.yml b/.github/workflows/ad-hoc-docker-image.yml index 30a8de48ea11..44cfce8435ee 100644 --- a/.github/workflows/ad-hoc-docker-image.yml +++ b/.github/workflows/ad-hoc-docker-image.yml @@ -14,6 +14,11 @@ on: required: false type: string default: ad-hoc + pg_tag: + description: Postgres tag to use for image + required: false + type: string + default: pg jobs: server-build: @@ -94,7 +99,7 @@ jobs: run: | run: | if [[ -f scripts/prepare_server_artifacts.sh ]]; then - scripts/prepare_server_artifacts.sh + PG_TAG=${{ inputs.pg_tag }} scripts/prepare_server_artifacts.sh else echo "No script found to prepare server artifacts" exit 1 diff --git a/app/client/packages/design-system/ads/src/Popover/Popover.styles.tsx b/app/client/packages/design-system/ads/src/Popover/Popover.styles.tsx index 50cb5421ae81..a5a23e329458 100644 --- a/app/client/packages/design-system/ads/src/Popover/Popover.styles.tsx +++ b/app/client/packages/design-system/ads/src/Popover/Popover.styles.tsx @@ -65,5 +65,5 @@ export const StyledBody = styled.div` max-height: calc( var(--popover-max-height) - calc(var(--popover-padding) * 2 + 25.5px) ); - overflow-y: scroll; + overflow-y: auto; `; diff --git a/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts b/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts index 4013281f9794..844dfb005b76 100644 --- a/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts +++ b/app/client/packages/design-system/theming/src/color/src/DarkModeTheme.ts @@ -410,8 +410,8 @@ export class DarkModeTheme implements ColorModeTheme { color.oklch.l = 0.22; } - if (this.seedChroma > 0.025) { - color.oklch.c = 0.025; + if (this.seedChroma > 0.005) { + color.oklch.c = 0.005; } if (this.seedIsAchromatic) { @@ -448,7 +448,7 @@ export class DarkModeTheme implements ColorModeTheme { private get bgNeutralSoftHover() { const color = this.bgNeutralSoft.clone(); - color.oklch.l += 0.01; + color.oklch.l += 0.02; return color; } @@ -456,7 +456,7 @@ export class DarkModeTheme implements ColorModeTheme { private get bgNeutralSoftActive() { const color = this.bgNeutralSoft.clone(); - color.oklch.l -= 0.01; + color.oklch.l -= 0.015; return color; } diff --git a/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts b/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts index d9573aef541e..4322adead1f2 100644 --- a/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts +++ b/app/client/packages/design-system/theming/src/color/src/LightModeTheme.ts @@ -432,7 +432,7 @@ export class LightModeTheme implements ColorModeTheme { private get bgNeutralSoft() { const color = this.bgNeutralSubtle.clone(); - color.oklch.l -= 0.03; + color.oklch.l -= 0.045; return color; } @@ -440,7 +440,7 @@ export class LightModeTheme implements ColorModeTheme { private get bgNeutralSoftHover() { const color = this.bgNeutralSoft.clone(); - color.oklch.l += 0.01; + color.oklch.l += 0.015; return color; } @@ -448,7 +448,7 @@ export class LightModeTheme implements ColorModeTheme { private get bgNeutralSoftActive() { const color = this.bgNeutralSoft.clone(); - color.oklch.l -= 0.01; + color.oklch.l -= 0.015; return color; } diff --git a/app/client/packages/design-system/widgets/src/components/Avatar/src/styles.module.css b/app/client/packages/design-system/widgets/src/components/Avatar/src/styles.module.css index f3172cc40392..abeb0956b13a 100644 --- a/app/client/packages/design-system/widgets/src/components/Avatar/src/styles.module.css +++ b/app/client/packages/design-system/widgets/src/components/Avatar/src/styles.module.css @@ -2,18 +2,18 @@ display: inline-flex; align-items: center; justify-content: center; - width: var(--sizing-8); - height: var(--sizing-8); + width: var(--icon-size-3); + height: var(--icon-size-3); } .avatar[data-size="small"] { - width: var(--sizing-6); - height: var(--sizing-6); + width: var(--icon-size-2); + height: var(--icon-size-2); } .avatar[data-size="large"] { - width: var(--sizing-10); - height: var(--sizing-10); + width: var(--icon-size-4); + height: var(--icon-size-4); } /* if the avatar has div, that means no source is provided. For this case, we want to add a background color and border radius */ diff --git a/app/client/packages/design-system/widgets/src/components/Text/src/styles.module.css b/app/client/packages/design-system/widgets/src/components/Text/src/styles.module.css index a6dae07300dd..ca3d6294f792 100644 --- a/app/client/packages/design-system/widgets/src/components/Text/src/styles.module.css +++ b/app/client/packages/design-system/widgets/src/components/Text/src/styles.module.css @@ -10,6 +10,11 @@ color: var(--color-fg-$(color)); } } + + /* Adding as special case as we can't add neutral-subtle to $colors variable, as that variable is used generically for many components ( button ). */ + &[data-color="neutral-subtle"] { + color: var(--color-fg-neutral-subtle); + } } .clampedText { diff --git a/app/client/packages/design-system/widgets/src/components/Text/src/types.ts b/app/client/packages/design-system/widgets/src/components/Text/src/types.ts index c2aa405326ca..66a4a4c5d5d4 100644 --- a/app/client/packages/design-system/widgets/src/components/Text/src/types.ts +++ b/app/client/packages/design-system/widgets/src/components/Text/src/types.ts @@ -13,7 +13,7 @@ export interface TextProps { /** Color of the text * @default inherit */ - color?: keyof typeof COLORS; + color?: keyof typeof COLORS | "neutral-subtle"; /** Sets the weight (or boldness) of the font * @default false */ diff --git a/app/client/packages/dsl/src/migrate/index.ts b/app/client/packages/dsl/src/migrate/index.ts index 811a6059cc5f..738e4ac3e1c9 100644 --- a/app/client/packages/dsl/src/migrate/index.ts +++ b/app/client/packages/dsl/src/migrate/index.ts @@ -146,7 +146,7 @@ const migrateUnversionedDSL = (currentDSL: DSLWidget) => { // A rudimentary transform function which updates the DSL based on its version. // A more modular approach needs to be designed. // This needs the widget config to be already built to migrate correctly -const migrateVersionedDSL = (currentDSL: DSLWidget, newPage = false) => { +const migrateVersionedDSL = async (currentDSL: DSLWidget, newPage = false) => { if (currentDSL.version === 1) { if (currentDSL.children && currentDSL.children.length > 0) currentDSL.children = currentDSL.children.map(updateContainers); @@ -205,7 +205,7 @@ const migrateVersionedDSL = (currentDSL: DSLWidget, newPage = false) => { } if (currentDSL.version === 12) { - currentDSL = migrateIncorrectDynamicBindingPathLists(currentDSL); + currentDSL = await migrateIncorrectDynamicBindingPathLists(currentDSL); currentDSL.version = 13; } @@ -619,15 +619,15 @@ const migrateVersionedDSL = (currentDSL: DSLWidget, newPage = false) => { return currentDSL; }; -export const migrateDSL = ( +export const migrateDSL = async ( currentDSL: DSLWidget, newPage = false, -): DSLWidget => { +): Promise => { if (currentDSL.version === undefined) { const initialDSL = migrateUnversionedDSL(currentDSL); - return migrateVersionedDSL(initialDSL, newPage) as DSLWidget; + return (await migrateVersionedDSL(initialDSL, newPage)) as DSLWidget; } else { - return migrateVersionedDSL(currentDSL, newPage) as DSLWidget; + return (await migrateVersionedDSL(currentDSL, newPage)) as DSLWidget; } }; diff --git a/app/client/packages/dsl/src/migrate/migrations/012-migrate-incorrect-dynamic-binding-path-lists.ts b/app/client/packages/dsl/src/migrate/migrations/012-migrate-incorrect-dynamic-binding-path-lists.ts index bc3d32aa9491..9b9effd865d0 100644 --- a/app/client/packages/dsl/src/migrate/migrations/012-migrate-incorrect-dynamic-binding-path-lists.ts +++ b/app/client/packages/dsl/src/migrate/migrations/012-migrate-incorrect-dynamic-binding-path-lists.ts @@ -7,7 +7,6 @@ import isString from "lodash/isString"; import memoize from "micro-memoize"; import { isObject, isUndefined } from "lodash"; import { generateReactKey, isDynamicValue } from "../utils"; -import widgetConfigs from "../helpers/widget-configs.json"; export const WidgetHeightLimits = { MAX_HEIGHT_IN_ROWS: 9000, @@ -511,8 +510,33 @@ export function addSearchConfigToPanelConfig(config: readonly any[]) { return configItem; }); } +// Cache for lazy-loaded widget configurations +let cachedWidgetConfigs: any | null = null; + +/* + Lazily load this file since it is very large and used in migrations for certain DSL versions. By lazily loading + this large file it can be reduce the main chunk only be loaded for certain limited conditions. +*/ +const loadWidgetConfig = async () => { + if (!cachedWidgetConfigs) { + try { + const { default: widgetConfigs } = await import( + "../helpers/widget-configs.json" + ); + + cachedWidgetConfigs = widgetConfigs; // Cache the module for future use + } catch (e) { + log.error("Error loading WidgetConfig", e); + } + } -const getWidgetPropertyPaneContentConfig = (type: string): readonly any[] => { + return cachedWidgetConfigs; +}; + +const getWidgetPropertyPaneContentConfig = async ( + type: string, +): Promise => { + const widgetConfigs = await loadWidgetConfig(); const propertyPaneContentConfig = (widgetConfigs as any)[type] .propertyPaneContentConfig; @@ -540,7 +564,11 @@ const getWidgetPropertyPaneContentConfig = (type: string): readonly any[] => { } }; -const getWidgetPropertyPaneStyleConfig = (type: string): readonly any[] => { +const getWidgetPropertyPaneStyleConfig = async ( + type: string, +): Promise => { + const widgetConfigs = await loadWidgetConfig(); + const propertyPaneStyleConfig = (widgetConfigs as any)[type] .propertyPaneStyleConfig; @@ -567,14 +595,20 @@ const getWidgetPropertyPaneStyleConfig = (type: string): readonly any[] => { } }; -const getWidgetPropertyPaneCombinedConfig = (type: string): readonly any[] => { - const contentConfig = getWidgetPropertyPaneContentConfig(type); - const styleConfig = getWidgetPropertyPaneStyleConfig(type); +const getWidgetPropertyPaneCombinedConfig = async ( + type: string, +): Promise => { + const contentConfig = await getWidgetPropertyPaneContentConfig(type); + const styleConfig = await getWidgetPropertyPaneStyleConfig(type); return [...contentConfig, ...styleConfig]; }; -const getWidgetPropertyPaneConfig = (type: string): readonly any[] => { +const getWidgetPropertyPaneConfig = async ( + type: string, +): Promise => { + const widgetConfigs = await loadWidgetConfig(); + const propertyPaneConfig = (widgetConfigs as any)[type].propertyPaneConfig; const features = (widgetConfigs as any)[type].features; @@ -590,7 +624,7 @@ const getWidgetPropertyPaneConfig = (type: string): readonly any[] => { return enhancedPropertyPaneConfig; } else { - const config = getWidgetPropertyPaneCombinedConfig(type); + const config = await getWidgetPropertyPaneCombinedConfig(type); if (config === undefined) { log.error("Widget property pane config not defined", type); @@ -957,14 +991,14 @@ const getAllPathsFromPropertyConfig = memoize( { maxSize: 1000 }, ); -export const migrateIncorrectDynamicBindingPathLists = ( +export const migrateIncorrectDynamicBindingPathLists = async ( currentDSL: Readonly, -): DSLWidget => { +): Promise => { const migratedDsl = { ...currentDSL, }; const dynamicBindingPathList: any[] = []; - const propertyPaneConfig = getWidgetPropertyPaneConfig(currentDSL.type); + const propertyPaneConfig = await getWidgetPropertyPaneConfig(currentDSL.type); const { bindingPaths } = getAllPathsFromPropertyConfig( currentDSL, propertyPaneConfig, @@ -984,8 +1018,10 @@ export const migrateIncorrectDynamicBindingPathLists = ( migratedDsl.dynamicBindingPathList = dynamicBindingPathList; if (currentDSL.children) { - migratedDsl.children = currentDSL.children.map( - migrateIncorrectDynamicBindingPathLists, + migratedDsl.children = await Promise.all( + currentDSL.children.map(async (value) => + migrateIncorrectDynamicBindingPathLists(value), + ), ); } diff --git a/app/client/packages/dsl/src/migrate/tests/DSLMigration.test.ts b/app/client/packages/dsl/src/migrate/tests/DSLMigration.test.ts index ae865ac739ef..76b4294ab87a 100644 --- a/app/client/packages/dsl/src/migrate/tests/DSLMigration.test.ts +++ b/app/client/packages/dsl/src/migrate/tests/DSLMigration.test.ts @@ -938,85 +938,82 @@ describe("Test all the migrations are running", () => { afterAll(() => { jest.clearAllMocks(); }); - migrations.forEach((migration: Migration) => { - /** - * Generates mock fucntion for each migration function. - * Mocks the implementation - */ - const version = migration.version ?? 0; + test("assert migration functions being called when migrate dsl has been called ", async () => { + migrations.forEach((migration: Migration) => { + /** + * Generates mock fucntion for each migration function. + * Mocks the implementation + */ + const version = migration.version ?? 0; - mockFnObj[version] = []; + mockFnObj[version] = []; - migration.functionLookup.forEach((lookup) => { - const { functionName, moduleObj } = lookup; + migration.functionLookup.forEach((lookup) => { + const { functionName, moduleObj } = lookup; - if (moduleObj) { - mockFnObj[version].push({ - spyOnFunc: jest - .spyOn(moduleObj, functionName) - .mockImplementation((dsl: any) => { - /** - * We need to delete the children property on the first migration(calculateDynamicHeight), - * to avoid the recursion in the second migration(updateContainers) - */ - dsl && delete dsl.children; + if (moduleObj) { + mockFnObj[version].push({ + spyOnFunc: jest + .spyOn(moduleObj, functionName) + .mockImplementation((dsl: any) => { + /** + * We need to delete the children property on the first migration(calculateDynamicHeight), + * to avoid the recursion in the second migration(updateContainers) + */ + dsl && delete dsl.children; - return { - version: dsl?.version, - validationFuncName: functionName, - }; - }), - }); - } + return { + version: dsl?.version, + validationFuncName: functionName, + }; + }), + }); + } + }); }); - }); - // Runs all the migrations - DSLMigrations.migrateDSL(originalDSLForDSLMigrations as unknown as DSLWidget); + // Runs all the migrations + await DSLMigrations.migrateDSL( + originalDSLForDSLMigrations as unknown as DSLWidget, + ); - migrations.forEach((item: any, testIdx: number) => { - const { functionLookup, version } = item; - const dslVersion = version ?? 0; + migrations.forEach((item: any) => { + const { functionLookup, version } = item; + const dslVersion = version ?? 0; - functionLookup.forEach( - (lookup: { moduleObj: any; functionName: string }, index: number) => { - const { functionName, moduleObj } = lookup; + functionLookup.forEach( + (lookup: { moduleObj: any; functionName: string }, index: number) => { + const { functionName, moduleObj } = lookup; - if (moduleObj) { - const mockObj = mockFnObj[dslVersion][index].spyOnFunc; - const calls = mockObj.mock?.calls; - const results = mockObj.mock?.results; - const resultsLastIdx = mockObj.mock.results.length - 1; - - describe(`Test ${testIdx}:`, () => { - test(`Has ${functionName} function executed?`, () => { - // Check if the migration function is called - expect(results[resultsLastIdx].value.validationFuncName).toEqual( - functionName, - ); - }); + if (moduleObj) { + const mockObj = mockFnObj[dslVersion][index].spyOnFunc; + const calls = mockObj.mock?.calls; + const results = mockObj.mock?.results; + const resultsLastIdx = mockObj.mock.results.length - 1; + // Check if the migration function is called + expect(results[resultsLastIdx].value.validationFuncName).toEqual( + functionName, + ); // Check if the migration function is called with the current DSL version calls.forEach((args: any) => { - test(`Does ${functionName} executes with DSL version: ${version}?`, () => { - if (args[0]?.version === version) { - expect(args[0]?.version).toEqual(version); - } - }); - test(`For ${functionName}, is the ${args[0]?.version} registerd in tests?`, () => { - expect( - Object.keys(mockFnObj).includes( - args[0]?.version.toString() ?? "0", - ), - ).toBe(true); - }); + // Does functionName executes with the correct DSL version number + if (args[0]?.version === version) { + expect(args[0]?.version).toEqual(version); + } + + // For a functionName is the correct DSL version registed in test cases + expect( + Object.keys(mockFnObj).includes( + args[0]?.version.toString() ?? "0", + ), + ).toBe(true); }); - }); - } - }, - ); + } + }, + ); + }); }); - test("Check the migration count matches the lates page version", () => { expect(migrations.length).toEqual(DSLMigrations.LATEST_DSL_VERSION); }); diff --git a/app/client/packages/dsl/src/migrate/tests/DSLMigrationsUtils.test.ts b/app/client/packages/dsl/src/migrate/tests/DSLMigrationsUtils.test.ts index 5bd449a9c59a..f79394c90c99 100644 --- a/app/client/packages/dsl/src/migrate/tests/DSLMigrationsUtils.test.ts +++ b/app/client/packages/dsl/src/migrate/tests/DSLMigrationsUtils.test.ts @@ -5,7 +5,7 @@ import type { DSLWidget } from "../types"; const ASSETS_CDN_URL = "https://assets.appsmith.com"; describe("correctly migrate dsl", () => { - it("migrateDSL for private widget", () => { + it("migrateDSL for private widget", async () => { const currentVersion = 49; // before adding privateWidgets to all List widgets const nextVersion = LATEST_DSL_VERSION; // It runs Two Migrations, Always Update as migration increases @@ -1229,12 +1229,12 @@ describe("correctly migrate dsl", () => { isLoading: false, }; - const actualNextDsl = migrateDSL(currentDSL); + const actualNextDsl = await migrateDSL(currentDSL); expect(actualNextDsl).toEqual(expectedNextDSL); }); - it("migrateDSL for theming v1", () => { + it("migrateDSL for theming v1", async () => { const currentVersion = 53; const nextVersion = LATEST_DSL_VERSION; const currentDSL: DSLWidget = { @@ -2452,7 +2452,7 @@ describe("correctly migrate dsl", () => { isLoading: false, }; - const actualNextDsl = migrateDSL(currentDSL); + const actualNextDsl = await migrateDSL(currentDSL); expect(actualNextDsl).toEqual(expectedNextDSL); }); @@ -3096,7 +3096,7 @@ describe("correctly migrate dsl", () => { expect(actualNextDSL).toEqual(expectedNextDSL); }); - it("correctly migrates currentIndex/currentRow properties for validations in table view", () => { + it("correctly migrates currentIndex/currentRow properties for validations in table view", async () => { const currentVersion = 78; const currentDSL: DSLWidget = { bottomRow: 740, @@ -3171,7 +3171,7 @@ describe("correctly migrate dsl", () => { }, ], }; - const nextDSL = migrateDSL(currentDSL); + const nextDSL = await migrateDSL(currentDSL); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const validations = (nextDSL.children || [])[0].primaryColumns.column1 diff --git a/app/client/packages/rts/src/controllers/Dsl/DslController.ts b/app/client/packages/rts/src/controllers/Dsl/DslController.ts index 6bfc0f5e7f4e..7f0f062d475d 100644 --- a/app/client/packages/rts/src/controllers/Dsl/DslController.ts +++ b/app/client/packages/rts/src/controllers/Dsl/DslController.ts @@ -8,9 +8,9 @@ export default class DSLController extends BaseController { super(); } - migrateDSL(req: Request, res: Response) { + async migrateDSL(req: Request, res: Response) { try { - const latestDSL = migrateDSLToLatest(req.body); + const latestDSL = await migrateDSLToLatest(req.body); super.sendResponse(res, latestDSL); } catch (err) { diff --git a/app/client/packages/rts/src/routes/dsl_routes.ts b/app/client/packages/rts/src/routes/dsl_routes.ts index ada447ac975f..d53c9ea6bcfb 100644 --- a/app/client/packages/rts/src/routes/dsl_routes.ts +++ b/app/client/packages/rts/src/routes/dsl_routes.ts @@ -1,6 +1,7 @@ import DSLController from "@controllers/Dsl/DslController"; import { Validator } from "@middlewares/Validator"; import express from "express"; +import type { Response, Request } from "express"; const router = express.Router(); const dslController = new DSLController(); @@ -12,6 +13,11 @@ router.get( dslController.getLatestDSLVersion, ); -router.post("/migrate", validator.validateRequest, dslController.migrateDSL); +router.post( + "/migrate", + validator.validateRequest, + async (req: Request, res: Response) => + await dslController.migrateDSL(req, res), +); export default router; diff --git a/app/client/packages/rts/src/services/DslService.ts b/app/client/packages/rts/src/services/DslService.ts index 0af89b3c6e21..fc06ffc25f8d 100644 --- a/app/client/packages/rts/src/services/DslService.ts +++ b/app/client/packages/rts/src/services/DslService.ts @@ -1,7 +1,7 @@ import { LATEST_DSL_VERSION, migrateDSL } from "@shared/dsl"; -export function migrateDSLToLatest(currentDsl) { - const latestDSL = migrateDSL(currentDsl); +export async function migrateDSLToLatest(currentDsl) { + const latestDSL = await migrateDSL(currentDsl); return latestDSL; } diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/PluginActionForm.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/PluginActionForm.tsx index d7ac7a21385a..4a85da9d0ee4 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/PluginActionForm.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/PluginActionForm.tsx @@ -19,10 +19,10 @@ const PluginActionForm = () => { {plugin.uiComponent === UIComponentTypes.GraphQLEditorForm && ( )} - {plugin.uiComponent === UIComponentTypes.DbEditorForm || - (plugin.uiComponent === UIComponentTypes.UQIDbEditorForm && ( - - ))} + {(plugin.uiComponent === UIComponentTypes.DbEditorForm || + plugin.uiComponent === UIComponentTypes.UQIDbEditorForm) && ( + + )} ); }; diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Zone/styles.module.css b/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Zone/styles.module.css index 8268c1cd02a3..b63260f2d7d1 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Zone/styles.module.css +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Zone/styles.module.css @@ -12,6 +12,10 @@ grid-template-columns: 1fr; } + & > div:empty { + display: none; + } + /* This section can be removed once the condition abouve each is resolved */ diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/ApiEditor/APIEditorForm.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/ApiEditor/APIEditorForm.tsx index 1546fcef01a0..f76893dc6f9c 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/ApiEditor/APIEditorForm.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/ApiEditor/APIEditorForm.tsx @@ -15,8 +15,6 @@ import { useAnalyticsOnRunClick, } from "PluginActionEditor/hooks"; -const FORM_NAME = API_EDITOR_FORM_NAME; - const APIEditorForm = () => { const { action } = usePluginActionContext(); const { handleRunClick } = useHandleRunClick(); @@ -43,7 +41,7 @@ const APIEditorForm = () => { theme={EditorTheme.LIGHT} /> } - formName={FORM_NAME} + formName={API_EDITOR_FORM_NAME} httpMethodOptions={HTTP_METHOD_OPTIONS} isChangePermitted={isChangePermitted} paginationUiComponent={ @@ -58,6 +56,7 @@ const APIEditorForm = () => { ); }; -export default reduxForm({ form: FORM_NAME, enableReinitialize: true })( - APIEditorForm, -); +export default reduxForm({ + form: API_EDITOR_FORM_NAME, + enableReinitialize: true, +})(APIEditorForm); diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/GraphQLEditorForm.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/GraphQLEditorForm.tsx index 677bf16e032e..35323cbece55 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/GraphQLEditorForm.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/GraphQLEditorForm.tsx @@ -12,8 +12,6 @@ import { getHasManageActionPermission } from "ee/utils/BusinessFeatures/permissi import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; import useGetFormActionValues from "../CommonEditorForm/hooks/useGetFormActionValues"; -const FORM_NAME = API_EDITOR_FORM_NAME; - function GraphQLEditorForm() { const { action } = usePluginActionContext(); const theme = EditorTheme.LIGHT; @@ -30,13 +28,13 @@ function GraphQLEditorForm() { } - formName={FORM_NAME} + formName={API_EDITOR_FORM_NAME} httpMethodOptions={GRAPHQL_HTTP_METHOD_OPTIONS} isChangePermitted={isChangePermitted} paginationUiComponent={ { } = props; const dispatch = useDispatch(); + const isActionRedesignEnabled = useFeatureFlag( + FEATURE_FLAG.release_actions_redesign_enabled, + ); + const actionResponse = useSelector((state) => getActionData(state, currentActionConfig.id), ); @@ -211,8 +218,12 @@ const QueryResponseTab = (props: Props) => { } const navigateToSettings = useCallback(() => { - dispatch(setPluginActionEditorSelectedTab(EDITOR_TABS.SETTINGS)); - }, [dispatch]); + if (isActionRedesignEnabled) { + dispatch(openPluginActionSettings(true)); + } else { + dispatch(setPluginActionEditorSelectedTab(EDITOR_TABS.SETTINGS)); + } + }, [dispatch, isActionRedesignEnabled]); const preparedStatementCalloutLinks: CalloutLinkProps[] = [ { diff --git a/app/client/src/PluginActionEditor/components/PluginActionSettings/ApiSettings.tsx b/app/client/src/PluginActionEditor/components/PluginActionSettings/ApiSettings.tsx new file mode 100644 index 000000000000..37ef02e21153 --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionSettings/ApiSettings.tsx @@ -0,0 +1,10 @@ +import { API_EDITOR_FORM_NAME } from "ee/constants/forms"; +import { reduxForm } from "redux-form"; +import PluginActionSettingsPopover, { + type SettingsProps, +} from "./SettingsPopover"; + +export default reduxForm({ + form: API_EDITOR_FORM_NAME, + enableReinitialize: true, +})(PluginActionSettingsPopover); diff --git a/app/client/src/PluginActionEditor/components/PluginActionSettings/QuerySettings.tsx b/app/client/src/PluginActionEditor/components/PluginActionSettings/QuerySettings.tsx new file mode 100644 index 000000000000..9f8984afb7df --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionSettings/QuerySettings.tsx @@ -0,0 +1,10 @@ +import { QUERY_EDITOR_FORM_NAME } from "ee/constants/forms"; +import { reduxForm } from "redux-form"; +import PluginActionSettingsPopover, { + type SettingsProps, +} from "./SettingsPopover"; + +export default reduxForm({ + form: QUERY_EDITOR_FORM_NAME, + enableReinitialize: true, +})(PluginActionSettingsPopover); diff --git a/app/client/src/PluginActionEditor/components/PluginActionSettings/SettingsPopover.tsx b/app/client/src/PluginActionEditor/components/PluginActionSettings/SettingsPopover.tsx new file mode 100644 index 000000000000..ef4045f01913 --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionSettings/SettingsPopover.tsx @@ -0,0 +1,142 @@ +import React, { useCallback, useEffect, useState } from "react"; +import { + Link, + Popover, + PopoverBody, + PopoverContent, + PopoverHeader, + PopoverTrigger, + ToggleButton, +} from "@appsmith/ads"; +import ActionSettings from "pages/Editor/ActionSettings"; +import { usePluginActionContext } from "../../PluginActionContext"; +import styled, { css } from "styled-components"; +import { + API_EDITOR_TAB_TITLES, + createMessage, + LEARN_MORE, +} from "ee/constants/messages"; +import { useDispatch, useSelector } from "react-redux"; +import { + isPluginActionSettingsOpen, + openPluginActionSettings, +} from "../../store"; +import { THEME } from "../../constants/PluginActionConstants"; +import { type DocsLink, openDoc } from "constants/DocumentationLinks"; + +export interface SettingsProps { + formName: string; + docsLink?: DocsLink; +} + +const Variables = css` + --popover-width: 280px; +`; + +/* TODO: Remove this after removing custom width from server side (Ankita) */ +const SettingsWrapper = styled.div` + display: flex; + flex-direction: column; + gap: var(--ads-v2-spaces-4); + + .t--form-control-INPUT_TEXT, + .t--form-control-DROP_DOWN { + > div { + min-width: unset; + width: 100%; + } + } +`; + +const StyledPopoverHeader = styled(PopoverHeader)` + margin-bottom: var(--ads-v2-spaces-5); +`; + +const StyledPopoverContent = styled(PopoverContent)` + ${Variables}; +`; + +const LearnMoreLink = styled(Link)` + span { + font-weight: bold; + } +`; + +const PluginActionSettingsPopover = (props: SettingsProps) => { + const { settingsConfig } = usePluginActionContext(); + const openSettings = useSelector(isPluginActionSettingsOpen); + const [isOpen, setIsOpen] = useState(false); + const dispatch = useDispatch(); + + useEffect(() => { + if (openSettings) { + handleOpenChange(true); + } + }, [openSettings]); + + const handleOpenChange = useCallback( + (open: boolean) => { + setIsOpen(open); + + if (openSettings && !open) { + dispatch(openPluginActionSettings(false)); + } + }, + [openSettings], + ); + + const handleEscapeKeyDown = () => { + handleOpenChange(false); + }; + + const handleButtonClick = () => { + handleOpenChange(true); + }; + + const handleLearnMoreClick = () => { + openDoc(props.docsLink as DocsLink); + }; + + return ( + + + + + + + {createMessage(API_EDITOR_TAB_TITLES.SETTINGS)} + + + + + {props.docsLink && ( + + {createMessage(LEARN_MORE)} + + )} + + + + + ); +}; + +export default PluginActionSettingsPopover; diff --git a/app/client/src/PluginActionEditor/components/PluginActionSettings/index.tsx b/app/client/src/PluginActionEditor/components/PluginActionSettings/index.tsx new file mode 100644 index 000000000000..7c8a886b7894 --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionSettings/index.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { UIComponentTypes } from "api/PluginApi"; +import { usePluginActionContext } from "../../PluginActionContext"; +import ApiSettings from "./ApiSettings"; +import QuerySettings from "./QuerySettings"; +import { + API_EDITOR_FORM_NAME, + QUERY_EDITOR_FORM_NAME, +} from "ee/constants/forms"; +import { DocsLink } from "constants/DocumentationLinks"; + +const API_FORM_COMPONENTS = [ + UIComponentTypes.ApiEditorForm, + UIComponentTypes.GraphQLEditorForm, +]; + +const PluginActionSettings = () => { + const { plugin } = usePluginActionContext(); + + return API_FORM_COMPONENTS.includes(plugin.uiComponent) ? ( + + ) : ( + + ); +}; + +export default PluginActionSettings; diff --git a/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx b/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx index c60a751490cc..43f3679e78c8 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionToolbar.tsx @@ -11,6 +11,7 @@ import { import { useToggle } from "@mantine/hooks"; import { useSelector } from "react-redux"; import { isActionRunning } from "../store"; +import PluginActionSettings from "./PluginActionSettings"; interface PluginActionToolbarProps { runOptions?: React.ReactNode; @@ -51,12 +52,7 @@ const PluginActionToolbar = (props: PluginActionToolbarProps) => { Run -