diff --git a/packages/builder/cypress/integration/createTable.spec.js b/packages/builder/cypress/integration/createTable.spec.js index d309f4fcb9..067505cfab 100644 --- a/packages/builder/cypress/integration/createTable.spec.js +++ b/packages/builder/cypress/integration/createTable.spec.js @@ -30,7 +30,7 @@ context("Create a Table", () => { // Unset table display column cy.contains("display column").click() cy.contains("Save Column").click() - cy.contains("nameupdated").should("have.text", "nameupdated") + cy.contains("nameupdated ").should("have.text", "nameupdated ") }) it("edits a row", () => { diff --git a/packages/builder/cypress/integration/createView.spec.js b/packages/builder/cypress/integration/createView.spec.js index bdc89bec53..cfafdc6705 100644 --- a/packages/builder/cypress/integration/createView.spec.js +++ b/packages/builder/cypress/integration/createView.spec.js @@ -1,3 +1,11 @@ +function removeSpacing(headers) { + let newHeaders = [] + for (let header of headers) { + newHeaders.push(header.replace(/\s\s+/g, " ")) + } + return newHeaders +} + context("Create a View", () => { before(() => { cy.visit("localhost:4001/_builder") @@ -28,7 +36,7 @@ context("Create a View", () => { const headers = Array.from($headers).map(header => header.textContent.trim() ) - expect(headers).to.deep.eq([ 'rating', 'age', 'group' ]) + expect(removeSpacing(headers)).to.deep.eq([ "rating Number", "age Number", "group Text" ]) }) }) @@ -60,13 +68,19 @@ context("Create a View", () => { const headers = Array.from($headers).map(header => header.textContent.trim() ) - expect(headers).to.deep.eq([ 'avg', 'sumsqr', 'count', 'max', 'min', 'sum', 'field' ]) + expect(removeSpacing(headers)).to.deep.eq([ "avg Number", + "sumsqr Number", + "count Number", + "max Number", + "min Number", + "sum Number", + "field Text" ]) }) cy.get(".ag-cell").then($values => { let values = Array.from($values).map(header => header.textContent.trim() ) - expect(values).to.deep.eq([ '31', '5347', '5', '49', '20', '155', 'age' ]) + expect(values).to.deep.eq([ "31", "5347", "5", "49", "20", "155", "age" ]) }) }) @@ -85,7 +99,7 @@ context("Create a View", () => { .find(".ag-cell") .then($values => { const values = Array.from($values).map(value => value.textContent) - expect(values).to.deep.eq([ 'Students', '23.333333333333332', '1650', '3', '25', '20', '70' ]) + expect(values).to.deep.eq([ "Students", "23.333333333333332", "1650", "3", "25", "20", "70" ]) }) }) diff --git a/packages/builder/package.json b/packages/builder/package.json index 94ef8dc3c4..bf4c4b4a89 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -63,7 +63,7 @@ } }, "dependencies": { - "@budibase/bbui": "^1.58.8", + "@budibase/bbui": "^1.58.12", "@budibase/client": "^0.7.8", "@budibase/colorpicker": "1.0.1", "@budibase/string-templates": "^0.7.8", diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index 7c48a1af2e..eaf64b23b9 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -136,7 +136,7 @@ const getContextBindings = (asset, componentId) => { // Replace certain bindings with a new property to help display components let runtimeBoundKey = key if (fieldSchema.type === "link") { - runtimeBoundKey = `${key}_count` + runtimeBoundKey = `${key}_text` } else if (fieldSchema.type === "attachment") { runtimeBoundKey = `${key}_first` } @@ -176,7 +176,7 @@ const getUserBindings = () => { // Replace certain bindings with a new property to help display components let runtimeBoundKey = key if (fieldSchema.type === "link") { - runtimeBoundKey = `${key}_count` + runtimeBoundKey = `${key}_text` } else if (fieldSchema.type === "attachment") { runtimeBoundKey = `${key}_first` } diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js index ee7a4da0fd..1634556c8d 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js @@ -198,6 +198,13 @@ export function makeDatasourceFormComponents(datasource) { if (fieldType === "options") { component.customProps({ placeholder: "Choose an option " }) } + if (fieldType === "link") { + let placeholder = + fieldSchema.relationshipType === "one-to-many" + ? "Choose an option" + : "Choose some options" + component.customProps({ placeholder }) + } if (fieldType === "boolean") { component.customProps({ text: field, label: "" }) } diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 195876cf05..fa04829634 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -301,4 +301,13 @@ padding-top: var(--spacing-xs); padding-bottom: var(--spacing-xs); } + + :global(.ag-header) { + height: 61px !important; + min-height: 61px !important; + } + + :global(.ag-header-row) { + height: 60px !important; + } diff --git a/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte b/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte index 2d65010a62..e929bbc206 100644 --- a/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte +++ b/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte @@ -2,6 +2,7 @@ import { onMount, onDestroy } from "svelte" import { Modal, ModalContent } from "@budibase/bbui" import CreateEditColumn from "../modals/CreateEditColumn.svelte" + import { FIELDS } from "constants/backend" const SORT_ICON_MAP = { asc: "ri-arrow-down-fill", @@ -51,6 +52,8 @@ column.removeEventListener("sortChanged", setSort) column.removeEventListener("filterActiveChanged", setFilterActive) }) + + $: type = FIELDS[field?.type?.toUpperCase()]?.name
(hovered = true)} on:mouseleave={() => (hovered = false)}> -
-
- {#if field.autocolumn}{/if} - {displayName} +
+
+
+ {displayName} + {#if field.autocolumn}{/if} +
+ {#if type} +
{type}
+ {/if}
- +
-
selectRelationship(row, columnName)}> - {count} - related row(s) +
selectRelationship(row, columnName)}> + {#each items as item} +
{item}
+ {/each}
diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 8ae492226f..1e3f820d69 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -6,6 +6,8 @@ TextButton, Select, Toggle, + Radio, + } from "@budibase/bbui" import { cloneDeep } from "lodash/fp" import { backendUiStore } from "builderStore" @@ -18,6 +20,7 @@ import ConfirmDialog from "components/common/ConfirmDialog.svelte" const AUTO_COL = "auto" + const LINK_TYPE = FIELDS.LINK.type let fieldDefinitions = cloneDeep(FIELDS) export let onClosed @@ -33,6 +36,15 @@ let primaryDisplay = $backendUiStore.selectedTable.primaryDisplay == null || $backendUiStore.selectedTable.primaryDisplay === field.name + + let relationshipTypes = [ + {text: 'Many to many (N:N)', value: 'many-to-many',}, + {text: 'One to many (1:N)', value: 'one-to-many',} + ] + let types = ['Many to many (N:N)', 'One to many (1:N)'] + + let selectedRelationshipType = relationshipTypes.find(type => type.value === field.relationshipType)?.text || 'Many to many (N:N)' + let indexes = [...($backendUiStore.selectedTable.indexes || [])] let confirmDeleteDialog let deletion @@ -44,17 +56,23 @@ $: uneditable = $backendUiStore.selectedTable?._id === TableNames.USERS && UNEDITABLE_USER_FIELDS.includes(field.name) + $: invalid = field.type === FIELDS.LINK.type && !field.tableId // used to select what different options can be displayed for column type $: canBeSearched = - field.type !== "link" && + field.type !== LINK_TYPE && field.subtype !== AUTO_COLUMN_SUB_TYPES.CREATED_BY && field.subtype !== AUTO_COLUMN_SUB_TYPES.UPDATED_BY - $: canBeDisplay = field.type !== "link" && field.type !== AUTO_COL + $: canBeDisplay = field.type !== LINK_TYPE && field.type !== AUTO_COL $: canBeRequired = - field.type !== "link" && !uneditable && field.type !== AUTO_COL + field.type !== LINK_TYPE && !uneditable && field.type !== AUTO_COL async function saveColumn() { + // Set relationship type if it's + if (field.type === 'link') { + field.relationshipType = relationshipTypes.find(type => type.text === selectedRelationshipType).value + } + if (field.type === AUTO_COL) { field = buildAutoColumn( $backendUiStore.draftTable.name, @@ -84,13 +102,17 @@ } } - function handleFieldConstraints(event) { + function handleTypeChange(event) { const definition = fieldDefinitions[event.target.value.toUpperCase()] if (!definition) { return } field.type = definition.type field.constraints = definition.constraints + // remove any extra fields that may not be related to this type + delete field.autocolumn + delete field.subtype + delete field.tableId } function onChangeRequired(e) { @@ -138,7 +160,7 @@ secondary thin label="Type" - on:change={handleFieldConstraints} + on:change={handleTypeChange} bind:value={field.type}> {#each Object.values(fieldDefinitions) as field} @@ -206,6 +228,16 @@ label="Max Value" bind:value={field.constraints.numericality.lessThanOrEqualTo} /> {:else if field.type === 'link'} +
+ +
+ {#each types as type} + + + + {/each} +
+
- + {#if typeof integration[configKey] === 'object'} + + + + {:else} +
+ + +
+ {/if} {/each} + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte index dbe289caba..4cb740ad41 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte @@ -2,7 +2,8 @@ import { onMount } from "svelte" import { backendUiStore } from "builderStore" import api from "builderStore/api" - import { Input, TextArea, Spacer } from "@budibase/bbui" + import { Input, Label, TextArea, Spacer } from "@budibase/bbui" + import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte" import ICONS from "../icons" export let integration = {} @@ -49,17 +50,6 @@
{/each}
- - {#if schema} - {#each Object.keys(schema) as configKey} - - - {/each} - {/if} diff --git a/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte index 92a541ac1d..dc07b31645 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte @@ -3,7 +3,6 @@ import { notifier } from "builderStore/store/notifications" import { DropdownMenu, Button, Input } from "@budibase/bbui" import ConfirmDialog from "components/common/ConfirmDialog.svelte" - import IntegrationConfigForm from "../TableIntegrationMenu//IntegrationConfigForm.svelte" import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" export let query diff --git a/packages/builder/src/components/common/Dropdowns/DropdownItem.svelte b/packages/builder/src/components/common/Dropdowns/DropdownItem.svelte index 2f179858a3..64b7743645 100644 --- a/packages/builder/src/components/common/Dropdowns/DropdownItem.svelte +++ b/packages/builder/src/components/common/Dropdowns/DropdownItem.svelte @@ -1,8 +1,8 @@ {#if value?.type === 'query'} - +
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor.svelte index fbf657f0e6..616a0196c0 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor.svelte @@ -1,5 +1,5 @@ + + +
+ {#each fields as field, idx} + + + {#if !readOnly} + deleteEntry(idx)} /> + {/if} + {/each} +
+{#if !readOnly} + +{/if} + + diff --git a/packages/builder/src/components/integration/QueryEditor.svelte b/packages/builder/src/components/integration/QueryEditor.svelte index 8a98cac567..42c6d7cef9 100644 --- a/packages/builder/src/components/integration/QueryEditor.svelte +++ b/packages/builder/src/components/integration/QueryEditor.svelte @@ -1,5 +1,6 @@ +{#if label} + + +{/if}