@@ -25,18 +25,7 @@
diff --git a/packages/bbui/src/Form/Core/Checkbox.svelte b/packages/bbui/src/Form/Core/Checkbox.svelte
index a5b366c262..8384c8ca09 100644
--- a/packages/bbui/src/Form/Core/Checkbox.svelte
+++ b/packages/bbui/src/Form/Core/Checkbox.svelte
@@ -47,7 +47,9 @@
-
{text || ""}
+ {#if text}
+
{text}
+ {/if}
diff --git a/packages/bbui/src/Table/SelectEditRenderer.svelte b/packages/bbui/src/Table/SelectEditRenderer.svelte
index e7a70979a0..c6eafa3ed0 100644
--- a/packages/bbui/src/Table/SelectEditRenderer.svelte
+++ b/packages/bbui/src/Table/SelectEditRenderer.svelte
@@ -8,9 +8,21 @@
export let allowEditRows = false
-{#if allowSelectRows}
-
-{/if}
-{#if allowEditRows}
-
Edit
-{/if}
+
+ {#if allowSelectRows}
+
+ {/if}
+ {#if allowEditRows}
+
Edit
+ {/if}
+
+
+
diff --git a/packages/bbui/src/Table/Table.svelte b/packages/bbui/src/Table/Table.svelte
index e89b4e849a..c9d7f12339 100644
--- a/packages/bbui/src/Table/Table.svelte
+++ b/packages/bbui/src/Table/Table.svelte
@@ -5,6 +5,7 @@
import SelectEditRenderer from "./SelectEditRenderer.svelte"
import { cloneDeep, deepGet } from "../helpers"
import ProgressCircle from "../ProgressCircle/ProgressCircle.svelte"
+ import Checkbox from "../Form/Checkbox.svelte"
/**
* The expected schema is our normal couch schemas for our tables.
@@ -31,7 +32,6 @@
export let allowEditRows = true
export let allowEditColumns = true
export let selectedRows = []
- export let editColumnTitle = "Edit"
export let customRenderers = []
export let disableSorting = false
export let autoSortColumns = true
@@ -50,6 +50,8 @@
// Table state
let height = 0
let loaded = false
+ let checkboxStatus = false
+
$: schema = fixSchema(schema)
$: if (!loading) loaded = true
$: fields = getFields(schema, showAutoColumns, autoSortColumns)
@@ -67,6 +69,16 @@
$: showEditColumn = allowEditRows || allowSelectRows
$: cellStyles = computeCellStyles(schema)
+ // Deselect the "select all" checkbox when the user navigates to a new page
+ $: {
+ let checkRowCount = rows.filter(o1 =>
+ selectedRows.some(o2 => o1._id === o2._id)
+ )
+ if (checkRowCount.length === 0) {
+ checkboxStatus = false
+ }
+ }
+
const fixSchema = schema => {
let fixedSchema = {}
Object.entries(schema || {}).forEach(([fieldName, fieldSchema]) => {
@@ -197,13 +209,32 @@
if (!allowSelectRows) {
return
}
- if (selectedRows.includes(row)) {
- selectedRows = selectedRows.filter(selectedRow => selectedRow !== row)
+ if (selectedRows.some(selectedRow => selectedRow._id === row._id)) {
+ selectedRows = selectedRows.filter(
+ selectedRow => selectedRow._id !== row._id
+ )
} else {
selectedRows = [...selectedRows, row]
}
}
+ const toggleSelectAll = e => {
+ const select = !!e.detail
+ if (select) {
+ // Add any rows which are not already in selected rows
+ rows.forEach(row => {
+ if (selectedRows.findIndex(x => x._id === row._id) === -1) {
+ selectedRows.push(row)
+ }
+ })
+ } else {
+ // Remove any rows from selected rows that are in the current data set
+ selectedRows = selectedRows.filter(el =>
+ rows.every(f => f._id !== el._id)
+ )
+ }
+ }
+
const computeCellStyles = schema => {
let styles = {}
Object.keys(schema || {}).forEach(field => {
@@ -244,7 +275,14 @@
- {editColumnTitle || ""}
+ {#if allowSelectRows}
+
+ {:else}
+ Edit
+ {/if}
{/if}
{#each fields as field}
@@ -302,11 +340,16 @@
{#if showEditColumn}
{
+ toggleSelectRow(row)
+ e.stopPropagation()
+ }}
>
toggleSelectRow(row)}
+ selected={selectedRows.findIndex(
+ selectedRow => selectedRow._id === row._id
+ ) !== -1}
onEdit={e => editRow(e, row)}
{allowSelectRows}
{allowEditRows}
diff --git a/packages/builder/cypress.json b/packages/builder/cypress.json
index fb9953ae6c..edad1f39d3 100644
--- a/packages/builder/cypress.json
+++ b/packages/builder/cypress.json
@@ -1,9 +1,10 @@
{
- "baseUrl": "http://localhost:10001",
+ "baseUrl": "http://localhost:4100",
"video": false,
"projectId": "bmbemn",
"env": {
- "PORT": "10001",
+ "PORT": "4100",
+ "WORKER_PORT": "4200",
"JWT_SECRET": "test",
"HOST_IP": ""
}
diff --git a/packages/builder/cypress/integration/createAutomation.spec.js b/packages/builder/cypress/integration/createAutomation.spec.js
index e8892d16e2..b9355f7faf 100644
--- a/packages/builder/cypress/integration/createAutomation.spec.js
+++ b/packages/builder/cypress/integration/createAutomation.spec.js
@@ -33,7 +33,7 @@ filterTests(['smoke', 'all'], () => {
cy.get(".spectrum-Button--cta").click()
})
cy.contains("Setup").click()
- cy.get(".spectrum-Picker-label").click()
+ cy.get(".spectrum-Picker-label").eq(1).click()
cy.contains("dog").click()
cy.get(".spectrum-Textfield-input")
.first()
diff --git a/packages/builder/cypress/integration/createTable.spec.js b/packages/builder/cypress/integration/createTable.spec.js
index bac6806bcd..81b7c2f045 100644
--- a/packages/builder/cypress/integration/createTable.spec.js
+++ b/packages/builder/cypress/integration/createTable.spec.js
@@ -27,10 +27,13 @@ filterTests(["smoke", "all"], () => {
it("updates a column on the table", () => {
cy.get(".title").click()
cy.get(".spectrum-Table-editIcon > use").click()
- cy.get("input").eq(1).type("updated", { force: true })
+ cy.get(".modal-inner-wrapper").within(() => {
+
+ cy.get("input").eq(0).type("updated", { force: true })
// Unset table display column
cy.get(".spectrum-Switch-input").eq(1).click()
cy.contains("Save Column").click()
+ })
cy.contains("nameupdated ").should("contain", "nameupdated")
})
diff --git a/packages/builder/cypress/integration/datasources/rest.spec.js b/packages/builder/cypress/integration/datasources/rest.spec.js
index f39d174831..58ba74795a 100644
--- a/packages/builder/cypress/integration/datasources/rest.spec.js
+++ b/packages/builder/cypress/integration/datasources/rest.spec.js
@@ -1,43 +1,45 @@
import filterTests from "../../support/filterTests"
-filterTests(['smoke', 'all'], () => {
- context("REST Datasource Testing", () => {
- before(() => {
- cy.login()
- cy.createTestApp()
- })
-
- const datasource = "REST"
- const restUrl = "https://api.openbrewerydb.org/breweries"
-
- it("Should add REST data source with incorrect API", () => {
- // Select REST data source
- cy.selectExternalDatasource(datasource)
- // Enter incorrect api & attempt to send query
- cy.wait(500)
- cy.get(".spectrum-Button").contains("Add query").click({ force: true })
- cy.intercept('**/preview').as('queryError')
- cy.get("input").clear().type("random text")
- cy.get(".spectrum-Button").contains("Send").click({ force: true })
- // Intercept Request after button click & apply assertions
- cy.wait("@queryError")
- cy.get("@queryError").its('response.body')
- .should('have.property', 'message', 'Invalid URL: http://random text?')
- cy.get("@queryError").its('response.body')
- .should('have.property', 'status', 400)
- })
-
- it("should add and configure a REST datasource", () => {
- // Select REST datasource and create query
- cy.selectExternalDatasource(datasource)
- cy.wait(500)
- // createRestQuery confirms query creation
- cy.createRestQuery("GET", restUrl)
- // Confirm status code response within REST datasource
- cy.get(".spectrum-FieldLabel")
- .contains("Status")
- .children()
- .should('contain', 200)
- })
+filterTests(["smoke", "all"], () => {
+ context("REST Datasource Testing", () => {
+ before(() => {
+ cy.login()
+ cy.createTestApp()
})
+
+ const datasource = "REST"
+ const restUrl = "https://api.openbrewerydb.org/breweries"
+
+ it("Should add REST data source with incorrect API", () => {
+ // Select REST data source
+ cy.selectExternalDatasource(datasource)
+ // Enter incorrect api & attempt to send query
+ cy.wait(500)
+ cy.get(".spectrum-Button").contains("Add query").click({ force: true })
+ cy.intercept("**/preview").as("queryError")
+ cy.get("input").clear().type("random text")
+ cy.get(".spectrum-Button").contains("Send").click({ force: true })
+ // Intercept Request after button click & apply assertions
+ cy.wait("@queryError")
+ cy.get("@queryError")
+ .its("response.body")
+ .should("have.property", "message", "Invalid URL: http://random text?")
+ cy.get("@queryError")
+ .its("response.body")
+ .should("have.property", "status", 400)
+ })
+
+ it("should add and configure a REST datasource", () => {
+ // Select REST datasource and create query
+ cy.selectExternalDatasource(datasource)
+ cy.wait(500)
+ // createRestQuery confirms query creation
+ cy.createRestQuery("GET", restUrl, "/breweries")
+ // Confirm status code response within REST datasource
+ cy.get(".spectrum-FieldLabel")
+ .contains("Status")
+ .children()
+ .should("contain", 200)
+ })
+ })
})
diff --git a/packages/builder/cypress/integration/queryLevelTransformers.spec.js b/packages/builder/cypress/integration/queryLevelTransformers.spec.js
index d6d4278eb4..e96a6dba29 100644
--- a/packages/builder/cypress/integration/queryLevelTransformers.spec.js
+++ b/packages/builder/cypress/integration/queryLevelTransformers.spec.js
@@ -1,115 +1,139 @@
import filterTests from "../support/filterTests"
-filterTests(['smoke', 'all'], () => {
+filterTests(["smoke", "all"], () => {
context("Query Level Transformers", () => {
before(() => {
cy.login()
cy.deleteApp("Cypress Tests")
cy.createApp("Cypress Tests")
})
-
+
it("should write a transformer function", () => {
- // Add REST datasource - contains API for breweries
- const datasource = "REST"
- const restUrl = "https://api.openbrewerydb.org/breweries"
- cy.selectExternalDatasource(datasource)
- cy.createRestQuery("GET", restUrl)
- cy.get(".spectrum-Tabs-itemLabel").contains("Transformer").click()
- // Get Transformer Function from file
- cy.readFile("cypress/support/queryLevelTransformerFunction.js").then((transformerFunction) => {
+ // Add REST datasource - contains API for breweries
+ const datasource = "REST"
+ const restUrl = "https://api.openbrewerydb.org/breweries"
+ cy.selectExternalDatasource(datasource)
+ cy.createRestQuery("GET", restUrl, "/breweries")
+ cy.get(".spectrum-Tabs-itemLabel").contains("Transformer").click()
+ // Get Transformer Function from file
+ cy.readFile("cypress/support/queryLevelTransformerFunction.js").then(
+ transformerFunction => {
cy.get(".CodeMirror textarea")
- // Highlight current text and overwrite with file contents
- .type(Cypress.platform === 'darwin' ? '{cmd}a' : '{ctrl}a', { force: true })
- .type(transformerFunction, { parseSpecialCharSequences: false })
- })
- // Send Query
- cy.intercept('**/queries/preview').as('query')
- cy.get(".spectrum-Button").contains("Send").click({ force: true })
- cy.wait("@query")
- // Assert against Status Code, body, & body rows
- cy.get("@query").its('response.statusCode')
- .should('eq', 200)
- cy.get("@query").its('response.body').should('not.be.empty')
- cy.get("@query").its('response.body.rows').should('not.be.empty')
- })
-
+ // Highlight current text and overwrite with file contents
+ .type(Cypress.platform === "darwin" ? "{cmd}a" : "{ctrl}a", {
+ force: true,
+ })
+ .type(transformerFunction, { parseSpecialCharSequences: false })
+ }
+ )
+ // Send Query
+ cy.intercept("**/queries/preview").as("query")
+ cy.get(".spectrum-Button").contains("Send").click({ force: true })
+ cy.wait("@query")
+ // Assert against Status Code, body, & body rows
+ cy.get("@query").its("response.statusCode").should("eq", 200)
+ cy.get("@query").its("response.body").should("not.be.empty")
+ cy.get("@query").its("response.body.rows").should("not.be.empty")
+ })
+
it("should add data to the previous query", () => {
// Add REST datasource - contains API for breweries
const datasource = "REST"
const restUrl = "https://api.openbrewerydb.org/breweries"
cy.selectExternalDatasource(datasource)
- cy.createRestQuery("GET", restUrl)
+ cy.createRestQuery("GET", restUrl, "/breweries")
cy.get(".spectrum-Tabs-itemLabel").contains("Transformer").click()
// Get Transformer Function with Data from file
- cy.readFile("cypress/support/queryLevelTransformerFunctionWithData.js").then((transformerFunction) => {
+ cy.readFile(
+ "cypress/support/queryLevelTransformerFunctionWithData.js"
+ ).then(transformerFunction => {
//console.log(transformerFunction[1])
cy.get(".CodeMirror textarea")
- // Highlight current text and overwrite with file contents
- .type(Cypress.platform === 'darwin' ? '{cmd}a' : '{ctrl}a', { force: true })
- .type(transformerFunction, { parseSpecialCharSequences: false })
+ // Highlight current text and overwrite with file contents
+ .type(Cypress.platform === "darwin" ? "{cmd}a" : "{ctrl}a", {
+ force: true,
+ })
+ .type(transformerFunction, { parseSpecialCharSequences: false })
})
// Send Query
- cy.intercept('**/queries/preview').as('query')
+ cy.intercept("**/queries/preview").as("query")
cy.get(".spectrum-Button").contains("Send").click({ force: true })
cy.wait("@query")
// Assert against Status Code, body, & body rows
- cy.get("@query").its('response.statusCode')
- .should('eq', 200)
- cy.get("@query").its('response.body').should('not.be.empty')
- cy.get("@query").its('response.body.rows').should('not.be.empty')
+ cy.get("@query").its("response.statusCode").should("eq", 200)
+ cy.get("@query").its("response.body").should("not.be.empty")
+ cy.get("@query").its("response.body.rows").should("not.be.empty")
})
-
+
it("should run an invalid query within the transformer section", () => {
// Add REST datasource - contains API for breweries
const datasource = "REST"
const restUrl = "https://api.openbrewerydb.org/breweries"
cy.selectExternalDatasource(datasource)
- cy.createRestQuery("GET", restUrl)
+ cy.createRestQuery("GET", restUrl, "/breweries")
cy.get(".spectrum-Tabs-itemLabel").contains("Transformer").click()
// Clear the code box and add "test"
cy.get(".CodeMirror textarea")
- .type(Cypress.platform === 'darwin' ? '{cmd}a' : '{ctrl}a', { force: true })
- .type("test")
+ .type(Cypress.platform === "darwin" ? "{cmd}a" : "{ctrl}a", {
+ force: true,
+ })
+ .type("test")
// Run Query and intercept
- cy.intercept('**/preview').as('queryError')
+ cy.intercept("**/preview").as("queryError")
cy.get(".spectrum-Button").contains("Send").click({ force: true })
cy.wait("@queryError")
cy.wait(500)
// Assert against message and status for the query error
- cy.get("@queryError").its('response.body').should('have.property', 'message', "test is not defined")
- cy.get("@queryError").its('response.body').should('have.property', 'status', 400)
+ cy.get("@queryError")
+ .its("response.body")
+ .should("have.property", "message", "test is not defined")
+ cy.get("@queryError")
+ .its("response.body")
+ .should("have.property", "status", 400)
})
-
+
xit("should run an invalid query via POST request", () => {
// POST request with transformer as null
- cy.request({method: 'POST',
- url: `${Cypress.config().baseUrl}/api/queries/`,
- body: {fields : {"headers":{},"queryString":null,"path":null},
- parameters : [],
- schema : {},
- name : "test",
- queryVerb : "read",
- transformer : null,
- datasourceId: "test"},
- // Expected 400 error - Transformer must be a string
- failOnStatusCode: false}).then((response) => {
+ cy.request({
+ method: "POST",
+ url: `${Cypress.config().baseUrl}/api/queries/`,
+ body: {
+ fields: { headers: {}, queryString: null, path: null },
+ parameters: [],
+ schema: {},
+ name: "test",
+ queryVerb: "read",
+ transformer: null,
+ datasourceId: "test",
+ },
+ // Expected 400 error - Transformer must be a string
+ failOnStatusCode: false,
+ }).then(response => {
expect(response.status).to.equal(400)
- expect(response.body.message).to.include('Invalid body - "transformer" must be a string')
+ expect(response.body.message).to.include(
+ 'Invalid body - "transformer" must be a string'
+ )
})
})
-
+
xit("should run an empty query", () => {
// POST request with Transformer as an empty string
- cy.request({method: 'POST',
- url: `${Cypress.config().baseUrl}/api/queries/preview`,
- body: {fields : {"headers":{},"queryString":null,"path":null},
- queryVerb : "read",
- transformer : "",
- datasourceId: "test"},
- // Expected 400 error - Transformer is not allowed to be empty
- failOnStatusCode: false}).then((response) => {
+ cy.request({
+ method: "POST",
+ url: `${Cypress.config().baseUrl}/api/queries/preview`,
+ body: {
+ fields: { headers: {}, queryString: null, path: null },
+ queryVerb: "read",
+ transformer: "",
+ datasourceId: "test",
+ },
+ // Expected 400 error - Transformer is not allowed to be empty
+ failOnStatusCode: false,
+ }).then(response => {
expect(response.status).to.equal(400)
- expect(response.body.message).to.include('Invalid body - "transformer" is not allowed to be empty')
+ expect(response.body.message).to.include(
+ 'Invalid body - "transformer" is not allowed to be empty'
+ )
})
})
})
diff --git a/packages/builder/cypress/setup.js b/packages/builder/cypress/setup.js
index ca5a65c7f5..e19c931ed9 100644
--- a/packages/builder/cypress/setup.js
+++ b/packages/builder/cypress/setup.js
@@ -4,17 +4,17 @@ const path = require("path")
const tmpdir = path.join(require("os").tmpdir(), ".budibase")
// normal development system
-const WORKER_PORT = "10002"
-const MAIN_PORT = cypressConfig.env.PORT
+const SERVER_PORT = cypressConfig.env.PORT
+const WORKER_PORT = cypressConfig.env.WORKER_PORT
+
process.env.BUDIBASE_API_KEY = "6BE826CB-6B30-4AEC-8777-2E90464633DE"
process.env.NODE_ENV = "cypress"
process.env.ENABLE_ANALYTICS = "false"
-process.env.PORT = MAIN_PORT
process.env.JWT_SECRET = cypressConfig.env.JWT_SECRET
process.env.COUCH_URL = `leveldb://${tmpdir}/.data/`
process.env.SELF_HOSTED = 1
-process.env.WORKER_URL = "http://localhost:10002/"
-process.env.APPS_URL = `http://localhost:${MAIN_PORT}/`
+process.env.WORKER_URL = `http://localhost:${WORKER_PORT}/`
+process.env.APPS_URL = `http://localhost:${SERVER_PORT}/`
process.env.MINIO_URL = `http://localhost:4004`
process.env.MINIO_ACCESS_KEY = "budibase"
process.env.MINIO_SECRET_KEY = "budibase"
@@ -33,11 +33,14 @@ exports.run = (
// require("dotenv").config({ path: resolve(dir, ".env") })
// don't make this a variable or top level require
// it will cause environment module to be loaded prematurely
- require(serverLoc)
+
+ // override the port with the worker port temporarily
process.env.PORT = WORKER_PORT
require(workerLoc)
- // reload main port for rest of system
- process.env.PORT = MAIN_PORT
+
+ // override the port with the server port
+ process.env.PORT = SERVER_PORT
+ require(serverLoc)
}
if (require.main === module) {
diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js
index 40fe6706c9..ede1038a58 100644
--- a/packages/builder/cypress/support/commands.js
+++ b/packages/builder/cypress/support/commands.js
@@ -39,7 +39,7 @@ Cypress.Commands.add("createApp", name => {
cy.get(".spectrum-Modal").within(() => {
cy.get("input").eq(0).type(name).should("have.value", name).blur()
cy.get(".spectrum-ButtonGroup").contains("Create app").click()
- cy.wait(5000)
+ cy.wait(10000)
})
cy.createTable("Cypress Tests", true)
})
@@ -116,10 +116,10 @@ Cypress.Commands.add("createTestTableWithData", () => {
Cypress.Commands.add("createTable", (tableName, initialTable) => {
if (!initialTable) {
cy.navigateToDataSection()
- cy.get(".add-button").click()
+ cy.get(`[data-cy="new-table"]`).click()
}
- cy.wait(7000)
- cy.get(".spectrum-Modal")
+ cy.wait(5000)
+ cy.get(".spectrum-Dialog-grid")
.contains("Budibase DB")
.click({ force: true })
.then(() => {
@@ -172,17 +172,19 @@ Cypress.Commands.add("addRow", values => {
Cypress.Commands.add("addRowMultiValue", values => {
cy.contains("Create row").click()
- cy.get(".spectrum-Form-itemField")
- .click()
- .then(() => {
- cy.get(".spectrum-Popover").within(() => {
- for (let i = 0; i < values.length; i++) {
- cy.get(".spectrum-Menu-item").eq(i).click()
- }
+ cy.get(".spectrum-Modal").within(() => {
+ cy.get(".spectrum-Form-itemField")
+ .click()
+ .then(() => {
+ cy.get(".spectrum-Popover").within(() => {
+ for (let i = 0; i < values.length; i++) {
+ cy.get(".spectrum-Menu-item").eq(i).click()
+ }
+ })
+ cy.get(".spectrum-Dialog-grid").click("top")
+ cy.get(".spectrum-ButtonGroup").contains("Create").click()
})
- cy.get(".spectrum-Dialog-grid").click("top")
- cy.get(".spectrum-ButtonGroup").contains("Create").click()
- })
+ })
})
Cypress.Commands.add("createUser", email => {
@@ -435,7 +437,7 @@ Cypress.Commands.add("addDatasourceConfig", (datasource, skipFetch) => {
}
})
-Cypress.Commands.add("createRestQuery", (method, restUrl) => {
+Cypress.Commands.add("createRestQuery", (method, restUrl, queryPrettyName) => {
// addExternalDatasource should be called prior to this
// Configures REST datasource & sends query
cy.wait(1000)
@@ -450,5 +452,5 @@ Cypress.Commands.add("createRestQuery", (method, restUrl) => {
cy.get(".spectrum-Button").contains("Save").click({ force: true })
cy.get(".hierarchy-items-container")
.should("contain", method)
- .and("contain", restUrl)
+ .and("contain", queryPrettyName)
})
diff --git a/packages/builder/package.json b/packages/builder/package.json
index bf8e865584..e6ba5875f2 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
- "version": "1.0.76-alpha.2",
+ "version": "1.0.81-alpha.0",
"license": "GPL-3.0",
"private": true,
"scripts": {
@@ -11,12 +11,13 @@
"rollup": "rollup -c -w",
"cy:setup": "ts-node ./cypress/ts/setup.ts",
"cy:setup:ci": "node ./cypress/setup.js",
- "cy:run": "xvfb-run cypress run --headed --browser chrome",
"cy:open": "cypress open",
- "cy:run:ci": "cypress run --record",
- "cy:test": "start-server-and-test cy:setup http://localhost:10001/builder cy:run",
- "cy:ci": "start-server-and-test cy:setup:ci http://localhost:10001/builder cy:run",
- "cy:debug": "start-server-and-test cy:setup http://localhost:10001/builder cy:open"
+ "cy:run": "cypress run",
+ "cy:run:ci": "xvfb-run cypress run --headed --browser chrome",
+ "cy:test": "start-server-and-test cy:setup http://localhost:4100/builder cy:run",
+ "cy:ci": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci",
+ "cy:debug": "start-server-and-test cy:setup http://localhost:4100/builder cy:open",
+ "cy:debug:ci": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:open"
},
"jest": {
"globals": {
@@ -64,10 +65,10 @@
}
},
"dependencies": {
- "@budibase/bbui": "^1.0.76-alpha.2",
- "@budibase/client": "^1.0.76-alpha.2",
- "@budibase/frontend-core": "^1.0.76-alpha.2",
- "@budibase/string-templates": "^1.0.76-alpha.2",
+ "@budibase/bbui": "^1.0.81-alpha.0",
+ "@budibase/client": "^1.0.81-alpha.0",
+ "@budibase/frontend-core": "^1.0.81-alpha.0",
+ "@budibase/string-templates": "^1.0.81-alpha.0",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
diff --git a/packages/builder/src/App.svelte b/packages/builder/src/App.svelte
index 97b7d70fad..0fb0fe59d5 100644
--- a/packages/builder/src/App.svelte
+++ b/packages/builder/src/App.svelte
@@ -8,8 +8,10 @@
const queryHandler = { parse, stringify }
-
+
+
+
diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js
index edb12c7e74..5b9bebcbf5 100644
--- a/packages/builder/src/builderStore/dataBinding.js
+++ b/packages/builder/src/builderStore/dataBinding.js
@@ -32,12 +32,14 @@ export const getBindableProperties = (asset, componentId) => {
const urlBindings = getUrlBindings(asset)
const deviceBindings = getDeviceBindings()
const stateBindings = getStateBindings()
+ const selectedRowsBindings = getSelectedRowsBindings(asset)
return [
...contextBindings,
...urlBindings,
...stateBindings,
...userBindings,
...deviceBindings,
+ ...selectedRowsBindings,
]
}
@@ -315,6 +317,44 @@ const getDeviceBindings = () => {
return bindings
}
+/**
+ * Gets all selected rows bindings for tables in the current asset.
+ */
+const getSelectedRowsBindings = asset => {
+ let bindings = []
+ if (get(store).clientFeatures?.rowSelection) {
+ // Add bindings for table components
+ let tables = findAllMatchingComponents(asset?.props, component =>
+ component._component.endsWith("table")
+ )
+ const safeState = makePropSafe("rowSelection")
+ bindings = bindings.concat(
+ tables.map(table => ({
+ type: "context",
+ runtimeBinding: `${safeState}.${makePropSafe(table._id)}.${makePropSafe(
+ "selectedRows"
+ )}`,
+ readableBinding: `${table._instanceName}.Selected rows`,
+ }))
+ )
+
+ // Add bindings for table blocks
+ let tableBlocks = findAllMatchingComponents(asset?.props, component =>
+ component._component.endsWith("tableblock")
+ )
+ bindings = bindings.concat(
+ tableBlocks.map(block => ({
+ type: "context",
+ runtimeBinding: `${safeState}.${makePropSafe(
+ block._id + "-table"
+ )}.${makePropSafe("selectedRows")}`,
+ readableBinding: `${block._instanceName}.Selected rows`,
+ }))
+ )
+ }
+ return bindings
+}
+
/**
* Gets all state bindings that are globally available.
*/
@@ -597,14 +637,9 @@ const buildFormSchema = component => {
* in the app.
*/
export const getAllStateVariables = () => {
- // Get all component containing assets
- let allAssets = []
- allAssets = allAssets.concat(get(store).layouts || [])
- allAssets = allAssets.concat(get(store).screens || [])
-
// Find all button action settings in all components
let eventSettings = []
- allAssets.forEach(asset => {
+ getAllAssets().forEach(asset => {
findAllMatchingComponents(asset.props, component => {
const settings = getComponentSettings(component._component)
settings
@@ -635,6 +670,15 @@ export const getAllStateVariables = () => {
return Array.from(bindingSet)
}
+export const getAllAssets = () => {
+ // Get all component containing assets
+ let allAssets = []
+ allAssets = allAssets.concat(get(store).layouts || [])
+ allAssets = allAssets.concat(get(store).screens || [])
+
+ return allAssets
+}
+
/**
* Recurses the input object to remove any instances of bindings.
*/
diff --git a/packages/builder/src/builderStore/store/automation/index.js b/packages/builder/src/builderStore/store/automation/index.js
index b901a71cb1..84e6033439 100644
--- a/packages/builder/src/builderStore/store/automation/index.js
+++ b/packages/builder/src/builderStore/store/automation/index.js
@@ -57,6 +57,7 @@ const automationActions = store => ({
return state
})
},
+
save: async automation => {
const response = await API.updateAutomation(automation)
store.update(state => {
@@ -130,6 +131,12 @@ const automationActions = store => ({
name: block.name,
})
},
+ toggleFieldControl: value => {
+ store.update(state => {
+ state.selectedBlock.rowControl = value
+ return state
+ })
+ },
deleteAutomationBlock: block => {
store.update(state => {
const idx =
diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js
index 9ce66db3c0..d8118c9c60 100644
--- a/packages/builder/src/builderStore/store/frontend.js
+++ b/packages/builder/src/builderStore/store/frontend.js
@@ -41,6 +41,7 @@ const INITIAL_FRONTEND_STATE = {
intelligentLoading: false,
deviceAwareness: false,
state: false,
+ rowSelection: false,
customThemes: false,
devicePreview: false,
messagePassing: false,
diff --git a/packages/builder/src/components/automation/AutomationBuilder/AutomationBuilder.svelte b/packages/builder/src/components/automation/AutomationBuilder/AutomationBuilder.svelte
index 7ce77a58e3..e852ee1a0d 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/AutomationBuilder.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/AutomationBuilder.svelte
@@ -3,14 +3,8 @@
import Flowchart from "./FlowChart/FlowChart.svelte"
$: automation = $automationStore.selectedAutomation?.automation
- function onSelect(block) {
- automationStore.update(state => {
- state.selectedBlock = block
- return state
- })
- }
{#if automation}
-
+
{/if}
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
index 777fcd710a..ca04fed8df 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte
@@ -14,7 +14,7 @@
} from "@budibase/bbui"
export let automation
- export let onSelect
+
let testDataModal
let blocks
let confirmDeleteDialog
@@ -45,7 +45,7 @@
{automation.name}
-
+
diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte
index f13a827f31..69dd67724a 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte
@@ -10,6 +10,7 @@
Button,
StatusLight,
ActionButton,
+ Select,
notifications,
} from "@budibase/bbui"
import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte"
@@ -18,7 +19,6 @@
import ActionModal from "./ActionModal.svelte"
import { externalActions } from "./ExternalActions"
- export let onSelect
export let block
export let testDataModal
let selected
@@ -28,6 +28,10 @@
let setupToggled
let blockComplete
+ $: rowControl = $automationStore.selectedAutomation.automation.rowControl
+ $: showBindingPicker =
+ block.stepId === "CREATE_ROW" || block.stepId === "UPDATE_ROW"
+
$: testResult = $automationStore.selectedAutomation.testResults?.steps.filter(
step => (block.id ? step.id === block.id : step.stepId === block.stepId)
)
@@ -44,12 +48,6 @@
$automationStore.selectedAutomation?.automation?.definition?.steps.length +
1
- // Logic for hiding / showing the add button.first we check if it has a child
- // then we check to see whether its inputs have been commpleted
- $: disableAddButton = isTrigger
- ? $automationStore.selectedAutomation?.automation?.definition?.steps
- .length > 0
- : !isTrigger && steps.length - blockIdx > 1
$: hasCompletedInputs = Object.keys(
block.schema?.inputs?.properties || {}
).every(x => block?.inputs[x])
@@ -64,6 +62,26 @@
notifications.error("Error saving notification")
}
}
+ function toggleFieldControl(evt) {
+ onSelect(block)
+ let rowControl
+ if (evt.detail === "Use values") {
+ rowControl = false
+ } else {
+ rowControl = true
+ }
+ automationStore.actions.toggleFieldControl(rowControl)
+ automationStore.actions.save(
+ $automationStore.selectedAutomation?.automation
+ )
+ }
+
+ async function onSelect(block) {
+ await automationStore.update(state => {
+ state.selectedBlock = block
+ return state
+ })
+ }