searching components
This commit is contained in:
parent
55bf142a95
commit
1413ee6f2c
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
const apiCall = (method) => (url, body, returnResponse=false) =>
|
||||||
|
fetch(url, {
|
||||||
|
method: method,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: body && JSON.stringify(body),
|
||||||
|
}).then(r => returnResponse ? r : r.json());
|
||||||
|
|
||||||
|
const post = apiCall("POST");
|
||||||
|
const get = apiCall("GET");
|
||||||
|
const patch = apiCall("PATCH");
|
||||||
|
const del = apiCall("DELETE");
|
||||||
|
|
||||||
|
export default {
|
||||||
|
post, get, patch, delete:del
|
||||||
|
};
|
|
@ -1,12 +1,31 @@
|
||||||
import {hierarchy as hierarchyFunctions,
|
import {
|
||||||
common, getTemplateApi } from "budibase-core";
|
hierarchy as hierarchyFunctions,
|
||||||
import {filter, cloneDeep, sortBy, map, last, keys,
|
common
|
||||||
cloneDeep, keyBy,
|
} from "budibase-core";
|
||||||
find, isEmpty, groupBy, reduce} from "lodash/fp";
|
import {
|
||||||
import {chain, getNode, validate,
|
filter,
|
||||||
constructHierarchy, templateApi} from "../common/core";
|
cloneDeep,
|
||||||
|
sortBy,
|
||||||
|
map,
|
||||||
|
last,
|
||||||
|
keys,
|
||||||
|
cloneDeep,
|
||||||
|
concat,
|
||||||
|
find,
|
||||||
|
isEmpty,
|
||||||
|
groupBy,
|
||||||
|
reduce
|
||||||
|
} from "lodash/fp";
|
||||||
|
import {
|
||||||
|
chain,
|
||||||
|
getNode,
|
||||||
|
validate,
|
||||||
|
constructHierarchy,
|
||||||
|
templateApi
|
||||||
|
} from "../common/core";
|
||||||
import {writable} from "svelte/store";
|
import {writable} from "svelte/store";
|
||||||
import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesObject"
|
import { defaultPagesObject } from "../userInterface/pagesParsing/defaultPagesObject"
|
||||||
|
import api from "./api";
|
||||||
|
|
||||||
const pipe = common.$;
|
const pipe = common.$;
|
||||||
|
|
||||||
|
@ -53,6 +72,8 @@ export const getStore = () => {
|
||||||
store.saveDerivedComponent = saveDerivedComponent(store);
|
store.saveDerivedComponent = saveDerivedComponent(store);
|
||||||
store.refreshComponents = refreshComponents(store);
|
store.refreshComponents = refreshComponents(store);
|
||||||
store.addComponentLibrary = addComponentLibrary(store);
|
store.addComponentLibrary = addComponentLibrary(store);
|
||||||
|
store.renameDerivedComponent = renameDerivedComponent(store);
|
||||||
|
store.deleteDerivedComponent = deleteDerivedComponent(store);
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,20 +86,20 @@ const initialise = (store, initial) => async () => {
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
if(!appname) {
|
if(!appname) {
|
||||||
initial.apps = await fetch(`/_builder/api/apps`)
|
initial.apps = await api.get(`/_builder/api/apps`);
|
||||||
.then(r => r.json());
|
|
||||||
initial.hasAppPackage = false;
|
initial.hasAppPackage = false;
|
||||||
store.set(initial);
|
store.set(initial);
|
||||||
return initial;
|
return initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pkg = await fetch(`/_builder/api/${appname}/appPackage`)
|
const pkg = await api.get(`/_builder/api/${appname}/appPackage`);
|
||||||
.then(r => r.json());
|
|
||||||
|
|
||||||
initial.appname = appname;
|
initial.appname = appname;
|
||||||
initial.hasAppPackage = true;
|
initial.hasAppPackage = true;
|
||||||
initial.hierarchy = pkg.appDefinition.hierarchy;
|
initial.hierarchy = pkg.appDefinition.hierarchy;
|
||||||
initial.accessLevels = pkg.accessLevels;
|
initial.accessLevels = pkg.accessLevels;
|
||||||
|
initial.derivedComponents = pkg.derivedComponents;
|
||||||
|
initial.rootComponents = pkg.rootComponents;
|
||||||
initial.actions = reduce((arr, action) => {
|
initial.actions = reduce((arr, action) => {
|
||||||
arr.push(action);
|
arr.push(action);
|
||||||
return arr;
|
return arr;
|
||||||
|
@ -344,23 +365,53 @@ const saveDerivedComponent = store => (derivedComponent) => {
|
||||||
|
|
||||||
s.derivedComponents = derivedComponents;
|
s.derivedComponents = derivedComponents;
|
||||||
|
|
||||||
fetch(`/_builder/api/${s.appname}/derivedcomponent`, {
|
api.post(`/_builder/api/${s.appname}/derivedcomponent`, derivedComponent);
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(derivedComponent),
|
|
||||||
});
|
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
})
|
})
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteDerivedComponent = store => name => {
|
||||||
|
store.update(s => {
|
||||||
|
|
||||||
|
const derivedComponents = pipe(s.derivedComponents, [
|
||||||
|
filter(c => c._name !== name)
|
||||||
|
]);
|
||||||
|
|
||||||
|
s.derivedComponents = derivedComponents;
|
||||||
|
|
||||||
|
api.delete(`/_builder/api/${s.appname}/derivedcomponent/${name}`);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const renameDerivedComponent = store => (oldname, newname) => {
|
||||||
|
store.update(s => {
|
||||||
|
|
||||||
|
const component = pipe(s.derivedComponents, [
|
||||||
|
find(c => c._name === name)
|
||||||
|
]);
|
||||||
|
|
||||||
|
component._name = newname;
|
||||||
|
|
||||||
|
const derivedComponents = pipe(s.derivedComponents, [
|
||||||
|
filter(c => c._name !== name),
|
||||||
|
concat(component)
|
||||||
|
]);
|
||||||
|
|
||||||
|
s.derivedComponent = derivedComponents;
|
||||||
|
|
||||||
|
api.delete(`/_builder/api/${s.appname}/derivedcomponent/${name}`);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const addComponentLibrary = store => async lib => {
|
const addComponentLibrary = store => async lib => {
|
||||||
|
|
||||||
const response =
|
const response =
|
||||||
await fetch(`/_builder/api/${db.appname}/components?${encodeURI(lib)}`);
|
await api.get(`/_builder/api/${db.appname}/components?${encodeURI(lib)}`,undefined, true);
|
||||||
|
|
||||||
const success = response.status === 200;
|
const success = response.status === 200;
|
||||||
|
|
||||||
|
@ -390,8 +441,7 @@ const addComponentLibrary = store => async lib => {
|
||||||
const refreshComponents = store => async () => {
|
const refreshComponents = store => async () => {
|
||||||
|
|
||||||
const components =
|
const components =
|
||||||
await fetch(`/_builder/api/${db.appname}/components`)
|
await api.get(`/_builder/api/${db.appname}/components`);
|
||||||
.then(r => jQuery.json());
|
|
||||||
|
|
||||||
const rootComponents = pipe(components, [
|
const rootComponents = pipe(components, [
|
||||||
keys,
|
keys,
|
||||||
|
@ -420,11 +470,5 @@ const savePackage = (store, s) => {
|
||||||
accessLevels:s.accessLevels
|
accessLevels:s.accessLevels
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(`/_builder/api/${s.appname}/appPackage`, {
|
api.post(`/_builder/api/${s.appname}/appPackage`, data);
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
import {
|
||||||
|
find,
|
||||||
|
isUndefined,
|
||||||
|
filter,
|
||||||
|
some,
|
||||||
|
includes
|
||||||
|
} from "lodash/fp";
|
||||||
|
|
||||||
|
import {
|
||||||
|
common
|
||||||
|
} from "budibase-core";
|
||||||
|
|
||||||
|
|
||||||
|
const pipe = common.$;
|
||||||
|
|
||||||
|
const normalString = s => (s||"").trim().toLowerCase();
|
||||||
|
|
||||||
|
const isRootComponent = c => isUndefined(c.inherits);
|
||||||
|
|
||||||
|
export const searchAllComponents = (derivedComponents, rootComponents, phrase) => {
|
||||||
|
|
||||||
|
const hasPhrase = (...vals) => pipe(vals, [
|
||||||
|
some(v => includes(phrase)(v))
|
||||||
|
]);
|
||||||
|
|
||||||
|
const rootComponentMatches = c =>
|
||||||
|
hasPhrase(c.name, ...(c.tags || []));
|
||||||
|
|
||||||
|
const derivedComponentMatches = c => {
|
||||||
|
if(hasPhrase(c.name, ...(c.tags || []))) return true;
|
||||||
|
|
||||||
|
const parent = getExactComponent(
|
||||||
|
derivedComponents,
|
||||||
|
rootComponents,
|
||||||
|
c.inherits);
|
||||||
|
|
||||||
|
if(isRootComponent(parent))
|
||||||
|
return rootComponentMatches(parent);
|
||||||
|
|
||||||
|
return derivedComponentMatches(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ([
|
||||||
|
...filter(derivedComponentMatches)(derivedComponents),
|
||||||
|
...filter(rootComponentMatches)(rootComponents)
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getExactComponent = (derivedComponents, rootComponents, name) => {
|
||||||
|
|
||||||
|
const stringEquals = (s1, s2) =>
|
||||||
|
normalString(s1) === normalString(s2);
|
||||||
|
|
||||||
|
const derived = pipe(derivedComponents,[
|
||||||
|
find(c => stringEquals(c.name, name))
|
||||||
|
]);
|
||||||
|
|
||||||
|
if(derived) return derived;
|
||||||
|
|
||||||
|
const root = pipe(rootComponents,[
|
||||||
|
find(c => stringEquals(c.name, name))
|
||||||
|
]);
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getAncestorProps = (derivedComponents, rootComponents, name, found=[]) => {
|
||||||
|
const thisComponent = getExactComponent(
|
||||||
|
derivedComponents, rootComponents, name);
|
||||||
|
|
||||||
|
if(isRootComponent(thisComponent))
|
||||||
|
return [thisComponent.props, ...found];
|
||||||
|
|
||||||
|
return getAncestorProps(
|
||||||
|
derivedComponents,
|
||||||
|
rootComponents,
|
||||||
|
thisComponent.inherits,
|
||||||
|
[{_component:thisComponent.inherits, ...thisComponent.props},
|
||||||
|
...found]);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
import {
|
||||||
|
searchAllComponents,
|
||||||
|
getExactComponent,
|
||||||
|
getAncestorProps
|
||||||
|
} from "../src/userInterface/pagesParsing/searchComponents";
|
||||||
|
|
||||||
|
|
||||||
|
describe("searchAllComponents", () => {
|
||||||
|
|
||||||
|
it("should match derived component by name", () => {
|
||||||
|
|
||||||
|
const results = searchAllComponents(
|
||||||
|
derivedComponents(),
|
||||||
|
rootComponents(),
|
||||||
|
"smalltextbox"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(results.length).toBe(1);
|
||||||
|
expect(results[0].name).toBe("common/SmallTextbox");
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getExactComponent", () => {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getAncestorProps", () => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
const derivedComponents = () => ([
|
||||||
|
{
|
||||||
|
inherits:"budibase-components/TextBox",
|
||||||
|
name: "common/SmallTextbox",
|
||||||
|
props: {
|
||||||
|
size: "small"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inherits:"common/SmallTextbox",
|
||||||
|
name: "common/PasswordBox",
|
||||||
|
tags: ["mask"],
|
||||||
|
props: {
|
||||||
|
isPassword: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inherits:"budibase-components/Button",
|
||||||
|
name:"PrimaryButton",
|
||||||
|
props: {
|
||||||
|
css:"btn-primary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const rootComponents = () => ([
|
||||||
|
{
|
||||||
|
name: "budibase-components/TextBox",
|
||||||
|
tags: ["Text", "input"],
|
||||||
|
props: {
|
||||||
|
size: {type:"options", options:["small", "medium", "large"]},
|
||||||
|
isPassword: "boolean",
|
||||||
|
placeholder: "string",
|
||||||
|
label:"string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "budibase-components/Button",
|
||||||
|
tags: ["input"],
|
||||||
|
props: {
|
||||||
|
size: {type:"options", options:["small", "medium", "large"]},
|
||||||
|
css: "string",
|
||||||
|
content: "component"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
|
@ -1,4 +1,9 @@
|
||||||
{
|
{
|
||||||
"_component": "./customComponents/textbox",
|
"inherits": "./customComponents/textbox",
|
||||||
"label": "hello"
|
"name": "myTextBox",
|
||||||
|
"tags": [],
|
||||||
|
"description": "A text input, with a label",
|
||||||
|
"props" : {
|
||||||
|
"label": "hello"
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,9 @@
|
||||||
{
|
{
|
||||||
"_component": "./moreCustomComponents/textbox",
|
"inherits": "./moreCustomComponents/textbox",
|
||||||
"label": "hello"
|
"name":"subfolder/otherTextBox.json",
|
||||||
|
"tags": [],
|
||||||
|
"description": "A text input, with a label",
|
||||||
|
"props" : {
|
||||||
|
"label": "hello"
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -68,8 +68,8 @@ it("/apppackage should get derivedComponents", async () => {
|
||||||
.expect(statusCodes.OK);
|
.expect(statusCodes.OK);
|
||||||
|
|
||||||
const expectedComponents = {
|
const expectedComponents = {
|
||||||
"myTextBox" : {...derivedComponent1, _name:"myTextBox"},
|
"myTextBox" : {...derivedComponent1, name:"myTextBox"},
|
||||||
"subfolder/otherTextBox": {...derivedComponent2, _name:"subfolder/otherTextBox"}
|
"subfolder/otherTextBox": {...derivedComponent2, name:"subfolder/otherTextBox"}
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(body.derivedComponents).toEqual(expectedComponents);
|
expect(body.derivedComponents).toEqual(expectedComponents);
|
||||||
|
@ -77,9 +77,11 @@ it("/apppackage should get derivedComponents", async () => {
|
||||||
|
|
||||||
it("should be able to create new derived component", async () => {
|
it("should be able to create new derived component", async () => {
|
||||||
const newDerivedComponent = {
|
const newDerivedComponent = {
|
||||||
_name: "newTextBox",
|
name: "newTextBox",
|
||||||
_component: "./customComponents/textbox",
|
inherits: "./customComponents/textbox",
|
||||||
label: "something"
|
props: {
|
||||||
|
label: "something"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
await app.post("/_builder/api/testApp/derivedcomponent", newDerivedComponent)
|
await app.post("/_builder/api/testApp/derivedcomponent", newDerivedComponent)
|
||||||
|
@ -93,9 +95,11 @@ it("should be able to create new derived component", async () => {
|
||||||
|
|
||||||
it("should be able to update derived component", async () => {
|
it("should be able to update derived component", async () => {
|
||||||
const updatedDerivedComponent = {
|
const updatedDerivedComponent = {
|
||||||
_name: "newTextBox",
|
name: "newTextBox",
|
||||||
_component: "./customComponents/textbox",
|
inherits: "./customComponents/textbox",
|
||||||
label: "something else"
|
props: {
|
||||||
|
label: "something else"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
await app.post("/_builder/api/testApp/derivedcomponent", updatedDerivedComponent)
|
await app.post("/_builder/api/testApp/derivedcomponent", updatedDerivedComponent)
|
||||||
|
|
|
@ -41,7 +41,7 @@ module.exports.getPackageForBuilder = async (config, appname) => {
|
||||||
|
|
||||||
rootComponents: await getRootComponents(appPath, pages),
|
rootComponents: await getRootComponents(appPath, pages),
|
||||||
|
|
||||||
derivedComponents: keyBy("_name")(
|
derivedComponents: keyBy("name")(
|
||||||
await fetchDerivedComponents(appPath))
|
await fetchDerivedComponents(appPath))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ module.exports.saveDerivedComponent = async (config, appname, component) => {
|
||||||
const appPath = appPackageFolder(config, appname);
|
const appPath = appPackageFolder(config, appname);
|
||||||
|
|
||||||
await writeJSON(
|
await writeJSON(
|
||||||
componentPath(appPath, component._name),
|
componentPath(appPath, component.name),
|
||||||
component,
|
component,
|
||||||
{encoding:"utf8", flag:"w"});
|
{encoding:"utf8", flag:"w"});
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,7 @@ const fetchDerivedComponents = async (appPath, relativePath = "") => {
|
||||||
const component =
|
const component =
|
||||||
await readJSON(itemFullPath);
|
await readJSON(itemFullPath);
|
||||||
|
|
||||||
component._name = itemRelativePath
|
component.name = itemRelativePath
|
||||||
.substring(0, itemRelativePath.length - 5)
|
.substring(0, itemRelativePath.length - 5)
|
||||||
.replace(/\\/g, "/");
|
.replace(/\\/g, "/");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue