Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefill card blocks #13527

Merged
merged 19 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cb3937b
Auto fill card blocks with bindings and reset when changing datasource
aptkingston Apr 18, 2024
e98a9f7
Update when empty setting enrichment runs to ensure card blocks prope…
aptkingston Apr 19, 2024
75bf928
Tidy up card binding logic
aptkingston Apr 19, 2024
bc29d35
Improve card binding autofill field selection
aptkingston Apr 19, 2024
e670dc1
Lint
aptkingston Apr 19, 2024
1ede3b3
Merge branch 'master' into prefill-cards
aptkingston Apr 19, 2024
ebbd0a8
Remove dates from card block autofill because the timestamps don't lo…
aptkingston Apr 19, 2024
e6aa5bc
Merge branch 'master' into prefill-cards
aptkingston Apr 26, 2024
d39ec25
Merge branch 'master' into prefill-cards
aptkingston May 13, 2024
8d5d459
Merge branch 'master' into prefill-cards
deanhannigan May 20, 2024
8498dd7
Merge branch 'master' into prefill-cards
deanhannigan May 20, 2024
0c63946
Merge branch 'master' into prefill-cards
aptkingston May 21, 2024
16fd09e
Merge branch 'master' of github.com:Budibase/budibase into prefill-cards
aptkingston May 21, 2024
e8de955
Merge branch 'master' into prefill-cards
aptkingston May 21, 2024
8fe020c
Merge branch 'prefill-cards' of github.com:Budibase/budibase into pre…
aptkingston May 21, 2024
a074cb6
Remove enrichEmptySettings from component patch function as any scree…
aptkingston May 21, 2024
35c5220
Add tests for enriching empty card block settings
aptkingston May 21, 2024
bf79601
Merge branch 'master' into prefill-cards
aptkingston May 21, 2024
35897b3
Merge branch 'master' into prefill-cards
aptkingston May 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 82 additions & 8 deletions packages/builder/src/stores/builder/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
previewStore,
tables,
componentTreeNodesStore,
} from "stores/builder/index"
} from "stores/builder"
import { buildFormSchema, getSchemaForDatasource } from "dataBinding"
import {
BUDIBASE_INTERNAL_DB_ID,
Expand All @@ -30,6 +30,7 @@ import {
} from "constants/backend"
import BudiStore from "../BudiStore"
import { Utils } from "@budibase/frontend-core"
import { FieldType } from "@budibase/types"

export const INITIAL_COMPONENTS_STATE = {
components: {},
Expand Down Expand Up @@ -296,6 +297,80 @@ export class ComponentStore extends BudiStore {
}
}
})

// Add default bindings to card blocks
if (component._component.endsWith("/cardsblock")) {
// Only proceed if the card is empty, i.e. we just changed datasource or
// just created the card
const cardKeys = ["cardTitle", "cardSubtitle", "cardDescription"]
if (cardKeys.every(key => !component[key]) && !component.cardImageURL) {
const { _id, dataSource } = component
if (dataSource) {
const { schema, table } = getSchemaForDatasource(screen, dataSource)

// Finds fields by types from the schema of the configured datasource
const findFieldTypes = fieldTypes => {
if (!Array.isArray(fieldTypes)) {
fieldTypes = [fieldTypes]
}
return Object.entries(schema || {})
.filter(([name, fieldSchema]) => {
return (
fieldTypes.includes(fieldSchema.type) &&
!fieldSchema.autoColumn &&
name !== table?.primaryDisplay &&
!name.startsWith("_")
)
})
.map(([name]) => name)
}

// Inserts a card binding for a certain setting
const addBinding = (key, fallback, ...parts) => {
if (parts.some(x => x == null)) {
component[key] = fallback
} else {
parts.unshift(`${_id}-repeater`)
component[key] = `{{ ${parts.map(safe).join(".")} }}`
}
}

// Extract good field candidates to prefill our cards with.
// Use the primary display as the best field, if it exists.
const shortFields = [
...findFieldTypes(FieldType.STRING),
...findFieldTypes(FieldType.OPTIONS),
...findFieldTypes(FieldType.ARRAY),
...findFieldTypes(FieldType.NUMBER),
]
const longFields = findFieldTypes(FieldType.LONGFORM)
if (schema?.[table?.primaryDisplay]) {
shortFields.unshift(table.primaryDisplay)
}

// Fill title and subtitle with short fields
addBinding("cardTitle", "Title", shortFields[0])
addBinding("cardSubtitle", "Subtitle", shortFields[1])

// Fill description with a long field if possible
const longField = longFields[0] ?? shortFields[2]
addBinding("cardDescription", "Description", longField)

// Attempt to fill the image setting.
// Check single attachment fields first.
let imgField = findFieldTypes(FieldType.ATTACHMENT_SINGLE)[0]
if (imgField) {
addBinding("cardImageURL", null, imgField, "url")
} else {
// Then try multi-attachment fields if no single ones exist
imgField = findFieldTypes(FieldType.ATTACHMENTS)[0]
if (imgField) {
addBinding("cardImageURL", null, imgField, 0, "url")
}
}
}
}
}
}

