diff --git a/packages/builder/cypress/integration/createBinding.spec.js b/packages/builder/cypress/integration/createBinding.spec.js index 99d741ab46..d901dc608d 100644 --- a/packages/builder/cypress/integration/createBinding.spec.js +++ b/packages/builder/cypress/integration/createBinding.spec.js @@ -20,7 +20,7 @@ context("Create Bindings", () => { cy.get("[data-cy=setting-text] input") .type("{{}{{}{{} Current User._id {}}{}}") .blur() - cy.getComponent(componentId).should("have.text", "{{{ user._id }}") + cy.getComponent(componentId).should("have.text", "{{{ [user].[_id] }}") }) }) diff --git a/packages/builder/cypress/integration/createComponents.spec.js b/packages/builder/cypress/integration/createComponents.spec.js index 56aca88837..3fc61f8d1a 100644 --- a/packages/builder/cypress/integration/createComponents.spec.js +++ b/packages/builder/cypress/integration/createComponents.spec.js @@ -43,7 +43,7 @@ context("Create Components", () => { it("should create a form and reset to match schema", () => { cy.addComponent("Form", "Form").then(() => { cy.get("[data-cy=Settings]").click() - cy.get("[data-cy=setting-datasource]") + cy.get("[data-cy=setting-dataSource]") .contains("Choose option") .click() cy.get(".dropdown") diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index a728b6d795..4d57311f5f 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -1,11 +1,11 @@ import { cloneDeep } from "lodash/fp" import { get } from "svelte/store" +import { findComponent, findComponentPath } from "./storeUtils" import { store } from "builderStore" import { tables as tablesStore, queries as queriesStores, } from "stores/backend/" -import { findComponentPath } from "./storeUtils" import { makePropSafe } from "@budibase/string-templates" import { TableNames } from "../constants" @@ -39,7 +39,7 @@ export const getDataProviderComponents = (asset, componentId) => { // Filter by only data provider components return path.filter(component => { const def = store.actions.components.getDefinition(component._component) - return def?.dataProvider + return def?.context != null }) } @@ -66,14 +66,25 @@ export const getActionProviderComponents = (asset, componentId, actionType) => { /** * Gets a datasource object for a certain data provider component */ -export const getDatasourceForProvider = component => { +export const getDatasourceForProvider = (asset, component) => { const def = store.actions.components.getDefinition(component?._component) if (!def) { return null } + // If this component has a dataProvider setting, go up the stack and use it + const dataProviderSetting = def.settings.find(setting => { + return setting.type === "dataProvider" + }) + if (dataProviderSetting) { + const settingValue = component[dataProviderSetting.key] + const providerId = extractLiteralHandlebarsID(settingValue) + const provider = findComponent(asset.props, providerId) + return getDatasourceForProvider(asset, provider) + } + // Extract datasource from component instance - const validSettingTypes = ["datasource", "table", "schema"] + const validSettingTypes = ["dataSource", "table", "schema"] const datasourceSetting = def.settings.find(setting => { return validSettingTypes.includes(setting.type) }) @@ -105,53 +116,68 @@ const getContextBindings = (asset, componentId) => { // Create bindings for each data provider dataProviders.forEach(component => { - const isForm = component._component.endsWith("/form") - const datasource = getDatasourceForProvider(component) - let tableName, schema + const def = store.actions.components.getDefinition(component._component) + const contextDefinition = def.context + let schema + let readablePrefix - // Forms are an edge case which do not need table schemas - if (isForm) { + if (contextDefinition.type === "form") { + // Forms do not need table schemas + // Their schemas are built from their component field names schema = buildFormSchema(component) - tableName = "Fields" - } else { + readablePrefix = "Fields" + } else if (contextDefinition.type === "static") { + // Static contexts are fully defined by the components + schema = {} + const values = contextDefinition.values || [] + values.forEach(value => { + schema[value.key] = { name: value.label, type: "string" } + }) + } else if (contextDefinition.type === "schema") { + // Schema contexts are generated dynamically depending on their data + const datasource = getDatasourceForProvider(asset, component) if (!datasource) { return } - - // Get schema and table for the datasource - const info = getSchemaForDatasource(datasource, isForm) + const info = getSchemaForDatasource(datasource) schema = info.schema - tableName = info.table?.name - - // Add _id and _rev fields for certain types - if (schema && ["table", "link"].includes(datasource.type)) { - schema["_id"] = { type: "string" } - schema["_rev"] = { type: "string" } - } + readablePrefix = info.table?.name } - if (!schema || !tableName) { + if (!schema) { return } const keys = Object.keys(schema).sort() // Create bindable properties for each schema field + const safeComponentId = makePropSafe(component._id) keys.forEach(key => { const fieldSchema = schema[key] - // Replace certain bindings with a new property to help display components + + // Make safe runtime binding and replace certain bindings with a + // new property to help display components let runtimeBoundKey = key if (fieldSchema.type === "link") { runtimeBoundKey = `${key}_text` } else if (fieldSchema.type === "attachment") { runtimeBoundKey = `${key}_first` } + const runtimeBinding = `${safeComponentId}.${makePropSafe( + runtimeBoundKey + )}` + // Optionally use a prefix with readable bindings + let readableBinding = component._instanceName + if (readablePrefix) { + readableBinding += `.${readablePrefix}` + } + readableBinding += `.${fieldSchema.name || key}` + + // Create the binding object bindings.push({ type: "context", - runtimeBinding: `${makePropSafe(component._id)}.${makePropSafe( - runtimeBoundKey - )}`, - readableBinding: `${component._instanceName}.${tableName}.${key}`, + runtimeBinding, + readableBinding, // Field schema and provider are required to construct relationship // datasource options, based on bindable properties fieldSchema, @@ -168,14 +194,12 @@ const getContextBindings = (asset, componentId) => { */ const getUserBindings = () => { let bindings = [] - const tables = get(tablesStore).list - const userTable = tables.find(table => table._id === TableNames.USERS) - const schema = { - ...userTable.schema, - _id: { type: "string" }, - _rev: { type: "string" }, - } + const { schema } = getSchemaForDatasource({ + type: "table", + tableId: TableNames.USERS, + }) const keys = Object.keys(schema).sort() + const safeUser = makePropSafe("user") keys.forEach(key => { const fieldSchema = schema[key] // Replace certain bindings with a new property to help display components @@ -188,7 +212,7 @@ const getUserBindings = () => { bindings.push({ type: "context", - runtimeBinding: `user.${runtimeBoundKey}`, + runtimeBinding: `${safeUser}.${makePropSafe(runtimeBoundKey)}`, readableBinding: `Current User.${key}`, // Field schema and provider are required to construct relationship // datasource options, based on bindable properties @@ -212,9 +236,10 @@ const getUrlBindings = asset => { params.push(part.replace(/:/g, "").replace(/\?/g, "")) } }) + const safeURL = makePropSafe("url") return params.map(param => ({ type: "context", - runtimeBinding: `url.${param}`, + runtimeBinding: `${safeURL}.${makePropSafe(param)}`, readableBinding: `URL.${param}`, })) } @@ -236,15 +261,6 @@ export const getSchemaForDatasource = (datasource, isForm = false) => { if (table) { if (type === "view") { schema = cloneDeep(table.views?.[datasource.name]?.schema) - - // Some calc views don't include a "name" property inside the schema - if (schema) { - Object.keys(schema).forEach(field => { - if (!schema[field].name) { - schema[field].name = field - } - }) - } } else if (type === "query" && isForm) { schema = {} const params = table.parameters || [] @@ -257,6 +273,21 @@ export const getSchemaForDatasource = (datasource, isForm = false) => { schema = cloneDeep(table.schema) } } + + // Add _id and _rev fields for certain types + if (schema && !isForm && ["table", "link"].includes(datasource.type)) { + schema["_id"] = { type: "string" } + schema["_rev"] = { type: "string" } + } + + // Ensure there are "name" properties for all fields + if (schema) { + Object.keys(schema).forEach(field => { + if (!schema[field].name) { + schema[field].name = field + } + }) + } } return { schema, table } } @@ -277,7 +308,7 @@ const buildFormSchema = component => { if (fieldSetting && component.field) { const type = fieldSetting.type.split("field/")[1] if (type) { - schema[component.field] = { name: component.field, type } + schema[component.field] = { type } } } component._children?.forEach(child => { @@ -330,6 +361,14 @@ function bindingReplacement(bindableProperties, textWithBindings, convertTo) { return result } +/** + * Extracts a component ID from a handlebars expression setting of + * {{ literal [componentId] }} + */ +function extractLiteralHandlebarsID(value) { + return value?.match(/{{\s*literal[\s[]+([a-fA-F0-9]+)[\s\]]*}}/)?.[1] +} + /** * Converts a readable data binding into a runtime data binding */ diff --git a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js index aeac80e7c1..7788245d46 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js @@ -37,7 +37,7 @@ const createScreen = table => { .customProps({ theme: "spectrum--lightest", size: "spectrum--medium", - datasource: { + dataSource: { label: table.name, tableId: table._id, type: "table", diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js index 0e48cf307e..67a1d1c916 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js @@ -25,7 +25,7 @@ export default function(tables) { export const ROW_DETAIL_TEMPLATE = "ROW_DETAIL_TEMPLATE" export const rowDetailUrl = table => sanitizeUrl(`/${table.name}/:id`) -function generateTitleContainer(table, title, formId) { +function generateTitleContainer(table, title, formId, repeaterId) { // have to override style for this, its missing margin const saveButton = makeSaveButton(table, formId).normalStyle({ background: "#000000", @@ -61,10 +61,9 @@ function generateTitleContainer(table, title, formId) { onClick: [ { parameters: { - providerId: formId, - rowId: `{{ ${makePropSafe(formId)}._id }}`, - revId: `{{ ${makePropSafe(formId)}._rev }}`, tableId: table._id, + rowId: `{{ ${makePropSafe(repeaterId)}.${makePropSafe("_id")} }}`, + revId: `{{ ${makePropSafe(repeaterId)}.${makePropSafe("_rev")} }}`, }, "##eventHandlerType": "Delete Row", }, @@ -84,18 +83,33 @@ function generateTitleContainer(table, title, formId) { } const createScreen = table => { - const screen = new Screen() - .component("@budibase/standard-components/rowdetail") - .table(table._id) - .instanceName(`${table.name} - Detail`) - .route(rowDetailUrl(table)) + const provider = new Component("@budibase/standard-components/dataprovider") + .instanceName(`Data Provider`) + .customProps({ + dataSource: { + label: table.name, + name: `all_${table._id}`, + tableId: table._id, + type: "table", + }, + filter: { + _id: `{{ ${makePropSafe("url")}.${makePropSafe("id")} }}`, + }, + limit: 1, + }) + + const repeater = new Component("@budibase/standard-components/repeater") + .instanceName("Repeater") + .customProps({ + dataProvider: `{{ literal ${makePropSafe(provider._json._id)} }}`, + }) const form = makeMainForm() .instanceName("Form") .customProps({ theme: "spectrum--lightest", size: "spectrum--medium", - datasource: { + dataSource: { label: table.name, tableId: table._id, type: "table", @@ -116,14 +130,24 @@ const createScreen = table => { // Add all children to the form const formId = form._json._id - const rowDetailId = screen._json.props._id + const repeaterId = repeater._json._id const heading = table.primaryDisplay - ? `{{ ${makePropSafe(rowDetailId)}.${makePropSafe(table.primaryDisplay)} }}` + ? `{{ ${makePropSafe(repeaterId)}.${makePropSafe(table.primaryDisplay)} }}` : null form .addChild(makeBreadcrumbContainer(table.name, heading || "Edit")) - .addChild(generateTitleContainer(table, heading || "Edit Row", formId)) + .addChild( + generateTitleContainer(table, heading || "Edit Row", formId, repeaterId) + ) .addChild(fieldGroup) - return screen.addChild(form).json() + repeater.addChild(form) + provider.addChild(repeater) + + return new Screen() + .component("@budibase/standard-components/container") + .instanceName(`${table.name} - Detail`) + .route(rowDetailUrl(table)) + .addChild(provider) + .json() } diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js index c999c22647..603c730508 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js @@ -2,6 +2,7 @@ import sanitizeUrl from "./utils/sanitizeUrl" import { newRowUrl } from "./newRowScreen" import { Screen } from "./utils/Screen" import { Component } from "./utils/Component" +import { makePropSafe } from "@budibase/string-templates" export default function(tables) { return tables.map(table => { @@ -70,21 +71,56 @@ function generateTitleContainer(table) { } const createScreen = table => { - const datagrid = new Component("@budibase/standard-components/datagrid") + const provider = new Component("@budibase/standard-components/dataprovider") + .instanceName(`Data Provider`) .customProps({ - datasource: { + dataSource: { label: table.name, name: `all_${table._id}`, tableId: table._id, type: "table", }, - editable: false, - theme: "alpine", - height: "540", - pagination: true, - detailUrl: `${rowListUrl(table)}/:id`, }) - .instanceName("Grid") + + const spectrumTable = new Component("@budibase/standard-components/table") + .customProps({ + dataProvider: `{{ literal ${makePropSafe(provider._json._id)} }}`, + theme: "spectrum--lightest", + showAutoColumns: false, + quiet: false, + size: "spectrum--medium", + rowCount: 8, + }) + .instanceName(`${table.name} Table`) + + const safeTableId = makePropSafe(spectrumTable._json._id) + const safeRowId = makePropSafe("_id") + const viewButton = new Component("@budibase/standard-components/button") + .customProps({ + text: "View", + onClick: [ + { + "##eventHandlerType": "Navigate To", + parameters: { + url: `${rowListUrl(table)}/{{ ${safeTableId}.${safeRowId} }}`, + }, + }, + ], + }) + .instanceName("View Button") + .normalStyle({ + background: "transparent", + "font-family": "Inter, sans-serif", + "font-weight": "500", + color: "#888", + "border-width": "0", + }) + .hoverStyle({ + color: "#4285f4", + }) + + spectrumTable.addChild(viewButton) + provider.addChild(spectrumTable) const mainContainer = new Component("@budibase/standard-components/container") .normalStyle({ @@ -105,14 +141,12 @@ const createScreen = table => { .type("div") .instanceName("Container") .addChild(generateTitleContainer(table)) - .addChild(datagrid) + .addChild(provider) return new Screen() .component("@budibase/standard-components/container") - .mainType("div") .route(rowListUrl(table)) .instanceName(`${table.name} - List`) - .name("") .addChild(mainContainer) .json() } diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js index 1634556c8d..19f950226c 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js @@ -119,6 +119,7 @@ export function makeSaveButton(table, formId) { { parameters: { providerId: formId, + tableId: table._id, }, "##eventHandlerType": "Save Row", }, diff --git a/packages/builder/src/components/design/AppPreview/componentStructure.json b/packages/builder/src/components/design/AppPreview/componentStructure.json index 16109b5a96..267289a804 100644 --- a/packages/builder/src/components/design/AppPreview/componentStructure.json +++ b/packages/builder/src/components/design/AppPreview/componentStructure.json @@ -1,7 +1,8 @@ [ "container", - "datagrid", - "list", + "dataprovider", + "table", + "repeater", "button", "search", { @@ -62,8 +63,7 @@ "children": [ "screenslot", "navigation", - "login", - "rowdetail" + "login" ] } ] diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataProviderSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataProviderSelect.svelte new file mode 100644 index 0000000000..e773ba5e5d --- /dev/null +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataProviderSelect.svelte @@ -0,0 +1,25 @@ + + + diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DatasourceSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte similarity index 100% rename from packages/builder/src/components/design/PropertiesPanel/PropertyControls/DatasourceSelect.svelte rename to packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte index 425c6f9c58..0895c78331 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/DeleteRow.svelte @@ -1,50 +1,38 @@
- {#if dataProviderComponents.length === 0} -
- Delete row can only be used within a component that provides data, such as - a List -
- {:else} - - - {/if} + + + + + (parameters.rowId = value.detail)} /> + + + (parameters.revId = value.detail)} />
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ExecuteQuery.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ExecuteQuery.svelte index cd870558b3..8166c0a322 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ExecuteQuery.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ExecuteQuery.svelte @@ -1,7 +1,7 @@ diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogIn.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogIn.svelte index c49369bf29..eee4dc2bc7 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogIn.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogIn.svelte @@ -30,7 +30,9 @@ display: grid; column-gap: var(--spacing-l); row-gap: var(--spacing-s); - grid-template-columns: auto 1fr auto 1fr; + grid-template-columns: auto 1fr; align-items: baseline; + max-width: 800px; + margin: 0 auto; } diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogOut.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogOut.svelte index 2b0aad8342..7306ed4e61 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogOut.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogOut.svelte @@ -1,7 +1,14 @@ -
This action doesn't require any additional settings.
+ + +
+ This action doesn't require any additional settings. +
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NavigateTo.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NavigateTo.svelte index 65071837e7..b9567c6523 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NavigateTo.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NavigateTo.svelte @@ -27,6 +27,8 @@ display: flex; flex-direction: row; align-items: baseline; + max-width: 800px; + margin: 0 auto; } .root :global(> div) { diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte index bea93615c2..a94a5e3002 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/SaveRow.svelte @@ -1,9 +1,9 @@
- {#if !dataProviderComponents.length} -
- Save Row can only be used within a component that provides data, such as a - Repeater -
- {:else} - + + Choosing a Data Source will automatically use the data it provides, but it's + optional.
+ You can always add or override fields manually. + +
+ - {#if parameters.providerId} + + + + {#if parameters.tableId} {/if} - {/if} +
diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/TriggerAutomation.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/TriggerAutomation.svelte index 1a3276800a..4ae5e320fc 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/TriggerAutomation.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/TriggerAutomation.svelte @@ -101,6 +101,11 @@ diff --git a/packages/standard-components/src/Repeater.svelte b/packages/standard-components/src/Repeater.svelte new file mode 100644 index 0000000000..6fc42cc19a --- /dev/null +++ b/packages/standard-components/src/Repeater.svelte @@ -0,0 +1,47 @@ + + +
+ {#if rows.length > 0} + {#if $component.children === 0 && $builderStore.inBuilder} +

Add some components to display.

+ {:else} + {#each rows as row} + + + + {/each} + {/if} + {:else if loaded && noRowsMessage} +

{noRowsMessage}

+ {/if} +
+ + diff --git a/packages/standard-components/src/RowDetail.svelte b/packages/standard-components/src/RowDetail.svelte deleted file mode 100644 index 8a413dfb43..0000000000 --- a/packages/standard-components/src/RowDetail.svelte +++ /dev/null @@ -1,57 +0,0 @@ - - -{#if row} - -
- -
-
-{/if} diff --git a/packages/standard-components/src/charts/ApexChart.svelte b/packages/standard-components/src/charts/ApexChart.svelte index 0c2e1b0e41..cf9cade436 100644 --- a/packages/standard-components/src/charts/ApexChart.svelte +++ b/packages/standard-components/src/charts/ApexChart.svelte @@ -2,7 +2,7 @@ import { getContext } from "svelte" import { chart } from "svelte-apexcharts" - const { styleable } = getContext("sdk") + const { styleable, builderStore } = getContext("sdk") const component = getContext("component") export let options @@ -10,7 +10,7 @@ {#if options}
-{:else if options === false} +{:else if builderStore.inBuilder}
Use the settings panel to build your chart -->
diff --git a/packages/standard-components/src/charts/BarChart.svelte b/packages/standard-components/src/charts/BarChart.svelte index 872f7e624d..a2259aa754 100644 --- a/packages/standard-components/src/charts/BarChart.svelte +++ b/packages/standard-components/src/charts/BarChart.svelte @@ -1,13 +1,9 @@ diff --git a/packages/standard-components/src/charts/CandleStickChart.svelte b/packages/standard-components/src/charts/CandleStickChart.svelte index c5ca0ebd49..9435b27cfd 100644 --- a/packages/standard-components/src/charts/CandleStickChart.svelte +++ b/packages/standard-components/src/charts/CandleStickChart.svelte @@ -1,13 +1,9 @@ diff --git a/packages/standard-components/src/charts/LineChart.svelte b/packages/standard-components/src/charts/LineChart.svelte index cdfb4f5fbf..47823955c0 100644 --- a/packages/standard-components/src/charts/LineChart.svelte +++ b/packages/standard-components/src/charts/LineChart.svelte @@ -1,14 +1,10 @@ diff --git a/packages/standard-components/src/charts/PieChart.svelte b/packages/standard-components/src/charts/PieChart.svelte index 7b8df25b01..19cb1d2472 100644 --- a/packages/standard-components/src/charts/PieChart.svelte +++ b/packages/standard-components/src/charts/PieChart.svelte @@ -1,13 +1,9 @@ diff --git a/packages/standard-components/src/forms/Form.svelte b/packages/standard-components/src/forms/Form.svelte index e51a31955e..5e810d5086 100644 --- a/packages/standard-components/src/forms/Form.svelte +++ b/packages/standard-components/src/forms/Form.svelte @@ -5,7 +5,7 @@ import { createValidatorFromConstraints } from "./validation" import { generateID } from "../helpers" - export let datasource + export let dataSource export let theme export let size export let disabled = false @@ -143,15 +143,15 @@ }) } - // Fetches the form schema from this form's datasource, if one exists + // Fetches the form schema from this form's dataSource, if one exists const fetchSchema = async () => { - if (!datasource?.tableId) { + if (!dataSource?.tableId) { schema = {} table = null } else { - table = await API.fetchTableDefinition(datasource?.tableId) + table = await API.fetchTableDefinition(dataSource?.tableId) if (table) { - if (datasource?.type === "query") { + if (dataSource?.type === "query") { schema = {} const params = table.parameters || [] params.forEach(param => { @@ -171,7 +171,7 @@ + data={{ ...$formState.values, tableId: dataSource?.tableId }}>
- import AttachmentList from "../../attachments/AttachmentList.svelte" - export let files - - - diff --git a/packages/standard-components/src/grid/AttachmentCell/Modal.svelte b/packages/standard-components/src/grid/AttachmentCell/Modal.svelte deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/standard-components/src/grid/Component.svelte b/packages/standard-components/src/grid/Component.svelte deleted file mode 100644 index 49754a86a5..0000000000 --- a/packages/standard-components/src/grid/Component.svelte +++ /dev/null @@ -1,193 +0,0 @@ - - -
- {#if dataLoaded} - {#if canAddDelete} -
- {#if selectedRows.length > 0} - - - Delete - {selectedRows.length} - row(s) - - {/if} -
- {/if} - (selectedRows = detail)} /> - {/if} - - - Are you sure you want to delete {selectedRows.length} row(s)? - - -
- - diff --git a/packages/standard-components/src/grid/CreateRow/Button.svelte b/packages/standard-components/src/grid/CreateRow/Button.svelte deleted file mode 100644 index ab3513de91..0000000000 --- a/packages/standard-components/src/grid/CreateRow/Button.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - -
- -
- -
Add New Row
- dispatch('newRow')} /> -
- - diff --git a/packages/standard-components/src/grid/CreateRow/Modal.svelte b/packages/standard-components/src/grid/CreateRow/Modal.svelte deleted file mode 100644 index 1cb9aa923d..0000000000 --- a/packages/standard-components/src/grid/CreateRow/Modal.svelte +++ /dev/null @@ -1,144 +0,0 @@ - - -
- {#each errorMessages as error} -

{error}

- {/each} -
- {#each fields as field} -
- - {#if schema[field].type === 'string' && schema[field].constraints.inclusion} - - {:else if schema[field].type === 'datetime'} - - {:else if schema[field].type === 'boolean'} - - {:else if schema[field].type === 'number'} - - {:else if schema[field].type === 'string'} - - {:else if schema[field].type === 'longform'} - - {:else if schema[field].type === 'attachment'} - - {/if} -
-
- {/each} -
-
-
-
- -
-
- -
-
- - diff --git a/packages/standard-components/src/grid/DateTime/Wrapper.svelte b/packages/standard-components/src/grid/DateTime/Wrapper.svelte deleted file mode 100644 index b493626d64..0000000000 --- a/packages/standard-components/src/grid/DateTime/Wrapper.svelte +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/packages/standard-components/src/grid/Relationship/RelationshipDisplay.svelte b/packages/standard-components/src/grid/Relationship/RelationshipDisplay.svelte deleted file mode 100644 index f269f8f2a8..0000000000 --- a/packages/standard-components/src/grid/Relationship/RelationshipDisplay.svelte +++ /dev/null @@ -1,75 +0,0 @@ - - -
- {#if linkedRows && linkedRows.length && displayColumn} - {#each linkedRows as linkedRow} - {#if linkedRow[displayColumn] != null && linkedRow[displayColumn] !== ''} -
{linkedRow[displayColumn]}
- {/if} - {/each} - {:else}{count} related row(s){/if} -
- - diff --git a/packages/standard-components/src/grid/Relationship/RelationshipLabel.svelte b/packages/standard-components/src/grid/Relationship/RelationshipLabel.svelte deleted file mode 100644 index ec64255855..0000000000 --- a/packages/standard-components/src/grid/Relationship/RelationshipLabel.svelte +++ /dev/null @@ -1,32 +0,0 @@ - - -
- {#each items as item} -
{item?.primaryDisplay ?? ''}
- {/each} -
- - diff --git a/packages/standard-components/src/grid/Select/Wrapper.svelte b/packages/standard-components/src/grid/Select/Wrapper.svelte deleted file mode 100644 index 4c4a8eaf21..0000000000 --- a/packages/standard-components/src/grid/Select/Wrapper.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/packages/standard-components/src/grid/ViewDetails/Cell.svelte b/packages/standard-components/src/grid/ViewDetails/Cell.svelte deleted file mode 100644 index f222429aa8..0000000000 --- a/packages/standard-components/src/grid/ViewDetails/Cell.svelte +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - diff --git a/packages/standard-components/src/grid/customRenderer.js b/packages/standard-components/src/grid/customRenderer.js deleted file mode 100644 index fd558baa98..0000000000 --- a/packages/standard-components/src/grid/customRenderer.js +++ /dev/null @@ -1,169 +0,0 @@ -// Custom renderers to handle special types -// https://www.ag-grid.com/javascript-grid-cell-rendering-components/ - -import AttachmentCell from "./AttachmentCell/Button.svelte" -import ViewDetails from "./ViewDetails/Cell.svelte" -import Select from "./Select/Wrapper.svelte" -import DatePicker from "./DateTime/Wrapper.svelte" -import RelationshipLabel from "./Relationship/RelationshipLabel.svelte" - -const renderers = new Map([ - ["boolean", booleanRenderer], - ["attachment", attachmentRenderer], - ["options", optionsRenderer], - ["link", linkedRowRenderer], - ["_id", viewDetailsRenderer], -]) - -export function getRenderer(schema, editable, SDK) { - if (renderers.get(schema.type)) { - return renderers.get(schema.type)( - schema.options, - schema.constraints, - editable, - SDK - ) - } else { - return false - } -} - -/* eslint-disable no-unused-vars */ -function booleanRenderer(options, constraints, editable, SDK) { - return params => { - const toggle = e => { - params.value = !params.value - params.setValue(e.currentTarget.checked) - } - let input = document.createElement("input") - input.style.display = "grid" - input.style.placeItems = "center" - input.style.height = "100%" - input.type = "checkbox" - input.checked = params.value - if (editable) { - input.addEventListener("click", toggle) - } else { - input.disabled = true - } - - return input - } -} -/* eslint-disable no-unused-vars */ -function attachmentRenderer(options, constraints, editable, SDK) { - return params => { - const container = document.createElement("div") - - const attachmentInstance = new AttachmentCell({ - target: container, - props: { - files: params.value || [], - SDK, - }, - }) - - const deleteFile = event => { - const newFilesArray = params.value.filter(file => file !== event.detail) - params.setValue(newFilesArray) - } - - attachmentInstance.$on("delete", deleteFile) - - return container - } -} -/* eslint-disable no-unused-vars */ -function dateRenderer(options, constraints, editable, SDK) { - return function(params) { - const container = document.createElement("div") - const toggle = e => { - params.setValue(e.detail[0][0]) - } - - // Options need to be passed in with minTime and maxTime! Needs bbui update. - new DatePicker({ - target: container, - props: { - value: params.value, - SDK, - }, - }) - - return container - } -} - -function optionsRenderer(options, constraints, editable, SDK) { - return params => { - if (!editable) return params.value - const container = document.createElement("div") - container.style.display = "grid" - container.style.placeItems = "center" - container.style.height = "100%" - const change = e => { - params.setValue(e.detail) - } - - const selectInstance = new Select({ - target: container, - props: { - value: params.value, - options: constraints.inclusion, - SDK, - }, - }) - - selectInstance.$on("change", change) - - return container - } -} -/* eslint-disable no-unused-vars */ -function linkedRowRenderer(options, constraints, editable, SDK) { - return params => { - let container = document.createElement("div") - container.style.display = "grid" - container.style.placeItems = "center" - container.style.height = "100%" - - new RelationshipLabel({ - target: container, - props: { - row: params.data, - columnName: params.column.colId, - SDK, - }, - }) - - return container - } -} - -/* eslint-disable no-unused-vars */ -function viewDetailsRenderer(options, constraints, editable, SDK) { - return params => { - let container = document.createElement("div") - container.style.display = "grid" - container.style.alignItems = "center" - container.style.height = "100%" - - let url = "/" - if (options.detailUrl) { - url = options.detailUrl.replace(":id", params.data._id) - } - if (!url.startsWith("/")) { - url = `/${url}` - } - - new ViewDetails({ - target: container, - props: { - url, - SDK, - }, - }) - - return container - } -} diff --git a/packages/standard-components/src/grid/valueSetters.js b/packages/standard-components/src/grid/valueSetters.js deleted file mode 100644 index 2da8182092..0000000000 --- a/packages/standard-components/src/grid/valueSetters.js +++ /dev/null @@ -1,6 +0,0 @@ -// https://www.ag-grid.com/javascript-grid-value-setters/ -// These handles values and makes sure they adhere to the data type provided by the table -export const number = params => { - params.data[params.colDef.field] = parseFloat(params.newValue) - return true -} diff --git a/packages/standard-components/src/index.js b/packages/standard-components/src/index.js index a9d8920895..d1ad792247 100644 --- a/packages/standard-components/src/index.js +++ b/packages/standard-components/src/index.js @@ -13,17 +13,16 @@ import { loadSpectrumIcons } from "./spectrum-icons" loadSpectrumIcons() export { default as container } from "./Container.svelte" -export { default as datagrid } from "./grid/Component.svelte" +export { default as dataprovider } from "./DataProvider.svelte" export { default as screenslot } from "./ScreenSlot.svelte" export { default as button } from "./Button.svelte" -export { default as list } from "./List.svelte" +export { default as repeater } from "./Repeater.svelte" export { default as stackedlist } from "./StackedList.svelte" export { default as card } from "./Card.svelte" export { default as text } from "./Text.svelte" export { default as login } from "./Login.svelte" export { default as navigation } from "./Navigation.svelte" export { default as link } from "./Link.svelte" -export { default as rowdetail } from "./RowDetail.svelte" export { default as heading } from "./Heading.svelte" export { default as image } from "./Image.svelte" export { default as embed } from "./Embed.svelte" @@ -34,3 +33,4 @@ export { default as search } from "./Search.svelte" export { default as backgroundimage } from "./BackgroundImage.svelte" export * from "./charts" export * from "./forms" +export * from "./table" diff --git a/packages/standard-components/src/table/AttachmentRenderer.svelte b/packages/standard-components/src/table/AttachmentRenderer.svelte new file mode 100644 index 0000000000..45bd97a849 --- /dev/null +++ b/packages/standard-components/src/table/AttachmentRenderer.svelte @@ -0,0 +1,39 @@ + + +{#each attachments as attachment} + {#if attachment.type.startsWith('image')} + {attachment.extension} + {:else} +
{attachment.extension}
+ {/if} +{/each} +{#if leftover} +
+{leftover} more
+{/if} + + diff --git a/packages/standard-components/src/table/BooleanRenderer.svelte b/packages/standard-components/src/table/BooleanRenderer.svelte new file mode 100644 index 0000000000..a71e28cb91 --- /dev/null +++ b/packages/standard-components/src/table/BooleanRenderer.svelte @@ -0,0 +1,38 @@ + + + + + diff --git a/packages/standard-components/src/table/CellRenderer.svelte b/packages/standard-components/src/table/CellRenderer.svelte new file mode 100644 index 0000000000..0c14ec2aa5 --- /dev/null +++ b/packages/standard-components/src/table/CellRenderer.svelte @@ -0,0 +1,27 @@ + + +{#if value != null && value !== ''} + {#if plainTypes.includes(type)} + + {:else if type === 'boolean'} + + {:else if type === 'datetime'} + + {:else if type === 'link'} + + {:else if type === 'attachment'} + + {/if} +{/if} diff --git a/packages/standard-components/src/table/DateTimeRenderer.svelte b/packages/standard-components/src/table/DateTimeRenderer.svelte new file mode 100644 index 0000000000..169adab163 --- /dev/null +++ b/packages/standard-components/src/table/DateTimeRenderer.svelte @@ -0,0 +1,13 @@ + + +
{dayjs(value).format('MMMM D YYYY, HH:mm')}
+ + diff --git a/packages/standard-components/src/table/RelationshipRenderer.svelte b/packages/standard-components/src/table/RelationshipRenderer.svelte new file mode 100644 index 0000000000..6c04000fa7 --- /dev/null +++ b/packages/standard-components/src/table/RelationshipRenderer.svelte @@ -0,0 +1,20 @@ + + +{#each relationships as relationship} + {#if relationship?.primaryDisplay} + + {relationship.primaryDisplay} + + {/if} +{/each} +{#if leftover} +
+{leftover} more
+{/if} diff --git a/packages/standard-components/src/table/StringRenderer.svelte b/packages/standard-components/src/table/StringRenderer.svelte new file mode 100644 index 0000000000..2756839616 --- /dev/null +++ b/packages/standard-components/src/table/StringRenderer.svelte @@ -0,0 +1,13 @@ + + +
{value}
+ + diff --git a/packages/standard-components/src/table/Table.svelte b/packages/standard-components/src/table/Table.svelte new file mode 100644 index 0000000000..8065b520e9 --- /dev/null +++ b/packages/standard-components/src/table/Table.svelte @@ -0,0 +1,290 @@ + + +{#if !loaded} +
+{:else} +
+
+
+ + + + {#if $component.children} + + {/if} + {#each fields as field} + + {/each} + + + + {#each sortedRows as row, idx} + lastVisibleRow}> + {#if idx >= firstVisibleRow && idx <= lastVisibleRow} + {#if $component.children} + + {/if} + {#each fields as field} + + {/each} + {/if} + + {/each} + +
+
+
sortBy(field)}> +
+
{schema[field]?.name}
+ +
+
+
+ + + +
+
+
+ +
+
+
+
+
+{/if} + + diff --git a/packages/standard-components/src/table/index.js b/packages/standard-components/src/table/index.js new file mode 100644 index 0000000000..410fe1ff6c --- /dev/null +++ b/packages/standard-components/src/table/index.js @@ -0,0 +1 @@ +export { default as table } from "./Table.svelte" diff --git a/packages/standard-components/yarn.lock b/packages/standard-components/yarn.lock index 8a026bd592..f6a65cec64 100644 --- a/packages/standard-components/yarn.lock +++ b/packages/standard-components/yarn.lock @@ -56,13 +56,6 @@ svelte-portal "^1.0.0" turndown "^7.0.0" -"@budibase/svelte-ag-grid@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@budibase/svelte-ag-grid/-/svelte-ag-grid-1.0.4.tgz#41cceec4bde2c4aea8b9da8f610fe36055c7709f" - integrity sha512-JZm6qujxnZpqw7Twbegr6se4sHhyWzN0Cibrk5bVBH32hBgzD6dd33fxwrjHKkWFxjys9wRT+cqYgYVlSt9E3w== - dependencies: - ag-grid-community "^24.0.0" - "@polka/url@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@polka/url/-/url-0.5.0.tgz#b21510597fd601e5d7c95008b76bf0d254ebfd31" @@ -132,72 +125,82 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@spectrum-css/actionbutton@^1.0.0-beta.1": - version "1.0.0-beta.1" - resolved "https://registry.yarnpkg.com/@spectrum-css/actionbutton/-/actionbutton-1.0.0-beta.1.tgz#a6684cac108d4a9daefe0be6df8201d3c369a0d6" - integrity sha512-QbrPMTkbkmh+dEBP66TFXmF5z3qSde+BnLR5hnlo2XMvKvnblX2VJStEbQ+hTKuSZXCRFADXyXD5o0NOYDTByQ== +"@spectrum-css/actionbutton@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/actionbutton/-/actionbutton-1.0.1.tgz#9c75da37ea6915919fb574c74bd60dacc03b6577" + integrity sha512-AUqtyNabHF451Aj9i3xz82TxS5Z6k1dttA68/1hMeU9kbPCSS4P6Viw3vaRGs9CSspuR8xnnhDgrq+F+zMy2Hw== -"@spectrum-css/button@^3.0.0-beta.6": - version "3.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@spectrum-css/button/-/button-3.0.0-beta.6.tgz#007919d3e7a6692e506dc9addcd46aee6b203b1a" - integrity sha512-ZoJxezt5Pc006RR7SMG7PfC0VAdWqaGDpd21N8SEykGuz/KmNulqGW8RiSZQGMVX/jk5ZCAthPrH8cI/qtKbMg== +"@spectrum-css/button@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/button/-/button-3.0.1.tgz#6db8c3e851baecd0f1c2d88fef37d49d01c6e643" + integrity sha512-YXrBtjIYisk4Vaxnp0RiE4gdElQX04P2mc4Pi2GlQ27dJKlHmufYcF+kAqGdtiyK5yjdN/vKRcC8y13aA4rusA== -"@spectrum-css/checkbox@^3.0.0-beta.6": - version "3.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@spectrum-css/checkbox/-/checkbox-3.0.0-beta.6.tgz#338c4e58c4570ac8023f7332794fcb45f5ae9374" - integrity sha512-Z0Mwu7yn2b+QcZaBqMpKhliTQiF8T/cRyKgTyaIACtJ0FAK5NBJ4h/X6SWW3iXtoUWCH4+p/Hdtq1iQHAFi1qQ== +"@spectrum-css/checkbox@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/checkbox/-/checkbox-3.0.1.tgz#6f36377d8bd556989ddd1dec2506dc295c5fcda8" + integrity sha512-fI0q2Cp6yU4ORyE6JWUSMYNgEtGf6AjYViZ2Weg3UPTYBQuWdQd8J0ZTcH38pDMyARFPRdiXgQ3KnyX5Hk5huw== -"@spectrum-css/fieldlabel@^3.0.0-beta.7": - version "3.0.0-beta.7" - resolved "https://registry.yarnpkg.com/@spectrum-css/fieldlabel/-/fieldlabel-3.0.0-beta.7.tgz#f37797565e21b3609b8fbc2dafcea8ea41ffa114" - integrity sha512-0pseiPghqlOdALsRtidveWyt2YjfSXTZWDlSkcne/J0/QXBJOQH/7Qfy7TmROQZYRB2LqH1VzmE1zbvGwr5Aog== +"@spectrum-css/fieldlabel@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/fieldlabel/-/fieldlabel-3.0.1.tgz#39f7c0f25cc2ff402afeff005341b0832f7c588c" + integrity sha512-LMfwrwIq8wEEvxFLobdLvXRwKrp8o9Fty4iJ9aYl2Rj1uXkfRd8qLz9HGZjLEE1OuJgoTBgamYABl7EvoA5PLw== -"@spectrum-css/icon@^3.0.0-beta.2": - version "3.0.0-beta.2" - resolved "https://registry.yarnpkg.com/@spectrum-css/icon/-/icon-3.0.0-beta.2.tgz#2dd7258ded74501b56e5fc42d0b6f0a3f4936aeb" - integrity sha512-BEHJ68YIXSwsNAqTdq/FrS4A+jtbKzqYrsGKXdDf93ql+fHWYXRCh1EVYGHx/1696mY73DhM4snMpKGIFtXGFA== +"@spectrum-css/icon@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/icon/-/icon-3.0.1.tgz#e300a6fc353c85c6b5d6e7a364408a940c31b177" + integrity sha512-cGFtIrcQ/7tthdkHK1npuEFiCdYVHLqwmLxghUYQw8Tb8KgJaw3OBO1tpjgsUizexNgu26BjVRIbGxNWuBXIHQ== -"@spectrum-css/inputgroup@^3.0.0-beta.7": - version "3.0.0-beta.7" - resolved "https://registry.yarnpkg.com/@spectrum-css/inputgroup/-/inputgroup-3.0.0-beta.7.tgz#9829812e349bf973fb8835f0586bf013c8c38d23" - integrity sha512-pZDpYhtTKZUVG31Rtx7imdwK2ohLyVuTEsl+mj2yDKn+2TOwYRxr6LdbfNhFN4xd0GtSqapKYfbgKBWYpIyiSw== +"@spectrum-css/inputgroup@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/inputgroup/-/inputgroup-3.0.1.tgz#8c5b257b57b3b2cf04e99355709365fa0d6838cc" + integrity sha512-asBRa1jTlld6plkcq4ySO+xl+OJlCMSOLoAFdSSIJowcSlCV0yDy7oeOhf5YQv9mMHFWTKlWUSoAKDZTguIPxA== -"@spectrum-css/menu@^3.0.0-beta.5": - version "3.0.0-beta.5" - resolved "https://registry.yarnpkg.com/@spectrum-css/menu/-/menu-3.0.0-beta.5.tgz#99d5ea7f6760b7a89d5d732f4e91b98dd3f82d74" - integrity sha512-jvPD5GbNdX31rdFBLxCG7KoUVGeeNYLzNXDpiGZsWme/djVTwitljgNe7bhVwCVlXZE7H20Ti/YrdafnE154Rw== +"@spectrum-css/label@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@spectrum-css/label/-/label-2.0.9.tgz#792f34b906ba81118f4d0edcc81a18da1ecd57cb" + integrity sha512-0vXhWIZoQDTg+I6MyMpwmeJ+yQHtxkZ7lLcEqxhJ2y7JXP2ftblz2sO4+9jB11ljepeVlV+B6LF1drU8mMu82A== -"@spectrum-css/page@^3.0.0-beta.0": - version "3.0.0-beta.0" - resolved "https://registry.yarnpkg.com/@spectrum-css/page/-/page-3.0.0-beta.0.tgz#885ea41b44861c5dc3aac904536f9e93c9109b58" - integrity sha512-+OD+l3aLisykxJnHfLkdkxMS1Uj1vKGYpKil7W0r5lSWU44eHyRgb8ZK5Vri1+sUO5SSf/CTybeVwtXME9wMLA== +"@spectrum-css/menu@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/menu/-/menu-3.0.1.tgz#2a376f991acc24e12ec892bb6b9db2650fc41fbe" + integrity sha512-Qjg0+1O0eC89sb/bRFq2AGnQ8XqhVy23TUXHyffNM8qdcMssnlny3QmhzjURCZKvx/Y5UytCpzhedPQqSpQwZg== + +"@spectrum-css/page@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/page/-/page-3.0.1.tgz#5e1c3dd5b1a1ee591f9d636b75f03665f542d846" + integrity sha512-LAlKF8km5BlsGPpZ2SNtwKOQIHn1lz0X93aczGZVZceOg73O4gyeoT5cx4vi1z+KtBRY5VMDWx3XgGtUwwjqwA== dependencies: - "@spectrum-css/vars" "^3.0.0-beta.2" + "@spectrum-css/vars" "^3.0.1" -"@spectrum-css/picker@^1.0.0-beta.3": - version "1.0.0-beta.3" - resolved "https://registry.yarnpkg.com/@spectrum-css/picker/-/picker-1.0.0-beta.3.tgz#476593597b5a9e0105397e4e39350869cf6e7965" - integrity sha512-jHzFnS5Frd3JSwZ6B8ymH/sVnNqAUBo9p93Zax4VHTUDsPTtTkvxj/Vxo4POmrJEL9v3qUB2Yk13rD2BSfEzLQ== +"@spectrum-css/picker@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/picker/-/picker-1.0.1.tgz#98991198576d26bd14160824e7b6f3c278ff930b" + integrity sha512-Rv4/UBOdNW1gs7WVBCJnPD5VFly8MqP++psDX6kcugUIcfJy0GC3acvElotmKRlCDk8Qxks2W2A0jKeSgphTmA== -"@spectrum-css/popover@^3.0.0-beta.6": - version "3.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@spectrum-css/popover/-/popover-3.0.0-beta.6.tgz#787611f020e091234e6ba7e946b0dbd0ed1a2fa2" - integrity sha512-dUJlwxoNpB6jOR0g/ywH2cPoUz2FVsL6xPfkm6BSsLp9ejhYy0/OFF4w0Q32Fu9qJDbWJ9qaoOlPpt7IjQ+/GQ== +"@spectrum-css/popover@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/popover/-/popover-3.0.1.tgz#5863c1efc53f98f9aba2de9186666780041303fc" + integrity sha512-LmOSj/yCwQQ9iGmCYnHiJsJR/HfPiGqI1Jl7pkKxBOCxYBMS/5+ans9vfCN2Qnd0eK7WSbfPg72S6mjye7db2Q== -"@spectrum-css/stepper@^3.0.0-beta.7": - version "3.0.0-beta.7" - resolved "https://registry.yarnpkg.com/@spectrum-css/stepper/-/stepper-3.0.0-beta.7.tgz#fc78435ce878c5e233af13e43ed2c3e8671a2bbc" - integrity sha512-TQL2OBcdEgbHBwehMGgqMuWdKZZQPGcBRV5FlF0TUdOT58lEqFAO43Gajqvyte1P23lNmnX8KuMwkRfQdn0RzA== +"@spectrum-css/stepper@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/stepper/-/stepper-3.0.1.tgz#7f270f53505e7dbe082591e8ea1c4c8f397e045a" + integrity sha512-IvZlGFJ8QPr9tUz5xvVN4hASaTRDPdKu9IIp25q/x0ecgSrKAM55e3EBWEYWy1H1JI3h+zlPnNRuK0VLhDbCYA== -"@spectrum-css/textfield@^3.0.0-beta.6": - version "3.0.0-beta.6" - resolved "https://registry.yarnpkg.com/@spectrum-css/textfield/-/textfield-3.0.0-beta.6.tgz#30c044ceb403d6ea82d8046fb8f767f7fe455da6" - integrity sha512-U7P8C3Xx8h5X+r+dZu1qbxceIxBn7ZSmMvJyC7MPSPcU3EwdzCUepERNGX7NrQdcX91XSNlPUOF7hZUognBwhQ== +"@spectrum-css/table@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/table/-/table-3.0.1.tgz#753e0e2498082c0c36b9600828516aff3ac338cd" + integrity sha512-XQ+srMTv9hK1H0nctWUtqyzitmvyb5TNR+7mjAmKRdkBRSTQQSipDhenxZp72ekzMtMoSYZVZ77kgo0Iw3Fpug== -"@spectrum-css/vars@^3.0.0-beta.2": - version "3.0.0-beta.2" - resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.0-beta.2.tgz#f0b3a2db44aa57b1a82e47ab392c716a3056a157" - integrity sha512-HpcRDUkSjKVWUi7+jf6zp33YszXs3qFljaaNVTVOf0m0mqjWWXHxgLrvYlFFlHp5ITbNXds5Cb7EgiXCKmVIpA== +"@spectrum-css/textfield@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/textfield/-/textfield-3.0.1.tgz#e875b8e37817378ad08fc4af7d53026df38911e5" + integrity sha512-MUV5q87CVxbkNdSNoxGrFbgyKc51ft/WWf3aVEoPdPw5yBnXqFe1w1YmAit5zYDOOhhs58sCLAlUcCMlOpkgrA== + +"@spectrum-css/vars@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-3.0.1.tgz#561fd69098f896a647242dd8d6108af603bfa31e" + integrity sha512-l4oRcCOqInChYXZN6OQhpe3isk6l4OE6Ys8cgdlsiKp53suNoQxyyd9p/eGRbCjZgH3xQ8nK0t4DHa7QYC0S6w== "@types/color-name@^1.1.1": version "1.1.1" @@ -238,11 +241,6 @@ abstract-leveldown@~0.12.0, abstract-leveldown@~0.12.1: dependencies: xtend "~3.0.0" -ag-grid-community@^24.0.0: - version "24.0.0" - resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-24.0.0.tgz#793a3c4a28e5148c5c8c8c6ff68e8f3c4ee6ca3d" - integrity sha512-TNNqTp918PCRicH/vE8fs1pu/hbWMc9Iem7gCjGsjlabBrGhMxCq7tYiBtguERuxdszbSImznjampnc0NvOoFg== - alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -864,6 +862,11 @@ csso@^4.0.2: dependencies: css-tree "1.0.0-alpha.39" +dayjs@^1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2" + integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw== + deep-equal@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" diff --git a/packages/string-templates/src/helpers/index.js b/packages/string-templates/src/helpers/index.js index 848851a7e8..05145f7c7a 100644 --- a/packages/string-templates/src/helpers/index.js +++ b/packages/string-templates/src/helpers/index.js @@ -35,7 +35,7 @@ const HELPERS = [ new Helper(HelperFunctionNames.LITERAL, value => { const type = typeof value const outputVal = type === "object" ? JSON.stringify(value) : value - return `{{-${LITERAL_MARKER}-${type}-${outputVal}-}}` + return `{{${LITERAL_MARKER} ${type}-${outputVal}}}` }), ] diff --git a/packages/string-templates/src/processors/postprocessor.js b/packages/string-templates/src/processors/postprocessor.js index b48dceace5..adc8362abe 100644 --- a/packages/string-templates/src/processors/postprocessor.js +++ b/packages/string-templates/src/processors/postprocessor.js @@ -21,13 +21,12 @@ module.exports.processors = [ if (!statement.includes(LITERAL_MARKER)) { return statement } - - const components = statement.split("-") - // pop and shift remove the empty array elements from the first and last dash - components.pop() - components.shift() - const type = components[1] - const value = components[2] + const splitMarkerIndex = statement.indexOf("-") + const type = statement.substring(12, splitMarkerIndex) + const value = statement.substring( + splitMarkerIndex + 1, + statement.length - 2 + ) switch (type) { case "string": return value diff --git a/packages/string-templates/test/helpers.spec.js b/packages/string-templates/test/helpers.spec.js index 89776583c9..08366a8296 100644 --- a/packages/string-templates/test/helpers.spec.js +++ b/packages/string-templates/test/helpers.spec.js @@ -1,15 +1,11 @@ -const { - processString, - processObject, - isValid, -} = require("../src/index") +const { processString, processObject, isValid } = require("../src/index") describe("test the custom helpers we have applied", () => { it("should be able to use the object helper", async () => { const output = await processString("object is {{ object obj }}", { obj: { a: 1 }, }) - expect(output).toBe("object is {\"a\":1}") + expect(output).toBe('object is {"a":1}') }) }) @@ -64,9 +60,12 @@ describe("test the array helpers", () => { }) it("should allow use of the filter helper", async () => { - const output = await processString("{{#filter array \"person\"}}THING{{else}}OTHER{{/filter}}", { - array, - }) + const output = await processString( + '{{#filter array "person"}}THING{{else}}OTHER{{/filter}}', + { + array, + } + ) expect(output).toBe("THING") }) @@ -78,7 +77,7 @@ describe("test the array helpers", () => { }) it("should allow use of the join helper", async () => { - const output = await processString("{{join array \"-\"}}", { + const output = await processString('{{join array "-"}}', { array, }) expect(output).toBe("hi-person-how-are-you") @@ -86,14 +85,14 @@ describe("test the array helpers", () => { it("should allow use of the sort helper", async () => { const output = await processString("{{sort array}}", { - array: ["d", "a", "c", "e"] + array: ["d", "a", "c", "e"], }) expect(output).toBe("a,c,d,e") }) it("should allow use of the unique helper", async () => { const output = await processString("{{unique array}}", { - array: ["a", "a", "b"] + array: ["a", "a", "b"], }) expect(output).toBe("a,b") }) @@ -102,7 +101,7 @@ describe("test the array helpers", () => { describe("test the number helpers", () => { it("should allow use of the addCommas helper", async () => { const output = await processString("{{ addCommas number }}", { - number: 10000000 + number: 10000000, }) expect(output).toBe("10,000,000") }) @@ -132,7 +131,7 @@ describe("test the number helpers", () => { describe("test the url helpers", () => { const url = "http://example.com?query=1" it("should allow use of the stripQueryString helper", async () => { - const output = await processString('{{stripQuerystring url }}', { + const output = await processString("{{stripQuerystring url }}", { url, }) expect(output).toBe("http://example.com") @@ -149,10 +148,12 @@ describe("test the url helpers", () => { const output = await processString("{{ object ( urlParse url ) }}", { url, }) - expect(output).toBe("{\"protocol\":\"http:\",\"slashes\":true,\"auth\":null,\"host\":\"example.com\"," + - "\"port\":null,\"hostname\":\"example.com\",\"hash\":null,\"search\":\"?query=1\"," + - "\"query\":\"query=1\",\"pathname\":\"/\",\"path\":\"/?query=1\"," + - "\"href\":\"http://example.com/?query=1\"}") + expect(output).toBe( + '{"protocol":"http:","slashes":true,"auth":null,"host":"example.com",' + + '"port":null,"hostname":"example.com","hash":null,"search":"?query=1",' + + '"query":"query=1","pathname":"/","path":"/?query=1",' + + '"href":"http://example.com/?query=1"}' + ) }) }) @@ -224,19 +225,25 @@ describe("test the string helpers", () => { }) it("should allow use of the startsWith helper", async () => { - const output = await processString("{{ #startsWith 'Hello' string }}Hi!{{ else }}Goodbye!{{ /startsWith }}", { - string: "Hello my name is Mike", - }) + const output = await processString( + "{{ #startsWith 'Hello' string }}Hi!{{ else }}Goodbye!{{ /startsWith }}", + { + string: "Hello my name is Mike", + } + ) expect(output).toBe("Hi!") }) }) describe("test the comparison helpers", () => { async function compare(func, a, b) { - const output = await processString(`{{ #${func} a b }}Success{{ else }}Fail{{ /${func} }}`, { - a, - b, - }) + const output = await processString( + `{{ #${func} a b }}Success{{ else }}Fail{{ /${func} }}`, + { + a, + b, + } + ) expect(output).toBe("Success") } it("should allow use of the lt helper", async () => { @@ -256,9 +263,12 @@ describe("test the comparison helpers", () => { }) it("should allow use of gte with a literal value", async () => { - const output = await processString(`{{ #gte a "50" }}s{{ else }}f{{ /gte }}`, { - a: 51, - }) + const output = await processString( + `{{ #gte a "50" }}s{{ else }}f{{ /gte }}`, + { + a: 51, + } + ) expect(output).toBe("s") }) }) @@ -273,21 +283,31 @@ describe("Test the literal helper", () => { it("should allow use of the literal specifier for an object", async () => { const output = await processString(`{{literal a}}`, { - a: {b: 1}, + a: { b: 1 }, }) expect(output.b).toBe(1) }) + + it("should allow use of the literal specifier for an object with dashes", async () => { + const output = await processString(`{{literal a}}`, { + a: { b: "i-have-dashes" }, + }) + expect(output.b).toBe("i-have-dashes") + }) }) describe("Cover a few complex use cases", () => { it("should allow use of three different collection helpers", async () => { - const output = await processString(`{{ join ( after ( split "My name is: Joe Smith" " " ) 3 ) " " }}`, {}) + const output = await processString( + `{{ join ( after ( split "My name is: Joe Smith" " " ) 3 ) " " }}`, + {} + ) expect(output).toBe("Joe Smith") }) it("should allow a complex array case", async () => { const output = await processString("{{ last ( sort ( unique array ) ) }}", { - array: ["a", "a", "d", "c", "e"] + array: ["a", "a", "d", "c", "e"], }) expect(output).toBe("e") }) @@ -299,7 +319,9 @@ describe("Cover a few complex use cases", () => { }) it("should make sure case is valid", () => { - const validity = isValid("{{ avg [c355ec2b422e54f988ae553c8acd811ea].[a] [c355ec2b422e54f988ae553c8acd811ea].[b] }}") + const validity = isValid( + "{{ avg [c355ec2b422e54f988ae553c8acd811ea].[a] [c355ec2b422e54f988ae553c8acd811ea].[b] }}" + ) expect(validity).toBe(true) }) @@ -314,7 +336,9 @@ describe("Cover a few complex use cases", () => { }) it("should confirm a subtraction validity", () => { - const validity = isValid("{{ subtract [c390c23a7f1b6441c98d2fe2a51248ef3].[total profit] [c390c23a7f1b6441c98d2fe2a51248ef3].[total revenue] }}") + const validity = isValid( + "{{ subtract [c390c23a7f1b6441c98d2fe2a51248ef3].[total profit] [c390c23a7f1b6441c98d2fe2a51248ef3].[total revenue] }}" + ) expect(validity).toBe(true) }) @@ -344,9 +368,11 @@ describe("Cover a few complex use cases", () => { }) it("getting a nice date from the user", async () => { - const input = {text: `{{ date user.subscriptionDue "DD-MM" }}`} - const context = JSON.parse(`{"user":{"email":"test@test.com","roleId":"ADMIN","type":"user","tableId":"ta_users","subscriptionDue":"2021-01-12T12:00:00.000Z","_id":"ro_ta_users_us_test@test.com","_rev":"2-24cc794985eb54183ecb93e148563f3d"}}`) + const input = { text: `{{ date user.subscriptionDue "DD-MM" }}` } + const context = JSON.parse( + `{"user":{"email":"test@test.com","roleId":"ADMIN","type":"user","tableId":"ta_users","subscriptionDue":"2021-01-12T12:00:00.000Z","_id":"ro_ta_users_us_test@test.com","_rev":"2-24cc794985eb54183ecb93e148563f3d"}}` + ) const output = await processObject(input, context) expect(output.text).toBe("12-01") }) -}) \ No newline at end of file +})