diff --git a/.DS_Store b/.DS_Store
index 363dbb1c4c..b16eb9ec68 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/packages/builder/assets/remixicon.woff b/packages/builder/assets/remixicon.woff
new file mode 100644
index 0000000000..62a756bd30
Binary files /dev/null and b/packages/builder/assets/remixicon.woff differ
diff --git a/packages/builder/assets/remixicon.woff2 b/packages/builder/assets/remixicon.woff2
new file mode 100644
index 0000000000..89a0b99ec6
Binary files /dev/null and b/packages/builder/assets/remixicon.woff2 differ
diff --git a/packages/builder/cypress/integration/createAutomation.spec.js b/packages/builder/cypress/integration/createAutomation.spec.js
index 67132c736b..c2a22c4f9b 100644
--- a/packages/builder/cypress/integration/createAutomation.spec.js
+++ b/packages/builder/cypress/integration/createAutomation.spec.js
@@ -54,7 +54,7 @@ context("Create a automation", () => {
})
it("should add row when a new row is added", () => {
- cy.contains("backend").click()
+ cy.contains("data").click()
cy.addRow(["Rover", 15])
cy.reload()
cy.contains("goodboy").should("have.text", "goodboy")
diff --git a/packages/builder/cypress/integration/createTable.spec.js b/packages/builder/cypress/integration/createTable.spec.js
index 16ee67cc5b..0715d61c0b 100644
--- a/packages/builder/cypress/integration/createTable.spec.js
+++ b/packages/builder/cypress/integration/createTable.spec.js
@@ -22,8 +22,10 @@ context("Create a Table", () => {
})
it("updates a column on the table", () => {
- cy.contains("name").click()
- cy.get("[data-cy='edit-column-header']").click()
+ cy.contains("header", "name")
+ .trigger("mouseover")
+ .find(".ri-pencil-line")
+ .click({ force: true })
cy.get(".actions input")
.first()
.type("updated")
@@ -34,24 +36,27 @@ context("Create a Table", () => {
})
it("edits a row", () => {
- cy.get("tbody .ri-more-line").click()
- cy.get("[data-cy=edit-row]").click()
+ cy.get("button").contains("Edit").click()
cy.get(".modal input").type("Updated")
cy.contains("Save").click()
cy.contains("RoverUpdated").should("have.text", "RoverUpdated")
})
it("deletes a row", () => {
- cy.get("tbody .ri-more-line").click()
- cy.get("[data-cy=delete-row]").click()
- cy.contains("Delete Row").click()
+ cy.get(".ag-checkbox-input").check({ force: true })
+ cy.contains("Delete 1 row(s)").click()
+ cy.get(".modal").contains("Delete").click()
cy.contains("RoverUpdated").should("not.exist")
})
it("deletes a column", () => {
- cy.contains("name").click()
- cy.get("[data-cy='delete-column-header']").click()
- cy.contains("Delete Column").click()
+ cy
+ .contains("header", "name")
+ .trigger("mouseover")
+ .find(".ri-pencil-line").click({ force: true })
+ cy.contains("Delete").click()
+ cy.wait(50)
+ cy.get(".buttons").contains("Delete").click()
cy.contains("nameupdated").should("not.exist")
})
diff --git a/packages/builder/cypress/integration/createView.spec.js b/packages/builder/cypress/integration/createView.spec.js
index 697bc35143..ca0dd60f8b 100644
--- a/packages/builder/cypress/integration/createView.spec.js
+++ b/packages/builder/cypress/integration/createView.spec.js
@@ -23,10 +23,10 @@ context("Create a View", () => {
cy.contains("Save View").click()
})
cy.get(".title").contains("Test View")
- cy.get("thead th div").should($headers => {
+ cy.get("[data-cy=table-header]").then($headers => {
expect($headers).to.have.length(3)
- const headers = $headers.map((i, header) => Cypress.$(header).text())
- expect(headers.get()).to.deep.eq(["group", "age", "rating"])
+ const headers = Array.from($headers).map(header => header.textContent.trim())
+ expect(headers).to.deep.eq(["group", "age", "rating"])
})
})
@@ -41,17 +41,18 @@ context("Create a View", () => {
.find("select")
.eq(1)
.select("More Than")
- cy.get("input").type(18)
+ cy.get(".menu-container").find("input").type(18)
cy.contains("Save").click()
- cy.get("tbody tr").should($values => {
+ cy.get("[role=rowgroup] .ag-row").get($values => {
expect($values).to.have.length(5)
})
})
it("creates a stats calculation view based on age", () => {
+ // Required due to responsive bug with ag grid in cypress
+ cy.viewport("macbook-15")
+
cy.contains("Calculate").click()
- // we may reinstate this - have commented this dropdown for now as there is only one option
- //cy.get(".menu-container").find("select").first().select("Statistics")
cy.get(".menu-container")
.find("select")
.eq(0)
@@ -62,10 +63,11 @@ context("Create a View", () => {
.eq(1)
.select("age")
cy.contains("Save").click()
- cy.get("thead th div").should($headers => {
+ cy.get(".ag-center-cols-viewport").scrollTo("100%")
+ cy.get("[data-cy=table-header]").then($headers => {
expect($headers).to.have.length(7)
- const headers = $headers.map((i, header) => Cypress.$(header).text())
- expect(headers.get()).to.deep.eq([
+ const headers = Array.from($headers).map(header => header.textContent.trim())
+ expect(headers).to.deep.eq([
"field",
"sum",
"min",
@@ -75,9 +77,9 @@ context("Create a View", () => {
"avg",
])
})
- cy.get("tbody td").should($values => {
- const values = $values.map((i, value) => Cypress.$(value).text())
- expect(values.get()).to.deep.eq([
+ cy.get(".ag-cell").then($values => {
+ const values = Array.from($values).map(header => header.textContent.trim())
+ expect(values).to.deep.eq([
"age",
"155",
"20",
@@ -90,18 +92,22 @@ context("Create a View", () => {
})
it("groups the view by group", () => {
+ // Required due to responsive bug with ag grid in cypress
+ cy.viewport("macbook-15")
+
cy.contains("Group By").click()
cy.get("select").select("group")
cy.contains("Save").click()
+ cy.get(".ag-center-cols-viewport").scrollTo("100%")
cy.contains("Students").should("be.visible")
cy.contains("Teachers").should("be.visible")
- cy.get("tbody tr")
- .first()
- .find("td")
- .should($values => {
- const values = $values.map((i, value) => Cypress.$(value).text())
- expect(values.get()).to.deep.eq([
+ cy
+ .get(".ag-row[row-index=0]")
+ .find(".ag-cell")
+ .then($values => {
+ const values = Array.from($values).map(value => value.textContent)
+ expect(values).to.deep.eq([
"Students",
"70",
"20",
diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js
index ece9565e18..b2a41d9d41 100644
--- a/packages/builder/cypress/support/commands.js
+++ b/packages/builder/cypress/support/commands.js
@@ -79,11 +79,13 @@ Cypress.Commands.add("createTable", tableName => {
Cypress.Commands.add("addColumn", (tableName, columnName, type) => {
// Select Table
- cy.contains(tableName).click()
+ cy.get(".root")
+ .contains(tableName)
+ .click()
cy.contains("Create New Column").click()
// Configure column
- cy.get(".menu-container").within(() => {
+ cy.get(".actions").within(() => {
cy.get("input")
.first()
.type(columnName)
@@ -149,7 +151,7 @@ Cypress.Commands.add("addButtonComponent", () => {
})
Cypress.Commands.add("navigateToFrontend", () => {
- cy.contains("frontend").click()
+ cy.contains("design").click()
})
Cypress.Commands.add("createScreen", (screenName, route) => {
diff --git a/packages/builder/package.json b/packages/builder/package.json
index cc37af2b63..671aaaa5c8 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -66,6 +66,7 @@
"@budibase/bbui": "^1.47.0",
"@budibase/client": "^0.2.6",
"@budibase/colorpicker": "^1.0.1",
+ "@budibase/svelte-ag-grid": "^0.0.16",
"@fortawesome/fontawesome-free": "^5.14.0",
"@sentry/browser": "5.19.1",
"@svelteschool/svelte-forms": "^0.7.0",
@@ -76,6 +77,7 @@
"lodash": "^4.17.13",
"mustache": "^4.0.1",
"posthog-js": "1.4.5",
+ "remixicon": "^2.5.0",
"shortid": "^2.2.15",
"svelte-loading-spinners": "^0.1.1",
"svelte-portal": "^0.1.0",
diff --git a/packages/builder/rollup.config.js b/packages/builder/rollup.config.js
index af51739200..b7c68cbaae 100644
--- a/packages/builder/rollup.config.js
+++ b/packages/builder/rollup.config.js
@@ -178,6 +178,10 @@ export default {
src: "node_modules/@budibase/bbui/dist/bbui.css",
dest: outputpath,
},
+ {
+ src: "node_modules/remixicon/fonts/*",
+ dest: outputpath,
+ },
],
}),
diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte
index 9a5eaef83a..8d72c12576 100644
--- a/packages/builder/src/components/backend/DataTable/DataTable.svelte
+++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte
@@ -12,6 +12,7 @@
$: title = $backendUiStore.selectedTable.name
$: schema = $backendUiStore.selectedTable.schema
+ $: tableId = $backendUiStore.selectedTable._id
$: tableView = {
schema,
name: $backendUiStore.selectedView.name,
diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte
index 3fa42aeb7c..8dd25da3cd 100644
--- a/packages/builder/src/components/backend/DataTable/Table.svelte
+++ b/packages/builder/src/components/backend/DataTable/Table.svelte
@@ -1,130 +1,132 @@
-
- {title}
- {#if loading}
-
-
-
- {/if}
-
+
{title}
+ {#if selectedRows.length > 0}
+
+ {/if}
-
-
-
- {#if allowEditing}
-
- {/if}
- {#each columns as header}
-
- {#if allowEditing}
-
- {:else}
-
- {/if}
- |
- {/each}
-
-
-
- {#if paginatedData.length === 0}
- {#if allowEditing}
- No data. |
- {/if}
- {#each columns as header, idx}
-
- {#if idx === 0 && !allowEditing}No data.{/if}
- |
- {/each}
- {/if}
- {#each paginatedData as row}
-
- {#if allowEditing}
-
-
- |
- {/if}
- {#each columns as header}
-
- {#if schema[header].type === 'link'}
- selectRelationship(row, header)}>
- {row[header] ? row[header].length : 0}
- related row(s)
-
- {:else if schema[header].type === 'attachment'}
-
- {:else}{getOr('', header, row)}{/if}
- |
- {/each}
-
- {/each}
-
-
-
+ {columnDefs}
+ {loading}
+ on:select={({ detail }) => (selectedRows = detail)} />
diff --git a/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte b/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte
new file mode 100644
index 0000000000..9588b34dd7
--- /dev/null
+++ b/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte
@@ -0,0 +1,125 @@
+
+
+ (hovered = true)}
+ on:mouseleave={() => (hovered = false)}>
+
+ {displayName}
+
+
+
+
+
+
+
+
+ {#if editable && hovered}
+
+
+
+ {/if}
+
+
+
+
+
+
+
diff --git a/packages/builder/src/components/backend/DataTable/TableHeader/index.js b/packages/builder/src/components/backend/DataTable/TableHeader/index.js
new file mode 100644
index 0000000000..538681a3c1
--- /dev/null
+++ b/packages/builder/src/components/backend/DataTable/TableHeader/index.js
@@ -0,0 +1,35 @@
+import TableHeader from "./TableHeader.svelte"
+
+export default class TableHeaderWrapper {
+ constructor() {}
+
+ init(params) {
+ this.agParams = params
+ this.container = document.createElement("div")
+ this.container.style.height = "100%"
+ this.container.style.width = "100%"
+
+ this.headerComponent = new TableHeader({
+ target: this.container,
+ props: params,
+ })
+ this.gui = this.container
+ }
+
+ // can get called more than once, you should return the HTML element
+ getGui() {
+ return this.gui
+ }
+
+ // gets called when a new Column Definition has been set for this header
+ refresh(params) {
+ this.agParams = params
+ this.headerComponent = new TableHeader({
+ target: this.container,
+ props: params,
+ })
+ }
+
+ // optional method, gets called once, when component is destroyed
+ destroy() {}
+}
diff --git a/packages/builder/src/components/backend/DataTable/TableLoadingOverlay/LoadingOverlay.svelte b/packages/builder/src/components/backend/DataTable/TableLoadingOverlay/LoadingOverlay.svelte
new file mode 100644
index 0000000000..cbf4cc43ed
--- /dev/null
+++ b/packages/builder/src/components/backend/DataTable/TableLoadingOverlay/LoadingOverlay.svelte
@@ -0,0 +1,29 @@
+
+
+
+
+
+
Loading Your Data
+
+
+
+
+
diff --git a/packages/builder/src/components/backend/DataTable/TableLoadingOverlay/index.js b/packages/builder/src/components/backend/DataTable/TableLoadingOverlay/index.js
new file mode 100644
index 0000000000..886391d537
--- /dev/null
+++ b/packages/builder/src/components/backend/DataTable/TableLoadingOverlay/index.js
@@ -0,0 +1,17 @@
+import LoadingOverlay from "./LoadingOverlay.svelte"
+
+export default class LoadingOverlayWrapper {
+ init(params) {
+ this.gui = document.createElement("div")
+ new LoadingOverlay({
+ target: this.gui,
+ props: {
+ params,
+ },
+ })
+ }
+
+ getGui() {
+ return this.gui
+ }
+}
diff --git a/packages/builder/src/components/backend/DataTable/TablePagination.svelte b/packages/builder/src/components/backend/DataTable/TablePagination.svelte
deleted file mode 100644
index ca5a58700d..0000000000
--- a/packages/builder/src/components/backend/DataTable/TablePagination.svelte
+++ /dev/null
@@ -1,136 +0,0 @@
-
-
-
-
-
diff --git a/packages/builder/src/components/backend/DataTable/buttons/CreateColumnButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/CreateColumnButton.svelte
index 7ddced256e..7677451a44 100644
--- a/packages/builder/src/components/backend/DataTable/buttons/CreateColumnButton.svelte
+++ b/packages/builder/src/components/backend/DataTable/buttons/CreateColumnButton.svelte
@@ -1,27 +1,28 @@
-
-