diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js
index bd3dcef252..c01fc9e3ae 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js
@@ -98,7 +98,7 @@ const createScreen = table => ({
label: "Deals",
name: `all_${table._id}`,
tableId: table._id,
- isTable: true,
+ type: "table",
},
_instanceName: `${table.name} Table`,
_children: [],
diff --git a/packages/builder/src/components/common/Checkbox.svelte b/packages/builder/src/components/common/Checkbox.svelte
index 3c5888d7c8..45e4947da6 100644
--- a/packages/builder/src/components/common/Checkbox.svelte
+++ b/packages/builder/src/components/common/Checkbox.svelte
@@ -12,13 +12,18 @@
-
+
diff --git a/packages/builder/src/components/userInterface/propertyCategories.js b/packages/builder/src/components/userInterface/propertyCategories.js
index bae44df7f5..efaa8fbd33 100644
--- a/packages/builder/src/components/userInterface/propertyCategories.js
+++ b/packages/builder/src/components/userInterface/propertyCategories.js
@@ -261,6 +261,16 @@ export const padding = [
]
export const size = [
+ {
+ label: "Flex",
+ key: "flex",
+ control: OptionSelect,
+ defaultValue: "0 1 auto",
+ options: [
+ { label: "Shrink", value: "0 1 auto" },
+ { label: "Grow", value: "1 1 auto" },
+ ],
+ },
{
label: "Width",
key: "width",
diff --git a/packages/builder/tests/fetchBindableProperties.spec.js b/packages/builder/tests/fetchBindableProperties.spec.js
index f89a2abbda..92b4b76a3b 100644
--- a/packages/builder/tests/fetchBindableProperties.spec.js
+++ b/packages/builder/tests/fetchBindableProperties.spec.js
@@ -1,13 +1,14 @@
import fetchBindableProperties from "../src/builderStore/fetchBindableProperties"
describe("fetch bindable properties", () => {
-
it("should return bindable properties from screen components", () => {
const result = fetchBindableProperties({
componentInstanceId: "heading-id",
- ...testData()
+ ...testData(),
})
- const componentBinding = result.find(r => r.instance._id === "search-input-id" && r.type === "instance")
+ const componentBinding = result.find(
+ r => r.instance._id === "search-input-id" && r.type === "instance"
+ )
expect(componentBinding).toBeDefined()
expect(componentBinding.type).toBe("instance")
expect(componentBinding.runtimeBinding).toBe("search-input-id.value")
@@ -16,29 +17,39 @@ describe("fetch bindable properties", () => {
it("should not return bindable components when not in their context", () => {
const result = fetchBindableProperties({
componentInstanceId: "heading-id",
- ...testData()
+ ...testData(),
})
- const componentBinding = result.find(r => r.instance._id === "list-item-input-id")
+ const componentBinding = result.find(
+ r => r.instance._id === "list-item-input-id"
+ )
expect(componentBinding).not.toBeDefined()
})
it("should return table schema, when inside a context", () => {
const result = fetchBindableProperties({
componentInstanceId: "list-item-input-id",
- ...testData()
+ ...testData(),
})
- const contextBindings = result.filter(r => r.instance._id === "list-id" && r.type==="context")
- // 2 fields + _id + _rev
+ const contextBindings = result.filter(
+ r => r.instance._id === "list-id" && r.type === "context"
+ )
+ // 2 fields + _id + _rev
expect(contextBindings.length).toBe(4)
-
- const namebinding = contextBindings.find(b => b.runtimeBinding === "data.name")
+
+ const namebinding = contextBindings.find(
+ b => b.runtimeBinding === "data.name"
+ )
expect(namebinding).toBeDefined()
expect(namebinding.readableBinding).toBe("list-name.Test Table.name")
-
- const descriptionbinding = contextBindings.find(b => b.runtimeBinding === "data.description")
+
+ const descriptionbinding = contextBindings.find(
+ b => b.runtimeBinding === "data.description"
+ )
expect(descriptionbinding).toBeDefined()
- expect(descriptionbinding.readableBinding).toBe("list-name.Test Table.description")
-
+ expect(descriptionbinding.readableBinding).toBe(
+ "list-name.Test Table.description"
+ )
+
const idbinding = contextBindings.find(b => b.runtimeBinding === "data._id")
expect(idbinding).toBeDefined()
expect(idbinding.readableBinding).toBe("list-name.Test Table._id")
@@ -47,35 +58,51 @@ describe("fetch bindable properties", () => {
it("should return table schema, for grantparent context", () => {
const result = fetchBindableProperties({
componentInstanceId: "child-list-item-input-id",
- ...testData()
+ ...testData(),
})
- const contextBindings = result.filter(r => r.type==="context")
+ const contextBindings = result.filter(r => r.type === "context")
// 2 fields + _id + _rev ... x 2 tables
expect(contextBindings.length).toBe(8)
-
- const namebinding_parent = contextBindings.find(b => b.runtimeBinding === "parent.data.name")
+
+ const namebinding_parent = contextBindings.find(
+ b => b.runtimeBinding === "parent.data.name"
+ )
expect(namebinding_parent).toBeDefined()
expect(namebinding_parent.readableBinding).toBe("list-name.Test Table.name")
-
- const descriptionbinding_parent = contextBindings.find(b => b.runtimeBinding === "parent.data.description")
+
+ const descriptionbinding_parent = contextBindings.find(
+ b => b.runtimeBinding === "parent.data.description"
+ )
expect(descriptionbinding_parent).toBeDefined()
- expect(descriptionbinding_parent.readableBinding).toBe("list-name.Test Table.description")
-
- const namebinding_own = contextBindings.find(b => b.runtimeBinding === "data.name")
+ expect(descriptionbinding_parent.readableBinding).toBe(
+ "list-name.Test Table.description"
+ )
+
+ const namebinding_own = contextBindings.find(
+ b => b.runtimeBinding === "data.name"
+ )
expect(namebinding_own).toBeDefined()
- expect(namebinding_own.readableBinding).toBe("child-list-name.Test Table.name")
-
- const descriptionbinding_own = contextBindings.find(b => b.runtimeBinding === "data.description")
+ expect(namebinding_own.readableBinding).toBe(
+ "child-list-name.Test Table.name"
+ )
+
+ const descriptionbinding_own = contextBindings.find(
+ b => b.runtimeBinding === "data.description"
+ )
expect(descriptionbinding_own).toBeDefined()
- expect(descriptionbinding_own.readableBinding).toBe("child-list-name.Test Table.description")
+ expect(descriptionbinding_own.readableBinding).toBe(
+ "child-list-name.Test Table.description"
+ )
})
it("should return bindable component props, from components in same context", () => {
const result = fetchBindableProperties({
componentInstanceId: "list-item-heading-id",
- ...testData()
+ ...testData(),
})
- const componentBinding = result.find(r => r.instance._id === "list-item-input-id" && r.type === "instance")
+ const componentBinding = result.find(
+ r => r.instance._id === "list-item-input-id" && r.type === "instance"
+ )
expect(componentBinding).toBeDefined()
expect(componentBinding.runtimeBinding).toBe("list-item-input-id.value")
})
@@ -83,125 +110,140 @@ describe("fetch bindable properties", () => {
it("should not return components from child context", () => {
const result = fetchBindableProperties({
componentInstanceId: "list-item-heading-id",
- ...testData()
+ ...testData(),
})
- const componentBinding = result.find(r => r.instance._id === "child-list-item-input-id" && r.type === "instance")
+ const componentBinding = result.find(
+ r =>
+ r.instance._id === "child-list-item-input-id" && r.type === "instance"
+ )
expect(componentBinding).not.toBeDefined()
})
-
+
it("should return bindable component props, from components in same context (when nested context)", () => {
const result = fetchBindableProperties({
componentInstanceId: "child-list-item-heading-id",
- ...testData()
+ ...testData(),
})
- const componentBinding = result.find(r => r.instance._id === "child-list-item-input-id" && r.type === "instance")
+ const componentBinding = result.find(
+ r =>
+ r.instance._id === "child-list-item-input-id" && r.type === "instance"
+ )
expect(componentBinding).toBeDefined()
- })
-
+ })
})
const testData = () => {
-
const screen = {
instanceName: "test screen",
name: "screen-id",
route: "/",
props: {
- _id:"screent-root-id",
+ _id: "screent-root-id",
_component: "@budibase/standard-components/container",
_children: [
{
_id: "heading-id",
_instanceName: "list item heading",
_component: "@budibase/standard-components/heading",
- text: "Screen Title"
+ text: "Screen Title",
},
{
_id: "search-input-id",
_instanceName: "Search Input",
_component: "@budibase/standard-components/input",
- value: "search phrase"
+ value: "search phrase",
},
{
_id: "list-id",
_component: "@budibase/standard-components/list",
_instanceName: "list-name",
- table: { isTable: true, tableId: "test-table-id", label: "Test Table", name: "all_test-table-id" },
+ table: {
+ type: "table",
+ tableId: "test-table-id",
+ label: "Test Table",
+ name: "all_test-table-id",
+ },
_children: [
{
_id: "list-item-heading-id",
_instanceName: "list item heading",
_component: "@budibase/standard-components/heading",
- text: "hello"
+ text: "hello",
},
{
_id: "list-item-input-id",
_instanceName: "List Item Input",
_component: "@budibase/standard-components/input",
- value: "list item"
+ value: "list item",
},
{
_id: "child-list-id",
_component: "@budibase/standard-components/list",
_instanceName: "child-list-name",
- table: { isTable: true, tableId: "test-table-id", label: "Test Table", name: "all_test-table-id"},
+ table: {
+ type: "table",
+ tableId: "test-table-id",
+ label: "Test Table",
+ name: "all_test-table-id",
+ },
_children: [
{
_id: "child-list-item-heading-id",
_instanceName: "child list item heading",
_component: "@budibase/standard-components/heading",
- text: "hello"
+ text: "hello",
},
{
_id: "child-list-item-input-id",
_instanceName: "Child List Item Input",
_component: "@budibase/standard-components/input",
- value: "child list item"
+ value: "child list item",
},
- ]
+ ],
},
- ]
+ ],
},
- ]
- }
+ ],
+ },
}
- const tables = [{
- _id: "test-table-id",
- name: "Test Table",
+ const tables = [
+ {
+ _id: "test-table-id",
+ name: "Test Table",
schema: {
name: {
- type: "string"
+ type: "string",
},
description: {
- type: "string"
- }
- }
- }]
+ type: "string",
+ },
+ },
+ },
+ ]
const components = {
- "@budibase/standard-components/container" : {
+ "@budibase/standard-components/container": {
props: {},
},
- "@budibase/standard-components/list" : {
+ "@budibase/standard-components/list": {
context: "table",
props: {
- table: "string"
+ table: "string",
},
},
- "@budibase/standard-components/input" : {
+ "@budibase/standard-components/input": {
bindable: "value",
props: {
- value: "string"
+ value: "string",
},
},
- "@budibase/standard-components/heading" : {
+ "@budibase/standard-components/heading": {
props: {
- text: "string"
+ text: "string",
},
},
}
return { screen, tables, components }
-
}
diff --git a/packages/server/src/utilities/appDirectoryTemplate/pages/main/page.json b/packages/server/src/utilities/appDirectoryTemplate/pages/main/page.json
index 916ad5d224..037c385f5c 100644
--- a/packages/server/src/utilities/appDirectoryTemplate/pages/main/page.json
+++ b/packages/server/src/utilities/appDirectoryTemplate/pages/main/page.json
@@ -1,9 +1,9 @@
{
- "title": "{{ name }}",
- "favicon": "./_shared/favicon.png",
- "stylesheets": [],
- "componentLibraries": ["@budibase/standard-components"],
- "props": {
+ "title": "{{ name }}",
+ "favicon": "./_shared/favicon.png",
+ "stylesheets": [],
+ "componentLibraries": ["@budibase/standard-components"],
+ "props": {
"_id": "private-master-root",
"_component": "@budibase/standard-components/container",
"_children": [
@@ -62,10 +62,11 @@
"_component": "##builtin/screenslot",
"_styles": {
"normal": {
- "padding": "0px",
- "align-items": "flex-start",
- "height": "100vh",
- "background-image": "None"
+ "flex": "1 1 auto",
+ "display": "flex",
+ "flex-direction": "column",
+ "justify-content": "flex-start",
+ "align-items": "stretch"
},
"hover": {},
"active": {},
@@ -84,10 +85,9 @@
"flex-direction": "column",
"align-items": "stretch",
"justify-content": "flex-start",
- "height": "",
- "max-width": "",
"margin-right": "auto",
- "margin-left": "auto"
+ "margin-left": "auto",
+ "min-height": "100%"
},
"selected": {}
},
@@ -95,6 +95,6 @@
"className": "",
"onLoad": []
},
- "_css": "",
- "uiFunctions": ""
+ "_css": "",
+ "uiFunctions": ""
}
diff --git a/packages/standard-components/src/DataGrid/Component.svelte b/packages/standard-components/src/DataGrid/Component.svelte
index b7a277435d..dd700c55f7 100644
--- a/packages/standard-components/src/DataGrid/Component.svelte
+++ b/packages/standard-components/src/DataGrid/Component.svelte
@@ -21,6 +21,11 @@
export let height = 500
export let pagination
+ // These can never change at runtime so don't need to be reactive
+ let canEdit = editable && datasource && datasource.type !== "view"
+ let canAddDelete = editable && datasource && datasource.type === "table"
+
+ let store = _bb.store
let dataLoaded = false
let data
let columnDefs
@@ -32,35 +37,42 @@
minWidth: 150,
filter: true,
},
- rowSelection: editable ? "multiple" : false,
- suppressRowClickSelection: !editable,
+ rowSelection: canEdit ? "multiple" : false,
+ suppressRowClickSelection: !canEdit,
paginationAutoPageSize: true,
pagination,
}
- let store = _bb.store
onMount(async () => {
- if (datasource.tableId) {
- const jsonTable = await _bb.api.get(`/api/tables/${datasource.tableId}`)
- table = await jsonTable.json()
- const { schema } = table
- if (!isEmpty(datasource)) {
- data = await fetchData(datasource, $store)
- columnDefs = Object.keys(schema).map((key, i) => {
- return {
- headerCheckboxSelection: i === 0 && editable,
- checkboxSelection: i === 0 && editable,
- valueSetter: setters.get(schema[key].type),
- headerName: key.charAt(0).toUpperCase() + key.slice(1),
- field: key,
- hide: shouldHideField(key),
- sortable: true,
- editable: editable,
- cellRenderer: getRenderer(schema[key], editable),
- autoHeight: true,
- }
- })
+ if (!isEmpty(datasource)) {
+ data = await fetchData(datasource, $store)
+ let schema = {}
+
+ // Get schema for datasource
+ // Views with "Calculate" applied provide their own schema.
+ // For everything else, use the tableId property to pull to table schema
+ if (datasource.schema) {
+ schema = datasource.schema
+ } else {
+ const jsonTable = await _bb.api.get(`/api/tables/${datasource.tableId}`)
+ table = await jsonTable.json()
+ schema = table.schema
}
+
+ columnDefs = Object.keys(schema).map((key, i) => {
+ return {
+ headerCheckboxSelection: i === 0 && canEdit,
+ checkboxSelection: i === 0 && canEdit,
+ valueSetter: setters.get(schema[key].type),
+ headerName: key.charAt(0).toUpperCase() + key.slice(1),
+ field: key,
+ hide: shouldHideField(key),
+ sortable: true,
+ editable: canEdit,
+ cellRenderer: getRenderer(schema[key], canEdit),
+ autoHeight: true,
+ }
+ })
dataLoaded = true
}
})
@@ -117,7 +129,7 @@
{#if dataLoaded}
- {#if editable}
+ {#if canAddDelete}
{#if selectedRows.length > 0}
diff --git a/packages/standard-components/src/DataTable.svelte b/packages/standard-components/src/DataTable.svelte
index 896ca6a5ca..bdb0c76183 100644
--- a/packages/standard-components/src/DataTable.svelte
+++ b/packages/standard-components/src/DataTable.svelte
@@ -12,7 +12,7 @@
export let color
export let stripeColor
export let borderColor
- export let datasource = {}
+ export let datasource
export let _bb
let data = []
@@ -35,14 +35,21 @@
const FETCH_TABLE_URL = `/api/tables/${tableId}`
const response = await _bb.api.get(FETCH_TABLE_URL)
const table = await response.json()
- schema = table.schema
+ return table.schema
}
onMount(async () => {
if (!isEmpty(datasource)) {
data = await fetchData(datasource, $store)
- if (data && data.length) {
- await fetchTable(data[0].tableId)
+
+ // Get schema for datasource
+ // Views with "Calculate" applied provide their own schema.
+ // For everything else, use the tableId property to pull to table schema
+ if (datasource.schema) {
+ schema = datasource.schema
+ headers = Object.keys(schema).filter(shouldDisplayField)
+ } else {
+ schema = await fetchTable(datasource.tableId)
headers = Object.keys(schema).filter(shouldDisplayField)
}
}
@@ -54,7 +61,6 @@
if (name === "type") return false
// tables are always tied to a single tableId, this is irrelevant
if (name === "tableId") return false
-
return true
}
@@ -95,11 +101,11 @@
{#each sorted as row (row._id)}
{#each headers as header}
- {#if schema[header]}
+ {#if schema[header] !== undefined}
- {#if schema[header].type === 'attachment'}
+ {#if schema[header] && schema[header].type === 'attachment'}
- {:else if schema[header].type === 'link'}
+ {:else if schema[header] && schema[header].type === 'link'}
{row[header] ? row[header].length : 0} related row(s) |
{:else}
{row[header] == null ? '' : row[header]} |
diff --git a/packages/standard-components/src/Form.svelte b/packages/standard-components/src/Form.svelte
index ed9a393440..29401e8ad4 100644
--- a/packages/standard-components/src/Form.svelte
+++ b/packages/standard-components/src/Form.svelte
@@ -14,7 +14,7 @@
let rowId
let errors = {}
- $: schema = $store.data && $store.data._table.schema
+ $: schema = $store.data && $store.data._table && $store.data._table.schema
$: fields = schema ? Object.keys(schema) : []
diff --git a/packages/standard-components/src/NewRow.svelte b/packages/standard-components/src/NewRow.svelte
index 817e07e0d0..e7446f9bdc 100644
--- a/packages/standard-components/src/NewRow.svelte
+++ b/packages/standard-components/src/NewRow.svelte
@@ -19,7 +19,7 @@
}
onMount(async () => {
- if (table) {
+ if (table && typeof table === "string") {
const tableObj = await fetchTable(table)
row.tableId = table
row._table = tableObj
diff --git a/packages/standard-components/src/fetchData.js b/packages/standard-components/src/fetchData.js
index 27f3fb9e12..68aca7eac6 100644
--- a/packages/standard-components/src/fetchData.js
+++ b/packages/standard-components/src/fetchData.js
@@ -6,16 +6,16 @@ export default async function fetchData(datasource, store) {
if (name) {
let rows = []
if (type === "table") {
- rows = await fetchTableData()
+ rows = fetchTableData()
} else if (type === "view") {
- rows = await fetchViewData()
+ rows = fetchViewData()
} else if (type === "link") {
- rows = await fetchLinkedRowsData()
+ rows = fetchLinkedRowsData()
}
// Fetch table schema so we can check for linked rows
if (rows && rows.length) {
- const table = await fetchTable(rows[0].tableId)
+ const table = await fetchTable()
const keys = Object.keys(table.schema)
rows.forEach(row => {
for (let key of keys) {
@@ -27,10 +27,12 @@ export default async function fetchData(datasource, store) {
}
return rows
+ } else {
+ return []
}
- async function fetchTable(id) {
- const FETCH_TABLE_URL = `/api/tables/${id}`
+ async function fetchTable() {
+ const FETCH_TABLE_URL = `/api/tables/${datasource.tableId}`
const response = await api.get(FETCH_TABLE_URL)
return await response.json()
}