Skip to content

Commit

Permalink
docs(demo): Add TS support + CORS demo (apache#118)
Browse files Browse the repository at this point in the history
* docs: [demo] add custom webpack config to resolve TS files

* docs: [demo] add @babel/polyfill

* docs: [demo][connection] add ConfigureCORS story for testing CORS

* docs: [demo][ConfigureCORS] better instructions

* fix: [demo] install an existing version of @superset-ui/connection

* docs: better CORS story, update webpack for @babel/polyfill
  • Loading branch information
williaster committed Mar 15, 2019
1 parent 57a64b1 commit 34581f3
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 1 deletion.
2 changes: 2 additions & 0 deletions packages/superset-ui-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
"homepage": "https://github.com/apache-superset/superset-ui#readme",
"dependencies": {
"@babel/core": "^7.1.2",
"@babel/polyfill": "^7.2.5",
"@storybook/addon-knobs": "^4.0.2",
"@storybook/addon-options": "^4.0.3",
"@storybook/react": "^4.0.2",
"@superset-ui/color": "^0.10.1",
"@superset-ui/connection": "^0.10.2",
"@superset-ui/number-format": "^0.10.1",
"@superset-ui/time-format": "^0.10.1",
"babel-loader": "^8.0.4",
Expand Down
19 changes: 19 additions & 0 deletions packages/superset-ui-demo/storybook-config/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module.exports = (baseConfig, env, config) => {
const customConfig = config;

customConfig.module.rules.push({
loader: require.resolve('babel-loader'),
options: {
presets: [
['@babel/preset-env', { useBuiltIns: 'entry' }],
'@babel/preset-react',
'@babel/preset-typescript',
],
},
test: /\.tsx?$/,
});

customConfig.resolve.extensions.push('.ts', '.tsx');

return customConfig;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';

export type Props = {
error: Error;
};

export default function ErrorMessage({ error }: Props) {
return (
<div className="alert alert-danger">
{error.stack || error.message}
{!error.message &&
!error.stack &&
(typeof error === 'object' ? JSON.stringify(error) : String(error))}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { ReactNode } from 'react';

export type Props = {
children: ReactNode;
expandableWhat?: string;
};

type State = {
open: boolean;
};

export default class Expandable extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { open: false };
this.handleToggle = this.handleToggle.bind(this);
}

handleToggle() {
this.setState(({ open }) => ({ open: !open }));
}

render() {
const { open } = this.state;
const { children, expandableWhat } = this.props;

return (
<div>
<button
type="button"
onClick={this.handleToggle}
className="btn btn-outline-primary btn-sm"
>
{`${open ? 'Hide' : 'Show'} ${expandableWhat}`}
</button>
<br />
<br />
{open ? children : null}
</div>
);
}
}
116 changes: 116 additions & 0 deletions packages/superset-ui-demo/storybook/shared/components/VerifyCORS.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React, { ReactNode } from 'react';
import { SupersetClient } from '@superset-ui/connection';
import ErrorMessage from './ErrorMessage';

export type Props = {
children: ({ payload }: { payload: object }) => ReactNode;
endpoint?: string;
host: string;
method?: 'POST' | 'GET';
postPayload?: string;
};

type State = {
didVerify: boolean;
error?: Error;
payload?: object;
};

export const renderError = error => (
<div>
The following error occurred, make sure you have <br />
1) configured CORS in Superset to receive requests from this domain. <br />
2) set the Superset host correctly below. <br />
3) debug the CORS configuration under the `@superset-ui/connection` stories.
<br />
<br />
<ErrorMessage error={error} />
</div>
);

export default class VerifyCORS extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { didVerify: false };
this.handleVerify = this.handleVerify.bind(this);
}

componentDidUpdate(prevProps) {
const { endpoint, host, postPayload, method } = this.props;
if (
(this.state.didVerify || this.state.error) &&
(prevProps.endpoint !== endpoint ||
prevProps.host !== host ||
prevProps.postPayload !== postPayload ||
prevProps.method !== method)
) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({ didVerify: false, error: undefined });
}
}

