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} - - {/each} - - - - {#if paginatedData.length === 0} - {#if allowEditing} - - {/if} - {#each columns as header, idx} - - {/each} - {/if} - {#each paginatedData as row} - - {#if allowEditing} - - {/if} - {#each columns as header} - - {/each} - - {/each} - -
-
Edit
-
- {#if allowEditing} - - {:else} -
{header}
- {/if} -
No data. - {#if idx === 0 && !allowEditing}No data.{/if} -
- - - {#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} -
- +
+ + {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 @@ + + +
+
+ Budibase icon + 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 @@ -
-
- -
Create Column
- -
- - + + + + + diff --git a/packages/builder/src/components/backend/DataTable/buttons/DeleteRowsButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/DeleteRowsButton.svelte new file mode 100644 index 0000000000..5690e4f76d --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/buttons/DeleteRowsButton.svelte @@ -0,0 +1,32 @@ + + +
+ + + Delete + {selectedRows.length} + row(s) + +
+ + Are you sure you want to delete + {selectedRows.length} + row{selectedRows.length > 1 ? 's' : ''}? + diff --git a/packages/builder/src/components/backend/DataTable/AttachmentList.svelte b/packages/builder/src/components/backend/DataTable/cells/AttachmentCell.svelte similarity index 100% rename from packages/builder/src/components/backend/DataTable/AttachmentList.svelte rename to packages/builder/src/components/backend/DataTable/cells/AttachmentCell.svelte diff --git a/packages/builder/src/components/backend/DataTable/cells/RelationshipCell.svelte b/packages/builder/src/components/backend/DataTable/cells/RelationshipCell.svelte new file mode 100644 index 0000000000..290ff0cf28 --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/cells/RelationshipCell.svelte @@ -0,0 +1,26 @@ + + +
selectRelationship(row, columnName)}> + {count} + related row(s) +
+ + diff --git a/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js b/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js new file mode 100644 index 0000000000..17165f9437 --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js @@ -0,0 +1,79 @@ +import AttachmentList from "./AttachmentCell.svelte" +import EditRow from "../modals/EditRow.svelte" +import DeleteRow from "../modals/DeleteRow.svelte" +import RelationshipDisplay from "./RelationshipCell.svelte" + +const renderers = { + attachment: attachmentRenderer, + link: linkedRowRenderer, +} + +export function getRenderer(schema, editable) { + if (renderers[schema.type]) { + return renderers[schema.type](schema.options, schema.constraints, editable) + } else { + return false + } +} + +export function deleteRowRenderer(params) { + const container = document.createElement("div") + + new DeleteRow({ + target: container, + props: { + row: params.data, + }, + }) + + return container +} + +export function editRowRenderer(params) { + const container = document.createElement("div") + + new EditRow({ + target: container, + props: { + row: params.data, + }, + }) + + return container +} + +/* eslint-disable no-unused-vars */ +function attachmentRenderer(options, constraints, editable) { + return params => { + const container = document.createElement("div") + + const attachmentInstance = new AttachmentList({ + target: container, + props: { + files: params.value || [], + }, + }) + + return container + } +} + +/* eslint-disable no-unused-vars */ +function linkedRowRenderer() { + return params => { + let container = document.createElement("div") + container.style.display = "grid" + container.style.height = "100%" + + new RelationshipDisplay({ + target: container, + props: { + row: params.data, + columnName: params.column.colId, + selectRelationship: params.selectRelationship, + }, + }) + + return container + } +} diff --git a/packages/builder/src/components/backend/DataTable/popovers/CreateEditColumnPopover.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte similarity index 75% rename from packages/builder/src/components/backend/DataTable/popovers/CreateEditColumnPopover.svelte rename to packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 80e8df72f0..c831ef8193 100644 --- a/packages/builder/src/components/backend/DataTable/popovers/CreateEditColumnPopover.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -1,10 +1,12 @@ -
+