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/createTable.spec.js b/packages/builder/cypress/integration/createTable.spec.js
index bb812540ea..266e31ba58 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")
// Unset table display column
cy.contains("display column").click()
@@ -32,24 +34,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 553f4c4b98..60bd8c15d1 100644
--- a/packages/builder/cypress/integration/createView.spec.js
+++ b/packages/builder/cypress/integration/createView.spec.js
@@ -23,10 +23,12 @@ context("Create a View", () => {
cy.contains("Save View").click()
})
cy.get(".table-title h1").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"])
})
})
@@ -35,25 +37,29 @@ context("Create a View", () => {
cy.contains("Add Filter").click()
cy.get(".menu-container").find("select").first().select("age")
cy.get(".menu-container").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).select("Statistics")
cy.wait(50)
cy.get(".menu-container").find("select").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",
@@ -63,33 +69,30 @@ 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([
- "age",
- "155",
- "20",
- "49",
- "5",
- "5347",
- "31",
- ])
+ cy.get(".ag-cell").then($values => {
+ const values = Array.from($values).map(header =>
+ header.textContent.trim()
+ )
+ expect(values).to.deep.eq(["age", "155", "20", "49", "5", "5347", "31"])
})
})
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 2587e9475e..015efdb187 100644
--- a/packages/builder/cypress/support/commands.js
+++ b/packages/builder/cypress/support/commands.js
@@ -31,9 +31,7 @@ Cypress.Commands.add("createApp", name => {
.then($body => {
if ($body.find("input[name=apiKey]").length) {
// input was found, do something else here
- cy.get("input[name=apiKey]")
- .type(name)
- .should("have.value", name)
+ cy.get("input[name=apiKey]").type(name).should("have.value", name)
cy.contains("Next").click()
}
})
@@ -44,12 +42,8 @@ Cypress.Commands.add("createApp", name => {
cy.contains("Next").click()
- cy.get("input[name=username]")
- .click()
- .type("test")
- cy.get("input[name=password]")
- .click()
- .type("test")
+ cy.get("input[name=username]").click().type("test")
+ cy.get("input[name=password]").click().type("test")
cy.contains("Submit").click()
cy.get("[data-cy=new-table]", {
timeout: 20000,
@@ -67,12 +61,8 @@ Cypress.Commands.add("createTable", tableName => {
// Enter table name
cy.get("[data-cy=new-table]").click()
cy.get(".modal").within(() => {
- cy.get("input")
- .first()
- .type(tableName)
- cy.get(".buttons")
- .contains("Create")
- .click()
+ cy.get("input").first().type(tableName)
+ cy.get(".buttons").contains("Create").click()
})
cy.contains(tableName).should("be.visible")
})
@@ -83,10 +73,8 @@ Cypress.Commands.add("addColumn", (tableName, columnName, type) => {
cy.contains("Create New Column").click()
// Configure column
- cy.get(".menu-container").within(() => {
- cy.get("input")
- .first()
- .type(columnName)
+ cy.get(".actions").within(() => {
+ cy.get("input").first().type(columnName)
// Unset table display column
cy.contains("display column").click()
cy.get("select").select(type)
@@ -99,15 +87,11 @@ Cypress.Commands.add("addRow", values => {
cy.get(".modal").within(() => {
for (let i = 0; i < values.length; i++) {
- cy.get("input")
- .eq(i)
- .type(values[i])
+ cy.get("input").eq(i).type(values[i])
}
// Save
- cy.get(".buttons")
- .contains("Create")
- .click()
+ cy.get(".buttons").contains("Create").click()
})
})
@@ -116,20 +100,12 @@ Cypress.Commands.add("createUser", (username, password, accessLevel) => {
cy.get(".toprightnav > .settings").click()
cy.contains("Users").click()
- cy.get("[name=Name]")
- .first()
- .type(username)
- cy.get("[name=Password]")
- .first()
- .type(password)
- cy.get("select")
- .first()
- .select(accessLevel)
+ cy.get("[name=Name]").first().type(username)
+ cy.get("[name=Password]").first().type(password)
+ cy.get("select").first().select(accessLevel)
// Save
- cy.get(".inputs")
- .contains("Create")
- .click()
+ cy.get(".inputs").contains("Create").click()
})
Cypress.Commands.add("addHeadlineComponent", text => {
@@ -155,13 +131,9 @@ Cypress.Commands.add("navigateToFrontend", () => {
Cypress.Commands.add("createScreen", (screenName, route) => {
cy.get("[data-cy=new-screen]").click()
cy.get(".modal").within(() => {
- cy.get("input")
- .eq(0)
- .type(screenName)
+ cy.get("input").eq(0).type(screenName)
if (route) {
- cy.get("input")
- .eq(1)
- .type(route)
+ cy.get("input").eq(1).type(route)
}
cy.contains("Create Screen").click()
})
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/RelationshipDataTable.svelte b/packages/builder/src/components/backend/DataTable/RelationshipDataTable.svelte
index d9bae810cb..ede4d54022 100644
--- a/packages/builder/src/components/backend/DataTable/RelationshipDataTable.svelte
+++ b/packages/builder/src/components/backend/DataTable/RelationshipDataTable.svelte
@@ -1,7 +1,6 @@
-
+
{title}
{#if loading}
@@ -53,78 +116,22 @@
+ {#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 @@
-
-