From 2537ee6980524b142eee1c5fb2b66ea5bcf3cd65 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 27 Jan 2025 13:13:11 +0100 Subject: [PATCH] Validate required settings messages from builder --- packages/builder/src/helpers/screen.ts | 2 +- .../src/stores/builder/screenComponent.ts | 142 ++++++++++++++---- .../client/src/components/Component.svelte | 6 +- .../error-states/ComponentErrorState.svelte | 2 +- 4 files changed, 114 insertions(+), 38 deletions(-) diff --git a/packages/builder/src/helpers/screen.ts b/packages/builder/src/helpers/screen.ts index 296a597adb..e9a478d059 100644 --- a/packages/builder/src/helpers/screen.ts +++ b/packages/builder/src/helpers/screen.ts @@ -38,7 +38,7 @@ export function findComponentsBySettingsType( return result } -function getManifestDefinition(component: Component) { +export function getManifestDefinition(component: Component) { const componentType = component._component.split("/").slice(-1)[0] const definition = clientManifest[componentType as keyof typeof clientManifest] diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index d8169fdedb..35e76464cf 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -2,11 +2,15 @@ import { derived } 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 { + findComponentsBySettingsType, + getManifestDefinition, +} from "@/helpers/screen" +import { UIDatasourceType, Screen, Component } from "@budibase/types" import { queries } from "./queries" import { views } from "./views" import { featureFlag } from "@/helpers" +import { findAllComponents } from "@/helpers/components" function reduceBy( key: TKey, @@ -42,34 +46,6 @@ export const screenComponentErrors = derived( if (!featureFlag.isEnabled("CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS")) { return {} } - function getInvalidDatasources( - screen: Screen, - datasources: Record - ) { - const result: Record = {} - for (const { component, setting } of findComponentsBySettingsType( - screen, - ["table", "dataSource"] - )) { - const componentSettings = component[setting.key] - const { label } = componentSettings - const type = componentSettings.type as UIDatasourceType - - const validationKey = validationKeyByType[type] - if (!validationKey) { - continue - } - const resourceId = componentSettings[validationKey] - if (!datasources[resourceId]) { - const friendlyTypeName = friendlyNameByType[type] ?? type - result[component._id!] = [ - `The ${friendlyTypeName} named "${label}" could not be found`, - ] - } - } - - return result - } const datasources = { ...reduceBy("_id", $tables.list), @@ -78,6 +54,110 @@ export const screenComponentErrors = derived( ...reduceBy("_id", $queries.list), } - return getInvalidDatasources($selectedScreen, datasources) + const errors = { + ...getInvalidDatasources($selectedScreen, datasources), + ...getMissingRequiredSettings($selectedScreen), + } + return errors } ) + +function getInvalidDatasources( + screen: Screen, + datasources: Record +) { + const result: Record = {} + for (const { component, setting } of findComponentsBySettingsType(screen, [ + "table", + "dataSource", + ])) { + const componentSettings = component[setting.key] + if (!componentSettings) { + continue + } + + const { label } = componentSettings + const type = componentSettings.type as UIDatasourceType + + const validationKey = validationKeyByType[type] + if (!validationKey) { + continue + } + const resourceId = componentSettings[validationKey] + if (!datasources[resourceId]) { + const friendlyTypeName = friendlyNameByType[type] ?? type + result[component._id!] = [ + `The ${friendlyTypeName} named "${label}" could not be found`, + ] + } + } + + return result +} + +function getAllComponentsInScreen(screen: Screen) { + const result: Component[] = [] + function recursiveCheck(component: Component) { + result.push(...findAllComponents(component)) + component._children?.forEach(recursiveCheck) + } + recursiveCheck(screen.props) + return result +} + +function getMissingRequiredSettings(screen: Screen) { + const allComponents = getAllComponentsInScreen(screen) + + const result: Record = {} + for (const component of allComponents) { + const definition = getManifestDefinition(component) + if (!("settings" in definition)) { + continue + } + + const missingRequiredSettings = definition.settings.filter( + (setting: any) => { + let empty = + component[setting.key] == null || component[setting.key] === "" + let missing = setting.required && empty + + // Check if this setting depends on another, as it may not be required + if (setting.dependsOn) { + const dependsOnKey = setting.dependsOn.setting || setting.dependsOn + const dependsOnValue = setting.dependsOn.value + const realDependentValue = component[dependsOnKey] + + const sectionDependsOnKey = + setting.sectionDependsOn?.setting || setting.sectionDependsOn + const sectionDependsOnValue = setting.sectionDependsOn?.value + const sectionRealDependentValue = component[sectionDependsOnKey] + + if (dependsOnValue == null && realDependentValue == null) { + return false + } + if (dependsOnValue != null && dependsOnValue !== realDependentValue) { + return false + } + + if ( + sectionDependsOnValue != null && + sectionDependsOnValue !== sectionRealDependentValue + ) { + return false + } + } + + return missing + } + ) + + if (missingRequiredSettings.length) { + result[component._id!] = missingRequiredSettings.map( + (s: any) => + `Add the ${s.label} setting to start using your component` + ) + } + } + + return result +} diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 236bcb7c7e..345603fe3a 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -695,11 +695,7 @@ use:gridLayout={gridMetadata} > {#if errorState} - + {:else} {#if children.length} diff --git a/packages/client/src/components/error-states/ComponentErrorState.svelte b/packages/client/src/components/error-states/ComponentErrorState.svelte index 7069b7a431..7292ffe55b 100644 --- a/packages/client/src/components/error-states/ComponentErrorState.svelte +++ b/packages/client/src/components/error-states/ComponentErrorState.svelte @@ -26,7 +26,7 @@ {#if requiredAncestor} {:else if errorMessage} - {errorMessage} + {@html errorMessage} {:else if requiredSetting} {/if}