mirror of
https://github.com/apache/superset.git
synced 2024-09-17 11:09:47 -04:00
chore: cleanup big number and table chart (#422)
They are moved to superset-ui by apache-superset/superset-ui#333
This commit is contained in:
parent
0176d1dd04
commit
7497a61be6
@ -1,33 +0,0 @@
|
||||
## @superset-ui/legacy-plugin-chart-table
|
||||
|
||||
[![Version](https://img.shields.io/npm/v/@superset-ui/legacy-plugin-chart-table.svg?style=flat-square)](https://img.shields.io/npm/v/@superset-ui/legacy-plugin-chart-table.svg?style=flat-square)
|
||||
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui-plugins.svg?path=packages%2Fsuperset-ui-legacy-plugin-chart-table&style=flat-square)](https://david-dm.org/apache-superset/superset-ui-plugins?path=packages/superset-ui-legacy-plugin-chart-table)
|
||||
|
||||
This plugin provides Table 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 TableChartPlugin from '@superset-ui/legacy-plugin-chart-table';
|
||||
|
||||
new TableChartPlugin().configure({ key: 'table' }).register();
|
||||
```
|
||||
|
||||
Then use it via `SuperChart`. See
|
||||
[storybook](https://apache-superset.github.io/superset-ui-plugins/?selectedKind=plugin-chart-table)
|
||||
for more details.
|
||||
|
||||
```js
|
||||
<SuperChart
|
||||
chartType="table"
|
||||
width={600}
|
||||
height={600}
|
||||
formData={...}
|
||||
queryData={{
|
||||
data: {...},
|
||||
}}
|
||||
/>
|
||||
```
|
@ -1,47 +0,0 @@
|
||||
{
|
||||
"name": "@superset-ui/legacy-plugin-chart-table",
|
||||
"version": "0.11.20",
|
||||
"description": "Superset Legacy Chart - Table",
|
||||
"sideEffects": [
|
||||
"*.css"
|
||||
],
|
||||
"main": "lib/index.js",
|
||||
"module": "esm/index.js",
|
||||
"files": [
|
||||
"esm",
|
||||
"lib"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/apache-superset/superset-ui-plugins.git"
|
||||
},
|
||||
"keywords": [
|
||||
"superset"
|
||||
],
|
||||
"author": "Superset",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/apache-superset/superset-ui-plugins/issues"
|
||||
},
|
||||
"homepage": "https://github.com/apache-superset/superset-ui-plugins#readme",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"datatables.net-bs": "^1.10.20",
|
||||
"xss": "^1.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/datatables.net": "^1.10.18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@superset-ui/chart": "^0.12.0",
|
||||
"@superset-ui/number-format": "^0.12.10",
|
||||
"@superset-ui/query": "^0.12.8",
|
||||
"@superset-ui/time-format": "^0.12.0",
|
||||
"@superset-ui/translation": "^0.12.0",
|
||||
"jquery": "^3.4.1",
|
||||
"react": "^16.8.0",
|
||||
"react-dom": "^16.8.0"
|
||||
}
|
||||
}
|
@ -1,264 +0,0 @@
|
||||
/**
|
||||
* 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 { t } from '@superset-ui/translation';
|
||||
import React, { useEffect, createRef } from 'react';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
import { formatNumber, NumberFormats } from '@superset-ui/number-format';
|
||||
import { getTimeFormatter } from '@superset-ui/time-format';
|
||||
import { filterXSS } from 'xss';
|
||||
|
||||
// initialize datatables.net
|
||||
import $ from 'jquery';
|
||||
import dt from 'datatables.net-bs/js/dataTables.bootstrap';
|
||||
import 'datatables.net-bs/css/dataTables.bootstrap.css';
|
||||
import './Table.css';
|
||||
|
||||
import { DataTableProps } from './transformProps';
|
||||
|
||||
// Depending on how the modules are imported, `dt` may be a CommonJS init function,
|
||||
// or the DataTable class itself. In case it is the former, we'd need to tell it
|
||||
// where is jQuery.
|
||||
if (!dt.$) {
|
||||
dt(window, $);
|
||||
}
|
||||
|
||||
const { PERCENT_3_POINT } = NumberFormats;
|
||||
const isProbablyHTML = (text: string) => /<[^>]+>/.test(text);
|
||||
|
||||
export default function ReactDataTable(props: DataTableProps) {
|
||||
const {
|
||||
data,
|
||||
height,
|
||||
alignPositiveNegative = false,
|
||||
colorPositiveNegative = false,
|
||||
columns,
|
||||
includeSearch = false,
|
||||
metrics: aggMetrics,
|
||||
pageLength,
|
||||
percentMetrics,
|
||||
tableTimestampFormat,
|
||||
// orderDesc,
|
||||
// TODO: add back the broken dashboard filters feature
|
||||
// filters = {},
|
||||
// onAddFilter = NOOP,
|
||||
// onRemoveFilter = NOOP,
|
||||
// tableFilter,
|
||||
// timeseriesLimitMetric,
|
||||
} = props;
|
||||
|
||||
const formatTimestamp = getTimeFormatter(tableTimestampFormat);
|
||||
const metrics = (aggMetrics || [])
|
||||
.concat(percentMetrics || [])
|
||||
// actual records must be of numeric types as well
|
||||
.filter(m => data[0] && typeof data[0][m] === 'number');
|
||||
|
||||
// check whethere a key is a metric
|
||||
const metricsSet = new Set(aggMetrics);
|
||||
const percentMetricsSet = new Set(percentMetrics);
|
||||
|
||||
// collect min/max for rendering bars
|
||||
const maxes: { [key: string]: number } = {};
|
||||
const mins: { [key: string]: number } = {};
|
||||
columns.forEach(({ key }) => {
|
||||
const vals = data.map(row => row[key]);
|
||||
if (metrics.includes(key)) {
|
||||
const nums = vals as number[];
|
||||
if (alignPositiveNegative) {
|
||||
maxes[key] = Math.max(...nums.map(Math.abs));
|
||||
} else {
|
||||
maxes[key] = Math.max(...nums);
|
||||
mins[key] = Math.min(...nums);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const viewportHeight = Math.min(height, window.innerHeight);
|
||||
const pageLengthChoices = [10, 25, 40, 50, 75, 100, 150, 200];
|
||||
const hasPagination = pageLength > 0;
|
||||
|
||||
const rootElem = createRef<HTMLDivElement>();
|
||||
|
||||
/**
|
||||
* Adjust styles after rendering the table
|
||||
*/
|
||||
function drawCallback(this: DataTables.JQueryDataTables) {
|
||||
const root = rootElem.current as HTMLElement;
|
||||
// force smaller pagination, because datatables-bs hard-corded pagination styles
|
||||
$('.pagination', root).addClass('pagination-sm');
|
||||
// display tr rows on current page
|
||||
$('tr', root).css('display', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Format text for cell value
|
||||
*/
|
||||
function cellText(key: string, format: string | undefined, val: unknown) {
|
||||
if (key === '__timestamp') {
|
||||
return formatTimestamp(val);
|
||||
}
|
||||
if (typeof val === 'string') {
|
||||
return filterXSS(val, { stripIgnoreTag: true });
|
||||
}
|
||||
if (percentMetricsSet.has(key)) {
|
||||
// in case percent metric can specify percent format in the future
|
||||
return formatNumber(format || PERCENT_3_POINT, val as number);
|
||||
}
|
||||
if (metricsSet.has(key)) {
|
||||
// default format '' will return human readable numbers (e.g. 50M, 33k)
|
||||
return formatNumber(format, val as number);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cell background to render columns as horizontal bar chart
|
||||
*/
|
||||
function cellBar(key: string, val: number) {
|
||||
const r = colorPositiveNegative && val < 0 ? 150 : 0;
|
||||
if (alignPositiveNegative) {
|
||||
const perc = Math.abs(Math.round((val / maxes[key]) * 100));
|
||||
// The 0.01 to 0.001 is a workaround for what appears to be a
|
||||
// CSS rendering bug on flat, transparent colors
|
||||
return (
|
||||
`linear-gradient(to right, rgba(${r},0,0,0.2), rgba(${r},0,0,0.2) ${perc}%, ` +
|
||||
`rgba(0,0,0,0.01) ${perc}%, rgba(0,0,0,0.001) 100%)`
|
||||
);
|
||||
}
|
||||
const posExtent = Math.abs(Math.max(maxes[key], 0));
|
||||
const negExtent = Math.abs(Math.min(mins[key], 0));
|
||||
const tot = posExtent + negExtent;
|
||||
const perc1 = Math.round((Math.min(negExtent + val, negExtent) / tot) * 100);
|
||||
const perc2 = Math.round((Math.abs(val) / tot) * 100);
|
||||
// The 0.01 to 0.001 is a workaround for what appears to be a
|
||||
// CSS rendering bug on flat, transparent colors
|
||||
return (
|
||||
`linear-gradient(to right, rgba(0,0,0,0.01), rgba(0,0,0,0.001) ${perc1}%, ` +
|
||||
`rgba(${r},0,0,0.2) ${perc1}%, rgba(${r},0,0,0.2) ${perc1 + perc2}%, ` +
|
||||
`rgba(0,0,0,0.01) ${perc1 + perc2}%, rgba(0,0,0,0.001) 100%)`
|
||||
);
|
||||
}
|
||||
|
||||
const options = {
|
||||
aaSorting: [], // initial sorting order, reset to [] to use backend ordering
|
||||
autoWidth: false,
|
||||
paging: hasPagination,
|
||||
pagingType: 'first_last_numbers',
|
||||
pageLength,
|
||||
lengthMenu: [
|
||||
[...pageLengthChoices, -1],
|
||||
[...pageLengthChoices, t('All')],
|
||||
],
|
||||
searching: includeSearch,
|
||||
language: {
|
||||
paginate: {
|
||||
first: t('First'),
|
||||
last: t('Last'),
|
||||
previous: t('Previous'),
|
||||
next: t('Next'),
|
||||
},
|
||||
},
|
||||
bInfo: false,
|
||||
scrollY: `${viewportHeight}px`,
|
||||
scrollCollapse: true,
|
||||
scrollX: true,
|
||||
drawCallback,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const $root = $(rootElem.current as HTMLElement);
|
||||
const dataTable = $root.find('table').DataTable(options);
|
||||
|
||||
// adjust table height
|
||||
const scrollHeadHeight = $root.find('.dataTables_scrollHead').height() || 0;
|
||||
const paginationHeight = $root.find('.dataTables_paginate').height() || 0;
|
||||
const searchBarHeight =
|
||||
$root
|
||||
.find('.dataTables_length,.dataTables_filter')
|
||||
.closest('.row')
|
||||
.height() || 0;
|
||||
const scrollBodyHeight = viewportHeight - scrollHeadHeight - paginationHeight - searchBarHeight;
|
||||
$root.find('.dataTables_scrollBody').css('max-height', scrollBodyHeight);
|
||||
|
||||
return () => {
|
||||
// there may be weird lifecycle issues, so put destroy in try/catch
|
||||
try {
|
||||
dataTable.destroy();
|
||||
// reset height
|
||||
$root.find('.dataTables_scrollBody').css('max-height', '');
|
||||
} catch (error) {
|
||||
// pass
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const tableElement = (
|
||||
<table className="table table-striped table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map(col => (
|
||||
// by default all columns will have sorting
|
||||
<th key={col.key} className="sorting" title={col.label}>
|
||||
{col.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.map((record, i) => (
|
||||
<tr
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={i}
|
||||
// hide rows after first page makes the initial render faster (less layout computation)
|
||||
style={{ display: pageLength > 0 && i >= pageLength ? 'none' : undefined }}
|
||||
>
|
||||
{columns.map(({ key, format }) => {
|
||||
const val = record[key];
|
||||
const keyIsMetric = metricsSet.has(key);
|
||||
const text = cellText(key, format, val);
|
||||
const isHtml = !keyIsMetric && isProbablyHTML(text);
|
||||
return (
|
||||
<td
|
||||
key={key}
|
||||
// only set innerHTML for actual html content, this saves time
|
||||
dangerouslySetInnerHTML={isHtml ? { __html: text } : undefined}
|
||||
data-sort={val}
|
||||
className={keyIsMetric ? 'text-right' : ''}
|
||||
style={{
|
||||
backgroundImage: keyIsMetric ? cellBar(key, val as number) : undefined,
|
||||
}}
|
||||
title={keyIsMetric || percentMetricsSet.has(key) ? String(val) : ''}
|
||||
>
|
||||
{isHtml ? null : text}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{ __html: ReactDOMServer.renderToStaticMarkup(tableElement) }}
|
||||
ref={rootElem}
|
||||
className="superset-legacy-chart-table"
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
.superset-legacy-chart-table {
|
||||
margin: 0px auto;
|
||||
}
|
||||
.superset-legacy-chart-table table {
|
||||
width: 100%;
|
||||
}
|
||||
.superset-legacy-chart-table .dt-metric {
|
||||
text-align: right;
|
||||
}
|
||||
.superset-legacy-chart-table div.dataTables_wrapper div.dataTables_paginate {
|
||||
line-height: 0;
|
||||
}
|
||||
.superset-legacy-chart-table div.dataTables_wrapper div.dataTables_paginate ul.pagination {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-table table.table thead th.sorting:after,
|
||||
.superset-legacy-chart-table table.table thead th.sorting_asc:after,
|
||||
.superset-legacy-chart-table table.table thead th.sorting_desc:after {
|
||||
top: auto;
|
||||
bottom: 6px;
|
||||
}
|
||||
.superset-legacy-chart-table td {
|
||||
white-space: pre-wrap;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
Before Width: | Height: | Size: 107 KiB |
@ -1,40 +0,0 @@
|
||||
/**
|
||||
* 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 { t } from '@superset-ui/translation';
|
||||
import { ChartMetadata, ChartPlugin } from '@superset-ui/chart';
|
||||
import transformProps from './transformProps';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
canBeAnnotationTypes: ['EVENT', 'INTERVAL'],
|
||||
description: '',
|
||||
name: t('Table'),
|
||||
thumbnail,
|
||||
useLegacyApi: true,
|
||||
});
|
||||
|
||||
export default class TableChartPlugin extends ChartPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
loadChart: () => import('./ReactDataTable'),
|
||||
metadata,
|
||||
transformProps,
|
||||
});
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
/**
|
||||
* 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 { ChartProps } from '@superset-ui/chart';
|
||||
import { QueryFormDataMetric } from '@superset-ui/query';
|
||||
|
||||
interface DataRecord {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface DataColumnMeta {
|
||||
// `key` is what is called `label` in the input props
|
||||
key: string;
|
||||
// `label` is verbose column name used for rendering
|
||||
label: string;
|
||||
format?: string;
|
||||
}
|
||||
|
||||
export interface DataTableProps {
|
||||
// Each object is { field1: value1, field2: value2 }
|
||||
data: DataRecord[];
|
||||
height: number;
|
||||
alignPositiveNegative: boolean;
|
||||
colorPositiveNegative: boolean;
|
||||
columns: DataColumnMeta[];
|
||||
metrics: string[];
|
||||
percentMetrics: string[];
|
||||
includeSearch: boolean;
|
||||
orderDesc: boolean;
|
||||
pageLength: number;
|
||||
tableTimestampFormat: string;
|
||||
// TODO: add filters back or clean up
|
||||
// filters: object;
|
||||
// onAddFilter?: (key: string, value: number[]) => void;
|
||||
// onRemoveFilter?: (key: string, value: number[]) => void;
|
||||
// tableFilter: boolean;
|
||||
// timeseriesLimitMetric: string | object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consolidate list of metrics to string, identified by its unique identifier
|
||||
*/
|
||||
const consolidateMetricShape = (metric: QueryFormDataMetric) => {
|
||||
if (typeof metric === 'string') return metric;
|
||||
// even thought `metric.optionName` is more unique, it's not used
|
||||
// anywhere else in `queryData` and cannot be used to access `data.records`.
|
||||
// The records are still keyed by `metric.label`.
|
||||
return metric.label;
|
||||
};
|
||||
|
||||
export default function transformProps(chartProps: ChartProps): DataTableProps {
|
||||
const { height, datasource, formData, queryData } = chartProps;
|
||||
|
||||
const {
|
||||
alignPn,
|
||||
colorPn,
|
||||
includeSearch,
|
||||
orderDesc,
|
||||
pageLength,
|
||||
metrics: metrics_,
|
||||
percentMetrics: percentMetrics_,
|
||||
tableTimestampFormat,
|
||||
} = formData;
|
||||
const { columnFormats, verboseMap } = datasource;
|
||||
const { records, columns: columns_ } = queryData.data;
|
||||
const metrics = (metrics_ ?? []).map(consolidateMetricShape);
|
||||
// percent metrics always starts with a '%' sign.
|
||||
const percentMetrics = (percentMetrics_ ?? [])
|
||||
.map(consolidateMetricShape)
|
||||
.map((x: string) => `%${x}`);
|
||||
const columns = columns_.map((key: string) => {
|
||||
let label = verboseMap[key] || key;
|
||||
|
||||
// make sure there is a " " after "%" for percent metrics
|
||||
if (label[0] === '%' && label[1] !== ' ') {
|
||||
label = `% ${label.slice(1)}`;
|
||||
}
|
||||
|
||||
return {
|
||||
key,
|
||||
label,
|
||||
format: columnFormats?.[key],
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
height,
|
||||
data: records,
|
||||
columns,
|
||||
metrics,
|
||||
percentMetrics,
|
||||
alignPositiveNegative: alignPn,
|
||||
colorPositiveNegative: colorPn,
|
||||
includeSearch,
|
||||
orderDesc,
|
||||
pageLength: pageLength && parseInt(pageLength, 10),
|
||||
tableTimestampFormat,
|
||||
};
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import { mount, CommonWrapper } from 'enzyme';
|
||||
import ReactDataTable from '../src/ReactDataTable';
|
||||
import transformProps from '../src/transformProps';
|
||||
import testData from './testData';
|
||||
|
||||
describe('legacy-table', () => {
|
||||
// Can test more prop transformation here. Not needed for now.
|
||||
describe('transformProps', () => {});
|
||||
|
||||
describe('ReactDataTable', () => {
|
||||
let wrap: CommonWrapper; // the ReactDataTable wraper
|
||||
|
||||
it('render basic data', () => {
|
||||
wrap = mount(<ReactDataTable {...transformProps(testData.basic)} />);
|
||||
const tree = wrap.render(); // returns a CheerioWrapper with jQuery-like API
|
||||
const cells = tree.find('td');
|
||||
expect(tree.hasClass('superset-legacy-chart-table')).toEqual(true);
|
||||
expect(cells).toHaveLength(4);
|
||||
expect(cells.eq(0).text()).toEqual('Michael');
|
||||
expect(cells.eq(3).attr('data-sort')).toEqual('2467');
|
||||
});
|
||||
|
||||
it('render advanced data', () => {
|
||||
// should successfull rerender with new props
|
||||
wrap.setProps(transformProps(testData.advanced));
|
||||
const tree = wrap.render();
|
||||
const cells = tree.find('td');
|
||||
expect(
|
||||
tree
|
||||
.find('th')
|
||||
.eq(1)
|
||||
.text(),
|
||||
).toEqual('Sum of Num');
|
||||
expect(cells.eq(2).text()).toEqual('12.346%');
|
||||
expect(cells.eq(4).text()).toEqual('2.47k');
|
||||
});
|
||||
|
||||
it('render empty data', () => {
|
||||
wrap.setProps(transformProps(testData.empty));
|
||||
const tree = wrap.render();
|
||||
expect(tree.text()).toContain('No data available in table');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,120 +0,0 @@
|
||||
/**
|
||||
* 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 { ChartProps } from '@superset-ui/chart';
|
||||
|
||||
const basicFormData = {
|
||||
alignPn: false,
|
||||
colorPn: false,
|
||||
includeSearch: false,
|
||||
orderDesc: true,
|
||||
pageLength: 0,
|
||||
metrics: [],
|
||||
percentMetrics: null,
|
||||
timeseriesLimitMetric: null,
|
||||
tableFilter: false,
|
||||
tableTimestampFormat: '%Y-%m-%d %H:%M:%S',
|
||||
};
|
||||
|
||||
const basicChartProps = {
|
||||
width: 200,
|
||||
height: 500,
|
||||
annotationData: {},
|
||||
datasource: {
|
||||
columnFormats: {},
|
||||
verboseMap: {},
|
||||
},
|
||||
rawDatasource: {},
|
||||
rawFormData: {},
|
||||
hooks: {},
|
||||
initialValues: {},
|
||||
queryData: {
|
||||
data: {
|
||||
columns: [],
|
||||
records: [],
|
||||
},
|
||||
},
|
||||
formData: basicFormData,
|
||||
};
|
||||
|
||||
/**
|
||||
* Basic data input
|
||||
*/
|
||||
const basic: ChartProps = {
|
||||
...basicChartProps,
|
||||
queryData: {
|
||||
data: {
|
||||
columns: ['name', 'sum__num'],
|
||||
records: [
|
||||
{
|
||||
name: 'Michael',
|
||||
sum__num: 2467063,
|
||||
'%pct_nice': 0.123456,
|
||||
},
|
||||
{
|
||||
name: 'Joe',
|
||||
sum__num: 2467,
|
||||
'%pct_nice': 0.00001,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Advanced data input with
|
||||
* - verbose map
|
||||
* - metric columns
|
||||
*/
|
||||
const advanced: ChartProps = {
|
||||
...basic,
|
||||
datasource: {
|
||||
columnFormats: {},
|
||||
verboseMap: {
|
||||
sum__num: 'Sum of Num',
|
||||
},
|
||||
},
|
||||
formData: {
|
||||
...basicFormData,
|
||||
metrics: ['sum__num'],
|
||||
percentMetrics: ['pct_nice'],
|
||||
},
|
||||
queryData: {
|
||||
data: {
|
||||
columns: ['name', 'sum__num', '%pct_nice'],
|
||||
records: [...basic.queryData.data.records],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const empty = {
|
||||
...advanced,
|
||||
queryData: {
|
||||
...advanced.queryData,
|
||||
data: {
|
||||
...advanced.queryData.data,
|
||||
records: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
basic,
|
||||
advanced,
|
||||
empty,
|
||||
};
|
@ -1,2 +0,0 @@
|
||||
declare module 'datatables.net-bs/js/dataTables.bootstrap';
|
||||
declare module '*.png';
|
@ -1,43 +0,0 @@
|
||||
## @superset-ui/legacy-preset-chart-big-number
|
||||
|
||||
[![Version](https://img.shields.io/npm/v/@superset-ui/legacy-preset-chart-big-number.svg?style=flat-square)](https://img.shields.io/npm/v/@superset-ui/legacy-preset-chart-big-number.svg?style=flat-square)
|
||||
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui-plugins.svg?path=packages%2Fsuperset-ui-legacy-preset-chart-big-number&style=flat-square)](https://david-dm.org/apache-superset/superset-ui-plugins?path=packages/superset-ui-legacy-preset-chart-big-number)
|
||||
|
||||
This plugin provides Big Number for Superset.
|
||||
|
||||
### Usage
|
||||
|
||||
Import the preset and register. This will register the `BigNumber` and `BigNumberTotal` charts with key `big-number` and `big-number-total`, respectively.
|
||||
|
||||
```js
|
||||
import { BigNumberChartPreset } from '@superset-ui/legacy-preset-chart-big-number';
|
||||
|
||||
new BigNumberChartPreset().register();
|
||||
```
|
||||
|
||||
or register charts one by one. 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 { BigNumberChartPlugin, BigNumberTotalChartPlugin } from '@superset-ui/legacy-preset-chart-big-number';
|
||||
|
||||
new BigNumberChartPlugin()
|
||||
.configure({ key: 'big-number' })
|
||||
.register();
|
||||
new BigNumberTotalChartPlugin()
|
||||
.configure({ key: 'big-number-total' })
|
||||
.register();
|
||||
```
|
||||
|
||||
Then use it via `SuperChart`. See [storybook](https://apache-superset.github.io/superset-ui-plugins/?selectedKind=plugin-chart-big-number) for more details.
|
||||
|
||||
```js
|
||||
<SuperChart
|
||||
chartType="big-number"
|
||||
width={600}
|
||||
height={600}
|
||||
formData={...}
|
||||
queryData={{
|
||||
data: {...},
|
||||
}}
|
||||
/>
|
||||
```
|
@ -1,47 +0,0 @@
|
||||
{
|
||||
"name": "@superset-ui/legacy-preset-chart-big-number",
|
||||
"version": "0.11.21",
|
||||
"description": "Superset Legacy Chart - Big Number",
|
||||
"sideEffects": [
|
||||
"*.css"
|
||||
],
|
||||
"main": "lib/index.js",
|
||||
"module": "esm/index.js",
|
||||
"files": [
|
||||
"esm",
|
||||
"lib"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/apache-superset/superset-ui-plugins.git"
|
||||
},
|
||||
"keywords": [
|
||||
"superset"
|
||||
],
|
||||
"author": "Superset",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/apache-superset/superset-ui-plugins/issues"
|
||||
},
|
||||
"homepage": "https://github.com/apache-superset/superset-ui-plugins#readme",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@data-ui/xy-chart": "^0.0.84",
|
||||
"@types/d3-color": "^1.2.2",
|
||||
"@types/shortid": "^0.0.29",
|
||||
"d3-color": "^1.2.3",
|
||||
"shortid": "^2.2.14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@superset-ui/chart": "^0.12.0",
|
||||
"@superset-ui/color": "^0.12.0",
|
||||
"@superset-ui/core": "^0.12.0",
|
||||
"@superset-ui/dimension": "^0.12.0",
|
||||
"@superset-ui/number-format": "^0.12.0",
|
||||
"@superset-ui/time-format": "^0.12.0",
|
||||
"@superset-ui/translation": "^0.12.0",
|
||||
"react": "^15 || ^16"
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
.superset-legacy-chart-big-number {
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell,
|
||||
Open Sans, Helvetica Neue, sans-serif;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-big-number.no-trendline .subheader-line {
|
||||
padding-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-big-number .text-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-big-number .text-container .alert {
|
||||
font-size: 11px;
|
||||
margin: -0.5em 0 0.4em;
|
||||
line-height: 1;
|
||||
padding: 2px 4px 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-big-number .header-line {
|
||||
position: relative;
|
||||
line-height: 1em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-big-number .header-line span {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-big-number .subheader-line {
|
||||
line-height: 1em;
|
||||
padding-bottom: 0;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-big-number.is-fallback-value .header-line,
|
||||
.superset-legacy-chart-big-number.is-fallback-value .subheader-line {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.superset-data-ui-tooltip {
|
||||
z-index: 1000;
|
||||
background: #000;
|
||||
}
|
@ -1,305 +0,0 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import shortid from 'shortid';
|
||||
import { t } from '@superset-ui/translation';
|
||||
import { getNumberFormatter } from '@superset-ui/number-format';
|
||||
import { XYChart, AreaSeries, CrossHair, LinearGradient } from '@data-ui/xy-chart';
|
||||
import { BRAND_COLOR } from '@superset-ui/color';
|
||||
import { computeMaxFontSize } from '@superset-ui/dimension';
|
||||
import NumberFormatter from '@superset-ui/number-format/lib/NumberFormatter';
|
||||
import { smartDateVerboseFormatter } from '@superset-ui/time-format';
|
||||
import TimeFormatter from '@superset-ui/time-format/lib/TimeFormatter';
|
||||
|
||||
import './BigNumber.css';
|
||||
|
||||
const defaultNumberFormatter = getNumberFormatter();
|
||||
|
||||
const CHART_MARGIN = {
|
||||
top: 4,
|
||||
right: 4,
|
||||
bottom: 4,
|
||||
left: 4,
|
||||
};
|
||||
|
||||
const PROPORTION = {
|
||||
// text size: proportion of the chart container sans trendline
|
||||
HEADER: 0.3,
|
||||
SUBHEADER: 0.125,
|
||||
// trendline size: proportion of the whole chart container
|
||||
TRENDLINE: 0.3,
|
||||
};
|
||||
|
||||
type TimeSeriesDatum = {
|
||||
x: number; // timestamp as a number
|
||||
y: number | null;
|
||||
};
|
||||
|
||||
export function renderTooltipFactory(
|
||||
formatDate = smartDateVerboseFormatter,
|
||||
formatValue = defaultNumberFormatter,
|
||||
) {
|
||||
return function renderTooltip({ datum: { x, y } }: { datum: TimeSeriesDatum }) {
|
||||
// even though `formatDate` supports timestamp as numbers, we need
|
||||
// `new Date` to pass type check
|
||||
return (
|
||||
<div style={{ padding: '4px 8px' }}>
|
||||
{formatDate(new Date(x))}
|
||||
<br />
|
||||
<strong>{y === null ? t('N/A') : formatValue(y)}</strong>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
type BigNumberVisProps = {
|
||||
className?: string;
|
||||
width: number;
|
||||
height: number;
|
||||
bigNumber?: number | null;
|
||||
bigNumberFallback?: TimeSeriesDatum;
|
||||
formatNumber: NumberFormatter;
|
||||
formatTime: TimeFormatter;
|
||||
fromDatetime?: number;
|
||||
toDatetime?: number;
|
||||
headerFontSize: number;
|
||||
subheader: string;
|
||||
subheaderFontSize: number;
|
||||
showTrendLine?: boolean;
|
||||
startYAxisAtZero?: boolean;
|
||||
timeRangeFixed?: boolean;
|
||||
trendLineData?: TimeSeriesDatum[];
|
||||
mainColor: string;
|
||||
};
|
||||
|
||||
class BigNumberVis extends React.PureComponent<BigNumberVisProps, {}> {
|
||||
private gradientId: string = shortid.generate();
|
||||
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
formatNumber: (num: number) => String(num),
|
||||
formatTime: smartDateVerboseFormatter.formatFunc,
|
||||
headerFontSize: PROPORTION.HEADER,
|
||||
mainColor: BRAND_COLOR,
|
||||
showTrendLine: false,
|
||||
startYAxisAtZero: true,
|
||||
subheader: '',
|
||||
subheaderFontSize: PROPORTION.SUBHEADER,
|
||||
timeRangeFixed: false,
|
||||
};
|
||||
|
||||
getClassName() {
|
||||
const { className, showTrendLine, bigNumberFallback } = this.props;
|
||||
const names = `superset-legacy-chart-big-number ${className} ${
|
||||
bigNumberFallback ? 'is-fallback-value' : ''
|
||||
}`;
|
||||
if (showTrendLine) return names;
|
||||
return `${names} no-trendline`;
|
||||
}
|
||||
|
||||
createTemporaryContainer() {
|
||||
const container = document.createElement('div');
|
||||
container.className = this.getClassName();
|
||||
container.style.position = 'absolute'; // so it won't disrupt page layout
|
||||
container.style.opacity = '0'; // and not visible
|
||||
return container;
|
||||
}
|
||||
|
||||
renderFallbackWarning() {
|
||||
const { bigNumberFallback, formatTime } = this.props;
|
||||
if (!bigNumberFallback) return null;
|
||||
return (
|
||||
<span
|
||||
className="alert alert-warning"
|
||||
role="alert"
|
||||
title={t(`Last available value seen on %s`, formatTime(bigNumberFallback.x))}
|
||||
>
|
||||
{t('Not up to date')}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
renderHeader(maxHeight: number) {
|
||||
const { bigNumber, formatNumber, width } = this.props;
|
||||
const text = bigNumber === null ? t('No data') : formatNumber(bigNumber);
|
||||
|
||||
const container = this.createTemporaryContainer();
|
||||
document.body.append(container);
|
||||
const fontSize = computeMaxFontSize({
|
||||
text,
|
||||
maxWidth: width,
|
||||
maxHeight,
|
||||
className: 'header-line',
|
||||
container,
|
||||
});
|
||||
document.body.removeChild(container);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="header-line"
|
||||
style={{
|
||||
fontSize,
|
||||
height: maxHeight,
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderSubheader(maxHeight: number) {
|
||||
const { bigNumber, subheader, width, bigNumberFallback } = this.props;
|
||||
let fontSize = 0;
|
||||
|
||||
const NO_DATA_OR_HASNT_LANDED = t(
|
||||
'No data after filtering or data is NULL for the latest time record',
|
||||
);
|
||||
const NO_DATA = t('Try applying different filters or ensuring your datasource has data');
|
||||
let text = subheader;
|
||||
if (bigNumber === null) {
|
||||
text = bigNumberFallback ? NO_DATA : NO_DATA_OR_HASNT_LANDED;
|
||||
}
|
||||
if (text) {
|
||||
const container = this.createTemporaryContainer();
|
||||
document.body.append(container);
|
||||
fontSize = computeMaxFontSize({
|
||||
text,
|
||||
maxWidth: width,
|
||||
maxHeight,
|
||||
className: 'subheader-line',
|
||||
container,
|
||||
});
|
||||
document.body.removeChild(container);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="subheader-line"
|
||||
style={{
|
||||
fontSize,
|
||||
height: maxHeight,
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
renderTrendline(maxHeight: number) {
|
||||
const {
|
||||
width,
|
||||
trendLineData,
|
||||
mainColor,
|
||||
subheader,
|
||||
startYAxisAtZero,
|
||||
formatNumber,
|
||||
formatTime,
|
||||
fromDatetime,
|
||||
timeRangeFixed,
|
||||
} = this.props;
|
||||
|
||||
// if can't find any non-null values, no point rendering the trendline
|
||||
if (!trendLineData?.some(d => d.y !== null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Apply a fixed X range if a time range is specified.
|
||||
//
|
||||
// XYChart checks the existence of `domain` property and decide whether to
|
||||
// apply a domain or not, so it must not be `null` or `undefined`
|
||||
const xScale: { type: string; domain?: number[] } = { type: 'timeUtc' };
|
||||
const tooltipData = trendLineData && [...trendLineData];
|
||||
if (tooltipData && timeRangeFixed && fromDatetime) {
|
||||
const toDatetime = this.props.toDatetime ?? Date.now();
|
||||
if (tooltipData[0].x > fromDatetime) {
|
||||
tooltipData.unshift({
|
||||
x: fromDatetime,
|
||||
y: null,
|
||||
});
|
||||
}
|
||||
if (tooltipData[tooltipData.length - 1].x < toDatetime) {
|
||||
tooltipData.push({
|
||||
x: toDatetime,
|
||||
y: null,
|
||||
});
|
||||
}
|
||||
xScale.domain = [fromDatetime, toDatetime];
|
||||
}
|
||||
return (
|
||||
<XYChart
|
||||
snapTooltipToDataX
|
||||
ariaLabel={`Big number visualization ${subheader}`}
|
||||
renderTooltip={renderTooltipFactory(formatTime, formatNumber)}
|
||||
xScale={xScale}
|
||||
yScale={{
|
||||
type: 'linear',
|
||||
includeZero: startYAxisAtZero,
|
||||
}}
|
||||
width={Math.floor(width)}
|
||||
height={maxHeight}
|
||||
margin={CHART_MARGIN}
|
||||
eventTrigger="container"
|
||||
>
|
||||
<LinearGradient id={this.gradientId} from={mainColor} to="#fff" />
|
||||
<AreaSeries data={tooltipData} fill={`url(#${this.gradientId})`} stroke={mainColor} />
|
||||
<CrossHair
|
||||
fullHeight
|
||||
stroke={mainColor}
|
||||
circleFill={mainColor}
|
||||
circleStroke="#fff"
|
||||
showHorizontalLine={false}
|
||||
strokeDasharray="5,2"
|
||||
/>
|
||||
</XYChart>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { showTrendLine, height, headerFontSize, subheaderFontSize } = this.props;
|
||||
const className = this.getClassName();
|
||||
|
||||
if (showTrendLine) {
|
||||
const chartHeight = Math.floor(PROPORTION.TRENDLINE * height);
|
||||
const allTextHeight = height - chartHeight;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className="text-container" style={{ height: allTextHeight }}>
|
||||
{this.renderFallbackWarning()}
|
||||
{this.renderHeader(Math.ceil(headerFontSize * (1 - PROPORTION.TRENDLINE) * height))}
|
||||
{this.renderSubheader(
|
||||
Math.ceil(subheaderFontSize * (1 - PROPORTION.TRENDLINE) * height),
|
||||
)}
|
||||
</div>
|
||||
{this.renderTrendline(chartHeight)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className} style={{ height }}>
|
||||
{this.renderHeader(Math.ceil(headerFontSize * height))}
|
||||
{this.renderSubheader(Math.ceil(subheaderFontSize * height))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default BigNumberVis;
|
Binary file not shown.
Before Width: | Height: | Size: 101 KiB |
Binary file not shown.
Before Width: | Height: | Size: 50 KiB |
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* 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 { t } from '@superset-ui/translation';
|
||||
import { ChartMetadata, ChartPlugin } from '@superset-ui/chart';
|
||||
import transformProps from './transformProps';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
description: '',
|
||||
name: t('Big Number with Trendline'),
|
||||
thumbnail,
|
||||
useLegacyApi: true,
|
||||
});
|
||||
|
||||
export default class BigNumberChartPlugin extends ChartPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
loadChart: () => import('./BigNumber'),
|
||||
metadata,
|
||||
transformProps,
|
||||
});
|
||||
}
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
/**
|
||||
* 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 * as color from 'd3-color';
|
||||
import { getNumberFormatter, NumberFormats } from '@superset-ui/number-format';
|
||||
import { ChartProps } from '@superset-ui/chart';
|
||||
import getTimeFormatterForGranularity from '../utils/getTimeFormatterForGranularity';
|
||||
|
||||
const TIME_COLUMN = '__timestamp';
|
||||
const formatPercentChange = getNumberFormatter(NumberFormats.PERCENT_SIGNED_1_POINT);
|
||||
|
||||
// we trust both the x (time) and y (big number) to be numeric
|
||||
type BigNumberDatum = {
|
||||
[TIME_COLUMN]: number;
|
||||
[key: string]: number | null;
|
||||
};
|
||||
|
||||
export default function transformProps(chartProps: ChartProps) {
|
||||
const { width, height, formData, queryData } = chartProps;
|
||||
const {
|
||||
colorPicker,
|
||||
compareLag: compareLagInput,
|
||||
compareSuffix = '',
|
||||
headerFontSize,
|
||||
metric,
|
||||
showTrendLine,
|
||||
startYAxisAtZero,
|
||||
subheader = '',
|
||||
subheaderFontSize,
|
||||
timeGrainSqla: granularity,
|
||||
vizType,
|
||||
timeRangeFixed = false,
|
||||
} = formData;
|
||||
let { yAxisFormat } = formData;
|
||||
const { data, from_dttm: fromDatetime, to_dttm: toDatetime } = queryData;
|
||||
const metricName = metric?.label ? metric.label : metric;
|
||||
const compareLag = Number(compareLagInput) || 0;
|
||||
const supportTrendLine = vizType === 'big_number';
|
||||
const supportAndShowTrendLine = supportTrendLine && showTrendLine;
|
||||
let formattedSubheader = subheader;
|
||||
|
||||
let mainColor;
|
||||
if (colorPicker) {
|
||||
const { r, g, b } = colorPicker;
|
||||
mainColor = color.rgb(r, g, b).hex();
|
||||
}
|
||||
|
||||
let trendLineData;
|
||||
let percentChange = 0;
|
||||
let bigNumber = data.length === 0 ? null : data[0][metricName];
|
||||
let bigNumberFallback;
|
||||
|
||||
if (data.length > 0) {
|
||||
const sortedData = (data as BigNumberDatum[])
|
||||
.map(d => ({ x: d[TIME_COLUMN], y: d[metricName] }))
|
||||
.sort((a, b) => b.x - a.x); // sort in time descending order
|
||||
|
||||
bigNumber = sortedData[0].y;
|
||||
if (bigNumber === null) {
|
||||
bigNumberFallback = sortedData.find(d => d.y !== null);
|
||||
bigNumber = bigNumberFallback ? bigNumberFallback.y : null;
|
||||
}
|
||||
|
||||
if (compareLag > 0) {
|
||||
const compareIndex = compareLag;
|
||||
if (compareIndex < sortedData.length) {
|
||||
const compareValue = sortedData[compareIndex].y;
|
||||
// compare values must both be non-nulls
|
||||
if (bigNumber !== null && compareValue !== null && compareValue !== 0) {
|
||||
percentChange = (bigNumber - compareValue) / Math.abs(compareValue);
|
||||
formattedSubheader = `${formatPercentChange(percentChange)} ${compareSuffix}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (supportTrendLine) {
|
||||
// must reverse to ascending order otherwise it confuses tooltip triggers
|
||||
sortedData.reverse();
|
||||
trendLineData = supportAndShowTrendLine ? sortedData : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
let className = '';
|
||||
if (percentChange > 0) {
|
||||
className = 'positive';
|
||||
} else if (percentChange < 0) {
|
||||
className = 'negative';
|
||||
}
|
||||
|
||||
if (!yAxisFormat && chartProps.datasource && chartProps.datasource.metrics) {
|
||||
chartProps.datasource.metrics.forEach(
|
||||
// eslint-disable-next-line camelcase
|
||||
(metricEntry: { metric_name?: string; d3format: string }) => {
|
||||
if (metricEntry.metric_name === metric && metricEntry.d3format) {
|
||||
yAxisFormat = metricEntry.d3format;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const formatNumber = getNumberFormatter(yAxisFormat);
|
||||
const formatTime = getTimeFormatterForGranularity(granularity);
|
||||
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
bigNumber,
|
||||
bigNumberFallback,
|
||||
className,
|
||||
formatNumber,
|
||||
formatTime,
|
||||
headerFontSize,
|
||||
subheaderFontSize,
|
||||
mainColor,
|
||||
showTrendLine: supportAndShowTrendLine,
|
||||
startYAxisAtZero,
|
||||
subheader: formattedSubheader,
|
||||
trendLineData,
|
||||
fromDatetime,
|
||||
toDatetime,
|
||||
timeRangeFixed,
|
||||
};
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 27 KiB |
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* 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 { t } from '@superset-ui/translation';
|
||||
import { ChartMetadata, ChartPlugin } from '@superset-ui/chart';
|
||||
import transformProps from '../BigNumber/transformProps';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
description: '',
|
||||
name: t('Big Number'),
|
||||
thumbnail,
|
||||
useLegacyApi: true,
|
||||
});
|
||||
|
||||
export default class BigNumberTotalChartPlugin extends ChartPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
loadChart: () => import('../BigNumber/BigNumber'),
|
||||
metadata,
|
||||
transformProps,
|
||||
});
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export { default as BigNumberChartPlugin } from './BigNumber/index';
|
||||
export { default as BigNumberTotalChartPlugin } from './BigNumberTotal/index';
|
||||
export { default as BigNumberChartPreset } from './preset';
|
@ -1,33 +0,0 @@
|
||||
/**
|
||||
* 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 { Preset } from '@superset-ui/core';
|
||||
import BigNumberChartPlugin from './BigNumber';
|
||||
import BigNumberTotalChartPlugin from './BigNumberTotal';
|
||||
|
||||
export default class BigNumberChartPreset extends Preset {
|
||||
constructor() {
|
||||
super({
|
||||
name: 'BigNumber charts',
|
||||
plugins: [
|
||||
new BigNumberChartPlugin().configure({ key: 'big_number' }),
|
||||
new BigNumberTotalChartPlugin().configure({ key: 'big_number_total' }),
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
/**
|
||||
* 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 transformProps from '../BigNumber/transformProps';
|
||||
|
||||
const formData = {
|
||||
metric: 'value',
|
||||
colorPicker: {
|
||||
r: 0,
|
||||
g: 122,
|
||||
b: 135,
|
||||
a: 1,
|
||||
},
|
||||
compareLag: 1,
|
||||
timeGrainSqla: 'P0.25Y',
|
||||
compareSuffix: 'over last quarter',
|
||||
vizType: 'big_number',
|
||||
yAxisFormat: '.3s',
|
||||
};
|
||||
|
||||
function generateProps(data: object[], extraFormData = {}, extraQueryData = {}) {
|
||||
return {
|
||||
width: 200,
|
||||
height: 500,
|
||||
annotationData: {},
|
||||
datasource: {
|
||||
columnFormats: {},
|
||||
verboseMap: {},
|
||||
},
|
||||
rawDatasource: {},
|
||||
rawFormData: {},
|
||||
hooks: {},
|
||||
initialValues: {},
|
||||
formData: {
|
||||
...formData,
|
||||
...extraFormData,
|
||||
},
|
||||
queryData: {
|
||||
data,
|
||||
...extraQueryData,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe('BigNumber', () => {
|
||||
describe('transformProps()', () => {
|
||||
const props = generateProps(
|
||||
[
|
||||
{
|
||||
__timestamp: 0,
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
__timestamp: 100,
|
||||
value: null,
|
||||
},
|
||||
],
|
||||
{ showTrendLine: true },
|
||||
);
|
||||
const transformed = transformProps(props);
|
||||
|
||||
it('timeRangeUseFallback', () => {
|
||||
// the first item is the last item sorted by __timestamp
|
||||
const lastDatum = transformed.trendLineData?.pop();
|
||||
expect(lastDatum?.x).toStrictEqual(100);
|
||||
expect(lastDatum?.y).toBeNull();
|
||||
expect(transformed.bigNumber).toStrictEqual(1);
|
||||
expect(transformed.bigNumberFallback).not.toBeNull();
|
||||
});
|
||||
|
||||
it('formatTime by ganularity', () => {
|
||||
expect(transformed.formatTime(new Date('2020-01-01'))).toStrictEqual('2020 Q1');
|
||||
});
|
||||
});
|
||||
});
|
@ -1,20 +0,0 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
declare module '@data-ui/xy-chart';
|
||||
declare module '*.png';
|
@ -1,70 +0,0 @@
|
||||
/**
|
||||
* 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 { getTimeFormatter, TimeFormats, smartDateVerboseFormatter } from '@superset-ui/time-format';
|
||||
|
||||
// Translate time granularity to d3-format
|
||||
const MINUTE = '%Y-%m-%d %H:%M';
|
||||
const SUNDAY_BASED_WEEK = '%Y W%U';
|
||||
const MONDAY_BASED_WEEK = '%Y W%W';
|
||||
const { DATABASE_DATE, DATABASE_DATETIME } = TimeFormats;
|
||||
|
||||
// search for `builtin_time_grains` in incubator-superset/superset/db_engine_specs/base.py
|
||||
const formats = {
|
||||
date: DATABASE_DATE,
|
||||
PT1S: DATABASE_DATETIME, // second
|
||||
PT1M: MINUTE, // minute
|
||||
PT5M: MINUTE, // 5 minute
|
||||
PT10M: MINUTE, // 10 minute
|
||||
PT15M: MINUTE, // 15 minute
|
||||
'PT0.5H': MINUTE, // half hour
|
||||
PT1H: '%Y-%m-%d %H:00', // hour
|
||||
P1D: DATABASE_DATE, // day
|
||||
P1W: SUNDAY_BASED_WEEK, // week
|
||||
P1M: '%Y-%m', // month
|
||||
'P0.25Y': '%Y Q%q', // quarter
|
||||
P1Y: '%Y', // year
|
||||
// d3-time-format weeks does not support weeks start on Sunday
|
||||
'1969-12-28T00:00:00Z/P1W': SUNDAY_BASED_WEEK, // 'week_start_sunday'
|
||||
'1969-12-29T00:00:00Z/P1W': MONDAY_BASED_WEEK, // 'week_start_monday'
|
||||
'P1W/1970-01-03T00:00:00Z': SUNDAY_BASED_WEEK, // 'week_ending_saturday'
|
||||
'P1W/1970-01-04T00:00:00Z': MONDAY_BASED_WEEK, // 'week_ending_sunday'
|
||||
};
|
||||
|
||||
type TimeGranularity =
|
||||
| 'date'
|
||||
| 'PT1S'
|
||||
| 'PT1M'
|
||||
| 'PT5M'
|
||||
| 'PT10M'
|
||||
| 'PT15M'
|
||||
| 'PT0.5H'
|
||||
| 'PT1H'
|
||||
| 'P1D'
|
||||
| 'P1W'
|
||||
| 'P0.25Y'
|
||||
| 'P1Y'
|
||||
| '1969-12-28T00:00:00Z/P1W'
|
||||
| '1969-12-29T00:00:00Z/P1W'
|
||||
| 'P1W/1970-01-03T00:00:00Z';
|
||||
|
||||
export default function getTimeFormatterForGranularity(granularity: TimeGranularity) {
|
||||
return granularity in formats
|
||||
? getTimeFormatter(formats[granularity])
|
||||
: smartDateVerboseFormatter;
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
/* eslint-disable no-magic-numbers */
|
||||
import React, { useState } from 'react';
|
||||
import { SuperChart } from '@superset-ui/chart';
|
||||
import { Props as SuperChartProps } from '@superset-ui/chart/lib/components/SuperChart';
|
||||
import data from './data';
|
||||
import birthNames from './birth_names.json';
|
||||
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
|
||||
function paginated(props: SuperChartProps, pageSize = 50) {
|
||||
if (props.formData) {
|
||||
props.formData = {
|
||||
...props.formData,
|
||||
page_length: pageSize,
|
||||
};
|
||||
}
|
||||
if (props.queryData?.form_data) {
|
||||
props.queryData.form_data = {
|
||||
...props.queryData.form_data,
|
||||
page_length: pageSize,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...props,
|
||||
};
|
||||
}
|
||||
|
||||
function adjustNumCols(props: SuperChartProps, numCols = 7) {
|
||||
const newProps = { ...props };
|
||||
if (props.queryData) {
|
||||
const { columns } = props.queryData.data;
|
||||
const curSize = columns.length;
|
||||
const newColumns = [...Array(numCols)].map((_, i) => {
|
||||
return columns[i % curSize];
|
||||
});
|
||||
newProps.queryData = {
|
||||
...props.queryData,
|
||||
data: {
|
||||
...props.queryData.data,
|
||||
columns: newColumns,
|
||||
},
|
||||
};
|
||||
}
|
||||
return newProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load sample data for testing
|
||||
* @param props the original props passed to SuperChart
|
||||
* @param pageSize number of records perpage
|
||||
* @param targetSize the target total number of records
|
||||
*/
|
||||
function loadData(props: SuperChartProps, pageSize = 50, targetSize = 5042) {
|
||||
if (!props.queryData) return props;
|
||||
const data = props.queryData && props.queryData.data;
|
||||
if (data.records.length > 0) {
|
||||
while (data.records.length < targetSize) {
|
||||
const records = data.records;
|
||||
data.records = records.concat(records).slice(0, targetSize);
|
||||
}
|
||||
}
|
||||
props.height = window.innerHeight - 130;
|
||||
return paginated(props, pageSize);
|
||||
}
|
||||
|
||||
export default [
|
||||
{
|
||||
renderStory: () => (
|
||||
<SuperChart
|
||||
chartType="table"
|
||||
width={400}
|
||||
height={400}
|
||||
datasource={{
|
||||
columnFormats: {},
|
||||
verboseMap: {
|
||||
name: 'name',
|
||||
sum__num: 'sum__num',
|
||||
},
|
||||
}}
|
||||
filters={{}}
|
||||
queryData={{ data }}
|
||||
formData={{
|
||||
alignPn: false,
|
||||
colorPn: false,
|
||||
includeSearch: false,
|
||||
metrics: ['sum__num'],
|
||||
orderDesc: true,
|
||||
pageLength: 0,
|
||||
percentMetrics: null,
|
||||
tableFilter: false,
|
||||
tableTimestampFormat: '%Y-%m-%d %H:%M:%S',
|
||||
timeseriesLimitMetric: null,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
storyName: 'Basic',
|
||||
storyPath: 'legacy-|plugin-chart-table|TableChartPlugin',
|
||||
},
|
||||
{
|
||||
renderStory() {
|
||||
const initialProps = loadData(birthNames);
|
||||
const [chartProps, setChartProps] = useState(initialProps);
|
||||
|
||||
const updatePageSize = (size: number) => {
|
||||
setChartProps(paginated(initialProps, size));
|
||||
};
|
||||
const updateNumCols = (numCols: number) => {
|
||||
setChartProps(adjustNumCols(initialProps, numCols));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="superset-body">
|
||||
<div className="panel">
|
||||
<div className="panel-heading form-inline">
|
||||
<div className="form-group">
|
||||
Initial page size:{' '}
|
||||
<div className="btn-group btn-group-sm">
|
||||
{[10, 25, 40, 50, 100, -1].map(pageSize => {
|
||||
return (
|
||||
<button
|
||||
key={pageSize}
|
||||
className="btn btn-default"
|
||||
onClick={() => updatePageSize(pageSize)}
|
||||
>
|
||||
{pageSize > 0 ? pageSize : 'All'}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group" style={{ marginLeft: 20 }}>
|
||||
Number of columns:{' '}
|
||||
<div className="btn-group btn-group-sm">
|
||||
{[1, 3, 5, 7, 9].map(numCols => {
|
||||
return (
|
||||
<button
|
||||
key={numCols}
|
||||
className="btn btn-default"
|
||||
onClick={() => updateNumCols(numCols)}
|
||||
>
|
||||
{numCols}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
<SuperChart {...chartProps} chartType="table" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
storyName: 'Big Table',
|
||||
storyPath: 'legacy-|plugin-chart-table|TableChartPlugin',
|
||||
},
|
||||
];
|
File diff suppressed because it is too large
Load Diff
@ -1,46 +0,0 @@
|
||||
/* eslint-disable sort-keys */
|
||||
export default {
|
||||
columns: ['name', 'sum__num'],
|
||||
records: [
|
||||
{
|
||||
name: 'Michael',
|
||||
sum__num: 2467063,
|
||||
},
|
||||
{
|
||||
name: 'Christopher',
|
||||
sum__num: 1725265,
|
||||
},
|
||||
{
|
||||
name: 'David',
|
||||
sum__num: 1570516,
|
||||
},
|
||||
{
|
||||
name: 'James',
|
||||
sum__num: 1506025,
|
||||
},
|
||||
{
|
||||
name: 'John',
|
||||
sum__num: 1426074,
|
||||
},
|
||||
{
|
||||
name: 'Matthew',
|
||||
sum__num: 1355803,
|
||||
},
|
||||
{
|
||||
name: 'Robert',
|
||||
sum__num: 1314800,
|
||||
},
|
||||
{
|
||||
name: 'Daniel',
|
||||
sum__num: 1159354,
|
||||
},
|
||||
{
|
||||
name: 'Joseph',
|
||||
sum__num: 1114098,
|
||||
},
|
||||
{
|
||||
name: 'William',
|
||||
sum__num: 1113701,
|
||||
},
|
||||
],
|
||||
};
|
@ -1,8 +0,0 @@
|
||||
import TableChartPlugin from '../../../../superset-ui-legacy-plugin-chart-table';
|
||||
import Stories from './Stories';
|
||||
|
||||
new TableChartPlugin().configure({ key: 'table' }).register();
|
||||
|
||||
export default {
|
||||
examples: [...Stories],
|
||||
};
|
@ -1,122 +0,0 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import { SuperChart } from '@superset-ui/chart';
|
||||
import testData from './data';
|
||||
|
||||
const TIME_COLUMN = '__timestamp';
|
||||
|
||||
const formData = {
|
||||
colorPicker: {
|
||||
r: 0,
|
||||
g: 122,
|
||||
b: 135,
|
||||
a: 1,
|
||||
},
|
||||
compareLag: 1,
|
||||
compareSuffix: 'over 10Y',
|
||||
metric: 'sum__SP_POP_TOTL',
|
||||
showTrendLine: true,
|
||||
startYAxisAtZero: true,
|
||||
vizType: 'big_number',
|
||||
yAxisFormat: '.3s',
|
||||
};
|
||||
|
||||
/**
|
||||
* Add null values to trendline data
|
||||
* @param data input data
|
||||
*/
|
||||
function withNulls(origData: object[], nullPosition: number = 3) {
|
||||
const data = [...origData];
|
||||
data[nullPosition] = {
|
||||
...data[nullPosition],
|
||||
sum__SP_POP_TOTL: null,
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
export default [
|
||||
{
|
||||
renderStory: () => (
|
||||
<SuperChart
|
||||
chartType="big-number"
|
||||
width={400}
|
||||
height={400}
|
||||
queryData={{ data: testData }}
|
||||
formData={formData}
|
||||
/>
|
||||
),
|
||||
storyName: 'Basic with Trendline',
|
||||
storyPath: 'legacy-|preset-chart-big-number|BigNumberChartPlugin',
|
||||
},
|
||||
{
|
||||
renderStory: () => (
|
||||
<SuperChart
|
||||
chartType="big-number"
|
||||
width={400}
|
||||
height={400}
|
||||
queryData={{ data: withNulls(testData, 3) }}
|
||||
formData={formData}
|
||||
/>
|
||||
),
|
||||
storyName: 'Null in the middle',
|
||||
storyPath: 'legacy-|preset-chart-big-number|BigNumberChartPlugin',
|
||||
},
|
||||
{
|
||||
renderStory: () => (
|
||||
<SuperChart
|
||||
chartType="big-number"
|
||||
width={400}
|
||||
height={400}
|
||||
queryData={{
|
||||
data: testData.slice(0, 9),
|
||||
from_dttm: testData[testData.length - 1][TIME_COLUMN],
|
||||
to_dttm: null,
|
||||
}}
|
||||
formData={{
|
||||
...formData,
|
||||
timeGrainSqla: 'P1Y',
|
||||
timeRangeFixed: true,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
storyName: 'Missing range start (fix time range)',
|
||||
storyPath: 'legacy-|preset-chart-big-number|BigNumberChartPlugin',
|
||||
},
|
||||
{
|
||||
renderStory: () => (
|
||||
<SuperChart
|
||||
chartType="big-number"
|
||||
width={400}
|
||||
height={400}
|
||||
queryData={{
|
||||
data: testData.slice(0, 9),
|
||||
from_dttm: testData[testData.length - 1][TIME_COLUMN],
|
||||
to_dttm: testData[0][TIME_COLUMN],
|
||||
}}
|
||||
formData={{
|
||||
...formData,
|
||||
timeRangeFixed: false,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
storyName: `Missing range start (don't fix range)`,
|
||||
storyPath: 'legacy-|preset-chart-big-number|BigNumberChartPlugin',
|
||||
},
|
||||
];
|
@ -1,59 +0,0 @@
|
||||
/* eslint-disable sort-keys */
|
||||
export default [
|
||||
{
|
||||
__timestamp: 1388534400000.0,
|
||||
sum__SP_POP_TOTL: 7237260256.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1356998400000.0,
|
||||
sum__SP_POP_TOTL: 7151135481.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1325376000000.0,
|
||||
sum__SP_POP_TOTL: 7066007165.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1293840000000.0,
|
||||
sum__SP_POP_TOTL: 6984252419.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1262304000000.0,
|
||||
sum__SP_POP_TOTL: 6901110512.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1230768000000.0,
|
||||
sum__SP_POP_TOTL: 6818457192.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1199145600000.0,
|
||||
sum__SP_POP_TOTL: 6735914031.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1167609600000.0,
|
||||
sum__SP_POP_TOTL: 6653571302.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1136073600000.0,
|
||||
sum__SP_POP_TOTL: 6572596462.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1104537600000.0,
|
||||
sum__SP_POP_TOTL: 6491857539.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1072915200000.0,
|
||||
sum__SP_POP_TOTL: 6411615629.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1041379200000.0,
|
||||
sum__SP_POP_TOTL: 6331766837.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 1009843200000.0,
|
||||
sum__SP_POP_TOTL: 6252469127.0,
|
||||
},
|
||||
{
|
||||
__timestamp: 978307200000.0,
|
||||
sum__SP_POP_TOTL: 617333941.0,
|
||||
},
|
||||
];
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* 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 { BigNumberChartPlugin } from '../../../../../superset-ui-legacy-preset-chart-big-number/src';
|
||||
import Stories from './Stories';
|
||||
|
||||
new BigNumberChartPlugin().configure({ key: 'big-number' }).register();
|
||||
|
||||
export default {
|
||||
examples: [...Stories],
|
||||
};
|
@ -1,60 +0,0 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import { SuperChart } from '@superset-ui/chart';
|
||||
import data from './data';
|
||||
|
||||
export default [
|
||||
{
|
||||
renderStory: () => (
|
||||
<SuperChart
|
||||
chartType="big-number-total"
|
||||
width={400}
|
||||
height={400}
|
||||
queryData={{ data }}
|
||||
formData={{
|
||||
metric: 'sum__num',
|
||||
subheader: 'total female participants',
|
||||
vizType: 'big_number_total',
|
||||
yAxisFormat: '.3s',
|
||||
}}
|
||||
/>
|
||||
),
|
||||
storyName: 'Basic',
|
||||
storyPath: 'legacy-|preset-chart-big-number|BigNumberTotalChartPlugin',
|
||||
},
|
||||
{
|
||||
renderStory: () => (
|
||||
<SuperChart
|
||||
chartType="big-number-total"
|
||||
width={400}
|
||||
height={400}
|
||||
queryData={{ data: [] }}
|
||||
formData={{
|
||||
metric: 'sum__num',
|
||||
subheader: 'total female participants',
|
||||
vizType: 'big_number_total',
|
||||
yAxisFormat: '.3s',
|
||||
}}
|
||||
/>
|
||||
),
|
||||
storyName: 'Basic No Data',
|
||||
storyPath: 'legacy-|preset-chart-big-number|BigNumberTotalChartPlugin',
|
||||
},
|
||||
];
|
@ -1,5 +0,0 @@
|
||||
export default [
|
||||
{
|
||||
sum__num: 32546308,
|
||||
},
|
||||
];
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* 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 { BigNumberTotalChartPlugin } from '../../../../../superset-ui-legacy-preset-chart-big-number/src';
|
||||
import Stories from './Stories';
|
||||
|
||||
new BigNumberTotalChartPlugin().configure({ key: 'big-number-total' }).register();
|
||||
|
||||
export default {
|
||||
examples: [...Stories],
|
||||
};
|
Loading…
Reference in New Issue
Block a user