handleVerify() {
const { endpoint, host, postPayload, method } = this.props;

SupersetClient.reset();

SupersetClient.configure({
credentials: 'include',
host,
mode: 'cors',
})
.init()
.then(() =>
// Test an endpoint if specified
endpoint
? SupersetClient.request({
endpoint,
method,
postPayload: postPayload ? JSON.parse(postPayload) : '',
})
: Promise.resolve({}),
)
.then(response => this.setState({ didVerify: true, error: undefined, payload: response }))
.catch((error: Response) => {
const { status, statusText = error } = error;
this.setState({ error: Error(`${status || ''}${status ? ':' : ''} ${statusText}`) });
});
}

render() {
const { didVerify, error, payload } = this.state;
const { children } = this.props;

return didVerify ? (
children({ payload })
) : (
<div className="row">
<div className="col-md-10">
This example requires CORS requests from this domain. <br />
<br />
1) enable CORS requests in your Superset App from {`${window.location.origin}`}
<br />
2) configure your Superset App host name below <br />
3) click below to verify authentication. You may debug CORS further using the
`@superset-ui/connection` story. <br />
<br />
<button
type="button"
onClick={this.handleVerify}
className="btn btn-outline-primary btn-sm"
>
Verify
</button>
<br />
<br />
</div>

{error && (
<div className="col-md-8">
<ErrorMessage error={error} />
</div>
)}
</div>
);
}
}
3 changes: 2 additions & 1 deletion packages/superset-ui-demo/storybook/stories/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '@babel/polyfill';
import { setAddon, storiesOf } from '@storybook/react';
import { withKnobs } from '@storybook/addon-knobs';
import JSXAddon from 'storybook-addon-jsx';
Expand Down Expand Up @@ -36,7 +37,7 @@ requireContext.keys().forEach(packageName => {

storiesOf(storyPath, module)
.addParameters({ options })
.addDecorator(withKnobs)
.addDecorator(withKnobs({ escapeHTML: false }))
.addWithJSX(storyName, renderStory);
});
}
Expand Down
35 changes: 35 additions & 0 deletions packages/superset-ui-demo/storybook/stories/mocks/formData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* eslint sort-keys: 'off' */
/** The form data defined here is based on default visualizations packaged with Apache Superset */

export const bigNumberFormData = {
datasource: '3__table',
viz_type: 'big_number',
slice_id: 54,
granularity_sqla: 'ds',
time_grain_sqla: 'P1D',
time_range: '100 years ago : now',
metric: 'sum__num',
adhoc_filters: [],
compare_lag: '5',
compare_suffix: 'over 5Y',
y_axis_format: '.3s',
show_trend_line: true,
start_y_axis_at_zero: true,
};

export const wordCloudFormData = {
datasource: '3__table',
viz_type: 'word_cloud',
slice_id: 60,
url_params: {},
granularity_sqla: 'ds',
time_grain_sqla: 'P1D',
time_range: '100 years ago : now',
series: 'name',
metric: 'sum__num',
adhoc_filters: [],
row_limit: 50,
size_from: 10,
size_to: 70,
rotation: 'square',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import { select, text } from '@storybook/addon-knobs';

import VerifyCORS from '../../shared/components/VerifyCORS';
import Expandable from '../../shared/components/Expandable';
import { bigNumberFormData } from '../mocks/formData';

const REQUEST_METHODS = ['GET', 'POST'];

export default [
{
renderStory: () => {
const host = text('Superset App host for CORS request', 'localhost:9000');
const endpoint = text('Endpoint to test (blank to test auth only)', undefined);
const method = endpoint ? select('Request method', REQUEST_METHODS, 'POST') : undefined;
const postPayload =
endpoint && method === 'POST'
? text('Optional POST payload', JSON.stringify({ form_data: bigNumberFormData }))
: undefined;

return (
<div style={{ margin: 16 }}>
<VerifyCORS
host={host}
endpoint={endpoint}
method={method}
postPayload={`${postPayload}`}
>
{({ payload }) => (
<>
<div className="alert alert-success">Success! Update knobs below to try again</div>
<br />
<Expandable expandableWhat="payload">
<br />
<pre style={{ fontSize: 11 }}>{JSON.stringify(payload, null, 2)}</pre>
</Expandable>
</>
)}
</VerifyCORS>
</div>
);
},
storyName: 'Configure CORS',
storyPath: '@superset-ui/connection',
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import ConnectionStories from './ConnectionStories';

export default {
examples: [...ConnectionStories],
};

0 comments on commit 34581f3

Please sign in to comment.