diff --git a/packages/cubejs-playground/src/DashboardSource.js b/packages/cubejs-playground/src/DashboardSource.js index 275603623a289..e7e1c9b795dfb 100644 --- a/packages/cubejs-playground/src/DashboardSource.js +++ b/packages/cubejs-playground/src/DashboardSource.js @@ -24,9 +24,9 @@ class DashboardSource { async load(createApp, { chartLibrary }) { this.loadError = null; if (createApp) { - await fetchWithRetry('/playground/ensure-dashboard-app', undefined, 5); + await fetchWithRetry('/playground/ensure-dashboard-app', undefined, 10); } - const res = await fetchWithRetry('/playground/dashboard-app-files', undefined, 5); + const res = await fetchWithRetry('/playground/dashboard-app-files', undefined, 10); const result = await res.json(); this.playgroundContext = await this.loadContext(); this.fileToTargetSource = {}; @@ -40,6 +40,9 @@ class DashboardSource { if (!result.error && this.ensureDashboardIsInApp({ chartLibrary })) { await this.persist(); } + if (!result.error) { + await this.ensureDependencies({}); + } } async loadContext() { @@ -85,6 +88,10 @@ class DashboardSource { const dependency = importName[0].indexOf('@') === 0 ? [importName[0], importName[1]].join('/') : importName[0]; return this.withPeerDependencies(dependency); }).reduce((a, b) => ({ ...a, ...b })); + await this.ensureDependencies(dependencies); + } + + async ensureDependencies(dependencies) { await fetchWithRetry('/playground/ensure-dependencies', { method: 'POST', headers: { @@ -93,7 +100,7 @@ class DashboardSource { body: JSON.stringify({ dependencies }) - }, 5); + }, 10); } // TODO move to dev server @@ -149,8 +156,8 @@ class DashboardSource { if (!dashboardAdded && headerElement) { this.appLayoutAdded = true; const scaffoldingFileToSnippet = { - 'react/App.js': new AppSnippet(), - 'react/index.js': new IndexSnippet(this.playgroundContext), + 'react/App.js': new AppSnippet(this.playgroundContext), + 'react/index.js': new IndexSnippet(), 'react/pages/ExplorePage.js': new ExploreSnippet(), 'react/components/ChartRenderer.js': new ChartRendererSnippet(chartLibrary) }; diff --git a/packages/cubejs-playground/src/scaffolding/react/App.js b/packages/cubejs-playground/src/scaffolding/react/App.js index c494e00bfe62b..793dc7c1e49eb 100644 --- a/packages/cubejs-playground/src/scaffolding/react/App.js +++ b/packages/cubejs-playground/src/scaffolding/react/App.js @@ -1,9 +1,21 @@ import React from "react"; import { ApolloProvider } from '@apollo/react-hooks'; import { Layout } from "antd"; +import cubejs from '@cubejs-client/core'; +import { CubeProvider } from "@cubejs-client/react"; import client from "./graphql/client"; import Header from "./components/Header"; +// change to your actual endpoint +const API_URL = undefined; +// should be refreshed by your backend API endpoint. More info: https://cube.dev/docs/security +const CUBEJS_TOKEN = undefined; + +const cubejsApi = cubejs( + CUBEJS_TOKEN, + { apiUrl: `${API_URL}/cubejs-api/v1` } +); + const AppLayout = ({ children }) => ( ( ); const App = ({ children }) => ( - - - {children} - - + + + + {children} + + + ); export default App; diff --git a/packages/cubejs-playground/src/scaffolding/react/components/ChartRenderer.js b/packages/cubejs-playground/src/scaffolding/react/components/ChartRenderer.js index e2310625df7cf..818bfe3b5088d 100644 --- a/packages/cubejs-playground/src/scaffolding/react/components/ChartRenderer.js +++ b/packages/cubejs-playground/src/scaffolding/react/components/ChartRenderer.js @@ -1,36 +1,33 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { QueryRenderer } from '@cubejs-client/react'; +import { useCubeQuery } from '@cubejs-client/react'; import { Spin } from 'antd'; const TypeToChartComponent = {}; -const renderChart = (Component) => ({ resultSet, error }) => ( - (resultSet && ) - || (error && error.toString()) - || () -); - -const ChartRenderer = ({ vizState, cubejsApi }) => vizState && ( - -); +const TypeToMemoChartComponent = Object.keys(TypeToChartComponent) + .map(key => ({ [key]: React.memo(TypeToChartComponent[key]) })) + .reduce((a, b) => ({ ...a, ...b })); + +const renderChart = Component => ({ resultSet, error }) => (resultSet && ) + || (error && error.toString()) || ; + +const ChartRenderer = ({ vizState }) => { + const { query, chartType } = vizState; + const component = TypeToMemoChartComponent[chartType]; + const renderProps = useCubeQuery(query); + + return component && renderChart(component)(renderProps); +}; ChartRenderer.propTypes = { vizState: PropTypes.object, - cubejsApi: PropTypes.object.isRequired + cubejsApi: PropTypes.object }; ChartRenderer.defaultProps = { - vizState: null + vizState: {}, + cubejsApi: null }; - export default ChartRenderer; diff --git a/packages/cubejs-playground/src/scaffolding/react/components/QueryBuilder/ExploreQueryBuilder.js b/packages/cubejs-playground/src/scaffolding/react/components/QueryBuilder/ExploreQueryBuilder.js index 2124f30f7190c..bf586d50c085a 100644 --- a/packages/cubejs-playground/src/scaffolding/react/components/QueryBuilder/ExploreQueryBuilder.js +++ b/packages/cubejs-playground/src/scaffolding/react/components/QueryBuilder/ExploreQueryBuilder.js @@ -17,6 +17,7 @@ const ExploreQueryBuilder = ({ vizState={vizState} setVizState={setVizState} cubejsApi={cubejsApi} + wrapWithQueryRenderer={false} render={({ validatedQuery, isQueryPresent, chartType, updateChartType, measures, availableMeasures, updateMeasures, diff --git a/packages/cubejs-playground/src/scaffolding/react/index.js b/packages/cubejs-playground/src/scaffolding/react/index.js index cc03a3150cf26..48dddcb4128cc 100644 --- a/packages/cubejs-playground/src/scaffolding/react/index.js +++ b/packages/cubejs-playground/src/scaffolding/react/index.js @@ -1,21 +1,10 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { HashRouter as Router, Route } from 'react-router-dom'; -import cubejs from '@cubejs-client/core'; import ExplorePage from './pages/ExplorePage'; import DashboardPage from './pages/DashboardPage'; import App from './App'; -// change to your actual endpoint -const API_URL = undefined; -// should be refreshed by your backend API endpoint. More info: https://cube.dev/docs/security -const CUBEJS_TOKEN = undefined; - -const cubejsApi = cubejs( - CUBEJS_TOKEN, - { apiUrl: `${API_URL}/cubejs-api/v1` } -); - ReactDOM.render( @@ -23,12 +12,12 @@ ReactDOM.render( key="index" exact path="/" - render={(props) => } + component={DashboardPage} /> } + component={ExplorePage} /> , diff --git a/packages/cubejs-playground/src/scaffolding/react/pages/DashboardPage.js b/packages/cubejs-playground/src/scaffolding/react/pages/DashboardPage.js index 7ddf938d59e38..71eb90e81ec64 100644 --- a/packages/cubejs-playground/src/scaffolding/react/pages/DashboardPage.js +++ b/packages/cubejs-playground/src/scaffolding/react/pages/DashboardPage.js @@ -22,7 +22,7 @@ const defaultLayout = i => ({ minH: 8 }); -const DashboardPage = ({ cubejsApi }) => { +const DashboardPage = () => { const { loading, error, data } = useQuery(GET_DASHBOARD_ITEMS); if (loading) { @@ -42,7 +42,7 @@ const DashboardPage = ({ cubejsApi }) => { const dashboardItem = item => (
- +
); diff --git a/packages/cubejs-playground/src/scaffolding/react/pages/ExplorePage.js b/packages/cubejs-playground/src/scaffolding/react/pages/ExplorePage.js index 672155581efe9..64d98f15ac12f 100644 --- a/packages/cubejs-playground/src/scaffolding/react/pages/ExplorePage.js +++ b/packages/cubejs-playground/src/scaffolding/react/pages/ExplorePage.js @@ -6,7 +6,7 @@ import ExploreQueryBuilder from "../components/QueryBuilder/ExploreQueryBuilder" import { GET_DASHBOARD_ITEM } from "../graphql/queries"; import TitleModal from "../components/TitleModal.js"; -const ExplorePage = withRouter(({ cubejsApi, history, location }) => { +const ExplorePage = withRouter(({ history, location }) => { const [addingToDashboard, setAddingToDashboard] = useState(false); const params = new URLSearchParams(location.search); const itemId = params.get("itemId"); @@ -48,7 +48,6 @@ const ExplorePage = withRouter(({ cubejsApi, history, location }) => { finalTitle={finalTitle} /> d.get('id').node.name === 'API_URL'); + apiUrl.get('init').replaceWith(t.stringLiteral(this.apiUrl)); + + const cubejsToken = targetSource.definitions.find(d => d.get('id').node.name === 'CUBEJS_TOKEN'); + cubejsToken.get('init').replaceWith(t.stringLiteral(this.cubejsToken)); + } } export default AppSnippet; diff --git a/packages/cubejs-playground/src/source/IndexSnippet.js b/packages/cubejs-playground/src/source/IndexSnippet.js index 0a7bf4ec42ad2..e1a9ea9ccbfb8 100644 --- a/packages/cubejs-playground/src/source/IndexSnippet.js +++ b/packages/cubejs-playground/src/source/IndexSnippet.js @@ -1,19 +1,15 @@ import traverse from "@babel/traverse"; import SourceSnippet from './SourceSnippet'; import ScaffoldingSources from '../codegen/ScaffoldingSources'; -import * as t from "@babel/types"; class IndexSnippet extends SourceSnippet { - constructor({ apiUrl, cubejsToken }) { + constructor() { super(ScaffoldingSources['react/index.js']); - this.apiUrl = apiUrl; - this.cubejsToken = cubejsToken; } mergeTo(targetSource) { super.mergeTo(targetSource); this.replaceRouter(targetSource); - this.replaceTokens(targetSource); } replaceRouter(targetSource) { @@ -44,14 +40,6 @@ class IndexSnippet extends SourceSnippet { } } - replaceTokens(targetSource) { - const apiUrl = targetSource.definitions.find(d => d.get('id').node.name === 'API_URL'); - apiUrl.get('init').replaceWith(t.stringLiteral(this.apiUrl)); - - const cubejsToken = targetSource.definitions.find(d => d.get('id').node.name === 'CUBEJS_TOKEN'); - cubejsToken.get('init').replaceWith(t.stringLiteral(this.cubejsToken)); - } - findAppOrRouter(targetSource) { let appElement = null; traverse(targetSource.ast, {