diff --git a/lerna.json b/lerna.json index 03a6c0f4e6..dfd7c6494b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.0.76-alpha.5", + "version": "1.0.79-alpha.5", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 198c62df25..b317e37d21 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "1.0.76-alpha.5", + "version": "1.0.79-alpha.5", "description": "Budibase backend core libraries used in server and worker", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 064bc7a55a..ace578e6fc 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "1.0.76-alpha.5", + "version": "1.0.79-alpha.5", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -38,7 +38,7 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "^1.2.1", - "@budibase/string-templates": "^1.0.72-alpha.0", + "@budibase/string-templates": "^1.0.79-alpha.5", "@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/avatar": "^3.0.2", 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/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/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/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 23f3d9f7dd..6c85b96cc7 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "1.0.76-alpha.5", + "version": "1.0.79-alpha.5", "license": "GPL-3.0", "private": true, "scripts": { @@ -13,7 +13,7 @@ "cy:setup:ci": "node ./cypress/setup.js", "cy:open": "cypress open", "cy:run": "cypress run", - "cy:run:ci": "xvfb-run cypress run --headed --browser chrome --record", + "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", @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^1.0.76-alpha.5", - "@budibase/client": "^1.0.76-alpha.5", - "@budibase/frontend-core": "^1.0.76-alpha.5", - "@budibase/string-templates": "^1.0.76-alpha.5", + "@budibase/bbui": "^1.0.79-alpha.5", + "@budibase/client": "^1.0.79-alpha.5", + "@budibase/frontend-core": "^1.0.79-alpha.5", + "@budibase/string-templates": "^1.0.79-alpha.5", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index edb12c7e74..60cce2b1fd 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,40 @@ 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)}`, + 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")}`, + readableBinding: `${block._instanceName}.Selected rows`, + })) + ) + } + return bindings +} + /** * Gets all state bindings that are globally available. */ @@ -597,14 +633,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 +666,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} -
+
- +
{/each}
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 + }) + }
(setupToggled = !setupToggled)} + on:click={() => { + onSelect(block) + setupToggled = !setupToggled + }} quiet icon={setupToggled ? "ChevronDown" : "ChevronRight"} > Setup {#if !isTrigger} -
deleteStep()}> - +
+ {#if showBindingPicker} +
+ {#each schemaFields as [field, schema]} {#if !schema.autocolumn} - {#if schemaHasOptions(schema) && schema.type !== "array"} -