/**
Expand Down Expand Up @@ -324,21 +399,21 @@ export class ComponentStore extends BudiStore {
...presetProps,
}

// Enrich empty settings
// Standard post processing
this.enrichEmptySettings(instance, {
parent,
screen: get(selectedScreen),
useDefaultValues: true,
})

// Migrate nested component settings
this.migrateSettings(instance)

// Add any extra properties the component needs
// Custom post processing for creation only
let extras = {}
if (definition.hasChildren) {
extras._children = []
}

// Add step name to form steps
if (componentName.endsWith("/formstep")) {
const parentForm = findClosestMatchingComponent(
get(selectedScreen).props,
Expand All @@ -351,6 +426,7 @@ export class ComponentStore extends BudiStore {
extras.step = formSteps.length + 1
extras._instanceName = `Step ${formSteps.length + 1}`
}

return {
...cloneDeep(instance),
...extras,
Expand Down Expand Up @@ -463,15 +539,13 @@ export class ComponentStore extends BudiStore {
if (!componentId || !screenId) {
const state = get(this.store)
componentId = componentId || state.selectedComponentId

const screenState = get(screenStore)
screenId = screenId || screenState.selectedScreenId
}
if (!componentId || !screenId || !patchFn) {
return
}
const patchScreen = screen => {
// findComponent looks in the tree not comp.settings[0]
let component = findComponent(screen.props, componentId)
if (!component) {
return false
Expand All @@ -480,7 +554,7 @@ export class ComponentStore extends BudiStore {
// Mutates the fetched component with updates
const patchResult = patchFn(component, screen)

// Mutates the component with any required settings updates
// Post processing
const migrated = this.migrateSettings(component)

// Returning an explicit false signifies that we should skip this
Expand Down
23 changes: 23 additions & 0 deletions packages/builder/src/stores/builder/tests/component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
DB_TYPE_EXTERNAL,
DEFAULT_BB_DATASOURCE_ID,
} from "constants/backend"
import { makePropSafe as safe } from "@budibase/string-templates"

// Could move to fixtures
const COMP_PREFIX = "@budibase/standard-components"
Expand Down Expand Up @@ -360,8 +361,30 @@ describe("Component store", () => {
resourceId: internalTableDoc._id,
type: "table",
})

return comp
}

it("enrichEmptySettings - initialise cards blocks with correct fields", async ctx => {
const comp = enrichSettingsDS("cardsblock", ctx)
const expectBinding = (setting, ...parts) => {
expect(comp[setting]).toStrictEqual(
`{{ ${safe(`${comp._id}-repeater`)}.${parts.map(safe).join(".")} }}`
)
}
expectBinding("cardTitle", internalTableDoc.schema.MediaTitle.name)
expectBinding("cardSubtitle", internalTableDoc.schema.MediaVersion.name)
expectBinding(
"cardDescription",
internalTableDoc.schema.MediaDescription.name
)
expectBinding(
"cardImageURL",
internalTableDoc.schema.MediaImage.name,
"url"
)
})

it("enrichEmptySettings - set default datasource for 'table' setting type", async ctx => {
enrichSettingsDS("formblock", ctx)
})
Expand Down
60 changes: 58 additions & 2 deletions packages/builder/src/stores/builder/tests/fixtures/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
DB_TYPE_EXTERNAL,
DEFAULT_BB_DATASOURCE_ID,
} from "constants/backend"
import { FieldType } from "@budibase/types"

const getDocId = () => {
return v4().replace(/-/g, "")
Expand Down Expand Up @@ -45,6 +46,52 @@ export const COMPONENT_DEFINITIONS = {
},
],
},
cardsblock: {
block: true,
name: "Cards Block",
settings: [
{
type: "dataSource",
label: "Data",
key: "dataSource",
required: true,
},
{
section: true,
name: "Cards",
settings: [
{
type: "text",
key: "cardTitle",
label: "Title",
nested: true,
resetOn: "dataSource",
},
{
type: "text",
key: "cardSubtitle",
label: "Subtitle",
nested: true,
resetOn: "dataSource",
},
{
type: "text",
key: "cardDescription",
label: "Description",
nested: true,
resetOn: "dataSource",
},
{
type: "text",
key: "cardImageURL",
label: "Image URL",
nested: true,
resetOn: "dataSource",
},
],
},
],
},
container: {
name: "Container",
},
Expand Down Expand Up @@ -262,14 +309,23 @@ export const internalTableDoc = {
name: "Media",
sourceId: BUDIBASE_INTERNAL_DB_ID,
sourceType: DB_TYPE_INTERNAL,
primaryDisplay: "MediaTitle",
schema: {
MediaTitle: {
name: "MediaTitle",
type: "string",
type: FieldType.STRING,
},
MediaVersion: {
name: "MediaVersion",
type: "string",
type: FieldType.STRING,
},
MediaDescription: {
name: "MediaDescription",
type: FieldType.LONGFORM,
},
MediaImage: {
name: "MediaImage",
type: FieldType.ATTACHMENT_SINGLE,
},
},
}
Expand Down
9 changes: 5 additions & 4 deletions packages/client/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6243,27 +6243,28 @@
"key": "cardTitle",
"label": "Title",
"nested": true,
"defaultValue": "Title"
"resetOn": "dataSource"
},
{
"type": "text",
"key": "cardSubtitle",
"label": "Subtitle",
"nested": true,
"defaultValue": "Subtitle"
"resetOn": "dataSource"
},
{
"type": "text",
"key": "cardDescription",
"label": "Description",
"nested": true,
"defaultValue": "Description"
"resetOn": "dataSource"
},
{
"type": "text",
"key": "cardImageURL",
"label": "Image URL",
"nested": true
"nested": true,
"resetOn": "dataSource"
},
{
"type": "boolean",
Expand Down
Loading