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} - - {/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..fc933e374c --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/buttons/DeleteRowsButton.svelte @@ -0,0 +1,34 @@ + + +
+ + + 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 74% rename from packages/builder/src/components/backend/DataTable/popovers/CreateEditColumnPopover.svelte rename to packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 9549041a18..c831ef8193 100644 --- a/packages/builder/src/components/backend/DataTable/popovers/CreateEditColumnPopover.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -1,26 +1,12 @@ -
+