From ab41ba129e41bc50c86c2794f91f661fda699f44 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 4 Feb 2025 11:10:20 +0100 Subject: [PATCH] Update screnComponent store to respect order --- .../src/stores/builder/screenComponent.ts | 288 ++++++++---------- 1 file changed, 127 insertions(+), 161 deletions(-) diff --git a/packages/builder/src/stores/builder/screenComponent.ts b/packages/builder/src/stores/builder/screenComponent.ts index 0ea89cc445..0c1e10f436 100644 --- a/packages/builder/src/stores/builder/screenComponent.ts +++ b/packages/builder/src/stores/builder/screenComponent.ts @@ -4,10 +4,8 @@ import { selectedScreen } from "./screens" import { viewsV2 } from "./viewsV2" import { UIDatasourceType, - Screen, Component, UIComponentError, - ScreenProps, ComponentDefinition, DependsOnComponentSetting, } from "@budibase/types" @@ -44,7 +42,18 @@ const validationKeyByType: Record = { jsonarray: "value", } -export const screenComponentErrors = derived( +export const screenComponentsList = derived( + [selectedScreen], + ([$selectedScreen]): Component[] => { + if (!$selectedScreen) { + return [] + } + + return findAllComponents($selectedScreen.props) + } +) + +export const screenComponentErrorList = derived( [selectedScreen, tables, views, viewsV2, queries, componentStore], ([ $selectedScreen, @@ -53,9 +62,9 @@ export const screenComponentErrors = derived( $viewsV2, $queries, $componentStore, - ]): Record => { + ]): UIComponentError[] => { if (!$selectedScreen) { - return {} + return [] } const datasources = { @@ -67,59 +76,73 @@ export const screenComponentErrors = derived( const { components: definitions } = $componentStore - const errors = { - ...getInvalidDatasources($selectedScreen, datasources, definitions), - ...getMissingAncestors($selectedScreen, definitions), - ...getMissingRequiredSettings($selectedScreen, definitions), + const errors: UIComponentError[] = [] + + function checkComponentErrors(component: Component, ancestors: string[]) { + errors.push(...getInvalidDatasources(component, datasources, definitions)) + errors.push(...getMissingRequiredSettings(component, definitions)) + errors.push(...getMissingAncestors(component, definitions, ancestors)) + + for (const child of component._children || []) { + checkComponentErrors(child, [...ancestors, component._component]) + } } + + checkComponentErrors($selectedScreen?.props, []) + return errors } ) function getInvalidDatasources( - screen: Screen, + component: Component, datasources: Record, definitions: Record ) { - const result: Record = {} - for (const { component, setting } of findComponentsBySettingsType( - screen, - ["table", "dataSource"], - definitions - )) { - const componentSettings = component[setting.key] - if (!componentSettings) { - continue - } + const result: UIComponentError[] = [] - const { label } = componentSettings - const type = componentSettings.type as UIDatasourceType + const datasourceTypes = ["table", "dataSource"] - const validationKey = validationKeyByType[type] - if (!validationKey) { - continue - } + const possibleSettings = definitions[component._component]?.settings?.filter( + s => datasourceTypes.includes(s.type) + ) + if (possibleSettings) { + for (const setting of possibleSettings) { + const componentSettings = component[setting.key] + if (!componentSettings) { + continue + } - const componentBindings = getBindableProperties(screen, component._id) + const { label } = componentSettings + const type = componentSettings.type as UIDatasourceType - const componentDatasources = { - ...reduceBy("rowId", bindings.extractRelationships(componentBindings)), - ...reduceBy("value", bindings.extractFields(componentBindings)), - ...reduceBy("value", bindings.extractJSONArrayFields(componentBindings)), - } + const validationKey = validationKeyByType[type] + if (!validationKey) { + continue + } - const resourceId = componentSettings[validationKey] - if (!{ ...datasources, ...componentDatasources }[resourceId]) { - const friendlyTypeName = friendlyNameByType[type] ?? type - result[component._id!] = [ - { + const componentBindings = getBindableProperties(screen, component._id) + + const componentDatasources = { + ...reduceBy("rowId", bindings.extractRelationships(componentBindings)), + ...reduceBy("value", bindings.extractFields(componentBindings)), + ...reduceBy( + "value", + bindings.extractJSONArrayFields(componentBindings) + ), + } + + const resourceId = componentSettings[validationKey] + if (!{ ...datasources, ...componentDatasources }[resourceId]) { + const friendlyTypeName = friendlyNameByType[type] ?? type + result.push({ componentId: component._id!, key: setting.key, label: setting.label || setting.key, message: `The ${friendlyTypeName} named "${label}" could not be found`, errorType: "setting", - }, - ] + }) + } } } @@ -142,62 +165,60 @@ function parseDependsOn(dependsOn: DependsOnComponentSetting | undefined): { } function getMissingRequiredSettings( - screen: Screen, + component: Component, definitions: Record ) { - const allComponents = findAllComponents(screen.props) as Component[] + const result: UIComponentError[] = [] - const result: Record = {} - for (const component of allComponents) { - const definition = definitions[component._component] + const definition = definitions[component._component] - const settings = getSettingsDefinition(definition) + const settings = getSettingsDefinition(definition) - const missingRequiredSettings = settings.filter(setting => { - let empty = - component[setting.key] == null || component[setting.key] === "" - let missing = setting.required && empty + const missingRequiredSettings = settings.filter(setting => { + 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 { key: dependsOnKey, value: dependsOnValue } = parseDependsOn( - setting.dependsOn - ) - const realDependentValue = - component[dependsOnKey as keyof typeof component] + // Check if this setting depends on another, as it may not be required + if (setting.dependsOn) { + const { key: dependsOnKey, value: dependsOnValue } = parseDependsOn( + setting.dependsOn + ) + const realDependentValue = + component[dependsOnKey as keyof typeof component] - const { key: sectionDependsOnKey, value: sectionDependsOnValue } = - parseDependsOn(setting.sectionDependsOn) - const sectionRealDependentValue = - component[sectionDependsOnKey as keyof typeof component] + const { key: sectionDependsOnKey, value: sectionDependsOnValue } = + parseDependsOn(setting.sectionDependsOn) + const sectionRealDependentValue = + component[sectionDependsOnKey as keyof typeof component] - if (dependsOnValue == null && realDependentValue == null) { - return false - } - if (dependsOnValue != null && dependsOnValue !== realDependentValue) { - return false - } - - if ( - sectionDependsOnValue != null && - sectionDependsOnValue !== sectionRealDependentValue - ) { - return false - } + if (dependsOnValue == null && realDependentValue == null) { + return false + } + if (dependsOnValue != null && dependsOnValue !== realDependentValue) { + return false } - return missing - }) + if ( + sectionDependsOnValue != null && + sectionDependsOnValue !== sectionRealDependentValue + ) { + return false + } + } - if (missingRequiredSettings?.length) { - result[component._id!] = missingRequiredSettings.map(s => ({ + return missing + }) + + if (missingRequiredSettings?.length) { + result.push( + ...missingRequiredSettings.map(s => ({ componentId: component._id!, key: s.key, label: s.label || s.key, message: `Add the ${s.label} setting to start using your component`, errorType: "setting", })) - } + ) } return result @@ -205,32 +226,28 @@ function getMissingRequiredSettings( const BudibasePrefix = "@budibase/standard-components/" function getMissingAncestors( - screen: Screen, - definitions: Record -) { - const result: Record = {} + component: Component, + definitions: Record, + ancestors: string[] +): UIComponentError[] { + const definition = definitions[component._component] - function checkMissingAncestors(component: Component, ancestors: string[]) { - for (const child of component._children || []) { - checkMissingAncestors(child, [...ancestors, component._component]) + if (!definition?.requiredAncestors?.length) { + return [] + } + + const result: UIComponentError[] = [] + const missingAncestors = definition.requiredAncestors.filter( + ancestor => !ancestors.includes(`${BudibasePrefix}${ancestor}`) + ) + + if (missingAncestors.length) { + const pluralise = (name: string) => { + return name.endsWith("s") ? `${name}'` : `${name}s` } - const definition = definitions[component._component] - - if (!definition?.requiredAncestors?.length) { - return - } - - const missingAncestors = definition.requiredAncestors.filter( - ancestor => !ancestors.includes(`${BudibasePrefix}${ancestor}`) - ) - - if (missingAncestors.length) { - const pluralise = (name: string) => { - return name.endsWith("s") ? `${name}'` : `${name}s` - } - - result[component._id!] = missingAncestors.map(ancestor => { + result.push( + ...missingAncestors.map(ancestor => { const ancestorDefinition = definitions[`${BudibasePrefix}${ancestor}`] return { componentId: component._id!, @@ -243,70 +260,19 @@ function getMissingAncestors( }, } }) - } + ) } - checkMissingAncestors(screen.props, []) return result } -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 - label: string | undefined - } - }[] = [] - - function recurseFieldComponentsInChildren(component: ScreenProps) { - if (!component) { - return - } - - const definition = definitions[component._component] - - const setting = definition?.settings?.find(s => typesArray.includes(s.type)) - if (setting) { - result.push({ - component, - setting: { type: setting.type, key: setting.key, label: setting.label }, - }) - } - component._children?.forEach(child => { - recurseFieldComponentsInChildren(child) - }) - } - - recurseFieldComponentsInChildren(screen?.props) - return result -} - -export const screenComponentErrorList = derived( - [screenComponentErrors], - ([$screenComponentErrors]): UIComponentError[] => { - if (!featureFlag.isEnabled("CHECK_COMPONENT_SETTINGS_ERRORS")) { - return [] - } - - return Object.values($screenComponentErrors).flatMap(errors => errors) - } -) - -export const screenComponentsList = derived( - [selectedScreen], - ([$selectedScreen]): Component[] => { - if (!$selectedScreen) { - return [] - } - - return findAllComponents($selectedScreen.props) +export const screenComponentErrors = derived( + [screenComponentErrorList], + ([$list]): Record => { + return $list.reduce>((obj, error) => { + obj[error.componentId] ??= [] + obj[error.componentId].push(error) + return obj + }, {}) } )