Merge branch 'master' of github.com:Budibase/budibase into chuckles-fixes
This commit is contained in:
commit
7ee64e8abd
|
@ -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: [],
|
||||
|
|
|
@ -12,13 +12,18 @@
|
|||
</script>
|
||||
|
||||
<input type="checkbox" class="checkbox" id="_checkbox" />
|
||||
<div on:click={handleChange}>
|
||||
<div class="checkbox-container" on:click={handleChange}>
|
||||
<div class="check-div" class:checked>
|
||||
<div class="tick_mark" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.checkbox-container {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,9 @@
|
|||
_id: "screenslot-placeholder",
|
||||
_component: "@budibase/standard-components/container",
|
||||
_styles: {
|
||||
normal: {},
|
||||
normal: {
|
||||
flex: "1 1 auto",
|
||||
},
|
||||
hover: {},
|
||||
active: {},
|
||||
selected: {},
|
||||
|
@ -46,6 +48,7 @@
|
|||
display: "flex",
|
||||
"flex-direction": "column",
|
||||
"align-items": "center",
|
||||
flex: "1 1 auto",
|
||||
},
|
||||
hover: {},
|
||||
active: {},
|
||||
|
|
|
@ -11,21 +11,6 @@ export default `<html>
|
|||
*, *:before, *:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.lay-__screenslot__text {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px 0;
|
||||
border: dashed 2px #ccc;
|
||||
font-family: sans-serif;
|
||||
font-size: 1.2rem;
|
||||
color: #999;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.container-screenslot-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -36,9 +21,8 @@ export default `<html>
|
|||
border-width: 1px;
|
||||
color: #000000;
|
||||
background: #fafafa;
|
||||
height: 94%;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.container-screenslot-placeholder span {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
|
|
|
@ -61,10 +61,10 @@
|
|||
sort: detailScreen.props._component,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return urls
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,28 +17,38 @@ 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")
|
||||
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()
|
||||
|
@ -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")
|
||||
expect(descriptionbinding_parent.readableBinding).toBe(
|
||||
"list-name.Test Table.description"
|
||||
)
|
||||
|
||||
const namebinding_own = contextBindings.find(b => b.runtimeBinding === "data.name")
|
||||
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")
|
||||
expect(namebinding_own.readableBinding).toBe(
|
||||
"child-list-name.Test Table.name"
|
||||
)
|
||||
|
||||
const descriptionbinding_own = contextBindings.find(b => b.runtimeBinding === "data.description")
|
||||
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 = [{
|
||||
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 }
|
||||
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
},
|
||||
|
|
|
@ -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)
|
||||
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 && editable,
|
||||
checkboxSelection: i === 0 && editable,
|
||||
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: editable,
|
||||
cellRenderer: getRenderer(schema[key], editable),
|
||||
editable: canEdit,
|
||||
cellRenderer: getRenderer(schema[key], canEdit),
|
||||
autoHeight: true,
|
||||
}
|
||||
})
|
||||
}
|
||||
dataLoaded = true
|
||||
}
|
||||
})
|
||||
|
@ -117,7 +129,7 @@
|
|||
|
||||
<div style="--grid-height: {height}px">
|
||||
{#if dataLoaded}
|
||||
{#if editable}
|
||||
{#if canAddDelete}
|
||||
<div class="controls">
|
||||
<CreateRowButton {_bb} {table} on:newRow={handleNewRow} />
|
||||
{#if selectedRows.length > 0}
|
||||
|
|
|
@ -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)}
|
||||
<tr>
|
||||
{#each headers as header}
|
||||
{#if schema[header]}
|
||||
{#if schema[header] !== undefined}
|
||||
<!-- Rudimentary solution for attachments on array given this entire table will be replaced by AG Grid -->
|
||||
{#if schema[header].type === 'attachment'}
|
||||
{#if schema[header] && schema[header].type === 'attachment'}
|
||||
<AttachmentList files={row[header]} />
|
||||
{:else if schema[header].type === 'link'}
|
||||
{:else if schema[header] && schema[header].type === 'link'}
|
||||
<td>{row[header] ? row[header].length : 0} related row(s)</td>
|
||||
{:else}
|
||||
<td>{row[header] == null ? '' : row[header]}</td>
|
||||
|
|
|
@ -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) : []
|
||||
</script>
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
}
|
||||
|
||||
onMount(async () => {
|
||||
if (table) {
|
||||
if (table && typeof table === "string") {
|
||||
const tableObj = await fetchTable(table)
|
||||
row.tableId = table
|
||||
row._table = tableObj
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue