Skip to content
This repository has been archived by the owner on Dec 10, 2021. It is now read-only.

Commit

Permalink
feat: waterfall chart
Browse files Browse the repository at this point in the history
  • Loading branch information
simcha90 committed Jul 27, 2020
1 parent 9c28e93 commit 921664c
Show file tree
Hide file tree
Showing 17 changed files with 972 additions and 0 deletions.
54 changes: 54 additions & 0 deletions plugins/plugin-chart-waterfall/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
## @superset-ui/plugin-chart-waterfall

[![Version](https://img.shields.io/npm/v/@superset-ui/plugin-chart-waterfall.svg?style=flat-square)](https://img.shields.io/npm/v/@superset-ui/plugin-chart-waterfall.svg?style=flat-square)
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui.svg?path=packages%2Fsuperset-ui-plugin-chart-waterfall&style=flat-square)](https://david-dm.org/apache-superset/superset-ui?path=packages/superset-ui-plugin-chart-waterfall)

This plugin provides Waterfall for Superset.

### Usage

Configure `key`, which can be any `string`, and register the plugin. This `key` will be used to lookup this chart throughout the app.

```js
import WaterfallChartPlugin from '@superset-ui/plugin-chart-waterfall';

new WaterfallChartPlugin()
.configure({ key: 'waterfall' })
.register();
```

Then use it via `SuperChart`. See [storybook](https://apache-superset.github.io/superset-ui/?selectedKind=plugin-chart-waterfall) for more details.

```js
<SuperChart
chartType="waterfall"
width={600}
height={600}
formData={...}
queryData={{
data: {...},
}}
/>
```

### File structure generated

```
├── README.md
├── package.json
├── src
│   ├── Waterfall.tsx
│   ├── images
│   │   └── thumbnail.png
│   ├── index.ts
│   ├── plugin
│   │   ├── buildQuery.ts
│   │   ├── controlPanel.ts
│   │   ├── index.ts
│   │   └── transformProps.ts
│   └── types.ts
├── test
│   └── index.test.ts
└── types
└── external.d.ts
```
45 changes: 45 additions & 0 deletions plugins/plugin-chart-waterfall/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "@superset-ui/plugin-chart-waterfall",
"version": "0.0.0",
"description": "Superset Chart - Waterfall",
"sideEffects": false,
"main": "lib/index.js",
"module": "esm/index.js",
"files": [
"esm",
"lib"
],
"repository": {
"type": "git",
"url": "git+https://github.com/apache-superset/superset-ui.git"
},
"keywords": [
"superset"
],
"author": "Superset",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/apache-superset/superset-ui/issues"
},
"homepage": "https://github.com/apache-superset/superset-ui#readme",
"publishConfig": {
"access": "public"
},
"peerDependencies": {
"@superset-ui/chart": "^0.14.1",
"@superset-ui/chart-controls": "^0.14.0",
"@superset-ui/query": "^0.14.1",
"@superset-ui/style": "^0.14.0",
"@superset-ui/translation": "^0.14.0",
"@superset-ui/validator": "^0.14.1",
"react": "^16.13.1"
},
"devDependencies": {
"@types/jest": "^26.0.0",
"jest": "^26.0.1"
},
"dependencies": {
"@types/recharts": "^1.8.14",
"recharts": "^1.8.5"
}
}
153 changes: 153 additions & 0 deletions plugins/plugin-chart-waterfall/src/components/Waterfall.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, { createRef, FC, useState } from 'react';
import { t } from '@superset-ui/translation';
import styled from '@superset-ui/style';
import { BarChart, Bar, LabelList, XAxis, YAxis, CartesianGrid, Tooltip } from 'recharts';
import WaterfallTick from './WaterfallTick';
import { valueFormatter } from './utils';
import WaterfallBar from './WaterfallBar';
import WaterfallLegend from './WaterfallLegend';

type TWaterfallStylesProps = {
height: number;
width: number;
};

export type TBarValue = { value: [number, number] };

export type TWaterfallData = {
[key: string]: string | boolean | number | TBarValue;
};

export type TWaterfallProps = {
xAxisDataKey?: string;
dataKey: string;
error?: string;
height: number;
resetFilters?: Function;
onBarClick?: Function;
width: number;
data?: TWaterfallData[];
};

const Styles = styled.div<TWaterfallStylesProps>`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: ${({ theme }) => theme.gridUnit * 4}px;
border-radius: ${({ theme }) => theme.gridUnit * 2}px;
height: ${({ height }) => height};
width: ${({ width }) => width};
overflow-y: scroll;
h3 {
/* You can use your props to control CSS! */
font-size: ${({ theme }) => theme.typography.sizes.xxl};
font-weight: bold;
}
`;

const Error = styled.div`
width: 100%;
display: flex;
justify-content: center;
align-items: center;
padding: ${({ theme }) => theme.gridUnit * 4}px;
border-radius: ${({ theme }) => theme.gridUnit * 2}px;
color: ${({ theme }) => theme.colors.warning.dark1};
background-color: ${({ theme }) => theme.colors.warning.light1};
`;

