This repository has been archived by the owner on Dec 10, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 271
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add chart companion components (#139)
* feat: add chart companion components * test: add more tests * test: improve coverage * test: fix failed test * test: mock ResizeObserver * fix: test coverage * test: fix set dimension * feat: make keyColumn optional * fix: address comments * fix: test delay
- Loading branch information
Showing
13 changed files
with
719 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
## @superset-ui/chart-composition | ||
|
||
[![Version](https://img.shields.io/npm/v/@superset-ui/chart-composition.svg?style=flat)](https://img.shields.io/npm/v/@superset-ui/chart-composition.svg?style=flat) | ||
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui.svg?path=packages%2Fsuperset-ui-chart-composition&style=flat-square)](https://david-dm.org/apache-superset/superset-ui?path=packages/superset-ui-chart-composition) | ||
|
||
Description | ||
|
||
#### Example usage | ||
|
||
```js | ||
import { | ||
ChartFrame, | ||
TooltipFrame, | ||
TooltipTable, | ||
WithLegend, | ||
} from '@superset-ui/chart-composition'; | ||
``` | ||
|
||
#### API | ||
|
||
`fn(args)` | ||
|
||
- Do something | ||
|
||
### Development | ||
|
||
`@data-ui/build-config` is used to manage the build configuration for this package including babel | ||
builds, jest testing, eslint, and prettier. |
22 changes: 22 additions & 0 deletions
22
packages/superset-ui-chart-composition/__mocks__/resize-observer-polyfill.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
const allCallbacks = []; | ||
|
||
export default function ResizeObserver(callback) { | ||
if (callback) { | ||
allCallbacks.push(callback); | ||
} | ||
|
||
return { | ||
disconnect: () => { | ||
allCallbacks.splice(allCallbacks.findIndex(callback), 1); | ||
}, | ||
observe: () => {}, | ||
}; | ||
} | ||
|
||
const DEFAULT_OUTPUT = [{ contentRect: { height: 300, width: 300 } }]; | ||
|
||
export function triggerResizeObserver(output = DEFAULT_OUTPUT) { | ||
allCallbacks.forEach(fn => { | ||
fn(output); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"name": "@superset-ui/chart-composition", | ||
"version": "0.0.0", | ||
"description": "Superset UI chart-composition", | ||
"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" | ||
}, | ||
"private": true, | ||
"dependencies": { | ||
"@vx/responsive": "^0.0.184", | ||
"@types/react": "^16.7.17", | ||
"csstype": "^2.6.4" | ||
}, | ||
"peerDependencies": { | ||
"@superset-ui/core": "^0.11.0", | ||
"react": "^15 || ^16" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import React, { PureComponent } from 'react'; | ||
import { isDefined } from '@superset-ui/core'; | ||
|
||
function checkNumber(input: any): input is number { | ||
return isDefined(input) && typeof input === 'number'; | ||
} | ||
|
||
type Props = { | ||
contentWidth?: number; | ||
contentHeight?: number; | ||
height: number; | ||
renderContent: ({ height, width }: { height: number; width: number }) => React.ReactNode; | ||
width: number; | ||
}; | ||
|
||
export default class ChartFrame extends PureComponent<Props, {}> { | ||
static defaultProps = { | ||
renderContent() {}, | ||
}; | ||
|
||
render() { | ||
const { contentWidth, contentHeight, width, height, renderContent } = this.props; | ||
|
||
const overflowX = checkNumber(contentWidth) && contentWidth > width; | ||
const overflowY = checkNumber(contentHeight) && contentHeight > height; | ||
|
||
if (overflowX || overflowY) { | ||
return ( | ||
<div | ||
style={{ | ||
height, | ||
overflowX: overflowX ? 'auto' : 'hidden', | ||
overflowY: overflowY ? 'auto' : 'hidden', | ||
width, | ||
}} | ||
> | ||
{renderContent({ | ||
height: Math.max(contentHeight || 0, height), | ||
width: Math.max(contentWidth || 0, width), | ||
})} | ||
</div> | ||
); | ||
} | ||
|
||
return renderContent({ height, width }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export { default as ChartFrame } from './ChartFrame'; | ||
export { default as WithLegend } from './legend/WithLegend'; | ||
export { default as TooltipFrame } from './tooltip/TooltipFrame'; | ||
export { default as TooltipTable } from './tooltip/TooltipTable'; |
130 changes: 130 additions & 0 deletions
130
packages/superset-ui-chart-composition/src/legend/WithLegend.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* eslint-disable sort-keys */ | ||
import React, { CSSProperties, ReactNode, PureComponent } from 'react'; | ||
import { ParentSize } from '@vx/responsive'; | ||
// eslint-disable-next-line import/no-unresolved | ||
import { FlexDirectionProperty } from 'csstype'; | ||
|
||
const defaultProps = { | ||
className: '', | ||
height: 'auto' as number | string, | ||
width: 'auto' as number | string, | ||
position: 'top', | ||
}; | ||
|
||
type Props = { | ||
className: string; | ||
debounceTime?: number; | ||
width: number | string; | ||
height: number | string; | ||
legendJustifyContent?: 'center' | 'flex-start' | 'flex-end'; | ||
position: 'top' | 'left' | 'bottom' | 'right'; | ||
renderChart: (dim: { width: number; height: number }) => ReactNode; | ||
renderLegend?: (params: { direction: string }) => ReactNode; | ||
} & Readonly<typeof defaultProps>; | ||
|
||
const LEGEND_STYLE_BASE: CSSProperties = { | ||
display: 'flex', | ||
flexGrow: 0, | ||
flexShrink: 0, | ||
fontSize: '0.9em', | ||
order: -1, | ||
paddingTop: '5px', | ||
}; | ||
|
||
const CHART_STYLE_BASE: CSSProperties = { | ||
flexBasis: 'auto', | ||
flexGrow: 1, | ||
flexShrink: 1, | ||
position: 'relative', | ||
}; | ||
|
||
class WithLegend extends PureComponent<Props, {}> { | ||
static defaultProps = defaultProps; | ||
|
||
getContainerDirection(): FlexDirectionProperty { | ||
const { position } = this.props; | ||
|
||
if (position === 'left') { | ||
return 'row'; | ||
} else if (position === 'right') { | ||
return 'row-reverse'; | ||
} else if (position === 'bottom') { | ||
return 'column-reverse'; | ||
} | ||
|
||
return 'column'; | ||
} | ||
|
||
getLegendJustifyContent() { | ||
const { legendJustifyContent, position } = this.props; | ||
if (legendJustifyContent) { | ||
return legendJustifyContent; | ||
} | ||
|
||
if (position === 'left' || position === 'right') { | ||
return 'flex-start'; | ||
} | ||
|
||
return 'flex-end'; | ||
} | ||
|
||
render() { | ||
const { | ||
className, | ||
debounceTime, | ||
width, | ||
height, | ||
position, | ||
renderChart, | ||
renderLegend, | ||
} = this.props; | ||
|
||
const isHorizontal = position === 'left' || position === 'right'; | ||
|
||
const style: CSSProperties = { | ||
display: 'flex', | ||
flexDirection: this.getContainerDirection(), | ||
height, | ||
width, | ||
}; | ||
|
||
const chartStyle: CSSProperties = { ...CHART_STYLE_BASE }; | ||
if (isHorizontal) { | ||
chartStyle.width = 0; | ||
} else { | ||
chartStyle.height = 0; | ||
} | ||
|
||
const legendDirection = isHorizontal ? 'column' : 'row'; | ||
const legendStyle: CSSProperties = { | ||
...LEGEND_STYLE_BASE, | ||
flexDirection: legendDirection, | ||
justifyContent: this.getLegendJustifyContent(), | ||
}; | ||
|
||
return ( | ||
<div className={`with-legend ${className}`} style={style}> | ||
{renderLegend && ( | ||
<div className="legend-container" style={legendStyle}> | ||
{renderLegend({ | ||
// Pass flexDirection for @vx/legend to arrange legend items | ||
direction: legendDirection, | ||
})} | ||
</div> | ||
)} | ||
<div className="main-container" style={chartStyle}> | ||
<ParentSize debounceTime={debounceTime}> | ||
{(parent: { width: number; height: number }) => | ||
parent.width > 0 && parent.height > 0 | ||
? // Only render when necessary | ||
renderChart(parent) | ||
: null | ||
} | ||
</ParentSize> | ||
</div> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default WithLegend; |
28 changes: 28 additions & 0 deletions
28
packages/superset-ui-chart-composition/src/tooltip/TooltipFrame.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import React, { PureComponent } from 'react'; | ||
|
||
const defaultProps = { | ||
className: '', | ||
}; | ||
|
||
type Props = { | ||
className?: string; | ||
children: React.ReactNode; | ||
} & Readonly<typeof defaultProps>; | ||
|
||
const CONTAINER_STYLE = { padding: 8 }; | ||
|
||
class TooltipFrame extends PureComponent<Props, {}> { | ||
static defaultProps = defaultProps; | ||
|
||
render() { | ||
const { className, children } = this.props; | ||
|
||
return ( | ||
<div className={className} style={CONTAINER_STYLE}> | ||
{children} | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default TooltipFrame; |
44 changes: 44 additions & 0 deletions
44
packages/superset-ui-chart-composition/src/tooltip/TooltipTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import React, { CSSProperties, PureComponent, ReactNode } from 'react'; | ||
|
||
interface TooltipRowData { | ||
key: string | number; | ||
keyColumn?: ReactNode; | ||
keyStyle?: CSSProperties; | ||
valueColumn: ReactNode; | ||
valueStyle?: CSSProperties; | ||
} | ||
|
||
const defaultProps = { | ||
className: '', | ||
data: [] as TooltipRowData[], | ||
}; | ||
|
||
type Props = { | ||
className?: string; | ||
data: TooltipRowData[]; | ||
} & Readonly<typeof defaultProps>; | ||
|
||
const VALUE_CELL_STYLE: CSSProperties = { paddingLeft: 8, textAlign: 'right' }; | ||
|
||
export default class TooltipTable extends PureComponent<Props, {}> { | ||
static defaultProps = defaultProps; | ||
|
||
render() { | ||
const { className, data } = this.props; | ||
|
||
return ( | ||
<table className={className}> | ||
<tbody> | ||
{data.map(({ key, keyColumn, keyStyle, valueColumn, valueStyle }, i) => ( | ||
<tr key={key}> | ||
<td style={keyStyle}>{keyColumn || key}</td> | ||
<td style={valueStyle ? { ...VALUE_CELL_STYLE, ...valueStyle } : VALUE_CELL_STYLE}> | ||
{valueColumn} | ||
</td> | ||
</tr> | ||
))} | ||
</tbody> | ||
</table> | ||
); | ||
} | ||
} |
Oops, something went wrong.