diff --git a/examples/src/components/BarChartExample.jsx b/examples/src/components/BarChartExample.jsx index 34ea39993a..22e66bf2c7 100644 --- a/examples/src/components/BarChartExample.jsx +++ b/examples/src/components/BarChartExample.jsx @@ -4,8 +4,6 @@ import { BarChart } from '@gooddata/react-components'; import '@gooddata/react-components/styles/css/main.css'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; import { totalSalesIdentifier, locationResortIdentifier, projectId } from '../utils/fixtures'; export class BarChartExample extends Component { @@ -53,8 +51,6 @@ export class BarChartExample extends Component { viewBy={locationResort} onLoadingChanged={this.onLoadingChanged} onError={this.onError} - LoadingComponent={Loading} - ErrorComponent={Error} /> ); diff --git a/examples/src/components/ColumnChartExample.jsx b/examples/src/components/ColumnChartExample.jsx index 7438c27d7d..fa0082f3d8 100644 --- a/examples/src/components/ColumnChartExample.jsx +++ b/examples/src/components/ColumnChartExample.jsx @@ -5,8 +5,6 @@ import { ColumnChart } from '@gooddata/react-components'; import '@gooddata/react-components/styles/css/main.css'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; import { totalSalesIdentifier, monthDateIdentifier, projectId } from '../utils/fixtures'; export class ColumnChartExample extends Component { @@ -54,8 +52,6 @@ export class ColumnChartExample extends Component { viewBy={month} onLoadingChanged={this.onLoadingChanged} onError={this.onError} - LoadingComponent={Loading} - ErrorComponent={Error} /> ); diff --git a/examples/src/components/CustomLegendExample.jsx b/examples/src/components/CustomLegendExample.jsx index 6f00ef98b0..856dd1d996 100644 --- a/examples/src/components/CustomLegendExample.jsx +++ b/examples/src/components/CustomLegendExample.jsx @@ -4,8 +4,6 @@ import { AfmComponents } from '@gooddata/react-components'; import '@gooddata/react-components/styles/css/main.css'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; import { projectId, franchiseFeesAdRoyaltyIdentifier, @@ -133,8 +131,6 @@ export class CustomChartExample extends Component { }} onError={this.onError} onLegendReady={this.onLegendReady} - LoadingComponent={Loading} - ErrorComponent={Error} /> diff --git a/examples/src/components/CustomVisualizationExample.jsx b/examples/src/components/CustomVisualizationExample.jsx index 283a3d838b..31a6fa6842 100644 --- a/examples/src/components/CustomVisualizationExample.jsx +++ b/examples/src/components/CustomVisualizationExample.jsx @@ -5,8 +5,6 @@ import { Visualization, CoreComponents } from '@gooddata/react-components'; import { ResponsiveContainer, BarChart, Bar, Legend, CartesianGrid, XAxis, YAxis } from 'recharts'; import { get, unzip, range } from 'lodash'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; import { projectId, franchiseFeesVisualizationIdentifier } from '../utils/fixtures'; import { DEFAULT_COLOR_PALETTE } from '../utils/colors'; @@ -108,8 +106,6 @@ export class CustomVisualization extends Component { const CustomBaseChart = (props) => { return ( } />); }; diff --git a/examples/src/components/DatePickerExample.jsx b/examples/src/components/DatePickerExample.jsx index 2edf7ba525..e3991f8dea 100644 --- a/examples/src/components/DatePickerExample.jsx +++ b/examples/src/components/DatePickerExample.jsx @@ -1,7 +1,7 @@ // (C) 2007-2018 GoodData Corporation /* eslint-disable react/jsx-closing-tag-location */ import React, { Component } from 'react'; -import { AfmComponents } from '@gooddata/react-components'; +import { AfmComponents, ErrorComponent } from '@gooddata/react-components'; import DatePicker from 'react-datepicker'; import moment from 'moment'; @@ -9,8 +9,6 @@ import '@gooddata/react-components/styles/css/main.css'; import 'react-datepicker/dist/react-datepicker.css'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; import { totalSalesIdentifier, monthDateIdentifier, dateDatasetIdentifier, projectId } from '../utils/fixtures'; const { ColumnChart } = AfmComponents; @@ -133,14 +131,12 @@ export class DatePickerExample extends Component {
{error - ? + ? : ()}

diff --git a/examples/src/components/DynamicMeasuresExample.jsx b/examples/src/components/DynamicMeasuresExample.jsx index 64ceb53e77..a52b7dbb46 100644 --- a/examples/src/components/DynamicMeasuresExample.jsx +++ b/examples/src/components/DynamicMeasuresExample.jsx @@ -1,14 +1,11 @@ // (C) 2007-2018 GoodData Corporation /* eslint-disable react/jsx-closing-tag-location */ import React, { Component } from 'react'; -import { LineChart, ColumnChart } from '@gooddata/react-components'; +import { LineChart, ColumnChart, ErrorComponent, LoadingComponent } from '@gooddata/react-components'; import sdk from 'gooddata'; - import '@gooddata/react-components/styles/css/main.css'; import { Layout } from './utils/Layout'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; import { SidebarItem } from './utils/SidebarItem'; import { monthDateIdentifier, @@ -34,9 +31,8 @@ export class DynamicMeasuresExample extends Component { return this.setState({ measureList: null, error: { - status: '404', - message: `No measures with tag ${franchiseFeesTag}. Please check your project. - Franchise fees measures should have assigned the tag ${franchiseFeesTag}.` + message: `No measures with tag ${franchiseFeesTag}`, + description: `Please check your project. Franchise fees measures should have assigned the tag ${franchiseFeesTag}.` } }); } @@ -48,7 +44,10 @@ export class DynamicMeasuresExample extends Component { ).catch((error) => { this.setState({ measureList: null, - error: { status: '400', message: `Error while requesting measures by tag ${franchiseFeesTag}. ${JSON.stringify(error)}` } + error: { + message: `There was Error while requesting measures by tag ${franchiseFeesTag}`, + description: JSON.stringify(error) + } }); }); } @@ -102,7 +101,7 @@ export class DynamicMeasuresExample extends Component { const { measureList, error } = this.state; if (error) { - return ; + return ; } const loadingBlock = (
@@ -116,7 +115,7 @@ export class DynamicMeasuresExample extends Component { align-items: center; } `} - +
); const sidebar = measureList @@ -185,8 +184,6 @@ export class DynamicMeasuresExample extends Component { trendBy={attribute} onLoadingChanged={this.onLoadingChanged} onError={this.onError} - LoadingComponent={Loading} - ErrorComponent={Error} config={config} /> @@ -196,15 +193,13 @@ export class DynamicMeasuresExample extends Component { measures={measures} onLoadingChanged={this.onLoadingChanged} onError={this.onError} - LoadingComponent={Loading} - ErrorComponent={Error} config={config} /> ); } else { - content = ; + content = ; } } diff --git a/examples/src/components/ExecuteExample.jsx b/examples/src/components/ExecuteExample.jsx index aa01d97f3b..e527a680db 100644 --- a/examples/src/components/ExecuteExample.jsx +++ b/examples/src/components/ExecuteExample.jsx @@ -1,11 +1,9 @@ // (C) 2007-2018 GoodData Corporation /* eslint-disable react/jsx-closing-tag-location */ import React, { Component } from 'react'; -import { Execute } from '@gooddata/react-components'; +import { Execute, LoadingComponent, ErrorComponent } from '@gooddata/react-components'; import { totalSalesIdentifier, projectId } from '../utils/fixtures'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; export class ExecuteExample extends Component { constructor(props) { @@ -55,10 +53,10 @@ export class ExecuteExample extends Component { executeChildrenFunction({ result, isLoading, error }) { if (error) { - return ; + return ; } if (isLoading) { - return ; + return ; } return (
@@ -112,7 +110,7 @@ export class ExecuteExample extends Component { {status}

-  (fails every second attempt) +  (fails every second attempt)

{/* We need to render the Execute component even in loading diff --git a/examples/src/components/GlobalFiltersExample.jsx b/examples/src/components/GlobalFiltersExample.jsx index d525d8491d..16586d49e9 100644 --- a/examples/src/components/GlobalFiltersExample.jsx +++ b/examples/src/components/GlobalFiltersExample.jsx @@ -15,8 +15,8 @@ import { menuCategoryAttributeDFIdentifier } from '../utils/fixtures'; import { Layout } from '../components/utils/Layout'; -import { Loading } from '../components/utils/Loading'; -import { Error } from '../components/utils/Error'; +import { CustomLoading } from '../components/utils/CustomLoading'; +import { CustomError } from '../components/utils/CustomError'; export class EmployeeProfile extends React.Component { static propTypes = { @@ -180,9 +180,9 @@ export class EmployeeProfile extends React.Component { projectId={projectId} format="$#,##0" LoadingComponent={ - (...otherProps) => + (...otherProps) => } - ErrorComponent={Error} + ErrorComponent={CustomError} /> @@ -193,9 +193,9 @@ export class EmployeeProfile extends React.Component { projectId={projectId} format="$#,##0" LoadingComponent={ - (...otherProps) => + (...otherProps) => } - ErrorComponent={Error} + ErrorComponent={CustomError} />
@@ -207,8 +207,8 @@ export class EmployeeProfile extends React.Component { viewBy={menuCategoryAttribute} filters={[employeeFilter]} projectId={projectId} - LoadingComponent={Loading} - ErrorComponent={Error} + LoadingComponent={CustomLoading} + ErrorComponent={CustomError} config={{ legend: { position: 'bottom' } }} /> @@ -221,8 +221,8 @@ export class EmployeeProfile extends React.Component { viewBy={menuItemNameAttribute} filters={[employeeFilter]} projectId={projectId} - LoadingComponent={Loading} - ErrorComponent={Error} + LoadingComponent={CustomLoading} + ErrorComponent={CustomError} /> @@ -238,12 +238,12 @@ export const GlobalFiltersExample = () => ( {({ validElements, error, isLoading }) => { if (error) { - return ; + return ; } if (isLoading) { return (
- +
); } diff --git a/examples/src/components/KpiExample.jsx b/examples/src/components/KpiExample.jsx index 6659bc1ffa..72d182ba06 100644 --- a/examples/src/components/KpiExample.jsx +++ b/examples/src/components/KpiExample.jsx @@ -5,8 +5,6 @@ import { Kpi } from '@gooddata/react-components'; import '@gooddata/react-components/styles/css/main.css'; import { totalSalesIdentifier, projectId } from '../utils/fixtures'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; export class KpiExample extends Component { render() { @@ -18,6 +16,8 @@ export class KpiExample extends Component { font-size: 50px; white-space: nowrap; vertical-align: bottom; + text-align: center; + min-width: 300px; line-height: 1.2em; font-weight: 700; width: 300px; @@ -26,8 +26,6 @@ export class KpiExample extends Component { ); diff --git a/examples/src/components/LineChartExample.jsx b/examples/src/components/LineChartExample.jsx index d3dc960791..ea4c193483 100644 --- a/examples/src/components/LineChartExample.jsx +++ b/examples/src/components/LineChartExample.jsx @@ -3,8 +3,6 @@ import React, { Component } from 'react'; import { LineChart } from '@gooddata/react-components'; import '@gooddata/react-components/styles/css/main.css'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; import { projectId, @@ -107,8 +105,6 @@ export class LineChartExample extends Component { onLoadingChanged={this.onLoadingChanged} onError={this.onError} config={{ colors: CUSTOM_COLORS }} - LoadingComponent={Loading} - ErrorComponent={Error} /> ); diff --git a/examples/src/components/PieChartExample.jsx b/examples/src/components/PieChartExample.jsx index b629737db4..a3bfede302 100644 --- a/examples/src/components/PieChartExample.jsx +++ b/examples/src/components/PieChartExample.jsx @@ -10,8 +10,6 @@ import { franchiseFeesInitialFranchiseFeeIdentifier, franchiseFeesIdentifierOngoingRoyalty } from '../utils/fixtures'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; export class PieChartExample extends Component { @@ -75,8 +73,6 @@ export class PieChartExample extends Component { measures={measures} onLoadingChanged={this.onLoadingChanged} onError={this.onError} - LoadingComponent={Loading} - ErrorComponent={Error} /> ); diff --git a/examples/src/components/ResponsiveExample.jsx b/examples/src/components/ResponsiveExample.jsx index 3ca0cdfc9a..eebea8a512 100644 --- a/examples/src/components/ResponsiveExample.jsx +++ b/examples/src/components/ResponsiveExample.jsx @@ -4,8 +4,6 @@ import { AfmComponents } from '@gooddata/react-components'; import '@gooddata/react-components/styles/css/main.css'; import Measure from 'react-measure'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; import { projectId, totalSalesIdentifier, locationResortIdentifier } from '../utils/fixtures'; export class ResponsiveExample extends Component { @@ -63,8 +61,6 @@ export class ResponsiveExample extends Component { height={contentRect.bounds.height} projectId={projectId} afm={afm} - LoadingComponent={Loading} - ErrorComponent={Error} /> )} diff --git a/examples/src/components/TableExample.jsx b/examples/src/components/TableExample.jsx index 44e4940b3c..ea037902e1 100644 --- a/examples/src/components/TableExample.jsx +++ b/examples/src/components/TableExample.jsx @@ -12,8 +12,6 @@ import { franchiseFeesInitialFranchiseFeeIdentifier, franchiseFeesIdentifierOngoingRoyalty } from '../utils/fixtures'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; export class TableExample extends Component { onLoadingChanged(...params) { @@ -125,8 +123,6 @@ export class TableExample extends Component { totals={totals} onLoadingChanged={this.onLoadingChanged} onError={this.onError} - LoadingComponent={Loading} - ErrorComponent={Error} /> ); diff --git a/examples/src/components/VisualizationColumnChartExample.jsx b/examples/src/components/VisualizationColumnChartExample.jsx index dc5affb3a5..fa6cdf41df 100644 --- a/examples/src/components/VisualizationColumnChartExample.jsx +++ b/examples/src/components/VisualizationColumnChartExample.jsx @@ -4,8 +4,6 @@ import '@gooddata/react-components/styles/css/main.css'; import { Visualization } from '@gooddata/react-components'; import { projectId, columnVisualizationIdentifier } from '../utils/fixtures'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; export class VisualizationColumnChartExample extends Component { render() { @@ -14,8 +12,6 @@ export class VisualizationColumnChartExample extends Component { ); diff --git a/examples/src/components/VisualizationTableExample.jsx b/examples/src/components/VisualizationTableExample.jsx index 2f610feb03..419f9c8cb4 100644 --- a/examples/src/components/VisualizationTableExample.jsx +++ b/examples/src/components/VisualizationTableExample.jsx @@ -4,8 +4,6 @@ import '@gooddata/react-components/styles/css/main.css'; import { Visualization } from '@gooddata/react-components'; import { projectId, tableVisualizationIdentifier } from '../utils/fixtures'; -import { Loading } from './utils/Loading'; -import { Error } from './utils/Error'; export class VisualizationTable extends Component { render() { @@ -14,8 +12,6 @@ export class VisualizationTable extends Component { ); diff --git a/examples/src/components/utils/Error.jsx b/examples/src/components/utils/CustomError.jsx similarity index 73% rename from examples/src/components/utils/Error.jsx rename to examples/src/components/utils/CustomError.jsx index 202328afb8..65d8adb59b 100644 --- a/examples/src/components/utils/Error.jsx +++ b/examples/src/components/utils/CustomError.jsx @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; import '@gooddata/react-components/styles/css/main.css'; -export const Error = ({ error, height }) => ( +export const CustomError = ({ message, height }) => (
( }} >
- {JSON.stringify(error)} + {message}
); -Error.propTypes = { - error: PropTypes.shape({ - status: PropTypes.string.isRequired, - message: PropTypes.string - }).isRequired, +CustomError.propTypes = { + message: PropTypes.string.isRequired, height: PropTypes.any }; -Error.defaultProps = { +CustomError.defaultProps = { height: undefined }; -export default Error; +export default CustomError; diff --git a/examples/src/components/utils/CustomLoading.jsx b/examples/src/components/utils/CustomLoading.jsx new file mode 100644 index 0000000000..d2adbda22b --- /dev/null +++ b/examples/src/components/utils/CustomLoading.jsx @@ -0,0 +1,49 @@ +// (C) 2007-2018 GoodData Corporation + +import React from 'react'; +import PropTypes from 'prop-types'; + +import '@gooddata/react-components/styles/css/main.css'; + +const baseAnimationDuration = 1.8; + +export const CustomLoading = ({ label, inline, height, width, imageHeight, imageWidth, color, speed }) => ( +
+ {label ?

{label}

: null} + loading…`} /> +
+); +CustomLoading.propTypes = { + color: PropTypes.string, + speed: PropTypes.number, + inline: PropTypes.bool, + height: PropTypes.any, + width: PropTypes.any, + imageHeight: PropTypes.any, + imageWidth: PropTypes.any, + label: PropTypes.string +}; +CustomLoading.defaultProps = { + color: '#14b2e2', + speed: 1, + inline: false, + height: '100%', + width: undefined, + imageHeight: 38, + imageWidth: undefined, + label: null +}; + + +export default CustomLoading; diff --git a/examples/src/components/utils/Loading.jsx b/examples/src/components/utils/Loading.jsx deleted file mode 100644 index dd09b17c68..0000000000 --- a/examples/src/components/utils/Loading.jsx +++ /dev/null @@ -1,44 +0,0 @@ -// (C) 2007-2018 GoodData Corporation - -import React from 'react'; -import PropTypes from 'prop-types'; - -import '@gooddata/react-components/styles/css/main.css'; - -const baseAnimationDuration = 1.8; - -export const Loading = ({ label, inline, height, width, color, speed }) => ( -
- {label ?

{label}

: null} - loading…`} /> -
-); -Loading.propTypes = { - color: PropTypes.string, - speed: PropTypes.number, - inline: PropTypes.bool, - height: PropTypes.any, - width: PropTypes.any, - label: PropTypes.string -}; -Loading.defaultProps = { - color: '#14b2e2', - speed: 1, - inline: false, - height: 38, - width: undefined, - label: null -}; - - -export default Loading; diff --git a/examples/src/index.jsx b/examples/src/index.jsx index fc77a69a15..6567b49062 100644 --- a/examples/src/index.jsx +++ b/examples/src/index.jsx @@ -13,7 +13,7 @@ import { import '@gooddata/goodstrap/lib/theme-indigo.scss'; import Header from './components/utils/Header'; import LoginOverlay from './components/utils/LoginOverlay'; -import { Error } from './components/utils/Error'; +import { CustomError } from './components/utils/CustomError'; import { routes, mainRoutes } from './routes/_list'; @@ -105,7 +105,7 @@ export class App extends React.Component { logoutAction={this.logout} /> {errorMessage - ? + ? : null }
diff --git a/examples/webpack.config.js b/examples/webpack.config.js index 23c20e8e44..6f5b056dcd 100644 --- a/examples/webpack.config.js +++ b/examples/webpack.config.js @@ -116,6 +116,7 @@ module.exports = ({ gdc = defaultBackend, link = false, basepath = '' } = {}) => node: { __filename: true }, + devtool: isProduction ? false : 'cheap-module-eval-source-map', module: { rules: [ { diff --git a/package.json b/package.json index 729860ef9a..2c72f7cf2d 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,13 @@ "test-storybook": "test-storybook" }, "typings": "dist/index.d.ts", + "test-storybook": { + "AFM components - PieChart": { + "error": { + "readySelector": ".s-error" + } + } + }, "jest": { "setupTestFrameworkScriptFile": "/jest.setup.js", "transform": { diff --git a/src/components/core/Headline.tsx b/src/components/core/Headline.tsx index 8aa0f1fdd4..c0097f5708 100644 --- a/src/components/core/Headline.tsx +++ b/src/components/core/Headline.tsx @@ -13,7 +13,7 @@ import { ICommonVisualizationProps, visualizationLoadingHOC, ILoadingInjectedProps, - commonDefaultprops + commonDefaultProps } from './base/VisualizationLoadingHOC'; import { BaseVisualization } from './base/BaseVisualization'; @@ -22,7 +22,7 @@ export { Requireable }; export class HeadlineStateless extends BaseVisualization { - public static defaultProps: Partial = commonDefaultprops; + public static defaultProps: Partial = commonDefaultProps; public static propTypes = HeadlinePropTypes; diff --git a/src/components/core/PureTable.tsx b/src/components/core/PureTable.tsx index c3177cd3d7..8587242989 100644 --- a/src/components/core/PureTable.tsx +++ b/src/components/core/PureTable.tsx @@ -27,7 +27,7 @@ import { ICommonVisualizationProps, visualizationLoadingHOC, ILoadingInjectedProps, - commonDefaultprops + commonDefaultProps } from './base/VisualizationLoadingHOC'; import { BaseVisualization } from './base/BaseVisualization'; @@ -52,7 +52,7 @@ class SimpleTable extends BaseVisualization { public static defaultProps: Partial = { - ...commonDefaultprops, + ...commonDefaultProps, stickyHeaderOffset: 0, height: null, maxHeight: null, diff --git a/src/components/core/base/BaseChart.tsx b/src/components/core/base/BaseChart.tsx index 3a640cb739..f5da0d5b40 100644 --- a/src/components/core/base/BaseChart.tsx +++ b/src/components/core/base/BaseChart.tsx @@ -16,7 +16,7 @@ import { ICommonVisualizationProps, visualizationLoadingHOC, ILoadingInjectedProps, - commonDefaultprops + commonDefaultProps } from './VisualizationLoadingHOC'; import { ChartPropTypes, Requireable } from '../../../proptypes/Chart'; import { BaseVisualization } from './BaseVisualization'; @@ -54,7 +54,7 @@ export interface IBaseChartProps extends IChartProps { export class StatelessBaseChart extends BaseVisualization { public static defaultProps: Partial = { - ...commonDefaultprops, + ...commonDefaultProps, onDataTooLarge: noop, onLegendReady: noop, config: {}, diff --git a/src/components/core/base/BaseVisualization.tsx b/src/components/core/base/BaseVisualization.tsx index b5aac1660a..86e2c9e91c 100644 --- a/src/components/core/base/BaseVisualization.tsx +++ b/src/components/core/base/BaseVisualization.tsx @@ -4,25 +4,46 @@ import { ICommonVisualizationProps, ILoadingInjectedProps } from './VisualizationLoadingHOC'; +import { ILoadingProps } from '../../simple/LoadingComponent'; +import { IErrorProps } from '../../simple/ErrorComponent'; export abstract class BaseVisualization

- extends React.Component { +extends React.Component { public render(): JSX.Element { const { - ErrorComponent, - LoadingComponent, execution, error, - isLoading + isLoading, + intl } = this.props; + const ErrorComponent = this.props.ErrorComponent as React.ComponentType; + const LoadingComponent = this.props.LoadingComponent as React.ComponentType; + if (error !== ErrorStates.OK) { - return ErrorComponent ? : null; + const errorMap = { + [ErrorStates.DATA_TOO_LARGE_TO_DISPLAY]: { + icon: 'icon-cloud-rain', + message: intl.formatMessage({ id: 'visualization.ErrorMessageDataTooLarge' }), + description: intl.formatMessage({ id: 'visualization.ErrorDescriptionDataTooLarge' }) + }, + [ErrorStates.UNKNOWN_ERROR]: { + message: intl.formatMessage({ id: 'visualization.ErrorMessageGeneric' }), + description: intl.formatMessage({ id: 'visualization.ErrorDescriptionGeneric' }) + } + }; + const errorProps = errorMap[errorMap.hasOwnProperty(error) ? error : ErrorStates.UNKNOWN_ERROR]; + return ErrorComponent ? ( + + ) : null; } if (isLoading || !execution) { - return LoadingComponent ? : null; + return LoadingComponent ? : null; } return this.renderVisualization(); diff --git a/src/components/core/base/VisualizationLoadingHOC.tsx b/src/components/core/base/VisualizationLoadingHOC.tsx index 3ab8db5c35..6b85212d77 100644 --- a/src/components/core/base/VisualizationLoadingHOC.tsx +++ b/src/components/core/base/VisualizationLoadingHOC.tsx @@ -15,6 +15,11 @@ import { getVisualizationOptions } from '../../../helpers/options'; import { convertErrors, checkEmptyResult } from '../../../helpers/errorHandlers'; import { IVisualizationProperties } from '../../../interfaces/VisualizationProperties'; import { IDataSourceProviderInjectedProps } from '../../afm/DataSourceProvider'; +import { injectIntl, InjectedIntl } from 'react-intl'; +import { IntlWrapper } from '../../core/base/IntlWrapper'; + +import { LoadingComponent, ILoadingProps } from '../../simple/LoadingComponent'; +import { ErrorComponent, IErrorProps } from '../../simple/ErrorComponent'; export type IExecutionDataPromise = Promise; @@ -23,8 +28,8 @@ export interface ICommonVisualizationProps extends IEvents { drillableItems?: IDrillableItem[]; afterRender?: Function; pushData?: Function; - ErrorComponent?: React.ComponentClass; - LoadingComponent?: React.ComponentClass; + ErrorComponent?: React.ComponentType; + LoadingComponent?: React.ComponentType; visualizationProperties?: IVisualizationProperties; } @@ -34,6 +39,7 @@ export interface ILoadingInjectedProps { onNegativeValues: Function; error: string; isLoading: boolean; + intl: InjectedIntl; } export interface IVisualizationLoadingState { @@ -48,12 +54,12 @@ const defaultErrorHandler = (error: any) => { } }; -export const commonDefaultprops: Partial = { +export const commonDefaultProps: Partial = { resultSpec: {}, onError: defaultErrorHandler, onLoadingChanged: noop, - ErrorComponent: null, - LoadingComponent: null, + ErrorComponent, + LoadingComponent, afterRender: noop, pushData: noop, locale: 'en-US', @@ -64,8 +70,7 @@ export const commonDefaultprops: Partial( InnerComponent: React.ComponentClass ): React.ComponentClass { - - return class WrappedComponent + class LoadingHOCWrapped extends React.Component { public static defaultProps: Partial = InnerComponent.defaultProps; @@ -95,6 +100,7 @@ export function visualizationLoadingHOC ); } @@ -189,5 +196,17 @@ export function visualizationLoadingHOC { + public render() { + return ( + + + + ); + } }; } diff --git a/src/components/core/base/test/BaseVisualization.spec.tsx b/src/components/core/base/test/BaseVisualization.spec.tsx index 55a306ce01..eb95794808 100644 --- a/src/components/core/base/test/BaseVisualization.spec.tsx +++ b/src/components/core/base/test/BaseVisualization.spec.tsx @@ -5,7 +5,8 @@ import { BaseVisualization } from '../BaseVisualization'; import { delay } from '../../../tests/utils'; import { ICommonVisualizationProps, - ILoadingInjectedProps + ILoadingInjectedProps, + commonDefaultProps } from '../VisualizationLoadingHOC'; import { LoadingComponent, @@ -14,13 +15,18 @@ import { } from '../../../tests/mocks'; import { ErrorStates } from '../../../../constants/errorStates'; import { oneMeasureResponse } from '../../../../execution/fixtures/ExecuteAfm.fixtures'; -import { IDataSourceProviderInjectedProps } from '../../../afm/DataSourceProvider'; + +const intlMock = { + formatMessage: ({ id }: { id: string}) => id +}; describe('Base visualization child', () => { const TestVizInnerComponent = DummyComponent; - class BaseVisualizationChild extends - BaseVisualization { + class BaseVisualizationChild extends BaseVisualization { + public static defaultProps: Partial = { + ...commonDefaultProps + }; protected renderVisualization() { return ; @@ -28,17 +34,17 @@ describe('Base visualization child', () => { } const createComponent = (customProps: Partial) => { - return mount>(( + return mount>( - )); + ); }; it('should render result of "renderVisualization" when correct execution provided', () => { @@ -54,14 +60,15 @@ describe('Base visualization child', () => { }); }); - it('should render null when loading if the loading component has not been specified', () => { + it('should render default LoadingComponent when loading if the loading component has not been specified', () => { const wrapper = createComponent({ isLoading: true, error: ErrorStates.OK }); return delay().then(() => { - expect(wrapper.html()).toBe(null); + const loadingComponent = wrapper.find('.s-loading'); + expect(loadingComponent.length).toBe(1); }); }); @@ -73,18 +80,19 @@ describe('Base visualization child', () => { }); return delay().then(() => { - const loadingComponent = wrapper.find(LoadingComponent); + const loadingComponent = wrapper.find('LoadingComponent'); expect(loadingComponent.length).toBe(1); }); }); - it('should render null on error when ErrorComponent has not been specified', () => { + it('should render default ErrorComponent on error when ErrorComponent has not been specified', () => { const wrapper = createComponent({ error: ErrorStates.DATA_TOO_LARGE_TO_COMPUTE }); return delay().then(() => { - expect(wrapper.html()).toBe(null); + const errorComponent = wrapper.find('ErrorComponent'); + expect(errorComponent.length).toBe(1); }); }); @@ -100,21 +108,20 @@ describe('Base visualization child', () => { const errorComponent = wrapper.find(ErrorComponent); expect(errorComponent.length).toBe(1); - expect(errorComponent.props().props).toBe(wrapper.props()); - expect(errorComponent.props().error).toEqual({ status: ErrorStates.DATA_TOO_LARGE_TO_COMPUTE }); + expect(errorComponent.props().code).toEqual(ErrorStates.DATA_TOO_LARGE_TO_COMPUTE); }); }); it('should render LoadingComponent when execution is null', () => { const wrapper = createComponent({ execution: null, - LoadingComponent + LoadingComponent, + isLoading: true }); return delay().then(() => { - const errorComponent = wrapper.find(LoadingComponent); - expect(errorComponent.length).toBe(1); - expect(errorComponent.props().props).toBe(wrapper.props()); + const loadingComponent = wrapper.find(LoadingComponent); + expect(loadingComponent.length).toBe(1); }); }); }); diff --git a/src/components/core/base/test/VisualizationLoadingHOC.spec.tsx b/src/components/core/base/test/VisualizationLoadingHOC.spec.tsx index 96dbdf4ef5..b8cc0b23a9 100644 --- a/src/components/core/base/test/VisualizationLoadingHOC.spec.tsx +++ b/src/components/core/base/test/VisualizationLoadingHOC.spec.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import * as PropTypes from 'prop-types'; -import { mount } from 'enzyme'; +import { mount, ReactWrapper } from 'enzyme'; import { oneMeasureDataSource, tooLargeDataSource, @@ -15,7 +15,7 @@ import { ICommonVisualizationProps, ILoadingInjectedProps, visualizationLoadingHOC, - commonDefaultprops + commonDefaultProps } from '../VisualizationLoadingHOC'; import { delay } from '../../../tests/utils'; import { oneMeasureResponse } from '../../../../execution/fixtures/ExecuteAfm.fixtures'; @@ -32,7 +32,7 @@ class TestInnerComponent public static defaultProps: Partial = { - ...commonDefaultprops, + ...commonDefaultProps, customPropFooBar: 123 }; @@ -79,7 +79,7 @@ describe('VisualizationLoadingHOC', () => { }); }); - it('should init loading automatically and while loading render LoadingComponent passing down props', () => { + it('should init loading automatically', () => { let onLoadingChanged; const startedLoading = new Promise((resolve) => { onLoadingChanged = resolve; @@ -131,11 +131,7 @@ describe('VisualizationLoadingHOC', () => { return delay().then(() => { wrapper.update(); const innerWrapped = wrapper.find(TestInnerComponent); - expect(innerWrapped.props()).toMatchObject({ - isLoading: false, - error: ErrorStates.DATA_TOO_LARGE_TO_COMPUTE, - execution: null - }); + expect(innerWrapped.props().error).toEqual('DATA_TOO_LARGE_TO_COMPUTE'); consoleErrorSpy.mockRestore(); }); @@ -253,7 +249,9 @@ describe('VisualizationLoadingHOC', () => { const consoleErrorSpy = jest.spyOn(global.console, 'error'); consoleErrorSpy.mockImplementation(jest.fn()); - wrapper.props().onError({ status: 'test status' }); + const innerComponent = wrapper.find('LoadingHOCWrapped') as ReactWrapper; + + innerComponent.props().onError({ status: 'test status' }); expect(consoleErrorSpy).toHaveBeenCalledWith({ status: 'test status' }); diff --git a/src/components/simple/ErrorComponent.tsx b/src/components/simple/ErrorComponent.tsx new file mode 100644 index 0000000000..bc63613408 --- /dev/null +++ b/src/components/simple/ErrorComponent.tsx @@ -0,0 +1,92 @@ + +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { Requireable } from 'prop-types'; // tslint:disable-line:no-duplicate-imports +export { Requireable }; + +export const ErrorPropTypes = { + icon: PropTypes.string, + message: PropTypes.string.isRequired, + cause: PropTypes.instanceOf(Error), + className: PropTypes.string, + style: PropTypes.object, + width: PropTypes.any, + height: PropTypes.any +}; + +export interface IErrorProps { + code?: string; + icon?: string; + message: string; + description?: string; + cause?: Error; + className?: string; + style?: object; + width?: any; + height?: any; +} + +export class ErrorComponent extends React.Component { + public static defaultProps: Partial = { + icon: 'icon-warning', + cause: null, + className: 'Error s-error', + width: undefined, + height: '100%', + style: { + display: 'flex', + flex: '1 0 auto', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + whiteSpace: 'normal', + lineHeight: 'normal', + fontFamily: 'avenir, Helvetica Neue, arial, sans-serif' + } + }; + + public static propTypes = ErrorPropTypes; + + public render() { + const { className, style, width, height, message, description, icon } = this.props; + + const wrapperStyle = { + ...style, + width, + height + }; + + return ( +

+
+ {icon ?
: null} + +
+ {message} +
+
+ {description} +
+
+
+ ); + } +} diff --git a/src/components/simple/Kpi.tsx b/src/components/simple/Kpi.tsx index d9916b04d5..a2fcef6b7f 100644 --- a/src/components/simple/Kpi.tsx +++ b/src/components/simple/Kpi.tsx @@ -5,23 +5,29 @@ import { colors2Object, numberFormat } from '@gooddata/numberjs'; import noop = require('lodash/noop'); import { AFM, Execution } from '@gooddata/typings'; import { Filters, Uri } from '@gooddata/data-layer'; +import { injectIntl, intlShape, InjectedIntlProps } from 'react-intl'; -import { Execute, IExecuteChildrenProps, IExecuteProps, ILoadingStateProps } from '../../execution/Execute'; +import { Execute, IExecuteChildrenProps, IExecuteProps } from '../../execution/Execute'; +import { LoadingComponent, ILoadingProps } from './LoadingComponent'; +import { IErrorProps } from './ErrorComponent'; import { IEvents } from '../../interfaces/Events'; import { KpiPropTypes, Requireable } from '../../proptypes/Kpi'; import { isEmptyResult } from '../../helpers/errorHandlers'; import { ErrorStates } from '../../constants/errorStates'; +import { IntlWrapper } from '../core/base/IntlWrapper'; + export { Requireable }; export interface IKpiProps extends IEvents { measure: string; projectId: string; + locale?: string; sdk?: ISdk; filters?: AFM.FilterItem[]; format?: string; ExecuteComponent?: React.ComponentType; - LoadingComponent?: React.ComponentType; - ErrorComponent?: React.ComponentType; + LoadingComponent?: React.ComponentType; + ErrorComponent?: React.ComponentType; } function buildAFM(measure: string, filters: AFM.FilterItem[] = []): AFM.IAfm { @@ -54,21 +60,51 @@ const resultSpec: AFM.IResultSpec = { ] }; -export class Kpi extends React.Component { +export const KpiError = (props: IErrorProps) => { + const message: string = props.message; + return ( + + {message} + + ); +}; + +export class KpiWrapped extends React.Component { public static defaultProps: Partial = { format: '#,#.##', filters: [], onError: defaultErrorHandler, onLoadingChanged: noop, ExecuteComponent: Execute, - LoadingComponent: null, - ErrorComponent: null + LoadingComponent: () => , + ErrorComponent: KpiError }; - public static propTypes = KpiPropTypes; + public static propTypes = { + ...KpiPropTypes, + intl: intlShape.isRequired + }; public render() { - const { ExecuteComponent, measure, filters, LoadingComponent, ErrorComponent, ...executeProps } = this.props; + const { + ExecuteComponent, + measure, + filters, + LoadingComponent, + ErrorComponent, + intl, + ...executeProps + } = this.props; const afm = buildAFM(measure, filters); return ( { > {({ result, error, isLoading }: IExecuteChildrenProps) => { if (error && error.status !== ErrorStates.OK) { - return ErrorComponent ? : null; + return ErrorComponent ? : null; } if (isLoading || !result) { - return LoadingComponent ? : null; + return LoadingComponent ? : null; } return ( {this.getFormattedResult(this.extractNumber(result))} @@ -104,3 +143,15 @@ export class Kpi extends React.Component { return parseFloat(result.executionResult.executionResult.data[0].toString()); } } + +export const IntlKpi = injectIntl(KpiWrapped); + +export class Kpi extends React.Component { + public render() { + return ( + + + + ); + } +} diff --git a/src/components/simple/LoadingComponent.tsx b/src/components/simple/LoadingComponent.tsx new file mode 100644 index 0000000000..07680126e8 --- /dev/null +++ b/src/components/simple/LoadingComponent.tsx @@ -0,0 +1,119 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; + +import { Requireable } from 'prop-types'; // tslint:disable-line:no-duplicate-imports +export { Requireable }; + +export const LoadingPropTypes = { + color: PropTypes.string, + speed: PropTypes.number, + inline: PropTypes.bool, + height: PropTypes.any, + width: PropTypes.any, + imageHeight: PropTypes.any, + imageWidth: PropTypes.any +}; + +export interface ILoadingProps { + color?: string; + speed?: number; + inline?: boolean; + height?: any; + width?: any; + imageHeight?: any; + imageWidth?: any; +} + +const baseAnimationDuration = 1.4; + +export class LoadingComponent extends React.Component { + public static defaultProps: Partial = { + color: '#94a1ad', + speed: 1, + inline: false, + height: '100%', + width: undefined, + imageHeight: 8, + imageWidth: undefined + }; + + public static propTypes = LoadingPropTypes; + + public render() { + const { inline, width, height, imageWidth, imageHeight, color, speed } = this.props; + const duration = baseAnimationDuration / speed; + const delay = duration / -5; + return ( +
+ loading… + + + + + `} + /> +
+ ); + } +} diff --git a/src/components/uri/Visualization.tsx b/src/components/uri/Visualization.tsx index 5279962877..40b4f62e86 100644 --- a/src/components/uri/Visualization.tsx +++ b/src/components/uri/Visualization.tsx @@ -24,6 +24,8 @@ import { ISubject } from '../../helpers/async'; import { getVisualizationTypeFromVisualizationClass } from '../../helpers/visualizationType'; import * as MdObjectHelper from '../../helpers/MdObjectHelper'; import { fillPoPTitlesAndAliases } from '../../helpers/popHelper'; +import { LoadingComponent, ILoadingProps } from '../simple/LoadingComponent'; +import { ErrorComponent, IErrorProps } from '../simple/ErrorComponent'; import { IDrillableItem, ErrorStates, @@ -74,8 +76,8 @@ export interface IVisualizationProps extends IEvents { BaseChartComponent?: any; TableComponent?: any; HeadlineComponent?: any; - ErrorComponent?: React.ComponentClass; - LoadingComponent?: React.ComponentClass; + ErrorComponent?: React.ComponentType; + LoadingComponent?: React.ComponentType; onLegendReady?: OnLegendReady; } @@ -138,8 +140,8 @@ export class VisualizationWrapped BaseChartComponent: BaseChart, TableComponent: SortableTable, HeadlineComponent: Headline, - LoadingComponent: null, - ErrorComponent: null + ErrorComponent, + LoadingComponent }; private visualizationUri: string; @@ -244,6 +246,7 @@ export class VisualizationWrapped onLoadingChanged, locale, config, + intl, BaseChartComponent, TableComponent, HeadlineComponent, @@ -254,12 +257,18 @@ export class VisualizationWrapped if (error !== null && error.status !== ErrorStates.OK) { return ErrorComponent - ? + ? ( + + ) : null; } if (isLoading || !dataSource) { return LoadingComponent - ? + ? : null; } diff --git a/src/execution/Execute.tsx b/src/execution/Execute.tsx index a76260f283..112c1436c6 100644 --- a/src/execution/Execute.tsx +++ b/src/execution/Execute.tsx @@ -13,12 +13,9 @@ import { setTelemetryHeaders } from '../helpers/utils'; export { Requireable }; -export type IDataTableFactory = (sdk: ISdk, projectId: string) => DataTable; +const UNKNOWN_ERROR = 'UNKNOWN_ERROR'; -export interface ILoadingStateProps { - error?: object; - props: object; -} +export type IDataTableFactory = (sdk: ISdk, projectId: string) => DataTable; export interface IExecuteProps extends IEvents { afm: AFM.IAfm; @@ -88,16 +85,17 @@ export class Execute extends React.Component { }); this.dataTable.onError((error: Execution.IError) => { - const { status } = error.response; + const statusMap = { + [ErrorCodes.HTTP_TOO_LARGE]: ErrorStates.DATA_TOO_LARGE_TO_COMPUTE, + [ErrorCodes.HTTP_BAD_REQUEST]: ErrorStates.BAD_REQUEST, + [UNKNOWN_ERROR]: ErrorStates.UNKNOWN_ERROR + }; + const errorCode = error && error.response && error.response.status || UNKNOWN_ERROR; + const status = statusMap[ statusMap.hasOwnProperty(errorCode) ? errorCode : UNKNOWN_ERROR]; const newError = { - status: ErrorStates.UNKNOWN_ERROR, + status, error }; - if (status === ErrorCodes.HTTP_TOO_LARGE) { - newError.status = ErrorStates.DATA_TOO_LARGE_TO_COMPUTE; - } else if (status === ErrorCodes.HTTP_BAD_REQUEST) { - newError.status = ErrorStates.BAD_REQUEST; - } this.setState({ result: null, diff --git a/src/index.ts b/src/index.ts index 29df220a2a..3420ec40f8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,8 @@ import { BaseChart, ILegendConfig, IBaseChartProps } from './components/core/bas import { Table as CoreTable } from './components/core/Table'; import { Headline as CoreHeadline } from './components/core/Headline'; import { ICommonVisualizationProps } from './components/core/base/VisualizationLoadingHOC'; +import { ErrorComponent } from './components/simple/ErrorComponent'; +import { LoadingComponent } from './components/simple/LoadingComponent'; import { Kpi } from './components/simple/Kpi'; import { Visualization, VisualizationEnvironment } from './components/uri/Visualization'; import { ErrorStates, ErrorCodes } from './constants/errorStates'; @@ -47,6 +49,8 @@ export { CoreComponents, ErrorCodes, ErrorStates, + ErrorComponent, + LoadingComponent, Execute, generateDimensions, Headline, diff --git a/src/translations/en-US.json b/src/translations/en-US.json index 88ab7ed287..d68a3453d5 100644 --- a/src/translations/en-US.json +++ b/src/translations/en-US.json @@ -25,6 +25,11 @@ "visualization.numericValues.p": "P", "visualization.numericValues.e": "E", "visualization.emptyValue": "empty value", + "visualization.ErrorMessageGeneric": "Sorry, we can't display this insight", + "visualization.ErrorDescriptionGeneric": "Contact your administrator.", + "visualization.ErrorMessageDataTooLarge": "Too many data points to display", + "visualization.ErrorDescriptionDataTooLarge": "Try applying filters.", + "visualization.ErrorMessageKpi": "Error", "gs.filter.loading": "Loading filter…", "gs.filter.error": "Error loading filter", "gs.list.loading": "Loading list…", diff --git a/stories/AfmComponentBarChart.tsx b/stories/AfmComponentBarChart.tsx index 263af8f8e8..dfdfc9638d 100644 --- a/stories/AfmComponentBarChart.tsx +++ b/stories/AfmComponentBarChart.tsx @@ -22,6 +22,8 @@ storiesOf('AFM components - BarChart', module) projectId="storybook" afm={AFM_TWO_MEASURES_ONE_ATTRIBUTE} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} />
) @@ -43,6 +45,8 @@ storiesOf('AFM components - BarChart', module) ] }} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -54,6 +58,8 @@ storiesOf('AFM components - BarChart', module) projectId="storybook" afm={AFM_ONE_RENAMED_MEASURE} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -66,6 +72,8 @@ storiesOf('AFM components - BarChart', module) afm={AFM_ONE_MEASURE_ONE_ATTRIBUTE} config={{ colors: CUSTOM_COLORS }} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) diff --git a/stories/AfmComponentColumnChart.tsx b/stories/AfmComponentColumnChart.tsx index 346c87ebe0..bf41840e6a 100644 --- a/stories/AfmComponentColumnChart.tsx +++ b/stories/AfmComponentColumnChart.tsx @@ -21,6 +21,8 @@ storiesOf('AFM components - ColumnChart', module) projectId="storybook" afm={AFM_TWO_MEASURES_ONE_ATTRIBUTE} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -32,6 +34,8 @@ storiesOf('AFM components - ColumnChart', module) projectId="storybook" afm={AFM_TWO_MEASURES_ONE_RENAMED_ATTRIBUTE} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -44,6 +48,8 @@ storiesOf('AFM components - ColumnChart', module) afm={AFM_ONE_MEASURE_ONE_ATTRIBUTE} config={{ colors: CUSTOM_COLORS }} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) diff --git a/stories/AfmComponentHeadline.tsx b/stories/AfmComponentHeadline.tsx index b830afd20a..f99a8ede2a 100644 --- a/stories/AfmComponentHeadline.tsx +++ b/stories/AfmComponentHeadline.tsx @@ -14,6 +14,8 @@ storiesOf('AFM components - Headline', module) ) diff --git a/stories/AfmComponentLineChart.tsx b/stories/AfmComponentLineChart.tsx index 1e9cb08229..8695e5f175 100644 --- a/stories/AfmComponentLineChart.tsx +++ b/stories/AfmComponentLineChart.tsx @@ -21,6 +21,8 @@ storiesOf('AFM components - LineChart', module) projectId="storybook" afm={AFM_TWO_MEASURES_ONE_ATTRIBUTE} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -32,6 +34,8 @@ storiesOf('AFM components - LineChart', module) projectId="storybook" afm={AFM_TWO_MEASURES_ONE_RENAMED_ATTRIBUTE} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -44,6 +48,8 @@ storiesOf('AFM components - LineChart', module) afm={AFM_ONE_MEASURE_ONE_ATTRIBUTE} config={{ colors: CUSTOM_COLORS }} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) diff --git a/stories/AfmComponentPieChart.tsx b/stories/AfmComponentPieChart.tsx index 4a92356465..923098b6ca 100644 --- a/stories/AfmComponentPieChart.tsx +++ b/stories/AfmComponentPieChart.tsx @@ -7,7 +7,8 @@ import { PieChart } from '../src/components/afm/PieChart'; import { AFM_ONE_MEASURE_ONE_ATTRIBUTE, AFM_TWO_MEASURES, - AFM_ONE_RENAMED_MEASURE_ONE_RENAMED_ATTRIBUTE + AFM_ONE_RENAMED_MEASURE_ONE_RENAMED_ATTRIBUTE, + AFM_TWO_MEASURES_ONE_ATTRIBUTE } from './data/afmComponentProps'; import { onErrorHandler } from './mocks'; import '../styles/scss/charts.scss'; @@ -20,6 +21,8 @@ storiesOf('AFM components - PieChart', module) projectId="storybook" afm={AFM_TWO_MEASURES} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -31,6 +34,8 @@ storiesOf('AFM components - PieChart', module) projectId="storybook" afm={AFM_ONE_MEASURE_ONE_ATTRIBUTE} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -42,6 +47,8 @@ storiesOf('AFM components - PieChart', module) projectId="storybook" afm={AFM_ONE_RENAMED_MEASURE_ONE_RENAMED_ATTRIBUTE} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -54,6 +61,20 @@ storiesOf('AFM components - PieChart', module) afm={AFM_ONE_MEASURE_ONE_ATTRIBUTE} config={{ legend: { position: 'bottom' } }} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} + /> + + ) + )) + .add('error', () => ( + screenshotWrap( +
+
) diff --git a/stories/AfmComponentTable.tsx b/stories/AfmComponentTable.tsx index ab577fc84a..c7d609bbf3 100644 --- a/stories/AfmComponentTable.tsx +++ b/stories/AfmComponentTable.tsx @@ -31,6 +31,8 @@ storiesOf('AFM components - Table', module) afm={AFM_TWO_MEASURES_ONE_ATTRIBUTE} onError={onErrorHandler} maxHeight={223} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -42,6 +44,8 @@ storiesOf('AFM components - Table', module) projectId="storybook" afm={AFM_ONE_RENAMED_MEASURE_ONE_RENAMED_ATTRIBUTE} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -54,6 +58,8 @@ storiesOf('AFM components - Table', module) afm={AFM_TWO_MEASURES_ONE_ATTRIBUTE_TOTALS} onError={onErrorHandler} resultSpec={RESULT_SPEC_TWO_MEASURES_ONE_ATTRIBUTE_TOTALS} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -68,6 +74,8 @@ storiesOf('AFM components - Table', module) totalsEditAllowed={true} resultSpec={RESULT_SPEC_TWO_MEASURES_ONE_ATTRIBUTE_TOTALS} pushData={logTotalsChange} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -91,6 +99,8 @@ storiesOf('AFM components - Table', module) totalsEditAllowed={true} resultSpec={RESULT_SPEC_TWO_MEASURES_ONE_ATTRIBUTE_CITIES_TOTALS} pushData={logTotalsChange} + LoadingComponent={null} + ErrorComponent={null} /> ) diff --git a/stories/BarChart.tsx b/stories/BarChart.tsx index 196d8d4ddd..a70864aead 100644 --- a/stories/BarChart.tsx +++ b/stories/BarChart.tsx @@ -16,6 +16,8 @@ storiesOf('BarChart', module) measures={[MEASURE_1, MEASURE_2]} viewBy={ATTRIBUTE_1} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -29,6 +31,8 @@ storiesOf('BarChart', module) viewBy={ATTRIBUTE_2} stackBy={ATTRIBUTE_1} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -40,6 +44,8 @@ storiesOf('BarChart', module) projectId="storybook" measures={[MEASURE_1_WITH_ALIAS]} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -53,6 +59,8 @@ storiesOf('BarChart', module) viewBy={ATTRIBUTE_1} config={{ colors: CUSTOM_COLORS }} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) diff --git a/stories/ColumnChart.tsx b/stories/ColumnChart.tsx index e1ef6723d6..e005dd0d1e 100644 --- a/stories/ColumnChart.tsx +++ b/stories/ColumnChart.tsx @@ -16,6 +16,8 @@ storiesOf('ColumnChart', module) measures={[MEASURE_1, MEASURE_2]} viewBy={ATTRIBUTE_1} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -28,6 +30,8 @@ storiesOf('ColumnChart', module) measures={[MEASURE_1, MEASURE_2]} viewBy={ATTRIBUTE_1_WITH_ALIAS} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -41,6 +45,8 @@ storiesOf('ColumnChart', module) viewBy={ATTRIBUTE_1} config={{ colors: CUSTOM_COLORS }} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) diff --git a/stories/Headline.tsx b/stories/Headline.tsx index a7ad7f1775..97a1b523d2 100644 --- a/stories/Headline.tsx +++ b/stories/Headline.tsx @@ -12,6 +12,8 @@ storiesOf('Headline', module) ) diff --git a/stories/Kpi.tsx b/stories/Kpi.tsx index e4bebd2be2..9cb25b62a8 100644 --- a/stories/Kpi.tsx +++ b/stories/Kpi.tsx @@ -1,24 +1,37 @@ // (C) 2007-2018 GoodData Corporation import * as React from 'react'; import { storiesOf } from '@storybook/react'; + import { Kpi } from '../src/components/simple/Kpi'; storiesOf('KPI', module) .add('KPI', () => (
Measure 1 with number: + {' '} -
Measure 9 with no data: + {' '} +
+ Error: + {' '} +
diff --git a/stories/LineChart.tsx b/stories/LineChart.tsx index 94cf92a45d..1a90b68f56 100644 --- a/stories/LineChart.tsx +++ b/stories/LineChart.tsx @@ -16,6 +16,8 @@ storiesOf('LineChart', module) measures={[MEASURE_1, MEASURE_2]} trendBy={ATTRIBUTE_1} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -28,6 +30,8 @@ storiesOf('LineChart', module) measures={[MEASURE_1, MEASURE_2]} trendBy={ATTRIBUTE_1_WITH_ALIAS} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -41,6 +45,8 @@ storiesOf('LineChart', module) trendBy={ATTRIBUTE_1} config={{ colors: CUSTOM_COLORS }} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) diff --git a/stories/LoadingAndError.tsx b/stories/LoadingAndError.tsx new file mode 100644 index 0000000000..7de65fd75d --- /dev/null +++ b/stories/LoadingAndError.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import { storiesOf } from '@storybook/react'; + +import { LoadingComponent } from '../src/components/simple/LoadingComponent'; +import { ErrorComponent } from '../src/components/simple/ErrorComponent'; + +storiesOf('Loading and Error', module) + .add('Loading default', () => ( +
+ +
+ )) + .add('Loading customised', () => ( +
+ +
+ )) + .add('Error default', () => ( +
+ +
+ )); diff --git a/stories/PieChart.tsx b/stories/PieChart.tsx index 8e82f0d025..5e1d07cf60 100644 --- a/stories/PieChart.tsx +++ b/stories/PieChart.tsx @@ -14,6 +14,8 @@ storiesOf('PieChart', module) projectId="storybook" measures={[MEASURE_1, MEASURE_2]} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -26,6 +28,8 @@ storiesOf('PieChart', module) measures={[MEASURE_1]} viewBy={ATTRIBUTE_1} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -38,6 +42,8 @@ storiesOf('PieChart', module) measures={[MEASURE_1_WITH_ALIAS]} viewBy={ATTRIBUTE_1_WITH_ALIAS} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -51,6 +57,8 @@ storiesOf('PieChart', module) viewBy={ATTRIBUTE_1} config={{ legend: { position: 'bottom' } }} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) diff --git a/stories/Table.tsx b/stories/Table.tsx index fd014beacf..18835fc52c 100644 --- a/stories/Table.tsx +++ b/stories/Table.tsx @@ -31,6 +31,8 @@ storiesOf('Table', module) measures={[MEASURE_1, MEASURE_2]} attributes={[ATTRIBUTE_1]} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -43,6 +45,8 @@ storiesOf('Table', module) measures={[MEASURE_1_WITH_ALIAS]} attributes={[ATTRIBUTE_1_WITH_ALIAS]} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -56,6 +60,8 @@ storiesOf('Table', module) attributes={[ATTRIBUTE_1]} totals={[TOTAL_M1_A1, TOTAL_M2_A1]} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -71,6 +77,8 @@ storiesOf('Table', module) totalsEditAllowed={true} onError={onErrorHandler} pushData={logTotalsChange} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -95,6 +103,8 @@ storiesOf('Table', module) totalsEditAllowed={true} onError={onErrorHandler} pushData={logTotalsChange} + LoadingComponent={null} + ErrorComponent={null} /> ) diff --git a/stories/Visualization.tsx b/stories/Visualization.tsx index a6b9fe7357..24a2ef6af3 100644 --- a/stories/Visualization.tsx +++ b/stories/Visualization.tsx @@ -54,6 +54,8 @@ class DynamicVisualization extends React.Component { onLoadingChanged={this.onLoadingChanged} onError={onErrorHandler} projectId={this.state.projectId} + LoadingComponent={null} + ErrorComponent={null} {...this.state} /> {this.state.isLoading @@ -92,6 +94,8 @@ storiesOf('Visualization', module) projectId="myproject" uri={'/gdc/md/myproject/obj/1001'} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -103,6 +107,8 @@ storiesOf('Visualization', module) projectId="myproject" identifier="1001" onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -116,6 +122,8 @@ storiesOf('Visualization', module) drillableItems={[{ identifier: '1' }, { identifier: '3' }]} onFiredDrillEvent={action('drill-event fired')} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -136,6 +144,8 @@ storiesOf('Visualization', module) projectId="myproject" identifier="1001" onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -148,6 +158,8 @@ storiesOf('Visualization', module) uri={'/gdc/md/myproject/obj/1003'} onError={onErrorHandler} locale="de-DE" + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -159,6 +171,8 @@ storiesOf('Visualization', module) projectId="myproject" uri={'/gdc/md/myproject/obj/1002'} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -176,6 +190,8 @@ storiesOf('Visualization', module) }} onLegendReady={action('onLegendReady')} // NOTE: onClick is not shown in action logger onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -199,6 +215,8 @@ storiesOf('Visualization', module) filters={[filter]} config={{ colors: CUSTOM_COLORS }} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ); @@ -212,6 +230,8 @@ storiesOf('Visualization', module) drillableItems={[{ identifier: '1' }, { identifier: '2' }]} onFiredDrillEvent={action('drill-event fired')} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -223,6 +243,8 @@ storiesOf('Visualization', module) projectId="myproject" uri={'/gdc/md/myproject/obj/1004'} onError={onErrorHandler} + LoadingComponent={null} + ErrorComponent={null} /> ) @@ -233,4 +255,14 @@ storiesOf('Visualization', module) ) + )) + .add('error', () => ( +
+ +
)); diff --git a/stories/__screenshots__/storybook_AFM_components_-_PieChart_-_error_0_screenshot-wrapper_0_desktop.png b/stories/__screenshots__/storybook_AFM_components_-_PieChart_-_error_0_screenshot-wrapper_0_desktop.png new file mode 100644 index 0000000000..045b593512 Binary files /dev/null and b/stories/__screenshots__/storybook_AFM_components_-_PieChart_-_error_0_screenshot-wrapper_0_desktop.png differ diff --git a/yarn.lock b/yarn.lock index d484d1b0a0..89a507b167 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3232,9 +3232,15 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" +electron-releases@^2.1.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/electron-releases/-/electron-releases-2.19.0.tgz#d9e70e3a42b412ed0f5c5f22e4a67bc059d98c91" + electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30: - version "1.3.31" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.31.tgz#00d832cba9fe2358652b0c48a8816c8e3a037e9f" + version "1.3.30" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.30.tgz#9666f532a64586651fc56a72513692e820d06a80" + dependencies: + electron-releases "^2.1.0" elliptic@^6.0.0: version "6.4.0" @@ -4750,12 +4756,18 @@ intl-relativeformat@^2.0.0: dependencies: intl-messageformat "^2.0.0" -invariant@2.2.2, invariant@^2.0.0, invariant@^2.1.0, invariant@^2.1.1, invariant@^2.2.2: +invariant@2.2.2, invariant@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" dependencies: loose-envify "^1.0.0" +invariant@^2.0.0, invariant@^2.1.0, invariant@^2.1.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.3.tgz#1a827dfde7dcbd7c323f0ca826be8fa7c5e9d688" + dependencies: + loose-envify "^1.0.0" + invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -5726,14 +5738,10 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" -lodash-es@^4.17.5: +lodash-es@^4.17.5, lodash-es@^4.2.1: version "4.17.7" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.7.tgz#db240a3252c3dd8360201ac9feef91ac977ea856" -lodash-es@^4.2.1: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7" - lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" @@ -5806,7 +5814,7 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@4.17.4, lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.0, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@~4.17.4: +lodash@4.17.4, lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@~4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -5814,7 +5822,7 @@ lodash@^3.10.1: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" -lodash@^4.17.2, lodash@^4.17.5: +lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" @@ -6186,8 +6194,8 @@ moment@2.20.0: resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.0.tgz#53396358994dd3a551e966a66af715ecb6c30ad0" "moment@>= 2.6.0": - version "2.20.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd" + version "2.21.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" move-concurrently@^1.0.1: version "1.0.1" @@ -7358,7 +7366,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@15.6.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0: +prop-types@15.6.0, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0: version "15.6.0" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" dependencies: @@ -7366,7 +7374,7 @@ prop-types@15.6.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, loose-envify "^1.3.1" object-assign "^4.1.1" -prop-types@^15.5.7: +prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.5.7: version "15.6.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" dependencies: @@ -7708,8 +7716,8 @@ react-responsive@1.2.10: prop-types "^15.5.7" react-side-effect@^1.0.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-1.1.3.tgz#512c25abe0dec172834c4001ec5c51e04d41bc5c" + version "1.1.5" + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-1.1.5.tgz#f26059e50ed9c626d91d661b9f3c8bb38cd0ff2d" dependencies: exenv "^1.2.1" shallowequal "^1.0.1" @@ -8949,8 +8957,8 @@ symbol-observable@1.0.1: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" symbol-observable@^1.0.1, symbol-observable@^1.0.3: - version "1.1.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.1.0.tgz#5c68fd8d54115d9dfb72a84720549222e8db9b32" + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" symbol-tree@^3.2.1: version "3.2.2"