+
+
-
-
+
{component.displayName}
-
)}
diff --git a/frontend/src/Editor/Components/Text.jsx b/frontend/src/Editor/Components/Text.jsx
index 3cd4c71670..2894709264 100644
--- a/frontend/src/Editor/Components/Text.jsx
+++ b/frontend/src/Editor/Components/Text.jsx
@@ -35,7 +35,9 @@ export const Text = function Text({
const computedStyles = {
color,
width,
- height
+ height,
+ display: 'flex',
+ alignItems: 'center'
};
return (
diff --git a/frontend/src/Editor/Components/components.js b/frontend/src/Editor/Components/components.js
index eb33e9dbe3..dfbe248c29 100644
--- a/frontend/src/Editor/Components/components.js
+++ b/frontend/src/Editor/Components/components.js
@@ -460,8 +460,8 @@ export const componentTypes = [
loadingState: { type: 'code', displayName: 'Show loading state' }
},
defaultSize: {
- width: 210,
- height: 24
+ width: 200,
+ height: 30
},
events: [
diff --git a/frontend/src/Editor/DataSourceManager/DataSourceTypes.js b/frontend/src/Editor/DataSourceManager/DataSourceTypes.js
index 986358f348..a2c3509c21 100644
--- a/frontend/src/Editor/DataSourceManager/DataSourceTypes.js
+++ b/frontend/src/Editor/DataSourceManager/DataSourceTypes.js
@@ -148,6 +148,22 @@ export const apiSources = [
},
customTesting: true
},
+ {
+ name: 'GraphQL',
+ kind: 'graphql',
+ options: {
+ url: { type: 'string' },
+ headers: { type: 'array' },
+ url_params: { type: 'array' },
+ body: { type: 'array' },
+ },
+ exposedVariables: {
+ isLoading: {},
+ data: {},
+ rawData: {}
+ },
+ customTesting: true
+ },
{
name: 'Stripe',
kind: 'stripe',
diff --git a/frontend/src/Editor/DataSourceManager/DefaultOptions.js b/frontend/src/Editor/DataSourceManager/DefaultOptions.js
index b3c4f82e7a..40c9f09ddf 100644
--- a/frontend/src/Editor/DataSourceManager/DefaultOptions.js
+++ b/frontend/src/Editor/DataSourceManager/DefaultOptions.js
@@ -67,6 +67,11 @@ export const defaultOptions = {
headers: { value: [['', '']] },
custom_auth_params: { value: [['', '']] }
},
+ graphql: {
+ url: { value: '' },
+ headers: { value: [['', '']] },
+ url_params: { value: [['', '']] }
+ },
googlesheets: {
access_type: { value: 'read' }
},
diff --git a/frontend/src/Editor/DataSourceManager/SourceComponents/Graphql.jsx b/frontend/src/Editor/DataSourceManager/SourceComponents/Graphql.jsx
new file mode 100644
index 0000000000..50efef480a
--- /dev/null
+++ b/frontend/src/Editor/DataSourceManager/SourceComponents/Graphql.jsx
@@ -0,0 +1,92 @@
+import React from 'react';
+import Button from 'react-bootstrap/Button';
+
+export const Graphql = ({
+ optionchanged, createDataSource, options, isSaving
+}) => {
+
+ function addNewKeyValuePair(option) {
+ const newPairs = [...options[option].value, ['', '']];
+ optionchanged(option, newPairs);
+ }
+
+ function removeKeyValuePair(option, index) {
+ options[option].value.splice(index, 1);
+ optionchanged(option, options[option].value);
+ }
+
+ function keyValuePairValueChanged(e, keyIndex, option, index) {
+ const value = e.target.value;
+ options[option].value[index][keyIndex] = value;
+ optionchanged(option, options[option].value);
+ }
+
+ return (
+
+
+
+
+ optionchanged('url', e.target.value)}
+ value={options.url.value}
+ />
+
+
+ {[{name: 'URL parameters', value: 'url_params'},{name: 'Headers', value: 'headers'}].map((option) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/frontend/src/Editor/DataSourceManager/SourceComponents/index.js b/frontend/src/Editor/DataSourceManager/SourceComponents/index.js
index f2504dcf41..149d8c09a4 100644
--- a/frontend/src/Editor/DataSourceManager/SourceComponents/index.js
+++ b/frontend/src/Editor/DataSourceManager/SourceComponents/index.js
@@ -10,6 +10,7 @@ import { Slack } from './Slack';
import { Mongodb } from './Mongodb';
import { Dynamodb } from './Dynamodb';
import { Airtable } from './Airtable';
+import { Graphql } from './Graphql';
import { Mssql } from './Mssql';
export const SourceComponents = {
@@ -25,5 +26,6 @@ export const SourceComponents = {
Mongodb,
Dynamodb,
Airtable,
+ Graphql,
Mssql
};
diff --git a/frontend/src/Editor/DraggableBox.jsx b/frontend/src/Editor/DraggableBox.jsx
index 749d15adc8..5274984260 100644
--- a/frontend/src/Editor/DraggableBox.jsx
+++ b/frontend/src/Editor/DraggableBox.jsx
@@ -5,28 +5,40 @@ import { getEmptyImage } from 'react-dnd-html5-backend';
import { Box } from './Box';
import { Resizable } from 're-resizable';
import { ConfigHandle } from './ConfigHandle';
-import { Rnd } from "react-rnd";
+import { Rnd } from 'react-rnd';
const resizerClasses = {
topRight: 'top-right',
bottomRight: 'bottom-right',
bottomLeft: 'bottom-left',
- topLeft: 'top-left'
+ topLeft: 'top-left',
};
const resizerStyles = {
topRight: {
- width: '12px', height: '12px', right: '-6px', top: '-6px'
+ width: '12px',
+ height: '12px',
+ right: '-6px',
+ top: '-6px',
},
bottomRight: {
- width: '12px', height: '12px', right: '-6px', bottom: '-6px'
+ width: '12px',
+ height: '12px',
+ right: '-6px',
+ bottom: '-6px',
},
bottomLeft: {
- width: '12px', height: '12px', left: '-6px', bottom: '-6px'
+ width: '12px',
+ height: '12px',
+ left: '-6px',
+ bottom: '-6px',
},
topLeft: {
- width: '12px', height: '12px', left: '-6px', top: '-6px'
- }
+ width: '12px',
+ height: '12px',
+ left: '-6px',
+ top: '-6px',
+ },
};
function getStyles(left, top, isDragging, component) {
@@ -39,7 +51,7 @@ function getStyles(left, top, isDragging, component) {
// IE fallback: hide the real node using CSS when dragging
// because IE will ignore our custom "empty image" drag preview.
opacity: isDragging ? 0 : 1,
- height: isDragging ? 0 : ''
+ height: isDragging ? 0 : '',
};
}
@@ -71,7 +83,7 @@ export const DraggableBox = function DraggableBox({
layouts,
scaleValue,
deviceWindowWidth,
- isSelectedComponent
+ isSelectedComponent,
}) {
const [isResizing, setResizing] = useState(false);
const [canDrag, setCanDrag] = useState(true);
@@ -81,11 +93,17 @@ export const DraggableBox = function DraggableBox({
() => ({
type: ItemTypes.BOX,
item: {
- id, title, component, zoomLevel, parent, layouts, currentLayout
+ id,
+ title,
+ component,
+ zoomLevel,
+ parent,
+ layouts,
+ currentLayout,
},
collect: (monitor) => ({
- isDragging: monitor.isDragging()
- })
+ isDragging: monitor.isDragging(),
+ }),
}),
[id, title, component, index, zoomLevel, parent, layouts, currentLayout]
);
@@ -104,14 +122,14 @@ export const DraggableBox = function DraggableBox({
display: 'inline-block',
alignItems: 'center',
justifyContent: 'center',
- padding: '2px'
+ padding: '2px',
};
let refProps = {};
if (mode === 'edit' && canDrag) {
refProps = {
- ref: drag
+ ref: drag,
};
}
@@ -123,53 +141,60 @@ export const DraggableBox = function DraggableBox({
top: 100,
left: 0,
width: 445,
- height: 500
- }
+ height: 500,
+ };
const layoutData = inCanvas ? layouts[currentLayout] || defaultData : defaultData;
const [currentLayoutOptions, setCurrentLayoutOptions] = useState(layoutData);
useEffect(() => {
- console.log(layoutData)
+ console.log(layoutData);
setCurrentLayoutOptions(layoutData);
}, [layoutData.height, layoutData.width, layoutData.left, layoutData.top, currentLayout]);
- function scaleWidth(width, scaleValue) {
+ function scaleWidth(width, scaleValue) {
let newWidth = width * scaleValue;
- if(currentLayout === 'desktop') return newWidth;
+ if (currentLayout === 'desktop') return newWidth;
- const diff = currentLayoutOptions.left + newWidth - deviceWindowWidth;
+ const diff = currentLayoutOptions.left + newWidth - deviceWindowWidth;
- if(diff > 0 ) {
+ if (diff > 0) {
setCurrentLayoutOptions({
...currentLayoutOptions,
- left: currentLayoutOptions.left - diff
+ left: currentLayoutOptions.left - diff,
});
return width;
}
return newWidth;
-
}
return (
-
+
{inCanvas ? (
-
setMouseOver(true)}
onMouseLeave={() => setMouseOver(false)}
>
-
setResizing(true)}
resizeHandleClasses={mouseOver ? resizerClasses : {}}
resizeHandleStyles={resizerStyles}
@@ -181,15 +206,15 @@ export const DraggableBox = function DraggableBox({
}}
>
- {mode === 'edit' && mouseOver &&
- configHandleClicked(id, component)}
- />
- }
+ {mode === 'edit' && mouseOver && (
+ configHandleClicked(id, component)}
+ />
+ )}
) : (
-
+
- this.setState(
- {
- app: data,
- isLoading: false,
- appDefinition: { ...this.state.appDefinition, ...data.definition },
- },
- () => {
- data.data_queries.forEach((query) => {
- if (query.options.runOnPageLoad) {
- runQuery(this, query.id, query.name);
- }
- });
- }
- )
- );
+ appService.getApp(appId).then((data) => this.setState(
+ {
+ app: data,
+ isLoading: false,
+ appDefinition: { ...this.state.appDefinition, ...data.definition },
+ slug: data.slug
+ },
+ () => {
+ data.data_queries.forEach((query) => {
+ if (query.options.runOnPageLoad) {
+ runQuery(this, query.id, query.name);
+ }
+ });
+ }
+ ));
this.fetchDataSources();
this.fetchDataQueries();
this.setState({
- appId,
currentSidebarTab: 2,
- selectedComponent: null,
+ selectedComponent: null
});
}
fetchDataSources = () => {
this.setState(
{
- loadingDataSources: true,
+ loadingDataSources: true
},
() => {
- datasourceService.getAll(this.state.appId).then((data) =>
- this.setState({
- dataSources: data.data_sources,
- loadingDataSources: false,
- })
- );
+ datasourceService.getAll(this.state.appId).then((data) => this.setState({
+ dataSources: data.data_sources,
+ loadingDataSources: false
+ }));
}
);
};
@@ -124,7 +125,7 @@ class Editor extends React.Component {
fetchDataQueries = () => {
this.setState(
{
- loadingDataQueries: true,
+ loadingDataQueries: true
},
() => {
dataqueryService.getAll(this.state.appId).then((data) => {
@@ -134,15 +135,15 @@ class Editor extends React.Component {
loadingDataQueries: false,
app: {
...this.state.app,
- data_queries: data.data_queries,
- },
+ data_queries: data.data_queries
+ }
},
() => {
let queryState = {};
data.data_queries.forEach((query) => {
queryState[query.name] = {
...DataSourceTypes.find((source) => source.kind === query.kind).exposedVariables,
- ...this.state.currentState.queries[query.name],
+ ...this.state.currentState.queries[query.name]
};
});
@@ -164,9 +165,9 @@ class Editor extends React.Component {
currentState: {
...this.state.currentState,
queries: {
- ...queryState,
- },
- },
+ ...queryState
+ }
+ }
});
}
);
@@ -192,9 +193,9 @@ class Editor extends React.Component {
currentState: {
...this.state.currentState,
components: {
- ...componentState,
- },
- },
+ ...componentState
+ }
+ }
});
};
@@ -209,7 +210,7 @@ class Editor extends React.Component {
switchSidebarTab = (tabIndex) => {
this.setState({
- currentSidebarTab: tabIndex,
+ currentSidebarTab: tabIndex
});
};
@@ -235,11 +236,15 @@ class Editor extends React.Component {
if (this.state.selectedComponent.hasOwnProperty('component')) {
const { id: selectedComponentId } = this.state.selectedComponent;
if (selectedComponentId === component.id) {
- this.setState({selectedComponent: null})
+ this.setState({ selectedComponent: null });
this.switchSidebarTab(2);
}
}
- }
+ };
+
+ handleSlugChange = (newSlug) => {
+ this.setState({ slug: newSlug });
+ };
removeComponent = (component) => {
let newDefinition = this.state.appDefinition;
@@ -267,10 +272,10 @@ class Editor extends React.Component {
[newDefinition.id]: {
...this.state.appDefinition.components[newDefinition.id],
component: newDefinition.component,
- layouts: newDefinition.layouts,
- },
- },
- },
+ layouts: newDefinition.layouts
+ }
+ }
+ }
});
};
@@ -282,10 +287,10 @@ class Editor extends React.Component {
...this.state.appDefinition.components,
[newComponent.id]: {
...this.state.appDefinition.components[newComponent.id],
- ...newComponent,
- },
- },
- },
+ ...newComponent
+ }
+ }
+ }
});
};
@@ -354,7 +359,7 @@ class Editor extends React.Component {
runQuery(this, dataQuery.id, dataQuery.name).then(() => {
toast.info(`Query (${dataQuery.name}) completed.`, {
hideProgressBar: true,
- position: 'bottom-center',
+ position: 'bottom-center'
});
});
}}
@@ -366,7 +371,7 @@ class Editor extends React.Component {
)}
{isLoading === true && (
)}
@@ -376,13 +381,13 @@ class Editor extends React.Component {
onNameChanged = (newName) => {
this.setState({
- app: { ...this.state.app, name: newName },
+ app: { ...this.state.app, name: newName }
});
};
toggleQueryPaneHeight = () => {
this.setState({
- queryPaneHeight: this.state.queryPaneHeight === '30%' ? '80%' : '30%',
+ queryPaneHeight: this.state.queryPaneHeight === '30%' ? '80%' : '30%'
});
};
@@ -399,12 +404,30 @@ class Editor extends React.Component {
this.setState({ selectedComponent: { id, component } });
};
+ filterQueries = (value) => {
+ if (value) {
+ const fuse = new Fuse(this.state.dataQueries, { keys: ['name'] });
+ const results = fuse.search(value);
+ this.setState({
+ dataQueries: results.map((result) => result.item),
+ dataQueriesDefaultText: results.length || 'No Queries found.'
+ });
+ } else {
+ this.fetchDataQueries();
+ }
+ }
+
+ toggleQuerySearch = () => {
+ this.setState({ showQuerySearchField: !this.state.showQuerySearchField });
+ }
+
render() {
const {
currentSidebarTab,
selectedComponent,
appDefinition,
appId,
+ slug,
dataSources,
loadingDataQueries,
dataQueries,
@@ -423,9 +446,10 @@ class Editor extends React.Component {
currentLayout,
deviceWindowWidth,
scaleValue,
+ dataQueriesDefaultText,
+ showQuerySearchField
} = this.state;
-
- const appLink = `/applications/${appId}`;
+ const appLink = slug ? `/applications/${slug}` : '';
return (
@@ -508,10 +532,10 @@ class Editor extends React.Component {
-
+
-
{app && }
+
+ {app
+ && }
+
{this.state.app && (
-
+
)}
@@ -552,12 +582,12 @@ class Editor extends React.Component {
alignItems: 'center',
justifyContent: 'center',
background: '#f0f0f0',
- zIndex: '200',
+ zIndex: '200'
}}
maxWidth={showLeftSidebar ? '30%' : '0%'}
defaultSize={{
width: '12%',
- height: '99%',
+ height: '99%'
}}
>
@@ -650,8 +680,7 @@ class Editor extends React.Component {
You haven't added data sources yet.