From 3b0c4d1008a13a33bb1c5ec68b3f6cf5531169cb Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 31 Jan 2025 09:08:19 +0100 Subject: [PATCH 1/8] Use existing definitions instead of using the manifest --- packages/builder/src/helpers/screen.ts | 46 ------------- .../src/stores/builder/screenComponent.ts | 69 ++++++++++++++++--- 2 files changed, 60 insertions(+), 55 deletions(-) delete mode 100644 packages/builder/src/helpers/screen.ts diff --git a/packages/builder/src/helpers/screen.ts b/packages/builder/src/helpers/screen.ts deleted file mode 100644 index 296a597adb..0000000000 --- a/packages/builder/src/helpers/screen.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Component, Screen, ScreenProps } from "@budibase/types" -import clientManifest from "@budibase/client/manifest.json" - -export function findComponentsBySettingsType( - screen: Screen, - type: string | string[] -) { - const typesArray = Array.isArray(type) ? type : [type] - - const result: { - component: Component - setting: { - type: string - key: string - } - }[] = [] - function recurseFieldComponentsInChildren(component: ScreenProps) { - if (!component) { - return - } - - const definition = getManifestDefinition(component) - const setting = - "settings" in definition && - definition.settings.find((s: any) => typesArray.includes(s.type)) - if (setting && "type" in setting) { - result.push({ - component, - setting: { type: setting.type!, key: setting.key! }, - }) - } - component._children?.forEach(child => { - recurseFieldComponentsInChildren(child) - }) - } - - recurseFieldComponentsInChildren(screen?.props) - return result -} - -function getManifestDefinition(component: Component) { - const componentType = component._component.split("/").slice(-1)[0] - const definition = - clientManifest[componentType as keyof typeof clientManifest] - return definition -} diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index 226d233090..640d4e6db3 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -1,13 +1,18 @@ -import { derived } from "svelte/store" +import { derived, get } from "svelte/store" import { tables } from "./tables" import { selectedScreen } from "./screens" import { viewsV2 } from "./viewsV2" -import { findComponentsBySettingsType } from "@/helpers/screen" -import { UIDatasourceType, Screen } from "@budibase/types" +import { + UIDatasourceType, + Screen, + Component, + ScreenProps, +} from "@budibase/types" import { queries } from "./queries" import { views } from "./views" import { bindings, featureFlag } from "@/helpers" import { getBindableProperties } from "@/dataBinding" +import { ComponentDefinition, componentStore } from "./components" function reduceBy( key: TKey, @@ -38,11 +43,15 @@ const validationKeyByType: Record = { } export const screenComponentErrors = derived( - [selectedScreen, tables, views, viewsV2, queries], - ([$selectedScreen, $tables, $views, $viewsV2, $queries]): Record< - string, - string[] - > => { + [selectedScreen, tables, views, viewsV2, queries, componentStore], + ([ + $selectedScreen, + $tables, + $views, + $viewsV2, + $queries, + $componentStore, + ]): Record => { if (!featureFlag.isEnabled("CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS")) { return {} } @@ -51,9 +60,11 @@ export const screenComponentErrors = derived( datasources: Record ) { const result: Record = {} + for (const { component, setting } of findComponentsBySettingsType( screen, - ["table", "dataSource"] + ["table", "dataSource"], + $componentStore.components )) { const componentSettings = component[setting.key] if (!componentSettings) { @@ -111,3 +122,43 @@ export const screenComponentErrors = derived( return getInvalidDatasources($selectedScreen, datasources) } ) + +function findComponentsBySettingsType( + screen: Screen, + type: string | string[], + definitions: Record +) { + const typesArray = Array.isArray(type) ? type : [type] + + const result: { + component: Component + setting: { + type: string + key: string + } + }[] = [] + + function recurseFieldComponentsInChildren(component: ScreenProps) { + if (!component) { + return + } + + const definition = definitions[component._component] + + const setting = definition?.settings?.find((s: any) => + typesArray.includes(s.type) + ) + if (setting && "type" in setting) { + result.push({ + component, + setting: { type: setting.type!, key: setting.key! }, + }) + } + component._children?.forEach(child => { + recurseFieldComponentsInChildren(child) + }) + } + + recurseFieldComponentsInChildren(screen?.props) + return result +} From fdb16f463e7028eb71d180f41b75b7fdd1232821 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 31 Jan 2025 09:08:54 +0100 Subject: [PATCH 2/8] Don't allow importing the manifest --- eslint-local-rules/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/eslint-local-rules/index.js b/eslint-local-rules/index.js index 9348706399..d9d894c33e 100644 --- a/eslint-local-rules/index.js +++ b/eslint-local-rules/index.js @@ -41,12 +41,11 @@ module.exports = { if ( /^@budibase\/[^/]+\/.*$/.test(importPath) && importPath !== "@budibase/backend-core/tests" && - importPath !== "@budibase/string-templates/test/utils" && - importPath !== "@budibase/client/manifest.json" + importPath !== "@budibase/string-templates/test/utils" ) { context.report({ node, - message: `Importing from @budibase is not allowed, except for @budibase/backend-core/tests, @budibase/string-templates/test/utils and @budibase/client/manifest.json.`, + message: `Importing from @budibase is not allowed, except for @budibase/backend-core/tests and @budibase/string-templates/test/utils.`, }) } }, From 4ea8b60b3b5961429b6a6d0874624261a4ce3fc1 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 30 Jan 2025 12:48:59 +0100 Subject: [PATCH 3/8] Renames --- packages/builder/src/stores/builder/components.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/stores/builder/components.ts b/packages/builder/src/stores/builder/components.ts index 90e1abfecf..043c2da275 100644 --- a/packages/builder/src/stores/builder/components.ts +++ b/packages/builder/src/stores/builder/components.ts @@ -452,7 +452,7 @@ export class ComponentStore extends BudiStore { * @returns */ createInstance( - componentName: string, + componentType: string, presetProps: any, parent: any ): Component | null { @@ -461,11 +461,13 @@ export class ComponentStore extends BudiStore { throw "A valid screen must be selected" } - const definition = this.getDefinition(componentName) + const definition = this.getDefinition(componentType) if (!definition) { return null } + let componentName = `New ${definition.friendlyName || definition.name}` + // Generate basic component structure let instance: Component = { _id: Helpers.uuid(), @@ -475,7 +477,7 @@ export class ComponentStore extends BudiStore { hover: {}, active: {}, }, - _instanceName: `New ${definition.friendlyName || definition.name}`, + _instanceName: componentName, ...presetProps, } @@ -500,7 +502,7 @@ export class ComponentStore extends BudiStore { } // Add step name to form steps - if (componentName.endsWith("/formstep")) { + if (componentType.endsWith("/formstep")) { const parentForm = findClosestMatchingComponent( screen.props, get(selectedComponent)?._id, @@ -529,14 +531,14 @@ export class ComponentStore extends BudiStore { * @returns */ async create( - componentName: string, + componentType: string, presetProps: any, parent: Component, index: number ) { const state = get(this.store) const componentInstance = this.createInstance( - componentName, + componentType, presetProps, parent ) From b7ce306e6a0949fb4f34c7e15bccb2b7a1f3c00f Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 30 Jan 2025 16:30:55 +0100 Subject: [PATCH 4/8] Add counter --- .../builder/src/stores/builder/components.ts | 9 +++++++++ packages/builder/src/stores/builder/index.js | 3 ++- .../src/stores/builder/screenComponent.ts | 16 +++++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/stores/builder/components.ts b/packages/builder/src/stores/builder/components.ts index 043c2da275..7aedf05438 100644 --- a/packages/builder/src/stores/builder/components.ts +++ b/packages/builder/src/stores/builder/components.ts @@ -20,6 +20,7 @@ import { previewStore, tables, componentTreeNodesStore, + screenComponents, } from "@/stores/builder" import { buildFormSchema, getSchemaForDatasource } from "@/dataBinding" import { @@ -467,6 +468,14 @@ export class ComponentStore extends BudiStore { } let componentName = `New ${definition.friendlyName || definition.name}` + const $screenComponents = get(screenComponents) + + const sameNameCount = $screenComponents.filter(c => + new RegExp(`^${componentName}( \\d*)?$`).test(c._instanceName) + ).length + if (sameNameCount) { + componentName = `${componentName} ${sameNameCount + 1}` + } // Generate basic component structure let instance: Component = { diff --git a/packages/builder/src/stores/builder/index.js b/packages/builder/src/stores/builder/index.js index 54c1aa39a2..7dd7f67828 100644 --- a/packages/builder/src/stores/builder/index.js +++ b/packages/builder/src/stores/builder/index.js @@ -16,7 +16,7 @@ import { userStore, userSelectedResourceMap, isOnlyUser } from "./users.js" import { deploymentStore } from "./deployments.js" import { contextMenuStore } from "./contextMenu.js" import { snippets } from "./snippets" -import { screenComponentErrors } from "./screenComponent" +import { screenComponents, screenComponentErrors } from "./screenComponent" // Backend import { tables } from "./tables" @@ -68,6 +68,7 @@ export { snippets, rowActions, appPublished, + screenComponents, screenComponentErrors, } diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index 226d233090..56e9d311a4 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -3,11 +3,12 @@ import { tables } from "./tables" import { selectedScreen } from "./screens" import { viewsV2 } from "./viewsV2" import { findComponentsBySettingsType } from "@/helpers/screen" -import { UIDatasourceType, Screen } from "@budibase/types" +import { UIDatasourceType, Screen, Component } from "@budibase/types" import { queries } from "./queries" import { views } from "./views" import { bindings, featureFlag } from "@/helpers" import { getBindableProperties } from "@/dataBinding" +import { findAllComponents } from "@/helpers/components" function reduceBy( key: TKey, @@ -111,3 +112,16 @@ export const screenComponentErrors = derived( return getInvalidDatasources($selectedScreen, datasources) } ) + +export const screenComponents = derived( + [selectedScreen], + ([$selectedScreen]) => { + if (!$selectedScreen) { + return [] + } + const allComponents = findAllComponents( + $selectedScreen.props + ) as Component[] + return allComponents + } +) From 6f2d279f0e48c0c3557dca806ff715036b22bb33 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 30 Jan 2025 16:47:04 +0100 Subject: [PATCH 5/8] Use existing utils --- packages/builder/src/helpers/duplicate.ts | 6 ++++-- .../builder/src/stores/builder/components.ts | 18 +++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/builder/src/helpers/duplicate.ts b/packages/builder/src/helpers/duplicate.ts index b4740b3e52..eb705c6525 100644 --- a/packages/builder/src/helpers/duplicate.ts +++ b/packages/builder/src/helpers/duplicate.ts @@ -76,13 +76,15 @@ export const getSequentialName = ( { getName, numberFirstItem, + separator = "", }: { getName?: (item: T) => string numberFirstItem?: boolean + separator?: string } = {} ) => { if (!prefix?.length) { - return null + return "" } const trimmedPrefix = prefix.trim() const firstName = numberFirstItem ? `${prefix}1` : trimmedPrefix @@ -107,5 +109,5 @@ export const getSequentialName = ( max = num } }) - return max === 0 ? firstName : `${prefix}${max + 1}` + return max === 0 ? firstName : `${prefix}${separator}${max + 1}` } diff --git a/packages/builder/src/stores/builder/components.ts b/packages/builder/src/stores/builder/components.ts index 7aedf05438..6693cbf4dc 100644 --- a/packages/builder/src/stores/builder/components.ts +++ b/packages/builder/src/stores/builder/components.ts @@ -38,6 +38,7 @@ import { Table, } from "@budibase/types" import { utils } from "@budibase/shared-core" +import { getSequentialName } from "@/helpers/duplicate" interface Component extends ComponentType { _id: string @@ -467,15 +468,14 @@ export class ComponentStore extends BudiStore { return null } - let componentName = `New ${definition.friendlyName || definition.name}` - const $screenComponents = get(screenComponents) - - const sameNameCount = $screenComponents.filter(c => - new RegExp(`^${componentName}( \\d*)?$`).test(c._instanceName) - ).length - if (sameNameCount) { - componentName = `${componentName} ${sameNameCount + 1}` - } + const componentName = getSequentialName( + get(screenComponents), + `New ${definition.friendlyName || definition.name}`, + { + getName: c => c._instanceName, + separator: " ", + } + ) // Generate basic component structure let instance: Component = { From 2f4e798cf592d12681371d005b49c3c0279c3b20 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 30 Jan 2025 16:57:48 +0100 Subject: [PATCH 6/8] Fix test --- packages/builder/src/helpers/tests/duplicate.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/helpers/tests/duplicate.test.ts b/packages/builder/src/helpers/tests/duplicate.test.ts index 131e76a6c2..5e956d7b1c 100644 --- a/packages/builder/src/helpers/tests/duplicate.test.ts +++ b/packages/builder/src/helpers/tests/duplicate.test.ts @@ -49,7 +49,7 @@ describe("getSequentialName", () => { it("handles nullish prefix", async () => { const name = getSequentialName([], null) - expect(name).toBe(null) + expect(name).toBe("") }) it("handles just the prefix", async () => { From a340ec0d29f0e466c769d52d76001f805fa3579c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 31 Jan 2025 09:10:07 +0100 Subject: [PATCH 7/8] Update feature flag --- packages/builder/src/stores/builder/screenComponent.ts | 2 +- packages/types/src/sdk/featureFlag.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index 684c08605a..f1a9440c02 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -53,7 +53,7 @@ export const screenComponentErrors = derived( $queries, $componentStore, ]): Record => { - if (!featureFlag.isEnabled("CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS")) { + if (!featureFlag.isEnabled("CHECK_COMPONENT_SETTINGS_ERRORS")) { return {} } function getInvalidDatasources( diff --git a/packages/types/src/sdk/featureFlag.ts b/packages/types/src/sdk/featureFlag.ts index d9f092c80a..97893a1b5e 100644 --- a/packages/types/src/sdk/featureFlag.ts +++ b/packages/types/src/sdk/featureFlag.ts @@ -1,6 +1,6 @@ export enum FeatureFlag { USE_ZOD_VALIDATOR = "USE_ZOD_VALIDATOR", - CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS = "CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS", + CHECK_COMPONENT_SETTINGS_ERRORS = "CHECK_COMPONENT_SETTINGS_ERRORS", // Account-portal DIRECT_LOGIN_TO_ACCOUNT_PORTAL = "DIRECT_LOGIN_TO_ACCOUNT_PORTAL", @@ -8,7 +8,7 @@ export enum FeatureFlag { export const FeatureFlagDefaults = { [FeatureFlag.USE_ZOD_VALIDATOR]: false, - [FeatureFlag.CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS]: false, + [FeatureFlag.CHECK_COMPONENT_SETTINGS_ERRORS]: false, // Account-portal [FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false, From 4a68d1d09be3087f63058ef47d1010608db1916b Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 31 Jan 2025 11:24:51 +0000 Subject: [PATCH 8/8] Bump version to 3.3.6 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index c16b958d24..15f405c847 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "3.3.5", + "version": "3.3.6", "npmClient": "yarn", "concurrency": 20, "command": {