Merge branch 'form-builder' into relationship-one-to-many
This commit is contained in:
commit
8a2cc83a3b
|
@ -9,6 +9,7 @@ import {
|
||||||
makeSaveButton,
|
makeSaveButton,
|
||||||
makeTableFormComponents,
|
makeTableFormComponents,
|
||||||
makeMainForm,
|
makeMainForm,
|
||||||
|
spectrumColor,
|
||||||
} from "./utils/commonComponents"
|
} from "./utils/commonComponents"
|
||||||
|
|
||||||
export default function(tables) {
|
export default function(tables) {
|
||||||
|
@ -52,6 +53,7 @@ function generateTitleContainer(table, title, formId) {
|
||||||
background: "transparent",
|
background: "transparent",
|
||||||
color: "#4285f4",
|
color: "#4285f4",
|
||||||
})
|
})
|
||||||
|
.customStyle(spectrumColor(700))
|
||||||
.text("Delete")
|
.text("Delete")
|
||||||
.customProps({
|
.customProps({
|
||||||
className: "",
|
className: "",
|
||||||
|
|
|
@ -29,6 +29,11 @@ export class Component extends BaseStructure {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customStyle(styling) {
|
||||||
|
this._json._styles.custom = styling
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
instanceName(name) {
|
instanceName(name) {
|
||||||
this._json._instanceName = name
|
this._json._instanceName = name
|
||||||
return this
|
return this
|
||||||
|
|
|
@ -3,6 +3,15 @@ import { Component } from "./Component"
|
||||||
import { rowListUrl } from "../rowListScreen"
|
import { rowListUrl } from "../rowListScreen"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
|
|
||||||
|
export function spectrumColor(number) {
|
||||||
|
// Acorn throws a parsing error in this file if the word g-l-o-b-a-l is found
|
||||||
|
// (without dashes - I can't even type it in a comment).
|
||||||
|
// God knows why. It seems to think optional chaining further down the
|
||||||
|
// file is invalid if the word g-l-o-b-a-l is found - hence the reason this
|
||||||
|
// statement is split into parts.
|
||||||
|
return "color: var(--spectrum-glo" + `bal-color-gray-${number});`
|
||||||
|
}
|
||||||
|
|
||||||
export function makeLinkComponent(tableName) {
|
export function makeLinkComponent(tableName) {
|
||||||
return new Component("@budibase/standard-components/link")
|
return new Component("@budibase/standard-components/link")
|
||||||
.normalStyle({
|
.normalStyle({
|
||||||
|
@ -12,6 +21,7 @@ export function makeLinkComponent(tableName) {
|
||||||
.hoverStyle({
|
.hoverStyle({
|
||||||
color: "#4285f4",
|
color: "#4285f4",
|
||||||
})
|
})
|
||||||
|
.customStyle(spectrumColor(700))
|
||||||
.text(tableName)
|
.text(tableName)
|
||||||
.customProps({
|
.customProps({
|
||||||
url: `/${tableName.toLowerCase()}`,
|
url: `/${tableName.toLowerCase()}`,
|
||||||
|
@ -52,6 +62,7 @@ export function makeBreadcrumbContainer(tableName, text, capitalise = false) {
|
||||||
"margin-right": "4px",
|
"margin-right": "4px",
|
||||||
"margin-left": "4px",
|
"margin-left": "4px",
|
||||||
})
|
})
|
||||||
|
.customStyle(spectrumColor(700))
|
||||||
.text(">")
|
.text(">")
|
||||||
.instanceName("Arrow")
|
.instanceName("Arrow")
|
||||||
|
|
||||||
|
@ -64,6 +75,7 @@ export function makeBreadcrumbContainer(tableName, text, capitalise = false) {
|
||||||
const identifierText = new Component("@budibase/standard-components/text")
|
const identifierText = new Component("@budibase/standard-components/text")
|
||||||
.type("none")
|
.type("none")
|
||||||
.normalStyle(textStyling)
|
.normalStyle(textStyling)
|
||||||
|
.customStyle(spectrumColor(700))
|
||||||
.text(text)
|
.text(text)
|
||||||
.instanceName("Identifier")
|
.instanceName("Identifier")
|
||||||
|
|
||||||
|
@ -132,6 +144,7 @@ export function makeTitleContainer(title) {
|
||||||
"margin-left": "0px",
|
"margin-left": "0px",
|
||||||
flex: "1 1 auto",
|
flex: "1 1 auto",
|
||||||
})
|
})
|
||||||
|
.customStyle(spectrumColor(900))
|
||||||
.type("h3")
|
.type("h3")
|
||||||
.instanceName("Title")
|
.instanceName("Title")
|
||||||
.text(title)
|
.text(title)
|
||||||
|
|
|
@ -36,7 +36,9 @@
|
||||||
{:else if type === 'boolean'}
|
{:else if type === 'boolean'}
|
||||||
<Toggle text={label} bind:checked={value} data-cy="{meta.name}-input" />
|
<Toggle text={label} bind:checked={value} data-cy="{meta.name}-input" />
|
||||||
{:else if type === 'link'}
|
{:else if type === 'link'}
|
||||||
<LinkedRowSelector bind:linkedRows={value} schema={meta} />
|
<div>
|
||||||
|
<LinkedRowSelector bind:linkedRows={value} schema={meta} />
|
||||||
|
</div>
|
||||||
{:else if type === 'longform'}
|
{:else if type === 'longform'}
|
||||||
<div>
|
<div>
|
||||||
<Label extraSmall grey>{label}</Label>
|
<Label extraSmall grey>{label}</Label>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<DetailSummary name={`${name}${changed ? ' *' : ''}`} on:open show={open} thin>
|
<DetailSummary name={`${name}${changed ? ' *' : ''}`} on:open show={open} thin>
|
||||||
{#if open}
|
{#if open}
|
||||||
<div>
|
<div>
|
||||||
{#each properties as prop (`${componentInstance._id}-${prop.key}`)}
|
{#each properties as prop (`${componentInstance._id}-${prop.key}-${prop.label}`)}
|
||||||
<PropertyControl
|
<PropertyControl
|
||||||
bindable={false}
|
bindable={false}
|
||||||
label={`${prop.label}${hasPropChanged(style, prop) ? ' *' : ''}`}
|
label={`${prop.label}${hasPropChanged(style, prop) ? ' *' : ''}`}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
const {
|
const {
|
||||||
supertest,
|
supertest,
|
||||||
createApplication,
|
createApplication,
|
||||||
defaultHeaders,
|
defaultHeaders,
|
||||||
builderEndpointShouldBlockNormalUsers,
|
builderEndpointShouldBlockNormalUsers,
|
||||||
getDocument,
|
getDocument,
|
||||||
insertDocument
|
insertDocument,
|
||||||
} = require("./couchTestUtils")
|
} = require("./couchTestUtils")
|
||||||
let { generateDatasourceID, generateQueryID } = require("../../../db/utils")
|
let { generateDatasourceID, generateQueryID } = require("../../../db/utils")
|
||||||
|
|
||||||
|
@ -21,11 +21,11 @@ const TEST_DATASOURCE = {
|
||||||
const TEST_QUERY = {
|
const TEST_QUERY = {
|
||||||
_id: generateQueryID(DATASOURCE_ID),
|
_id: generateQueryID(DATASOURCE_ID),
|
||||||
datasourceId: DATASOURCE_ID,
|
datasourceId: DATASOURCE_ID,
|
||||||
name:"New Query",
|
name: "New Query",
|
||||||
parameters:[],
|
parameters: [],
|
||||||
fields:{},
|
fields: {},
|
||||||
schema:{},
|
schema: {},
|
||||||
queryVerb:"read",
|
queryVerb: "read",
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("/queries", () => {
|
describe("/queries", () => {
|
||||||
|
@ -37,8 +37,8 @@ describe("/queries", () => {
|
||||||
let query
|
let query
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
({ request, server } = await supertest())
|
;({ request, server } = await supertest())
|
||||||
});
|
})
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
server.close()
|
server.close()
|
||||||
|
@ -47,7 +47,7 @@ describe("/queries", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
app = await createApplication(request)
|
app = await createApplication(request)
|
||||||
appId = app.instance._id
|
appId = app.instance._id
|
||||||
});
|
})
|
||||||
|
|
||||||
async function createDatasource() {
|
async function createDatasource() {
|
||||||
return await insertDocument(appId, TEST_DATASOURCE)
|
return await insertDocument(appId, TEST_DATASOURCE)
|
||||||
|
@ -63,65 +63,68 @@ describe("/queries", () => {
|
||||||
.post(`/api/queries`)
|
.post(`/api/queries`)
|
||||||
.send(TEST_QUERY)
|
.send(TEST_QUERY)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect('Content-Type', /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.res.statusMessage).toEqual(`Query ${TEST_QUERY.name} saved successfully.`);
|
expect(res.res.statusMessage).toEqual(
|
||||||
expect(res.body).toEqual({
|
`Query ${TEST_QUERY.name} saved successfully.`
|
||||||
_rev: res.body._rev,
|
)
|
||||||
...TEST_QUERY,
|
expect(res.body).toEqual({
|
||||||
});
|
_rev: res.body._rev,
|
||||||
|
...TEST_QUERY,
|
||||||
})
|
})
|
||||||
});
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("fetch", () => {
|
describe("fetch", () => {
|
||||||
let datasource
|
let datasource
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
datasource = await createDatasource()
|
datasource = await createDatasource()
|
||||||
});
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
delete datasource._rev
|
delete datasource._rev
|
||||||
});
|
})
|
||||||
|
|
||||||
it("returns all the queries from the server", async () => {
|
it("returns all the queries from the server", async () => {
|
||||||
const query = await createQuery()
|
const query = await createQuery()
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/queries`)
|
.get(`/api/queries`)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect('Content-Type', /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
const queries = res.body;
|
const queries = res.body
|
||||||
expect(queries).toEqual([
|
expect(queries).toEqual([
|
||||||
{
|
{
|
||||||
"_rev": query.rev,
|
_rev: query.rev,
|
||||||
...TEST_QUERY
|
...TEST_QUERY,
|
||||||
}
|
readable: true,
|
||||||
]);
|
},
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
await builderEndpointShouldBlockNormalUsers({
|
await builderEndpointShouldBlockNormalUsers({
|
||||||
request,
|
request,
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/api/datasources`,
|
url: `/api/datasources`,
|
||||||
appId: appId,
|
appId: appId,
|
||||||
})
|
|
||||||
})
|
})
|
||||||
});
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("destroy", () => {
|
describe("destroy", () => {
|
||||||
let datasource;
|
let datasource
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
datasource = await createDatasource()
|
datasource = await createDatasource()
|
||||||
});
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
delete datasource._rev
|
delete datasource._rev
|
||||||
});
|
})
|
||||||
|
|
||||||
it("deletes a query and returns a success message", async () => {
|
it("deletes a query and returns a success message", async () => {
|
||||||
const query = await createQuery()
|
const query = await createQuery()
|
||||||
|
@ -134,10 +137,10 @@ describe("/queries", () => {
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/queries`)
|
.get(`/api/queries`)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect('Content-Type', /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.body).toEqual([])
|
expect(res.body).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
@ -148,5 +151,5 @@ describe("/queries", () => {
|
||||||
appId: appId,
|
appId: appId,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
|
@ -138,6 +138,13 @@ class LinkController {
|
||||||
// iterate through the link IDs in the row field, see if any don't exist already
|
// iterate through the link IDs in the row field, see if any don't exist already
|
||||||
for (let linkId of rowField) {
|
for (let linkId of rowField) {
|
||||||
if (linkId && linkId !== "" && linkDocIds.indexOf(linkId) === -1) {
|
if (linkId && linkId !== "" && linkDocIds.indexOf(linkId) === -1) {
|
||||||
|
// first check the doc we're linking to exists
|
||||||
|
try {
|
||||||
|
await this._db.get(linkId)
|
||||||
|
} catch (err) {
|
||||||
|
// skip links that don't exist
|
||||||
|
continue
|
||||||
|
}
|
||||||
operations.push(
|
operations.push(
|
||||||
new LinkDocument(
|
new LinkDocument(
|
||||||
table._id,
|
table._id,
|
||||||
|
|
|
@ -1294,12 +1294,6 @@
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "Label",
|
"label": "Label",
|
||||||
"key": "label"
|
"key": "label"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"label": "Placeholder",
|
|
||||||
"key": "placeholder",
|
|
||||||
"placeholder": "Choose an option"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue