Merge pull request #15445 from Budibase/BUDI-9016/extract-componenterrors-from-client
Extract componenterrors from client
This commit is contained in:
commit
7c2375b078
|
@ -4,6 +4,7 @@
|
|||
import type {
|
||||
Component,
|
||||
ComponentCondition,
|
||||
ComponentSetting,
|
||||
EventHandler,
|
||||
Screen,
|
||||
} from "@budibase/types"
|
||||
|
@ -21,7 +22,6 @@
|
|||
processStringSync,
|
||||
} from "@budibase/string-templates"
|
||||
import DrawerBindableInput from "@/components/common/bindings/DrawerBindableInput.svelte"
|
||||
import { type ComponentSetting } from "@/stores/builder/components"
|
||||
|
||||
interface ComponentUsingState {
|
||||
id: string
|
||||
|
|
|
@ -33,6 +33,8 @@ import {
|
|||
import { BudiStore } from "../BudiStore"
|
||||
import { Utils } from "@budibase/frontend-core"
|
||||
import {
|
||||
ComponentDefinition,
|
||||
ComponentSetting,
|
||||
Component as ComponentType,
|
||||
ComponentCondition,
|
||||
FieldType,
|
||||
|
@ -55,30 +57,6 @@ export interface ComponentState {
|
|||
selectedScreenId?: string | null
|
||||
}
|
||||
|
||||
export interface ComponentDefinition {
|
||||
component: string
|
||||
name: string
|
||||
friendlyName?: string
|
||||
hasChildren?: boolean
|
||||
settings?: ComponentSetting[]
|
||||
features?: Record<string, boolean>
|
||||
typeSupportPresets?: Record<string, any>
|
||||
legalDirectChildren: string[]
|
||||
illegalChildren: string[]
|
||||
}
|
||||
|
||||
export interface ComponentSetting {
|
||||
key: string
|
||||
type: string
|
||||
label?: string
|
||||
section?: string
|
||||
name?: string
|
||||
defaultValue?: any
|
||||
selectAllFields?: boolean
|
||||
resetOn?: string | string[]
|
||||
settings?: ComponentSetting[]
|
||||
}
|
||||
|
||||
export const INITIAL_COMPONENTS_STATE: ComponentState = {
|
||||
components: {},
|
||||
customComponents: [],
|
||||
|
|
|
@ -6,14 +6,17 @@ import {
|
|||
UIDatasourceType,
|
||||
Screen,
|
||||
Component,
|
||||
UIComponentError,
|
||||
ScreenProps,
|
||||
ComponentDefinition,
|
||||
} from "@budibase/types"
|
||||
import { queries } from "./queries"
|
||||
import { views } from "./views"
|
||||
import { bindings, featureFlag } from "@/helpers"
|
||||
import { getBindableProperties } from "@/dataBinding"
|
||||
import { componentStore, ComponentDefinition } from "./components"
|
||||
import { findAllComponents } from "@/helpers/components"
|
||||
import { bindings } from "@/helpers"
|
||||
import { getBindableProperties } from "@/dataBinding"
|
||||
import { componentStore } from "./components"
|
||||
import { getSettingsDefinition } from "@budibase/frontend-core"
|
||||
|
||||
function reduceBy<TItem extends {}, TKey extends keyof TItem>(
|
||||
key: TKey,
|
||||
|
@ -52,61 +55,10 @@ export const screenComponentErrors = derived(
|
|||
$viewsV2,
|
||||
$queries,
|
||||
$componentStore,
|
||||
]): Record<string, string[]> => {
|
||||
if (!featureFlag.isEnabled("CHECK_COMPONENT_SETTINGS_ERRORS")) {
|
||||
]): Record<string, UIComponentError[]> => {
|
||||
if (!$selectedScreen) {
|
||||
return {}
|
||||
}
|
||||
function getInvalidDatasources(
|
||||
screen: Screen,
|
||||
datasources: Record<string, any>
|
||||
) {
|
||||
const result: Record<string, string[]> = {}
|
||||
|
||||
for (const { component, setting } of findComponentsBySettingsType(
|
||||
screen,
|
||||
["table", "dataSource"],
|
||||
$componentStore.components
|
||||
)) {
|
||||
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 componentBindings = getBindableProperties(
|
||||
$selectedScreen,
|
||||
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[component._id!] = [
|
||||
`The ${friendlyTypeName} named "${label}" could not be found`,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const datasources = {
|
||||
...reduceBy("_id", $tables.list),
|
||||
|
@ -115,15 +67,169 @@ export const screenComponentErrors = derived(
|
|||
...reduceBy("_id", $queries.list),
|
||||
}
|
||||
|
||||
if (!$selectedScreen) {
|
||||
// Skip validation if a screen is not selected.
|
||||
return {}
|
||||
}
|
||||
const { components: definitions } = $componentStore
|
||||
|
||||
return getInvalidDatasources($selectedScreen, datasources)
|
||||
const errors = {
|
||||
...getInvalidDatasources($selectedScreen, datasources, definitions),
|
||||
...getMissingAncestors($selectedScreen, definitions),
|
||||
...getMissingRequiredSettings($selectedScreen, definitions),
|
||||
}
|
||||
return errors
|
||||
}
|
||||
)
|
||||
|
||||
function getInvalidDatasources(
|
||||
screen: Screen,
|
||||
datasources: Record<string, any>,
|
||||
definitions: Record<string, ComponentDefinition>
|
||||
) {
|
||||
const result: Record<string, UIComponentError[]> = {}
|
||||
for (const { component, setting } of findComponentsBySettingsType(
|
||||
screen,
|
||||
["table", "dataSource"],
|
||||
definitions
|
||||
)) {
|
||||
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 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[component._id!] = [
|
||||
{
|
||||
key: setting.key,
|
||||
message: `The ${friendlyTypeName} named "${label}" could not be found`,
|
||||
errorType: "setting",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function getMissingRequiredSettings(
|
||||
screen: Screen,
|
||||
definitions: Record<string, ComponentDefinition>
|
||||
) {
|
||||
const allComponents = findAllComponents(screen.props) as Component[]
|
||||
|
||||
const result: Record<string, UIComponentError[]> = {}
|
||||
for (const component of allComponents) {
|
||||
const definition = definitions[component._component]
|
||||
|
||||
const settings = getSettingsDefinition(definition)
|
||||
|
||||
const missingRequiredSettings = 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) => ({
|
||||
key: s.key,
|
||||
message: `Add the <mark>${s.label}</mark> setting to start using your component`,
|
||||
errorType: "setting",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const BudibasePrefix = "@budibase/standard-components/"
|
||||
function getMissingAncestors(
|
||||
screen: Screen,
|
||||
definitions: Record<string, ComponentDefinition>
|
||||
) {
|
||||
const result: Record<string, UIComponentError[]> = {}
|
||||
|
||||
function checkMissingAncestors(component: Component, ancestors: string[]) {
|
||||
for (const child of component._children || []) {
|
||||
checkMissingAncestors(child, [...ancestors, component._component])
|
||||
}
|
||||
|
||||
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 => {
|
||||
const ancestorDefinition = definitions[`${BudibasePrefix}${ancestor}`]
|
||||
return {
|
||||
message: `${pluralise(definition.name)} need to be inside a
|
||||
<mark>${ancestorDefinition.name}</mark>`,
|
||||
errorType: "ancestor-setting",
|
||||
ancestor: {
|
||||
name: ancestorDefinition.name,
|
||||
fullType: `${BudibasePrefix}${ancestor}`,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
checkMissingAncestors(screen.props, [])
|
||||
return result
|
||||
}
|
||||
|
||||
export function findComponentsBySettingsType(
|
||||
screen: Screen,
|
||||
type: string | string[],
|
||||
|
@ -149,10 +255,10 @@ export function findComponentsBySettingsType(
|
|||
const setting = definition?.settings?.find((s: any) =>
|
||||
typesArray.includes(s.type)
|
||||
)
|
||||
if (setting && "type" in setting) {
|
||||
if (setting) {
|
||||
result.push({
|
||||
component,
|
||||
setting: { type: setting.type!, key: setting.key! },
|
||||
setting: { type: setting.type, key: setting.key },
|
||||
})
|
||||
}
|
||||
component._children?.forEach(child => {
|
||||
|
|
|
@ -19,8 +19,8 @@ import {
|
|||
Screen,
|
||||
Component,
|
||||
SaveScreenResponse,
|
||||
ComponentDefinition,
|
||||
} from "@budibase/types"
|
||||
import { ComponentDefinition } from "./components"
|
||||
|
||||
interface ScreenState {
|
||||
screens: Screen[]
|
||||
|
|
|
@ -11,11 +11,8 @@
|
|||
<script>
|
||||
import { getContext, setContext, onMount } from "svelte"
|
||||
import { writable, get } from "svelte/store"
|
||||
import {
|
||||
enrichProps,
|
||||
propsAreSame,
|
||||
getSettingsDefinition,
|
||||
} from "utils/componentProps"
|
||||
import { enrichProps, propsAreSame } from "utils/componentProps"
|
||||
import { getSettingsDefinition } from "@budibase/frontend-core"
|
||||
import {
|
||||
builderStore,
|
||||
devToolsStore,
|
||||
|
@ -29,7 +26,6 @@
|
|||
import EmptyPlaceholder from "components/app/EmptyPlaceholder.svelte"
|
||||
import ScreenPlaceholder from "components/app/ScreenPlaceholder.svelte"
|
||||
import ComponentErrorState from "components/error-states/ComponentErrorState.svelte"
|
||||
import { BudibasePrefix } from "../stores/components.js"
|
||||
import {
|
||||
decodeJSBinding,
|
||||
findHBSBlocks,
|
||||
|
@ -102,8 +98,6 @@
|
|||
let definition
|
||||
let settingsDefinition
|
||||
let settingsDefinitionMap
|
||||
let missingRequiredSettings = false
|
||||
let componentErrors = false
|
||||
|
||||
// Temporary styles which can be added in the app preview for things like
|
||||
// DND. We clear these whenever a new instance is received.
|
||||
|
@ -141,18 +135,11 @@
|
|||
$: componentErrors = instance?._meta?.errors
|
||||
$: hasChildren = !!definition?.hasChildren
|
||||
$: showEmptyState = definition?.showEmptyState !== false
|
||||
$: hasMissingRequiredSettings = missingRequiredSettings?.length > 0
|
||||
$: hasMissingRequiredSettings = !!componentErrors?.find(
|
||||
e => e.errorType === "setting"
|
||||
)
|
||||
$: editable = !!definition?.editable && !hasMissingRequiredSettings
|
||||
$: hasComponentErrors = componentErrors?.length > 0
|
||||
$: requiredAncestors = definition?.requiredAncestors || []
|
||||
$: missingRequiredAncestors = requiredAncestors.filter(
|
||||
ancestor => !$component.ancestors.includes(`${BudibasePrefix}${ancestor}`)
|
||||
)
|
||||
$: hasMissingRequiredAncestors = missingRequiredAncestors?.length > 0
|
||||
$: errorState =
|
||||
hasMissingRequiredSettings ||
|
||||
hasMissingRequiredAncestors ||
|
||||
hasComponentErrors
|
||||
|
||||
// Interactive components can be selected, dragged and highlighted inside
|
||||
// the builder preview
|
||||
|
@ -218,7 +205,7 @@
|
|||
styles: normalStyles,
|
||||
draggable,
|
||||
definition,
|
||||
errored: errorState,
|
||||
errored: hasComponentErrors,
|
||||
}
|
||||
|
||||
// When dragging and dropping, pad components to allow dropping between
|
||||
|
@ -251,9 +238,8 @@
|
|||
name,
|
||||
editing,
|
||||
type: instance._component,
|
||||
errorState,
|
||||
errorState: hasComponentErrors,
|
||||
parent: id,
|
||||
ancestors: [...($component?.ancestors ?? []), instance._component],
|
||||
path: [...($component?.path ?? []), id],
|
||||
darkMode,
|
||||
})
|
||||
|
@ -310,40 +296,6 @@
|
|||
staticSettings = instanceSettings.staticSettings
|
||||
dynamicSettings = instanceSettings.dynamicSettings
|
||||
|
||||
// Check if we have any missing required settings
|
||||
missingRequiredSettings = settingsDefinition.filter(setting => {
|
||||
let empty = instance[setting.key] == null || instance[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 = instance[dependsOnKey]
|
||||
|
||||
const sectionDependsOnKey =
|
||||
setting.sectionDependsOn?.setting || setting.sectionDependsOn
|
||||
const sectionDependsOnValue = setting.sectionDependsOn?.value
|
||||
const sectionRealDependentValue = instance[sectionDependsOnKey]
|
||||
|
||||
if (dependsOnValue == null && realDependentValue == null) {
|
||||
return false
|
||||
}
|
||||
if (dependsOnValue != null && dependsOnValue !== realDependentValue) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
sectionDependsOnValue != null &&
|
||||
sectionDependsOnValue !== sectionRealDependentValue
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return missing
|
||||
})
|
||||
|
||||
// When considering bindings we can ignore children, so we remove that
|
||||
// before storing the reference stringified version
|
||||
const noChildren = JSON.stringify({ ...instance, _children: null })
|
||||
|
@ -686,7 +638,7 @@
|
|||
class:pad
|
||||
class:parent={hasChildren}
|
||||
class:block={isBlock}
|
||||
class:error={errorState}
|
||||
class:error={hasComponentErrors}
|
||||
class:root={isRoot}
|
||||
data-id={id}
|
||||
data-name={name}
|
||||
|
@ -694,12 +646,8 @@
|
|||
data-parent={$component.id}
|
||||
use:gridLayout={gridMetadata}
|
||||
>
|
||||
{#if errorState}
|
||||
<ComponentErrorState
|
||||
{missingRequiredSettings}
|
||||
{missingRequiredAncestors}
|
||||
{componentErrors}
|
||||
/>
|
||||
{#if hasComponentErrors}
|
||||
<ComponentErrorState {componentErrors} />
|
||||
{:else}
|
||||
<svelte:component this={constructor} bind:this={ref} {...initialSettings}>
|
||||
{#if children.length}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<script>
|
||||
import { Layout, Toggle } from "@budibase/bbui"
|
||||
import { getSettingsDefinition } from "@budibase/frontend-core"
|
||||
import DevToolsStat from "./DevToolsStat.svelte"
|
||||
import { componentStore } from "stores/index.js"
|
||||
import { getSettingsDefinition } from "utils/componentProps.js"
|
||||
|
||||
let showEnrichedSettings = true
|
||||
|
||||
|
|
|
@ -1,21 +1,15 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from "svelte"
|
||||
import { Icon } from "@budibase/bbui"
|
||||
import MissingRequiredSetting from "./MissingRequiredSetting.svelte"
|
||||
import MissingRequiredAncestor from "./MissingRequiredAncestor.svelte"
|
||||
import { UIComponentError } from "@budibase/types"
|
||||
import ComponentErrorStateCta from "./ComponentErrorStateCTA.svelte"
|
||||
|
||||
export let missingRequiredSettings:
|
||||
| { key: string; label: string }[]
|
||||
| undefined
|
||||
export let missingRequiredAncestors: string[] | undefined
|
||||
export let componentErrors: string[] | undefined
|
||||
export let componentErrors: UIComponentError[] | undefined
|
||||
|
||||
const component = getContext("component")
|
||||
const { styleable, builderStore } = getContext("sdk")
|
||||
|
||||
$: styles = { ...$component.styles, normal: {}, custom: null, empty: true }
|
||||
$: requiredSetting = missingRequiredSettings?.[0]
|
||||
$: requiredAncestor = missingRequiredAncestors?.[0]
|
||||
$: errorMessage = componentErrors?.[0]
|
||||
</script>
|
||||
|
||||
|
@ -23,12 +17,10 @@
|
|||
{#if $component.errorState}
|
||||
<div class="component-placeholder" use:styleable={styles}>
|
||||
<Icon name="Alert" color="var(--spectrum-global-color-static-red-600)" />
|
||||
{#if requiredAncestor}
|
||||
<MissingRequiredAncestor {requiredAncestor} />
|
||||
{:else if errorMessage}
|
||||
{errorMessage}
|
||||
{:else if requiredSetting}
|
||||
<MissingRequiredSetting {requiredSetting} />
|
||||
{#if errorMessage}
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags-->
|
||||
{@html errorMessage.message}
|
||||
<ComponentErrorStateCta error={errorMessage} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from "svelte"
|
||||
import { UIComponentError } from "@budibase/types"
|
||||
|
||||
export let error: UIComponentError | undefined
|
||||
|
||||
const component = getContext("component")
|
||||
const { builderStore } = getContext("sdk")
|
||||
</script>
|
||||
|
||||
{#if error}
|
||||
{#if error.errorType === "setting"}
|
||||
<span>-</span>
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span
|
||||
class="spectrum-Link"
|
||||
on:click={() => {
|
||||
builderStore.actions.highlightSetting(error.key)
|
||||
}}
|
||||
>
|
||||
Show me
|
||||
</span>
|
||||
{:else if error.errorType === "ancestor-setting"}
|
||||
<span>-</span>
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span
|
||||
class="spectrum-Link"
|
||||
on:click={() => {
|
||||
builderStore.actions.addParentComponent(
|
||||
$component.id,
|
||||
error.ancestor.fullType
|
||||
)
|
||||
}}
|
||||
>
|
||||
Add {error.ancestor.name}
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
|
@ -1,43 +0,0 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import { BudibasePrefix } from "stores/components"
|
||||
|
||||
export let requiredAncestor
|
||||
|
||||
const component = getContext("component")
|
||||
const { builderStore, componentStore } = getContext("sdk")
|
||||
|
||||
$: definition = componentStore.actions.getComponentDefinition($component.type)
|
||||
$: fullAncestorType = `${BudibasePrefix}${requiredAncestor}`
|
||||
$: ancestorDefinition =
|
||||
componentStore.actions.getComponentDefinition(fullAncestorType)
|
||||
$: pluralName = getPluralName(definition?.name, $component.type)
|
||||
$: ancestorName = getAncestorName(ancestorDefinition?.name, requiredAncestor)
|
||||
|
||||
const getPluralName = (name, type) => {
|
||||
if (!name) {
|
||||
name = type.replace(BudibasePrefix, "")
|
||||
}
|
||||
return name.endsWith("s") ? `${name}'` : `${name}s`
|
||||
}
|
||||
|
||||
const getAncestorName = name => {
|
||||
return name || requiredAncestor
|
||||
}
|
||||
</script>
|
||||
|
||||
<span>
|
||||
{pluralName} need to be inside a
|
||||
<mark>{ancestorName}</mark>
|
||||
</span>
|
||||
<span>-</span>
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span
|
||||
class="spectrum-Link"
|
||||
on:click={() => {
|
||||
builderStore.actions.addParentComponent($component.id, fullAncestorType)
|
||||
}}
|
||||
>
|
||||
Add {ancestorName}
|
||||
</span>
|
|
@ -1,22 +0,0 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
|
||||
export let requiredSetting
|
||||
|
||||
const { builderStore } = getContext("sdk")
|
||||
</script>
|
||||
|
||||
<span>
|
||||
Add the <mark>{requiredSetting.label}</mark> setting to start using your component
|
||||
</span>
|
||||
<span>-</span>
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<span
|
||||
class="spectrum-Link"
|
||||
on:click={() => {
|
||||
builderStore.actions.highlightSetting(requiredSetting.key)
|
||||
}}
|
||||
>
|
||||
Show me
|
||||
</span>
|
|
@ -11,7 +11,15 @@ export interface SDK {
|
|||
generateGoldenSample: any
|
||||
builderStore: Readable<{
|
||||
inBuilder: boolean
|
||||
}>
|
||||
}> & {
|
||||
actions: {
|
||||
highlightSetting: (key: string) => void
|
||||
addParentComponent: (
|
||||
componentId: string,
|
||||
fullAncestorType: string
|
||||
) => void
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type Component = Readable<{
|
||||
|
|
|
@ -97,26 +97,3 @@ export const propsUseBinding = (props, bindingKey) => {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the definition of this component's settings from the manifest
|
||||
*/
|
||||
export const getSettingsDefinition = definition => {
|
||||
if (!definition) {
|
||||
return []
|
||||
}
|
||||
let settings = []
|
||||
definition.settings?.forEach(setting => {
|
||||
if (setting.section) {
|
||||
settings = settings.concat(
|
||||
(setting.settings || [])?.map(childSetting => ({
|
||||
...childSetting,
|
||||
sectionDependsOn: setting.dependsOn,
|
||||
}))
|
||||
)
|
||||
} else {
|
||||
settings.push(setting)
|
||||
}
|
||||
})
|
||||
return settings
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { ComponentDefinition, ComponentSetting } from "@budibase/types"
|
||||
|
||||
/**
|
||||
* Gets the definition of this component's settings from the manifest
|
||||
*/
|
||||
export const getSettingsDefinition = (
|
||||
definition: ComponentDefinition
|
||||
): ComponentSetting[] => {
|
||||
if (!definition) {
|
||||
return []
|
||||
}
|
||||
let settings: ComponentSetting[] = []
|
||||
definition.settings?.forEach(setting => {
|
||||
if (setting.section) {
|
||||
settings = settings.concat(
|
||||
(setting.settings || [])?.map(childSetting => ({
|
||||
...childSetting,
|
||||
sectionDependsOn: setting.dependsOn,
|
||||
}))
|
||||
)
|
||||
} else {
|
||||
settings.push(setting)
|
||||
}
|
||||
})
|
||||
return settings
|
||||
}
|
|
@ -13,3 +13,4 @@ export * from "./download"
|
|||
export * from "./settings"
|
||||
export * from "./relatedColumns"
|
||||
export * from "./table"
|
||||
export * from "./components"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
export enum FeatureFlag {
|
||||
USE_ZOD_VALIDATOR = "USE_ZOD_VALIDATOR",
|
||||
CHECK_COMPONENT_SETTINGS_ERRORS = "CHECK_COMPONENT_SETTINGS_ERRORS",
|
||||
|
||||
// Account-portal
|
||||
DIRECT_LOGIN_TO_ACCOUNT_PORTAL = "DIRECT_LOGIN_TO_ACCOUNT_PORTAL",
|
||||
|
@ -8,7 +7,6 @@ export enum FeatureFlag {
|
|||
|
||||
export const FeatureFlagDefaults = {
|
||||
[FeatureFlag.USE_ZOD_VALIDATOR]: false,
|
||||
[FeatureFlag.CHECK_COMPONENT_SETTINGS_ERRORS]: false,
|
||||
|
||||
// Account-portal
|
||||
[FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false,
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
interface BaseUIComponentError {
|
||||
message: string
|
||||
}
|
||||
|
||||
interface UISettingComponentError extends BaseUIComponentError {
|
||||
errorType: "setting"
|
||||
key: string
|
||||
}
|
||||
|
||||
interface UIAncestorComponentError extends BaseUIComponentError {
|
||||
errorType: "ancestor-setting"
|
||||
ancestor: {
|
||||
name: string
|
||||
fullType: string
|
||||
}
|
||||
}
|
||||
|
||||
export type UIComponentError =
|
||||
| UISettingComponentError
|
||||
| UIAncestorComponentError
|
|
@ -1,2 +1,34 @@
|
|||
export * from "./sidepanel"
|
||||
export * from "./codeEditor"
|
||||
export * from "./errors"
|
||||
|
||||
export interface ComponentDefinition {
|
||||
component: string
|
||||
name: string
|
||||
friendlyName?: string
|
||||
hasChildren?: boolean
|
||||
settings?: ComponentSetting[]
|
||||
features?: Record<string, boolean>
|
||||
typeSupportPresets?: Record<string, any>
|
||||
legalDirectChildren: string[]
|
||||
requiredAncestors?: string[]
|
||||
illegalChildren: string[]
|
||||
}
|
||||
|
||||
export interface ComponentSetting {
|
||||
key: string
|
||||
type: string
|
||||
label?: string
|
||||
section?: string
|
||||
name?: string
|
||||
defaultValue?: any
|
||||
selectAllFields?: boolean
|
||||
resetOn?: string | string[]
|
||||
settings?: ComponentSetting[]
|
||||
dependsOn?:
|
||||
| string
|
||||
| {
|
||||
setting: string
|
||||
value: string
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue