Merge branch 'develop' into bug/sev3/require-option-values

This commit is contained in:
Mel O'Hagan 2022-04-11 15:18:10 +01:00
commit dfa46651b6
26 changed files with 170 additions and 82 deletions

View File

@ -1,5 +1,5 @@
{ {
"version": "1.0.105-alpha.8", "version": "1.0.105-alpha.9",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "1.0.105-alpha.8", "version": "1.0.105-alpha.9",
"description": "Budibase backend core libraries used in server and worker", "description": "Budibase backend core libraries used in server and worker",
"main": "src/index.js", "main": "src/index.js",
"author": "Budibase", "author": "Budibase",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "1.0.105-alpha.8", "version": "1.0.105-alpha.9",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",
@ -38,7 +38,7 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1", "@adobe/spectrum-css-workflow-icons": "^1.2.1",
"@budibase/string-templates": "^1.0.105-alpha.8", "@budibase/string-templates": "^1.0.105-alpha.9",
"@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2", "@spectrum-css/avatar": "^3.0.2",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "1.0.105-alpha.8", "version": "1.0.105-alpha.9",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -65,10 +65,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.105-alpha.8", "@budibase/bbui": "^1.0.105-alpha.9",
"@budibase/client": "^1.0.105-alpha.8", "@budibase/client": "^1.0.105-alpha.9",
"@budibase/frontend-core": "^1.0.105-alpha.8", "@budibase/frontend-core": "^1.0.105-alpha.9",
"@budibase/string-templates": "^1.0.105-alpha.8", "@budibase/string-templates": "^1.0.105-alpha.9",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -2,9 +2,15 @@ export default function (url) {
return url return url
.split("/") .split("/")
.map(part => { .map(part => {
// if parameter, then use as is part = decodeURIComponent(part)
if (part.startsWith(":")) return part part = part.replace(/ /g, "-")
return encodeURIComponent(part.replace(/ /g, "-"))
// If parameter, then use as is
if (!part.startsWith(":")) {
part = encodeURIComponent(part)
}
return part
}) })
.join("/") .join("/")
.toLowerCase() .toLowerCase()

View File

@ -6,15 +6,10 @@
export let overlayEnabled = true export let overlayEnabled = true
let imageError = false let imageError = false
let imageLoaded = false
const imageRenderError = () => { const imageRenderError = () => {
imageError = true imageError = true
} }
const imageLoadSuccess = () => {
imageLoaded = true
}
</script> </script>
<div class="template-card" style="background-color:{backgroundColour};"> <div class="template-card" style="background-color:{backgroundColour};">
@ -23,8 +18,7 @@
alt={name} alt={name}
src={imageSrc} src={imageSrc}
on:error={imageRenderError} on:error={imageRenderError}
on:load={imageLoadSuccess} class:error={imageError}
class={`${imageLoaded ? "loaded" : ""}`}
/> />
<div style={`display:${imageError ? "block" : "none"}`}> <div style={`display:${imageError ? "block" : "none"}`}>
<svg <svg
@ -104,15 +98,14 @@
width: 100%; width: 100%;
} }
.template-card img.loaded {
display: block;
}
.template-card img { .template-card img {
display: none; display: block;
max-width: 100%; max-width: 100%;
border-radius: var(--border-radius-s) 0px var(--border-radius-s) 0px; border-radius: var(--border-radius-s) 0px var(--border-radius-s) 0px;
} }
.template-card img.error {
display: none;
}
.template-card:hover { .template-card:hover {
background: var(--spectrum-alias-background-color-tertiary); background: var(--spectrum-alias-background-color-tertiary);

View File

@ -3,13 +3,14 @@
import PathTree from "./PathTree.svelte" import PathTree from "./PathTree.svelte"
let routes = {} let routes = {}
$: paths = Object.keys(routes || {}).sort() let paths = []
$: { $: allRoutes = $store.routes
const allRoutes = $store.routes $: selectedScreenId = $store.selectedScreenId
$: updatePaths(allRoutes, $selectedAccessRole, selectedScreenId)
const updatePaths = (allRoutes, selectedRoleId, selectedScreenId) => {
const sortedPaths = Object.keys(allRoutes || {}).sort() const sortedPaths = Object.keys(allRoutes || {}).sort()
const selectedRoleId = $selectedAccessRole
const selectedScreenId = $store.selectedScreenId
let found = false let found = false
let firstValidScreenId let firstValidScreenId
@ -41,11 +42,15 @@
}) })
}) })
}) })
routes = filteredRoutes routes = { ...filteredRoutes }
paths = Object.keys(routes || {}).sort()
// Select the correct role for the current screen ID // Select the correct role for the current screen ID
if (!found && screenRoleId) { if (!found && screenRoleId) {
selectedAccessRole.set(screenRoleId) selectedAccessRole.set(screenRoleId)
if (screenRoleId !== selectedRoleId) {
updatePaths(allRoutes, screenRoleId, selectedScreenId)
}
} }
// If the selected screen isn't in this filtered list, select the first one // If the selected screen isn't in this filtered list, select the first one

View File

@ -19,7 +19,7 @@
.filter(a => a.definition.trigger?.stepId === "APP") .filter(a => a.definition.trigger?.stepId === "APP")
.map(automation => { .map(automation => {
const schema = Object.entries( const schema = Object.entries(
automation.definition.trigger.inputs.fields automation.definition.trigger.inputs.fields || {}
).map(([name, type]) => ({ name, type })) ).map(([name, type]) => ({ name, type }))
return { return {

View File

@ -3,6 +3,7 @@
import { roles } from "stores/backend" import { roles } from "stores/backend"
export let value export let value
export let error
</script> </script>
<Select <Select
@ -11,4 +12,5 @@
options={$roles} options={$roles}
getOptionLabel={role => role.name} getOptionLabel={role => role.name}
getOptionValue={role => role._id} getOptionValue={role => role._id}
{error}
/> />

View File

@ -8,14 +8,50 @@
import { currentAsset, store } from "builderStore" import { currentAsset, store } from "builderStore"
import { FrontendTypes } from "constants" import { FrontendTypes } from "constants"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
import { allScreens, selectedAccessRole } from "builderStore"
export let componentInstance export let componentInstance
export let bindings export let bindings
function setAssetProps(name, value, parser) { let errors = {}
if (parser && typeof parser === "function") {
const routeTaken = url => {
const roleId = get(selectedAccessRole) || "BASIC"
return get(allScreens).some(
screen =>
screen.routing.route.toLowerCase() === url.toLowerCase() &&
screen.routing.roleId === roleId
)
}
const roleTaken = roleId => {
const url = get(currentAsset)?.routing.route
return get(allScreens).some(
screen =>
screen.routing.route.toLowerCase() === url.toLowerCase() &&
screen.routing.roleId === roleId
)
}
const setAssetProps = (name, value, parser, validate) => {
if (parser) {
value = parser(value) value = parser(value)
} }
if (validate) {
const error = validate(value)
errors = {
...errors,
[name]: error,
}
if (error) {
return
}
} else {
errors = {
...errors,
[name]: null,
}
}
const selectedAsset = get(currentAsset) const selectedAsset = get(currentAsset)
store.update(state => { store.update(state => {
@ -38,7 +74,6 @@
} }
const screenSettings = [ const screenSettings = [
// { key: "description", label: "Description", control: Input },
{ {
key: "routing.route", key: "routing.route",
label: "Route", label: "Route",
@ -49,8 +84,26 @@
} }
return sanitizeUrl(val) return sanitizeUrl(val)
}, },
validate: val => {
const exisingValue = get(currentAsset)?.routing.route
if (val !== exisingValue && routeTaken(val)) {
return "That URL is already in use for this role"
}
return null
},
},
{
key: "routing.roleId",
label: "Access",
control: RoleSelect,
validate: val => {
const exisingValue = get(currentAsset)?.routing.roleId
if (val !== exisingValue && roleTaken(val)) {
return "That role is already in use for this URL"
}
return null
},
}, },
{ key: "routing.roleId", label: "Access", control: RoleSelect },
{ key: "layoutId", label: "Layout", control: LayoutSelect }, { key: "layoutId", label: "Layout", control: LayoutSelect },
] ]
</script> </script>
@ -62,9 +115,11 @@
control={def.control} control={def.control}
label={def.label} label={def.label}
key={def.key} key={def.key}
error="asdasds"
value={deepGet($currentAsset, def.key)} value={deepGet($currentAsset, def.key)}
onChange={val => setAssetProps(def.key, val, def.parser)} onChange={val => setAssetProps(def.key, val, def.parser, def.validate)}
{bindings} {bindings}
props={{ error: errors[def.key] }}
/> />
{/each} {/each}
</DetailSummary> </DetailSummary>

View File

@ -15,7 +15,7 @@
import { onMount } from "svelte" import { onMount } from "svelte"
import { templates } from "stores/portal" import { templates } from "stores/portal"
let loaded = false let loaded = $templates?.length
let template let template
let creationModal = false let creationModal = false
let creatingApp = false let creatingApp = false

View File

@ -40,7 +40,7 @@
let unpublishModal let unpublishModal
let iconModal let iconModal
let creatingApp = false let creatingApp = false
let loaded = false let loaded = $apps?.length || $templates?.length
let searchTerm = "" let searchTerm = ""
let cloud = $admin.cloud let cloud = $admin.cloud
let appName = "" let appName = ""
@ -292,8 +292,8 @@
<div class="title"> <div class="title">
<div class="welcome"> <div class="welcome">
<Layout noPadding gap="XS"> <Layout noPadding gap="XS">
<Heading size="M">{welcomeHeader}</Heading> <Heading size="L">{welcomeHeader}</Heading>
<Body size="S"> <Body size="M">
{welcomeBody} {welcomeBody}
</Body> </Body>
</Layout> </Layout>
@ -301,7 +301,7 @@
<div class="buttons"> <div class="buttons">
<Button <Button
dataCy="create-app-btn" dataCy="create-app-btn"
size="L" size="M"
icon="Add" icon="Add"
cta cta
on:click={initiateAppCreation} on:click={initiateAppCreation}
@ -311,7 +311,7 @@
{#if $apps?.length > 0} {#if $apps?.length > 0}
<Button <Button
icon="Experience" icon="Experience"
size="L" size="M"
quiet quiet
secondary secondary
on:click={$goto("/builder/portal/apps/templates")} on:click={$goto("/builder/portal/apps/templates")}
@ -348,7 +348,7 @@
{#if enrichedApps.length} {#if enrichedApps.length}
<Layout noPadding gap="S"> <Layout noPadding gap="S">
<div class="title"> <div class="title">
<Detail size="L">My apps</Detail> <Detail size="L">Apps</Detail>
{#if enrichedApps.length > 1} {#if enrichedApps.length > 1}
<div class="app-actions"> <div class="app-actions">
{#if cloud} {#if cloud}

View File

@ -5,7 +5,7 @@
import { onMount } from "svelte" import { onMount } from "svelte"
import { templates } from "stores/portal" import { templates } from "stores/portal"
let loaded = false let loaded = $templates?.length
onMount(async () => { onMount(async () => {
try { try {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "1.0.105-alpha.8", "version": "1.0.105-alpha.9",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "1.0.105-alpha.8", "version": "1.0.105-alpha.9",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -19,9 +19,9 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.105-alpha.8", "@budibase/bbui": "^1.0.105-alpha.9",
"@budibase/frontend-core": "^1.0.105-alpha.8", "@budibase/frontend-core": "^1.0.105-alpha.9",
"@budibase/string-templates": "^1.0.105-alpha.8", "@budibase/string-templates": "^1.0.105-alpha.9",
"@spectrum-css/button": "^3.0.3", "@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3", "@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3", "@spectrum-css/divider": "^1.0.3",

View File

@ -102,7 +102,7 @@
white-space: nowrap; white-space: nowrap;
} }
.spectrum-Card-footer { .spectrum-Card-footer {
word-wrap: anywhere; word-wrap: break-word;
white-space: pre-wrap; white-space: pre-wrap;
} }
.horizontal .spectrum-Card-coverPhoto { .horizontal .spectrum-Card-coverPhoto {

View File

@ -125,7 +125,11 @@
{#if schemaLoaded} {#if schemaLoaded}
<Block> <Block>
<div class="card-list" use:styleable={$component.styles}> <div class="card-list" use:styleable={$component.styles}>
<BlockComponent type="form" bind:id={formId} props={{ dataSource }}> <BlockComponent
type="form"
bind:id={formId}
props={{ dataSource, disableValidation: true }}
>
{#if title || enrichedSearchColumns?.length || showTitleButton} {#if title || enrichedSearchColumns?.length || showTitleButton}
<div class="header" class:mobile={$context.device.mobile}> <div class="header" class:mobile={$context.device.mobile}>
<div class="title"> <div class="title">

View File

@ -106,7 +106,11 @@
{#if schemaLoaded} {#if schemaLoaded}
<Block> <Block>
<div class={size} use:styleable={$component.styles}> <div class={size} use:styleable={$component.styles}>
<BlockComponent type="form" bind:id={formId} props={{ dataSource }}> <BlockComponent
type="form"
bind:id={formId}
props={{ dataSource, disableValidation: true }}
>
{#if title || enrichedSearchColumns?.length || showTitleButton} {#if title || enrichedSearchColumns?.length || showTitleButton}
<div class="header" class:mobile={$context.device.mobile}> <div class="header" class:mobile={$context.device.mobile}>
<div class="title"> <div class="title">

View File

@ -34,7 +34,7 @@
color: var(--spectrum-global-color-gray-700) !important; color: var(--spectrum-global-color-gray-700) !important;
} }
div :global(.apexcharts-datalabel) { div :global(.apexcharts-datalabel) {
fill: var(--spectrum-global-color-gray-800); fill: white;
} }
div :global(.apexcharts-tooltip) { div :global(.apexcharts-tooltip) {
background-color: var(--spectrum-global-color-gray-200) !important; background-color: var(--spectrum-global-color-gray-200) !important;
@ -45,4 +45,12 @@
background-color: var(--spectrum-global-color-gray-100) !important; background-color: var(--spectrum-global-color-gray-100) !important;
border-color: var(--spectrum-global-color-gray-300) !important; border-color: var(--spectrum-global-color-gray-300) !important;
} }
div :global(.apexcharts-theme-dark .apexcharts-tooltip-text) {
color: white;
}
div
:global(.apexcharts-theme-dark
.apexcharts-tooltip-series-group.apexcharts-active) {
padding-bottom: 0;
}
</style> </style>

View File

@ -9,6 +9,10 @@
export let disabled = false export let disabled = false
export let actionType = "Create" export let actionType = "Create"
// Not exposed as a builder setting. Used internally to disable validation
// for fields rendered in things like search blocks.
export let disableValidation = false
const context = getContext("context") const context = getContext("context")
const { API, fetchDatasourceSchema } = getContext("sdk") const { API, fetchDatasourceSchema } = getContext("sdk")
@ -102,6 +106,7 @@
{schema} {schema}
{table} {table}
{initialValues} {initialValues}
{disableValidation}
> >
<slot /> <slot />
</InnerForm> </InnerForm>

View File

@ -10,6 +10,7 @@
export let size export let size
export let schema export let schema
export let table export let table
export let disableValidation = false
const component = getContext("component") const component = getContext("component")
const { styleable, Provider, ActionTypes } = getContext("sdk") const { styleable, Provider, ActionTypes } = getContext("sdk")
@ -141,7 +142,9 @@
// Create validation function based on field schema // Create validation function based on field schema
const schemaConstraints = schema?.[field]?.constraints const schemaConstraints = schema?.[field]?.constraints
const validator = createValidatorFromConstraints( const validator = disableValidation
? null
: createValidatorFromConstraints(
schemaConstraints, schemaConstraints,
validationRules, validationRules,
field, field,
@ -164,7 +167,7 @@
// If this field has already been registered and we previously had an // If this field has already been registered and we previously had an
// error set, then re-run the validator to see if we can unset it // error set, then re-run the validator to see if we can unset it
if (fieldState.error) { if (fieldState.error) {
initialError = validator(initialValue) initialError = validator?.(initialValue)
} }
} }
@ -254,7 +257,7 @@
} }
// Update field state // Update field state
const error = validator ? validator(value) : null const error = validator?.(value)
fieldInfo.update(state => { fieldInfo.update(state => {
state.fieldState.value = value state.fieldState.value = value
state.fieldState.error = error state.fieldState.error = error
@ -288,7 +291,9 @@
// Create new validator // Create new validator
const schemaConstraints = schema?.[field]?.constraints const schemaConstraints = schema?.[field]?.constraints
const validator = createValidatorFromConstraints( const validator = disableValidation
? null
: createValidatorFromConstraints(
schemaConstraints, schemaConstraints,
validationRules, validationRules,
field, field,

View File

@ -329,13 +329,13 @@ export const enrichButtonActions = (actions, context) => {
return actions return actions
} }
const handlers = actions.map(def => handlerMap[def["##eventHandlerType"]])
return async eventContext => {
// Button context is built up as actions are executed. // Button context is built up as actions are executed.
// Inherit any previous button context which may have come from actions // Inherit any previous button context which may have come from actions
// before a confirmable action since this breaks the chain. // before a confirmable action since this breaks the chain.
let buttonContext = context.actions || [] let buttonContext = context.actions || []
const handlers = actions.map(def => handlerMap[def["##eventHandlerType"]])
return async eventContext => {
for (let i = 0; i < handlers.length; i++) { for (let i = 0; i < handlers.length; i++) {
try { try {
// Skip any non-existent action definitions // Skip any non-existent action definitions
@ -346,6 +346,7 @@ export const enrichButtonActions = (actions, context) => {
// Built total context for this action // Built total context for this action
const totalContext = { const totalContext = {
...context, ...context,
state: get(stateStore),
actions: buttonContext, actions: buttonContext,
eventContext, eventContext,
} }

View File

@ -1,12 +1,12 @@
{ {
"name": "@budibase/frontend-core", "name": "@budibase/frontend-core",
"version": "1.0.105-alpha.8", "version": "1.0.105-alpha.9",
"description": "Budibase frontend core libraries used in builder and client", "description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.105-alpha.8", "@budibase/bbui": "^1.0.105-alpha.9",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"svelte": "^3.46.2" "svelte": "^3.46.2"
} }

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.105-alpha.8", "version": "1.0.105-alpha.9",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -68,9 +68,9 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@apidevtools/swagger-parser": "^10.0.3", "@apidevtools/swagger-parser": "^10.0.3",
"@budibase/backend-core": "^1.0.105-alpha.8", "@budibase/backend-core": "^1.0.105-alpha.9",
"@budibase/client": "^1.0.105-alpha.8", "@budibase/client": "^1.0.105-alpha.9",
"@budibase/string-templates": "^1.0.105-alpha.8", "@budibase/string-templates": "^1.0.105-alpha.9",
"@bull-board/api": "^3.7.0", "@bull-board/api": "^3.7.0",
"@bull-board/koa": "^3.7.0", "@bull-board/koa": "^3.7.0",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "1.0.105-alpha.8", "version": "1.0.105-alpha.9",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.105-alpha.8", "version": "1.0.105-alpha.9",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -31,8 +31,8 @@
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "^1.0.105-alpha.8", "@budibase/backend-core": "^1.0.105-alpha.9",
"@budibase/string-templates": "^1.0.105-alpha.8", "@budibase/string-templates": "^1.0.105-alpha.9",
"@koa/router": "^8.0.0", "@koa/router": "^8.0.0",
"@sentry/node": "^6.0.0", "@sentry/node": "^6.0.0",
"@techpass/passport-openidconnect": "^0.3.0", "@techpass/passport-openidconnect": "^0.3.0",