const Notification = styled.div`
cursor: pointer;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
padding: ${({ theme }) => theme.gridUnit * 4}px;
border-radius: ${({ theme }) => theme.gridUnit * 2}px;
color: ${({ theme }) => theme.colors.info.dark1};
background-color: ${({ theme }) => theme.colors.info.light1};
`;

const Waterfall: FC<TWaterfallProps> = ({
onBarClick = () => {},
xAxisDataKey,
dataKey,
data,
height,
width,
error,
}) => {
const rootElem = createRef<HTMLDivElement>();
const [notification, setNotification] = useState<string | null>(null);

const handleBarClick = (barData: TWaterfallData) => {
onBarClick(barData);
setNotification(t('Bar was clicked, filter will be emitted on a dashboard'));
};
const closeNotification = () => setNotification(null);

const renderLabel: (barValue: TBarValue) => string = ({ value }) =>
valueFormatter(value[1] - value[0]);

return (
<Styles ref={rootElem} height={height} width={width}>
{notification && <Notification onClick={closeNotification}>{notification}</Notification>}
{error ? (
<Error>{error}</Error>
) : (
<div>
<WaterfallLegend />
<BarChart
margin={{ bottom: 100, top: 20 }}
width={width - 20}
height={height - 100}
data={data}
barCategoryGap={0}
>
<CartesianGrid vertical={false} />
<XAxis dataKey={xAxisDataKey} dy={30} angle={45} tick={WaterfallTick} interval={0} />
<YAxis tickFormatter={valueFormatter} />
<Tooltip />
<Bar
dataKey={dataKey}
shape={props => (
// @ts-ignore
<WaterfallBar {...props} numberOfBars={data?.length} />
)}
onClick={handleBarClick}
>
{/*
// @ts-ignore */}
<LabelList dataKey={dataKey} position="top" content={renderLabel} />
</Bar>
</BarChart>
</div>
)}
</Styles>
);
};
<<<<<<< HEAD
=======

>>>>>>> feat: waterfall chart
export default Waterfall;
63 changes: 63 additions & 0 deletions plugins/plugin-chart-waterfall/src/components/WaterfallBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { FC } from 'react';
import { supersetTheme, styled } from '@superset-ui/style';
import { BarProps } from 'recharts';

interface IWaterfallBarProps extends BarProps {
__TOTAL__?: boolean;
numberOfBars?: number;
index?: number;
}

const ClickableRect = styled.rect`
cursor: pointer;
`;

const WaterfallBar: FC<IWaterfallBarProps> = ({
x = 0,
y = 0,
width = 0,
height = 0,
__TOTAL__,
index = 0,
numberOfBars = 0,
}) => {
const isNegative = height < 0;
// eslint-disable-next-line no-negated-condition
let fill = !isNegative ? supersetTheme.colors.success.base : supersetTheme.colors.error.base;
if (__TOTAL__) {
fill = supersetTheme.colors.info.base;
}
if (isNegative) {
// eslint-disable-next-line no-param-reassign
y += height;
// eslint-disable-next-line no-param-reassign
height = Math.abs(height);
}
// eslint-disable-next-line no-negated-condition
const lineY = !isNegative ? y : y + height;
return (
<>
{index !== numberOfBars - 1 && (
<line
x1={x + 0.1 * width}
y1={lineY}
x2={x + 2 * width - 0.1 * width}
y2={lineY}
style={{
stroke: supersetTheme.colors.grayscale.base,
strokeWidth: 1,
}}
/>
)}
<ClickableRect
x={x + 0.1 * width}
y={y}
width={width - 0.2 * width}
height={height}
fill={fill}
/>
</>
);
};

export default WaterfallBar;
54 changes: 54 additions & 0 deletions plugins/plugin-chart-waterfall/src/components/WaterfallLegend.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';
import { t } from '@superset-ui/translation';
import styled, { supersetTheme } from '@superset-ui/style';

const LEGEND = [
{ label: t('Increase'), color: supersetTheme.colors.success.base },
{ label: t('Decrease'), color: supersetTheme.colors.error.base },
{ label: t('Total'), color: supersetTheme.colors.info.base },
{ label: t('Other'), color: supersetTheme.colors.alert.base },
];

const Legend = styled.div`
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: no-wrap;
& > * {
margin-left: 10px;
}
`;

const LegendItem = styled.div`
display: flex;
justify-content: center;
align-items: center;
flex-wrap: no-wrap;
`;

const LegendIcon = styled.div`
margin-right: 5px;
width: 10px;
height: 10px;
border-radius: 50%;
background-color: ${({ theme, color }) => color};
`;

const LegendLabel = styled.div`
line-height: 0;
font-size: ${({ theme }) => theme.typography.sizes.l};
`;

const WaterfallLegend = () => (
<Legend data-test-id="legend">
{LEGEND.map(item => (
<LegendItem key={item.label}>
<LegendIcon color={item.color} />
<LegendLabel>{item.label}</LegendLabel>
</LegendItem>
))}
</Legend>
);

export default WaterfallLegend;
Loading

0 comments on commit 921664c

Please sign in to comment.