diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index e2f6a79affc53..27aebdb4220b4 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -10,10 +10,16 @@
.github/workflows/docker-ephemeral-env.yml @robdiciuccio @craig-rueda @rusackas @eschutho @dpgaspar @nytai @mistercrunch
.github/workflows/ephemeral*.yml @robdiciuccio @craig-rueda @rusackas @eschutho @dpgaspar @nytai @mistercrunch
-# Notify some committers of changes in the Select component
+# Notify some committers of changes in the components
/superset-frontend/src/components/Select/ @michael-s-molina @geido @ktmud
+/superset-frontend/src/components/MetadataBar/ @michael-s-molina
+/superset-frontend/src/components/DropdownContainer/ @michael-s-molina
# Notify Helm Chart maintainers about changes in it
/helm/superset/ @craig-rueda @dpgaspar @villebro
+
+# Notify E2E test maintainers of changes
+
+/superset-frontend/cypress-base/ @jinghua-qa @geido @eschutho @rusackas @betodealmeida
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 0e506cf9fb920..f821eb35246bc 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,6 +1,6 @@
---
name: Bug report
-about: Create a report to help us improve
+about: Create a report to help us improve Superset's stability! For feature requests please open a discussion at https://github.com/apache/superset/discussions/categories/ideas
labels: "#bug"
---
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 8e6e0da9c9597..0000000000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-labels: "#enhancement"
-
----
-
-Github Discussions is our new home for discussing features and improvements!
-
-https://github.com/apache/superset/discussions/categories/ideas
-
-We'd like to keep Github Issues focuses on bugs and SIP's (Superset Improvement Proposals)!
-
-Please note that feature requests opened as Github Issues will be moved to Discussions.
diff --git a/.github/ISSUE_TEMPLATE/sip.md b/.github/ISSUE_TEMPLATE/sip.md
index 6c526d6d1fa04..c2b0a14b91400 100644
--- a/.github/ISSUE_TEMPLATE/sip.md
+++ b/.github/ISSUE_TEMPLATE/sip.md
@@ -1,14 +1,16 @@
---
name: SIP
-about: Superset Improvement Proposal
+about: Superset Improvement Proposal (See SIP-0: https://github.com/apache/superset/issues/5602)
labels: "#SIP"
+title: "[SIP] Your Title Here (do not add SIP number)"
+asignees: "apache/superset-committers"
---
*Please make sure you are familiar with the SIP process documented*
-(here)[https://github.com/apache/superset/issues/5602]. The SIP number should be the next number after the latest SIP listed [here](https://github.com/apache/superset/issues?q=is%3Aissue+label%3Asip).
+(here)[https://github.com/apache/superset/issues/5602]. The SIP will be numbered by a committer upon acceptance.
-## [SIP-\
JSON string containing additional connection configuration.
This is used to provide connection information for systems like Hive, Presto, and BigQuery, which do not conform to the username:password syntax normally used by SQLAlchemy.
JSON string containing additional connection configuration.
This is used to provide connection information for systems like Hive, Presto, and BigQuery, which do not conform to the username:password syntax normally used by SQLAlchemy.
JSON string containing additional connection configuration.
This is used to provide connection information for systems like Hive, Presto, and BigQuery, which do not conform to the username:password syntax normally used by SQLAlchemy.
JSON string containing additional connection configuration.
This is used to provide connection information for systems like Hive, Presto, and BigQuery, which do not conform to the username:password syntax normally used by SQLAlchemy.
JSON string containing additional connection configuration.
This is used to provide connection information for systems like Hive, Presto, and BigQuery, which do not conform to the username:password syntax normally used by SQLAlchemy.
JSON string containing additional connection configuration.
This is used to provide connection information for systems like Hive, Presto, and BigQuery, which do not conform to the username:password syntax normally used by SQLAlchemy.
JSON string containing additional connection configuration.
This is used to provide connection information for systems like Hive, Presto, and BigQuery, which do not conform to the username:password syntax normally used by SQLAlchemy.
JSON string containing extra configuration elements.
1. The engine_params
object gets unpacked into the sqlalchemy.create_engine call, while the metadata_params
gets unpacked into the sqlalchemy.MetaData call.
2. The metadata_cache_timeout
is a cache timeout setting in seconds for metadata fetch of this database. Specify it as \"metadata_cache_timeout\": {\"schema_cache_timeout\": 600, \"table_cache_timeout\": 600}. If unset, cache will not be enabled for the functionality. A timeout of 0 indicates that the cache never expires.
3. The schemas_allowed_for_file_upload
is a comma separated list of schemas that CSVs are allowed to upload to. Specify it as \"schemas_allowed_for_file_upload\": [\"public\", \"csv_upload\"]. If database flavor does not support schema or any schema is allowed to be accessed, just leave the list empty
4. The version
field is a string specifying the this db's version. This should be used with Presto DBs so that the syntax is correct
5. The allows_virtual_table_explore
field is a boolean specifying whether or not the Explore button in SQL Lab results is shown.
6. The disable_data_preview
field is a boolean specifying whether or not data preview queries will be run when fetching table metadata in SQL Lab.
JSON string containing additional connection configuration.
This is used to provide connection information for systems like Hive, Presto, and BigQuery, which do not conform to the username:password syntax normally used by SQLAlchemy.
{error ? error.toString() : 'Unknown Error'}
{componentStack.split('\n').map((row: string) => (
{row}
diff --git a/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx
index 9a6240daff713..99d7b6dbec7aa 100644
--- a/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/chart/components/SuperChart.tsx
@@ -26,6 +26,7 @@ import { ParentSize } from '@vx/responsive';
import { createSelector } from 'reselect';
import { withTheme } from '@emotion/react';
import { parseLength, Dimension } from '../../dimension';
+import getChartMetadataRegistry from '../registries/ChartMetadataRegistrySingleton';
import SuperChartCore, { Props as SuperChartCoreProps } from './SuperChartCore';
import DefaultFallbackComponent from './FallbackComponent';
import ChartProps, { ChartPropsConfig } from '../models/ChartProps';
@@ -60,7 +61,7 @@ export type Props = Omit &
FallbackComponent?: React.ComponentType;
/** Event listener for unexpected errors from chart */
onErrorBoundary?: ErrorBoundaryProps['onError'];
- /** Prop for form plugins uisng superchart */
+ /** Prop for form plugins using superchart */
showOverflow?: boolean;
/** Prop for popovercontainer ref */
parentRef?: RefObject;
@@ -140,6 +141,9 @@ class SuperChart extends React.PureComponent {
this.core = core;
};
+ private getQueryCount = () =>
+ getChartMetadataRegistry().get(this.props.chartType)?.queryObjectCount ?? 1;
+
renderChart(width: number, height: number) {
const {
id,
@@ -174,9 +178,11 @@ class SuperChart extends React.PureComponent {
const noResultQueries =
enableNoResults &&
(!queriesData ||
- queriesData.every(
- ({ data }) => !data || (Array.isArray(data) && data.length === 0),
- ));
+ queriesData
+ .slice(0, this.getQueryCount())
+ .every(
+ ({ data }) => !data || (Array.isArray(data) && data.length === 0),
+ ));
if (noResultQueries) {
chart = noResults || (
{
if (error) {
return (
- ERROR
+ {t('ERROR')}
chartType="{chartType}"
—
{error.toString()}
diff --git a/superset-frontend/packages/superset-ui-core/src/chart/models/ChartMetadata.ts b/superset-frontend/packages/superset-ui-core/src/chart/models/ChartMetadata.ts
index 7a25fe86208d1..1d55d2a9859f0 100644
--- a/superset-frontend/packages/superset-ui-core/src/chart/models/ChartMetadata.ts
+++ b/superset-frontend/packages/superset-ui-core/src/chart/models/ChartMetadata.ts
@@ -48,6 +48,7 @@ export interface ChartMetadataConfig {
// label: ChartLabel.DEPRECATED which will display a "deprecated" label on the chart.
label?: ChartLabel | null;
labelExplanation?: string | null;
+ queryObjectCount?: number;
}
export default class ChartMetadata {
@@ -87,6 +88,8 @@ export default class ChartMetadata {
labelExplanation?: string | null;
+ queryObjectCount: number;
+
constructor(config: ChartMetadataConfig) {
const {
name,
@@ -106,6 +109,7 @@ export default class ChartMetadata {
deprecated = false,
label = null,
labelExplanation = null,
+ queryObjectCount = 1,
} = config;
this.name = name;
@@ -134,6 +138,7 @@ export default class ChartMetadata {
this.deprecated = deprecated;
this.label = label;
this.labelExplanation = labelExplanation;
+ this.queryObjectCount = queryObjectCount;
}
canBeAnnotationType(type: string): boolean {
diff --git a/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts b/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts
index e673817118102..e02aeca4f54de 100644
--- a/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts
+++ b/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts
@@ -50,6 +50,8 @@ type Hooks = {
* also handles "change" and "remove".
*/
onAddFilter?: (newFilters: DataRecordFilters, merge?: boolean) => void;
+ /** handle right click */
+ onContextMenu?: HandlerFunction;
/** handle errors */
onError?: HandlerFunction;
/** use the vis as control to update state */
@@ -88,6 +90,8 @@ export interface ChartPropsConfig {
filterState?: FilterState;
/** Set of actual behaviors that this instance of chart should use */
behaviors?: Behavior[];
+ /** Chart display settings related to current view context */
+ displaySettings?: JsonObject;
/** Application section of the chart on the screen (in what components/screen it placed) */
appSection?: AppSection;
/** is the chart refreshing its contents */
@@ -130,12 +134,18 @@ export default class ChartProps {
behaviors: Behavior[];
+ displaySettings?: JsonObject;
+
appSection?: AppSection;
isRefreshing?: boolean;
inputRef?: RefObject;
+ inContextMenu?: boolean;
+
+ emitCrossFilters?: boolean;
+
theme: SupersetTheme;
constructor(config: ChartPropsConfig & { formData?: FormData } = {}) {
@@ -149,11 +159,14 @@ export default class ChartProps {
initialValues = {},
queriesData = [],
behaviors = [],
+ displaySettings = {},
width = DEFAULT_WIDTH,
height = DEFAULT_HEIGHT,
appSection,
isRefreshing,
inputRef,
+ inContextMenu = false,
+ emitCrossFilters = false,
theme,
} = config;
this.width = width;
@@ -169,9 +182,12 @@ export default class ChartProps {
this.ownState = ownState;
this.filterState = filterState;
this.behaviors = behaviors;
+ this.displaySettings = displaySettings;
this.appSection = appSection;
this.isRefreshing = isRefreshing;
this.inputRef = inputRef;
+ this.inContextMenu = inContextMenu;
+ this.emitCrossFilters = emitCrossFilters;
this.theme = theme;
}
}
@@ -190,9 +206,12 @@ ChartProps.createSelector = function create(): ChartPropsSelector {
input => input.ownState,
input => input.filterState,
input => input.behaviors,
+ input => input.displaySettings,
input => input.appSection,
input => input.isRefreshing,
input => input.inputRef,
+ input => input.inContextMenu,
+ input => input.emitCrossFilters,
input => input.theme,
(
annotationData,
@@ -206,9 +225,12 @@ ChartProps.createSelector = function create(): ChartPropsSelector {
ownState,
filterState,
behaviors,
+ displaySettings,
appSection,
isRefreshing,
inputRef,
+ inContextMenu,
+ emitCrossFilters,
theme,
) =>
new ChartProps({
@@ -223,9 +245,12 @@ ChartProps.createSelector = function create(): ChartPropsSelector {
filterState,
width,
behaviors,
+ displaySettings,
appSection,
isRefreshing,
inputRef,
+ inContextMenu,
+ emitCrossFilters,
theme,
}),
);
diff --git a/superset-frontend/packages/superset-ui-core/src/chart/types/Base.ts b/superset-frontend/packages/superset-ui-core/src/chart/types/Base.ts
index 0bfae7777e7df..f9f1a360b6273 100644
--- a/superset-frontend/packages/superset-ui-core/src/chart/types/Base.ts
+++ b/superset-frontend/packages/superset-ui-core/src/chart/types/Base.ts
@@ -25,6 +25,12 @@ export type HandlerFunction = (...args: unknown[]) => void;
export enum Behavior {
INTERACTIVE_CHART = 'INTERACTIVE_CHART',
NATIVE_FILTER = 'NATIVE_FILTER',
+
+ /**
+ * Include `DRILL_TO_DETAIL` behavior if plugin handles `contextmenu` event
+ * when dimensions are right-clicked on.
+ */
+ DRILL_TO_DETAIL = 'DRILL_TO_DETAIL',
}
export enum AppSection {
@@ -73,4 +79,11 @@ export const chartLabelWeight: Record = {
},
};
+export enum AxisType {
+ category = 'category',
+ value = 'value',
+ time = 'time',
+ log = 'log',
+}
+
export default {};
diff --git a/superset-frontend/packages/superset-ui-core/src/color/CategoricalColorScale.ts b/superset-frontend/packages/superset-ui-core/src/color/CategoricalColorScale.ts
index 2a6a4d63a0473..5f29ad4775996 100644
--- a/superset-frontend/packages/superset-ui-core/src/color/CategoricalColorScale.ts
+++ b/superset-frontend/packages/superset-ui-core/src/color/CategoricalColorScale.ts
@@ -51,7 +51,7 @@ class CategoricalColorScale extends ExtensibleFunction {
* @param {*} parentForcedColors optional parameter that comes from parent
* (usually CategoricalColorNamespace) and supersede this.forcedColors
*/
- constructor(colors: string[], parentForcedColors?: ColorsLookup) {
+ constructor(colors: string[], parentForcedColors: ColorsLookup = {}) {
super((value: string, sliceId?: number) => this.getColor(value, sliceId));
this.originColors = colors;
@@ -67,18 +67,11 @@ class CategoricalColorScale extends ExtensibleFunction {
const cleanedValue = stringifyAndTrim(value);
const sharedLabelColor = getSharedLabelColor();
- const parentColor =
- this.parentForcedColors && this.parentForcedColors[cleanedValue];
- if (parentColor) {
- sharedLabelColor.addSlice(cleanedValue, parentColor, sliceId);
- return parentColor;
- }
-
- const forcedColor = this.forcedColors[cleanedValue];
- if (forcedColor) {
- sharedLabelColor.addSlice(cleanedValue, forcedColor, sliceId);
- return forcedColor;
- }
+ // priority: parentForcedColors > forcedColors > labelColors
+ let color =
+ this.parentForcedColors?.[cleanedValue] ||
+ this.forcedColors?.[cleanedValue] ||
+ sharedLabelColor.getColorMap().get(cleanedValue);
if (isFeatureEnabled(FeatureFlag.USE_ANALAGOUS_COLORS)) {
const multiple = Math.floor(
@@ -90,8 +83,10 @@ class CategoricalColorScale extends ExtensibleFunction {
this.range(this.originColors.concat(newRange));
}
}
-
- const color = this.scale(cleanedValue);
+ const newColor = this.scale(cleanedValue);
+ if (!color) {
+ color = newColor;
+ }
sharedLabelColor.addSlice(cleanedValue, color, sliceId);
return color;
diff --git a/superset-frontend/packages/superset-ui-core/src/color/ColorSchemeRegistry.ts b/superset-frontend/packages/superset-ui-core/src/color/ColorSchemeRegistry.ts
index d36b598bcd0c3..e270ff6f0ef9d 100644
--- a/superset-frontend/packages/superset-ui-core/src/color/ColorSchemeRegistry.ts
+++ b/superset-frontend/packages/superset-ui-core/src/color/ColorSchemeRegistry.ts
@@ -28,7 +28,16 @@ export default class ColorSchemeRegistry extends RegistryWithDefaultKey {
});
}
- get(key?: string) {
- return super.get(key) as T | undefined;
+ get(key?: string, strict = false) {
+ const target = super.get(key) as T | undefined;
+
+ // fallsback to default scheme if any
+ if (!strict && !target) {
+ const defaultKey = super.getDefaultKey();
+ if (defaultKey) {
+ return super.get(defaultKey) as T | undefined;
+ }
+ }
+ return target;
}
}
diff --git a/superset-frontend/packages/superset-ui-core/src/color/SharedLabelColorSingleton.ts b/superset-frontend/packages/superset-ui-core/src/color/SharedLabelColorSingleton.ts
index 10a14df075910..bc417e6036a51 100644
--- a/superset-frontend/packages/superset-ui-core/src/color/SharedLabelColorSingleton.ts
+++ b/superset-frontend/packages/superset-ui-core/src/color/SharedLabelColorSingleton.ts
@@ -18,113 +18,81 @@
*/
import { CategoricalColorNamespace } from '.';
-import { FeatureFlag, isFeatureEnabled, makeSingleton } from '../utils';
-import { getAnalogousColors } from './utils';
+import { makeSingleton } from '../utils';
+export enum SharedLabelColorSource {
+ dashboard,
+ explore,
+}
export class SharedLabelColor {
- sliceLabelColorMap: Record>;
+ sliceLabelMap: Map;
- constructor() {
- // { sliceId1: { label1: color1 }, sliceId2: { label2: color2 } }
- this.sliceLabelColorMap = {};
- }
+ colorMap: Map;
- getColorMap(
- colorNamespace?: string,
- colorScheme?: string,
- updateColorScheme?: boolean,
- ) {
- if (colorScheme) {
- const categoricalNamespace =
- CategoricalColorNamespace.getNamespace(colorNamespace);
- const sharedLabels = this.getSharedLabels();
- let generatedColors: string[] = [];
- let sharedLabelMap;
+ source: SharedLabelColorSource;
- if (sharedLabels.length) {
- const colorScale = categoricalNamespace.getScale(colorScheme);
- const colors = colorScale.range();
- if (isFeatureEnabled(FeatureFlag.USE_ANALAGOUS_COLORS)) {
- const multiple = Math.ceil(sharedLabels.length / colors.length);
- generatedColors = getAnalogousColors(colors, multiple);
- sharedLabelMap = sharedLabels.reduce(
- (res, label, index) => ({
- ...res,
- [label.toString()]: generatedColors[index],
- }),
- {},
- );
- } else {
- // reverse colors to reduce color conflicts
- colorScale.range(colors.reverse());
- sharedLabelMap = sharedLabels.reduce(
- (res, label) => ({
- ...res,
- [label.toString()]: colorScale(label),
- }),
- {},
- );
- }
- }
+ constructor() {
+ // { sliceId1: [label1, label2, ...], sliceId2: [label1, label2, ...] }
+ this.sliceLabelMap = new Map();
+ this.colorMap = new Map();
+ this.source = SharedLabelColorSource.dashboard;
+ }
- const labelMap = Object.keys(this.sliceLabelColorMap).reduce(
- (res, sliceId) => {
- // get new color scale instance
- const colorScale = categoricalNamespace.getScale(colorScheme);
- return {
- ...res,
- ...Object.keys(this.sliceLabelColorMap[sliceId]).reduce(
- (res, label) => ({
- ...res,
- [label]: updateColorScheme
- ? colorScale(label)
- : this.sliceLabelColorMap[sliceId][label],
- }),
- {},
- ),
- };
- },
- {},
- );
+ updateColorMap(colorNamespace?: string, colorScheme?: string) {
+ const categoricalNamespace =
+ CategoricalColorNamespace.getNamespace(colorNamespace);
+ const newColorMap = new Map();
+ this.colorMap.clear();
+ this.sliceLabelMap.forEach(labels => {
+ const colorScale = categoricalNamespace.getScale(colorScheme);
+ labels.forEach(label => {
+ const newColor = colorScale(label);
+ newColorMap.set(label, newColor);
+ });
+ });
+ this.colorMap = newColorMap;
+ }
- return {
- ...labelMap,
- ...sharedLabelMap,
- };
- }
- return undefined;
+ getColorMap() {
+ return this.colorMap;
}
addSlice(label: string, color: string, sliceId?: number) {
- if (!sliceId) return;
- this.sliceLabelColorMap[sliceId] = {
- ...this.sliceLabelColorMap[sliceId],
- [label]: color,
- };
+ if (
+ this.source !== SharedLabelColorSource.dashboard ||
+ sliceId === undefined
+ )
+ return;
+ const labels = this.sliceLabelMap.get(sliceId) || [];
+ if (!labels.includes(label)) {
+ labels.push(label);
+ this.sliceLabelMap.set(sliceId, labels);
+ }
+ this.colorMap.set(label, color);
}
removeSlice(sliceId: number) {
- delete this.sliceLabelColorMap[sliceId];
+ if (this.source !== SharedLabelColorSource.dashboard) return;
+ this.sliceLabelMap.delete(sliceId);
+ const newColorMap = new Map();
+ this.sliceLabelMap.forEach(labels => {
+ labels.forEach(label => {
+ newColorMap.set(label, this.colorMap.get(label));
+ });
+ });
+ this.colorMap = newColorMap;
}
- clear() {
- this.sliceLabelColorMap = {};
+ reset() {
+ const copyColorMap = new Map(this.colorMap);
+ copyColorMap.forEach((_, label) => {
+ this.colorMap.set(label, '');
+ });
}
- getSharedLabels() {
- const tempLabels = new Set();
- const result = new Set();
- Object.keys(this.sliceLabelColorMap).forEach(sliceId => {
- const colorMap = this.sliceLabelColorMap[sliceId];
- Object.keys(colorMap).forEach(label => {
- if (tempLabels.has(label) && !result.has(label)) {
- result.add(label);
- } else {
- tempLabels.add(label);
- }
- });
- });
- return [...result];
+ clear() {
+ this.sliceLabelMap.clear();
+ this.colorMap.clear();
}
}
diff --git a/superset-frontend/packages/superset-ui-core/src/color/index.ts b/superset-frontend/packages/superset-ui-core/src/color/index.ts
index e1cde3ba3e2d5..3bbdb5d0dc578 100644
--- a/superset-frontend/packages/superset-ui-core/src/color/index.ts
+++ b/superset-frontend/packages/superset-ui-core/src/color/index.ts
@@ -35,6 +35,7 @@ export * from './utils';
export {
default as getSharedLabelColor,
SharedLabelColor,
+ SharedLabelColorSource,
} from './SharedLabelColorSingleton';
export const BRAND_COLOR = '#00A699';
diff --git a/superset-frontend/packages/superset-ui-core/src/connection/SupersetClient.ts b/superset-frontend/packages/superset-ui-core/src/connection/SupersetClient.ts
index 530c710809068..f102cd197dabd 100644
--- a/superset-frontend/packages/superset-ui-core/src/connection/SupersetClient.ts
+++ b/superset-frontend/packages/superset-ui-core/src/connection/SupersetClient.ts
@@ -44,6 +44,7 @@ const SupersetClient: SupersetClientInterface = {
get: request => getInstance().get(request),
init: force => getInstance().init(force),
isAuthenticated: () => getInstance().isAuthenticated(),
+ getGuestToken: () => getInstance().getGuestToken(),
post: request => getInstance().post(request),
postForm: (...args) => getInstance().postForm(...args),
put: request => getInstance().put(request),
diff --git a/superset-frontend/packages/superset-ui-core/src/connection/SupersetClientClass.ts b/superset-frontend/packages/superset-ui-core/src/connection/SupersetClientClass.ts
index b7281d025903c..fd040faed0423 100644
--- a/superset-frontend/packages/superset-ui-core/src/connection/SupersetClientClass.ts
+++ b/superset-frontend/packages/superset-ui-core/src/connection/SupersetClientClass.ts
@@ -158,6 +158,10 @@ export default class SupersetClientClass {
return this.csrfToken !== null && this.csrfToken !== undefined;
}
+ getGuestToken() {
+ return this.guestToken;
+ }
+
async get(
requestConfig: RequestConfig & { parseMethod?: T },
) {
diff --git a/superset-frontend/packages/superset-ui-core/src/connection/callApi/callApi.ts b/superset-frontend/packages/superset-ui-core/src/connection/callApi/callApi.ts
index 7c3fe21fdb8a4..c682e5b7300df 100644
--- a/superset-frontend/packages/superset-ui-core/src/connection/callApi/callApi.ts
+++ b/superset-frontend/packages/superset-ui-core/src/connection/callApi/callApi.ts
@@ -94,7 +94,7 @@ export default async function callApi({
cache !== 'no-store' &&
cache !== 'reload' &&
CACHE_AVAILABLE &&
- (window.location && window.location.protocol) === 'https:'
+ window.location?.protocol === 'https:'
) {
let supersetCache: Cache | null = null;
try {
@@ -146,10 +146,23 @@ export default async function callApi({
Object.keys(payload).forEach(key => {
const value = (payload as JsonObject)[key] as JsonValue;
if (typeof value !== 'undefined') {
- formData.append(
- key,
- stringify ? JSON.stringify(value) : String(value),
- );
+ let valueString;
+ try {
+ // We have seen instances where casting to String() throws error
+ // This check allows all valid attributes to be appended to the formData
+ // while logging error to console for any attribute that fails the cast to String
+ valueString = stringify ? JSON.stringify(value) : String(value);
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.error(
+ `Unable to convert attribute '${key}' to a String(). '${key}' was not added to the formData in request.body for call to ${url}`,
+ value,
+ e,
+ );
+ }
+ if (valueString !== undefined) {
+ formData.append(key, valueString);
+ }
}
});
request.body = formData;
diff --git a/superset-frontend/packages/superset-ui-core/src/connection/callApi/parseResponse.ts b/superset-frontend/packages/superset-ui-core/src/connection/callApi/parseResponse.ts
index a0b9f149113a4..15beca6e150ad 100644
--- a/superset-frontend/packages/superset-ui-core/src/connection/callApi/parseResponse.ts
+++ b/superset-frontend/packages/superset-ui-core/src/connection/callApi/parseResponse.ts
@@ -16,6 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
+import JSONbig from 'json-bigint';
+import { cloneDeepWith } from 'lodash';
import { ParseMethod, TextResponse, JsonResponse } from '../types';
@@ -25,7 +27,7 @@ export default async function parseResponse(
) {
type ReturnType = T extends 'raw' | null
? Response
- : T extends 'json' | undefined
+ : T extends 'json' | 'json-bigint' | undefined
? JsonResponse
: T extends 'text'
? TextResponse
@@ -46,6 +48,19 @@ export default async function parseResponse(
};
return result as ReturnType;
}
+ if (parseMethod === 'json-bigint') {
+ const rawData = await response.text();
+ const json = JSONbig.parse(rawData);
+ const result: JsonResponse = {
+ response,
+ // `json-bigint` could not handle floats well, see sidorares/json-bigint#62
+ // TODO: clean up after json-bigint>1.0.1 is released
+ json: cloneDeepWith(json, (value: any) =>
+ value?.isInteger?.() === false ? Number(value) : undefined,
+ ),
+ };
+ return result as ReturnType;
+ }
// by default treat this as json
if (parseMethod === undefined || parseMethod === 'json') {
const json = await response.json();
@@ -56,6 +71,6 @@ export default async function parseResponse(
return result as ReturnType;
}
throw new Error(
- `Expected parseResponse=json|text|raw|null, got '${parseMethod}'.`,
+ `Expected parseResponse=json|json-bigint|text|raw|null, got '${parseMethod}'.`,
);
}
diff --git a/superset-frontend/packages/superset-ui-core/src/connection/types.ts b/superset-frontend/packages/superset-ui-core/src/connection/types.ts
index 06025956754dd..a63ffd8b68a08 100644
--- a/superset-frontend/packages/superset-ui-core/src/connection/types.ts
+++ b/superset-frontend/packages/superset-ui-core/src/connection/types.ts
@@ -70,7 +70,13 @@ export type Method = RequestInit['method'];
export type Mode = RequestInit['mode'];
export type Redirect = RequestInit['redirect'];
export type ClientTimeout = number | undefined;
-export type ParseMethod = 'json' | 'text' | 'raw' | null | undefined;
+export type ParseMethod =
+ | 'json'
+ | 'json-bigint'
+ | 'text'
+ | 'raw'
+ | null
+ | undefined;
export type Signal = RequestInit['signal'];
export type Stringify = boolean;
export type Url = string;
@@ -152,6 +158,7 @@ export interface SupersetClientInterface
| 'init'
| 'isAuthenticated'
| 'reAuthenticate'
+ | 'getGuestToken'
> {
configure: (config?: ClientConfig) => SupersetClientInterface;
reset: () => void;
diff --git a/superset-frontend/packages/superset-ui-core/src/math-expression/index.ts b/superset-frontend/packages/superset-ui-core/src/math-expression/index.ts
index ae3db1f069a94..8ee4d272d8bb0 100644
--- a/superset-frontend/packages/superset-ui-core/src/math-expression/index.ts
+++ b/superset-frontend/packages/superset-ui-core/src/math-expression/index.ts
@@ -106,7 +106,7 @@ export function evalExpression(expression: string, value: number): number {
const subExpressions = String(parsedExpression).split('=');
parsedExpression = subExpressions[1] ?? subExpressions[0];
// we can ignore the type requirement on `TOKENS`, as value is always `number`
- // and doesn't need to consider `number | underfined`.
+ // and doesn't need to consider `number | undefined`.
// @ts-ignore
return Number(mexp.eval(parsedExpression, TOKENS, { x: value }));
}
diff --git a/superset-frontend/packages/superset-ui-core/src/models/Registry.ts b/superset-frontend/packages/superset-ui-core/src/models/Registry.ts
index 90a8065e29ac2..e876bc2b50f59 100644
--- a/superset-frontend/packages/superset-ui-core/src/models/Registry.ts
+++ b/superset-frontend/packages/superset-ui-core/src/models/Registry.ts
@@ -169,7 +169,7 @@ export default class Registry<
const item = this.items[key];
if (item !== undefined) {
if ('loader' in item) {
- return item.loader && item.loader();
+ return item.loader?.();
}
return item.value;
diff --git a/superset-frontend/packages/superset-ui-core/src/number-format/NumberFormats.ts b/superset-frontend/packages/superset-ui-core/src/number-format/NumberFormats.ts
index 11701586c0340..605da5d30e7b7 100644
--- a/superset-frontend/packages/superset-ui-core/src/number-format/NumberFormats.ts
+++ b/superset-frontend/packages/superset-ui-core/src/number-format/NumberFormats.ts
@@ -52,6 +52,7 @@ const SI = SI_3_DIGIT;
const SMART_NUMBER = 'SMART_NUMBER';
const SMART_NUMBER_SIGNED = 'SMART_NUMBER_SIGNED';
+const OVER_MAX_HIDDEN = 'OVER_MAX_HIDDEN';
const NumberFormats = {
DOLLAR,
@@ -82,6 +83,7 @@ const NumberFormats = {
SI_3_DIGIT,
SMART_NUMBER,
SMART_NUMBER_SIGNED,
+ OVER_MAX_HIDDEN,
};
export default NumberFormats;
diff --git a/superset-frontend/packages/superset-ui-core/src/query/DatasourceKey.ts b/superset-frontend/packages/superset-ui-core/src/query/DatasourceKey.ts
index 2fe4bcf139059..38a38e10b13a0 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/DatasourceKey.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/DatasourceKey.ts
@@ -27,8 +27,8 @@ export default class DatasourceKey {
constructor(key: string) {
const [idStr, typeStr] = key.split('__');
this.id = parseInt(idStr, 10);
- this.type =
- typeStr === 'table' ? DatasourceType.Table : DatasourceType.Druid;
+ this.type = DatasourceType.Table; // default to SqlaTable model
+ this.type = typeStr === 'query' ? DatasourceType.Query : this.type;
}
public toString() {
diff --git a/superset-frontend/packages/superset-ui-core/src/query/buildQueryContext.ts b/superset-frontend/packages/superset-ui-core/src/query/buildQueryContext.ts
index c805811e5faf5..aa1f470ba8f61 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/buildQueryContext.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/buildQueryContext.ts
@@ -23,6 +23,8 @@ import { QueryFieldAliases, QueryFormData } from './types/QueryFormData';
import { QueryContext, QueryObject } from './types/Query';
import { SetDataMaskHook } from '../chart';
import { JsonObject } from '../connection';
+import { normalizeTimeColumn } from './normalizeTimeColumn';
+import { isXAxisSet } from './getXAxis';
const WRAP_IN_ARRAY = (baseQueryObject: QueryObject) => [baseQueryObject];
@@ -45,13 +47,19 @@ export default function buildQueryContext(
typeof options === 'function'
? { buildQuery: options, queryFields: {} }
: options || {};
- const queries = buildQuery(buildQueryObject(formData, queryFields));
+ let queries = buildQuery(buildQueryObject(formData, queryFields));
+ // --- query mutator begin ---
+ // todo(Yongjie): move the query mutator into buildQueryObject instead of buildQueryContext
queries.forEach(query => {
if (Array.isArray(query.post_processing)) {
// eslint-disable-next-line no-param-reassign
query.post_processing = query.post_processing.filter(Boolean);
}
});
+ if (isXAxisSet(formData)) {
+ queries = queries.map(query => normalizeTimeColumn(formData, query));
+ }
+ // --- query mutator end ---
return {
datasource: new DatasourceKey(formData.datasource).toObject(),
force: formData.force || false,
diff --git a/superset-frontend/packages/superset-ui-core/src/query/buildQueryObject.ts b/superset-frontend/packages/superset-ui-core/src/query/buildQueryObject.ts
index 52fa1ffed0c2e..cf434f138577c 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/buildQueryObject.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/buildQueryObject.ts
@@ -20,18 +20,20 @@
/* eslint-disable camelcase */
import {
AdhocFilter,
- QueryFieldAliases,
- QueryFormColumn,
- QueryFormData,
QueryObject,
QueryObjectFilterClause,
- isPhysicalColumn,
- isAdhocColumn,
+ isQueryFormMetric,
} from './types';
+import {
+ QueryFieldAliases,
+ QueryFormMetric,
+ QueryFormData,
+} from './types/QueryFormData';
import processFilters from './processFilters';
import extractExtras from './extractExtras';
import extractQueryFields from './extractQueryFields';
import { overrideExtraFormData } from './processExtraFormData';
+import { isDefined } from '../utils';
/**
* Build the common segments of all query objects (e.g. the granularity field derived from
@@ -92,16 +94,16 @@ export default function buildQueryObject(
...extras,
...filterFormData,
});
- const normalizeSeriesLimitMetric = (column: QueryFormColumn | undefined) => {
- if (isAdhocColumn(column) || isPhysicalColumn(column)) {
- return column;
+ const normalizeSeriesLimitMetric = (metric: QueryFormMetric | undefined) => {
+ if (isQueryFormMetric(metric)) {
+ return metric;
}
return undefined;
};
let queryObject: QueryObject = {
// fallback `null` to `undefined` so they won't be sent to the backend
- // (JSON.strinify will ignore `undefined`.)
+ // (JSON.stringify will ignore `undefined`.)
time_range: time_range || undefined,
since: since || undefined,
until: until || undefined,
@@ -121,10 +123,11 @@ export default function buildQueryObject(
? undefined
: numericRowOffset,
series_columns,
- series_limit,
- series_limit_metric: normalizeSeriesLimitMetric(series_limit_metric),
- timeseries_limit: limit ? Number(limit) : 0,
- timeseries_limit_metric: timeseries_limit_metric || undefined,
+ series_limit: series_limit ?? (isDefined(limit) ? Number(limit) : 0),
+ series_limit_metric:
+ normalizeSeriesLimitMetric(series_limit_metric) ??
+ timeseries_limit_metric ??
+ undefined,
order_desc: typeof order_desc === 'undefined' ? true : order_desc,
url_params: url_params || undefined,
custom_params,
diff --git a/superset-frontend/packages/superset-ui-core/src/query/constants.ts b/superset-frontend/packages/superset-ui-core/src/query/constants.ts
index 4a3fe5ff5474d..7976e87a4a281 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/constants.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/constants.ts
@@ -1,11 +1,3 @@
-import {
- ExtraFormDataAppend,
- ExtraFormDataOverrideExtras,
- ExtraFormDataOverrideRegular,
- ExtraFormDataOverride,
- QueryObject,
-} from './types';
-
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -24,10 +16,19 @@ import {
* specific language governing permissions and limitations
* under the License.
*/
+import {
+ ExtraFormDataAppend,
+ ExtraFormDataOverrideExtras,
+ ExtraFormDataOverrideRegular,
+ ExtraFormDataOverride,
+ QueryObject,
+} from './types';
+
export const DTTM_ALIAS = '__timestamp';
+export const NO_TIME_RANGE = 'No filter';
export const EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS: (keyof ExtraFormDataOverrideExtras)[] =
- ['druid_time_origin', 'relative_start', 'relative_end', 'time_grain_sqla'];
+ ['relative_start', 'relative_end', 'time_grain_sqla'];
export const EXTRA_FORM_DATA_APPEND_KEYS: (keyof ExtraFormDataAppend)[] = [
'adhoc_filters',
diff --git a/superset-frontend/packages/superset-ui-core/src/query/extractExtras.ts b/superset-frontend/packages/superset-ui-core/src/query/extractExtras.ts
index aea2881b25dce..39a4b4b2d8030 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/extractExtras.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/extractExtras.ts
@@ -18,10 +18,9 @@
*/
/* eslint-disable camelcase */
+import { TimeGranularity, QueryFormData } from '@superset-ui/core';
import {
AppliedTimeExtras,
- isDruidFormData,
- QueryFormData,
QueryObjectExtras,
QueryObjectFilterClause,
TimeColumnConfigKey,
@@ -30,8 +29,7 @@ import {
type ExtraFilterQueryField = {
time_range?: string;
granularity_sqla?: string;
- time_grain_sqla?: string;
- druid_time_origin?: string;
+ time_grain_sqla?: TimeGranularity;
granularity?: string;
};
@@ -58,7 +56,6 @@ export default function extractExtras(formData: QueryFormData): ExtractedExtra {
__time_range: 'time_range',
__time_col: 'granularity_sqla',
__time_grain: 'time_grain_sqla',
- __time_origin: 'druid_time_origin',
__granularity: 'granularity',
};
@@ -66,28 +63,21 @@ export default function extractExtras(formData: QueryFormData): ExtractedExtra {
if (filter.col in reservedColumnsToQueryField) {
const key = filter.col as TimeColumnConfigKey;
const queryField = reservedColumnsToQueryField[key];
- extract[queryField] = filter.val as string;
+ extract[queryField] = filter.val as TimeGranularity;
applied_time_extras[key] = filter.val as string;
} else {
filters.push(filter);
}
});
- // map to undeprecated names and remove deprecated fields
- if (isDruidFormData(formData) && !extract.druid_time_origin) {
- extras.druid_time_origin = formData.druid_time_origin;
- delete extract.druid_time_origin;
- } else {
- // SQL
- extras.time_grain_sqla =
- extract.time_grain_sqla || formData.time_grain_sqla;
- extract.granularity =
- extract.granularity_sqla ||
- formData.granularity ||
- formData.granularity_sqla;
- delete extract.granularity_sqla;
- delete extract.time_grain_sqla;
- }
+ // SQL
+ extras.time_grain_sqla = extract.time_grain_sqla || formData.time_grain_sqla;
+ extract.granularity =
+ extract.granularity_sqla ||
+ formData.granularity ||
+ formData.granularity_sqla;
+ delete extract.granularity_sqla;
+ delete extract.time_grain_sqla;
return extract;
}
diff --git a/superset-frontend/packages/superset-ui-core/src/query/extractTimegrain.ts b/superset-frontend/packages/superset-ui-core/src/query/extractTimegrain.ts
index e98ec5db4ec1b..b5b3f9617bd8a 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/extractTimegrain.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/extractTimegrain.ts
@@ -18,7 +18,7 @@
*/
/* eslint-disable no-underscore-dangle */
-import { QueryFormData } from './types';
+import { QueryFormData } from '@superset-ui/core';
import { TimeGranularity } from '../time-format';
export default function extractTimegrain(
diff --git a/superset-frontend/packages/superset-ui-core/src/query/getColumnLabel.ts b/superset-frontend/packages/superset-ui-core/src/query/getColumnLabel.ts
index f449a44cb2702..f1b4e6ffa7308 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/getColumnLabel.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/getColumnLabel.ts
@@ -23,8 +23,8 @@ export default function getColumnLabel(column: QueryFormColumn): string {
if (isPhysicalColumn(column)) {
return column;
}
- if (column.label) {
+ if (column?.label) {
return column.label;
}
- return column.sqlExpression;
+ return column?.sqlExpression;
}
diff --git a/superset-frontend/packages/superset-ui-core/src/query/getMetricLabel.ts b/superset-frontend/packages/superset-ui-core/src/query/getMetricLabel.ts
index 3f6f31af7b9f9..7ac7930c6a564 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/getMetricLabel.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/getMetricLabel.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { isAdhocMetricSimple, isSavedMetric, QueryFormMetric } from './types';
+import { QueryFormMetric, isSavedMetric, isAdhocMetricSimple } from './types';
export default function getMetricLabel(metric: QueryFormMetric): string {
if (isSavedMetric(metric)) {
diff --git a/superset-frontend/packages/superset-ui-core/src/query/getXAxis.ts b/superset-frontend/packages/superset-ui-core/src/query/getXAxis.ts
new file mode 100644
index 0000000000000..7c329c2a8bdff
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-core/src/query/getXAxis.ts
@@ -0,0 +1,57 @@
+/**
+ * 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 {
+ DTTM_ALIAS,
+ FeatureFlag,
+ isFeatureEnabled,
+ getColumnLabel,
+ isQueryFormColumn,
+ QueryFormData,
+ QueryFormColumn,
+ Optional,
+} from '@superset-ui/core';
+
+export const isXAxisSet = (formData: QueryFormData) =>
+ isQueryFormColumn(formData.x_axis);
+
+export const hasGenericChartAxes = isFeatureEnabled(
+ FeatureFlag.GENERIC_CHART_AXES,
+);
+
+export const getXAxisColumn = (
+ formData: QueryFormData,
+): Optional => {
+ // The formData should be "raw form_data" -- the snake_case version of formData rather than camelCase.
+ if (!(formData.granularity_sqla || formData.x_axis)) {
+ return undefined;
+ }
+
+ if (isXAxisSet(formData)) {
+ return formData.x_axis;
+ }
+ return DTTM_ALIAS;
+};
+
+export const getXAxisLabel = (formData: QueryFormData): Optional => {
+ const col = getXAxisColumn(formData);
+ if (col) {
+ return getColumnLabel(col);
+ }
+ return undefined;
+};
diff --git a/superset-frontend/packages/superset-ui-core/src/query/index.ts b/superset-frontend/packages/superset-ui-core/src/query/index.ts
index 9bbfbc59fba86..bb83e3d340fd2 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/index.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/index.ts
@@ -28,6 +28,9 @@ export { default as getColumnLabel } from './getColumnLabel';
export { default as getMetricLabel } from './getMetricLabel';
export { default as DatasourceKey } from './DatasourceKey';
export { default as normalizeOrderBy } from './normalizeOrderBy';
+export { normalizeTimeColumn } from './normalizeTimeColumn';
+export { default as extractQueryFields } from './extractQueryFields';
+export * from './getXAxis';
export * from './types/AnnotationLayer';
export * from './types/QueryFormData';
diff --git a/superset-frontend/packages/superset-ui-core/src/query/normalizeOrderBy.ts b/superset-frontend/packages/superset-ui-core/src/query/normalizeOrderBy.ts
index 3df72b58d2ba5..e38e682abbdfa 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/normalizeOrderBy.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/normalizeOrderBy.ts
@@ -39,24 +39,24 @@ export default function normalizeOrderBy(
// ensure that remove invalid orderby clause
const cloneQueryObject = { ...queryObject };
- delete cloneQueryObject.timeseries_limit_metric;
+ delete cloneQueryObject.series_limit_metric;
delete cloneQueryObject.legacy_order_by;
delete cloneQueryObject.order_desc;
delete cloneQueryObject.orderby;
const isAsc = !queryObject.order_desc;
if (
- queryObject.timeseries_limit_metric !== undefined &&
- queryObject.timeseries_limit_metric !== null &&
- !isEmpty(queryObject.timeseries_limit_metric)
+ queryObject.series_limit_metric !== undefined &&
+ queryObject.series_limit_metric !== null &&
+ !isEmpty(queryObject.series_limit_metric)
) {
return {
...cloneQueryObject,
- orderby: [[queryObject.timeseries_limit_metric, isAsc]],
+ orderby: [[queryObject.series_limit_metric, isAsc]],
};
}
- // todo: Removed `legacy_ordery_by` after refactoring
+ // todo: Removed `legacy_order_by` after refactoring
if (
queryObject.legacy_order_by !== undefined &&
queryObject.legacy_order_by !== null &&
diff --git a/superset-frontend/packages/superset-ui-core/src/query/normalizeTimeColumn.ts b/superset-frontend/packages/superset-ui-core/src/query/normalizeTimeColumn.ts
new file mode 100644
index 0000000000000..f7ea0d0e60061
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-core/src/query/normalizeTimeColumn.ts
@@ -0,0 +1,84 @@
+/**
+ * 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 omit from 'lodash/omit';
+
+import {
+ AdhocColumn,
+ isAdhocColumn,
+ isPhysicalColumn,
+ QueryFormColumn,
+ QueryFormData,
+ QueryObject,
+} from './types';
+import { isXAxisSet } from './getXAxis';
+
+export function normalizeTimeColumn(
+ formData: QueryFormData,
+ queryObject: QueryObject,
+): QueryObject {
+ // The formData should be "raw form_data" -- the snake_case version of formData rather than camelCase.
+ if (!isXAxisSet(formData)) {
+ return queryObject;
+ }
+
+ const { columns: _columns, extras: _extras } = queryObject;
+ const mutatedColumns: QueryFormColumn[] = [...(_columns || [])];
+ const axisIdx = _columns?.findIndex(
+ col =>
+ (isPhysicalColumn(col) &&
+ isPhysicalColumn(formData.x_axis) &&
+ col === formData.x_axis) ||
+ (isAdhocColumn(col) &&
+ isAdhocColumn(formData.x_axis) &&
+ col.sqlExpression === formData.x_axis.sqlExpression),
+ );
+ if (
+ axisIdx !== undefined &&
+ axisIdx > -1 &&
+ formData.x_axis &&
+ Array.isArray(_columns)
+ ) {
+ if (isAdhocColumn(_columns[axisIdx])) {
+ mutatedColumns[axisIdx] = {
+ timeGrain: _extras?.time_grain_sqla,
+ columnType: 'BASE_AXIS',
+ ...(_columns[axisIdx] as AdhocColumn),
+ };
+ } else {
+ mutatedColumns[axisIdx] = {
+ timeGrain: _extras?.time_grain_sqla,
+ columnType: 'BASE_AXIS',
+ sqlExpression: formData.x_axis,
+ label: formData.x_axis,
+ expressionType: 'SQL',
+ };
+ }
+
+ const newQueryObject = omit(queryObject, [
+ 'extras.time_grain_sqla',
+ 'is_timeseries',
+ ]);
+ newQueryObject.columns = mutatedColumns;
+
+ return newQueryObject;
+ }
+
+ // fallback, return original queryObject
+ return queryObject;
+}
diff --git a/superset-frontend/packages/superset-ui-core/src/query/processFilters.ts b/superset-frontend/packages/superset-ui-core/src/query/processFilters.ts
index 239f1c49afbe5..8ad1f8b620db4 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/processFilters.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/processFilters.ts
@@ -43,7 +43,6 @@ export default function processFilters(
const { adhoc_filters, extras = {}, filters = [], where } = formData;
const simpleWhere: QueryObjectFilterClause[] = filters;
- const simpleHaving: QueryObjectFilterClause[] = [];
const freeformWhere: string[] = [];
if (where) freeformWhere.push(where);
const freeformHaving: string[] = [];
@@ -54,8 +53,6 @@ export default function processFilters(
const filterClause = convertFilter(filter);
if (clause === 'WHERE') {
simpleWhere.push(filterClause);
- } else {
- simpleHaving.push(filterClause);
}
} else {
const { sqlExpression } = filter;
@@ -69,7 +66,6 @@ export default function processFilters(
// some filter-related fields need to go in `extras`
extras.having = freeformHaving.map(sanitizeClause).join(' AND ');
- extras.having_druid = simpleHaving;
extras.where = freeformWhere.map(sanitizeClause).join(' AND ');
return {
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/AdvancedAnalytics.ts b/superset-frontend/packages/superset-ui-core/src/query/types/AdvancedAnalytics.ts
index 463b07c1dcc5a..ea270e2d12ecc 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/AdvancedAnalytics.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/AdvancedAnalytics.ts
@@ -29,7 +29,7 @@ export interface RollingWindow {
min_periods?: number;
}
-export enum ComparisionType {
+export enum ComparisonType {
Values = 'values',
Difference = 'difference',
Percentage = 'percentage',
@@ -37,7 +37,7 @@ export enum ComparisionType {
}
export interface TimeCompare {
time_compare?: string;
- comparison_type?: ComparisionType;
+ comparison_type?: ComparisonType;
}
export default {};
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/AnnotationLayer.ts b/superset-frontend/packages/superset-ui-core/src/query/types/AnnotationLayer.ts
index 6dfe0cfc78c47..bac743cb25c53 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/AnnotationLayer.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/AnnotationLayer.ts
@@ -125,7 +125,8 @@ export type AnnotationLayer =
| EventAnnotationLayer
| IntervalAnnotationLayer
| FormulaAnnotationLayer
- | TimeseriesAnnotationLayer;
+ | TimeseriesAnnotationLayer
+ | TableAnnotationLayer;
export function isFormulaAnnotationLayer(
layer: AnnotationLayer,
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Column.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Column.ts
index 4e9a13651e4ab..a009515363400 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/Column.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/Column.ts
@@ -19,6 +19,7 @@
*/
import { GenericDataType } from './QueryResponse';
+import { QueryFormColumn } from './QueryFormData';
export interface AdhocColumn {
hasCustomLabel?: boolean;
@@ -26,6 +27,9 @@ export interface AdhocColumn {
optionName?: string;
sqlExpression: string;
expressionType: 'SQL';
+ columnType?: 'BASE_AXIS' | 'SERIES';
+ timeGrain?: string;
+ datasourceWarning?: boolean;
}
/**
@@ -37,8 +41,7 @@ export type PhysicalColumn = string;
* Column information defined in datasource.
*/
export interface Column {
- advanced_data_type?: string;
- id: number;
+ id?: number;
type?: string;
type_generic?: GenericDataType;
column_name: string;
@@ -50,16 +53,29 @@ export interface Column {
expression?: string | null;
database_expression?: string | null;
python_date_format?: string | null;
-}
-export default {};
+ // used for advanced_data_type
+ optionName?: string;
+ filterBy?: string;
+ value?: string;
+ advanced_data_type?: string;
+}
-export function isPhysicalColumn(
- column?: AdhocColumn | PhysicalColumn,
-): column is PhysicalColumn {
+export function isPhysicalColumn(column?: any): column is PhysicalColumn {
return typeof column === 'string';
}
-export function isAdhocColumn(column?: AdhocColumn | PhysicalColumn) {
- return (column as AdhocColumn)?.sqlExpression !== undefined;
+export function isAdhocColumn(column?: any): column is AdhocColumn {
+ return (
+ typeof column !== 'string' &&
+ column?.sqlExpression !== undefined &&
+ column?.label !== undefined &&
+ (column?.expressionType === undefined || column?.expressionType === 'SQL')
+ );
+}
+
+export function isQueryFormColumn(column: any): column is QueryFormColumn {
+ return isPhysicalColumn(column) || isAdhocColumn(column);
}
+
+export default {};
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Dashboard.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Dashboard.ts
index 4089512de4973..b916e27a394e8 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/Dashboard.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/Dashboard.ts
@@ -82,7 +82,6 @@ export type Filter = {
adhoc_filters?: AdhocFilter[];
granularity_sqla?: string;
granularity?: string;
- druid_time_origin?: string;
time_grain_sqla?: string;
time_range?: string;
requiredFirst?: boolean;
@@ -92,6 +91,8 @@ export type Filter = {
description: string;
};
+export type FilterWithDataMask = Filter & { dataMask: DataMaskWithId };
+
export type Divider = Partial> & {
id: string;
title: string;
@@ -105,6 +106,15 @@ export function isNativeFilter(
return filterElement.type === NativeFilterType.NATIVE_FILTER;
}
+export function isNativeFilterWithDataMask(
+ filterElement: Filter | Divider,
+): filterElement is FilterWithDataMask {
+ return (
+ isNativeFilter(filterElement) &&
+ (filterElement as FilterWithDataMask).dataMask?.filterState?.value
+ );
+}
+
export function isFilterDivider(
filterElement: Filter | Divider,
): filterElement is Divider {
@@ -117,10 +127,15 @@ export type Filters = {
[filterId: string]: Filter | Divider;
};
+export type PartialFilters = {
+ [filterId: string]: Partial;
+};
+
export type NativeFiltersState = {
filters: Filters;
filterSets: FilterSets;
focusedFilterId?: string;
+ hoveredFilterId?: string;
};
export type DashboardComponentMetadata = {
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts
index 03916dee5ebb6..9639a000d0151 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts
@@ -21,7 +21,6 @@ import { Metric } from './Metric';
export enum DatasourceType {
Table = 'table',
- Druid = 'druid',
Query = 'query',
Dataset = 'dataset',
SlTable = 'sl_table',
@@ -47,7 +46,7 @@ export interface Datasource {
};
}
-export const DEFAULT_METRICS = [
+export const DEFAULT_METRICS: Metric[] = [
{
metric_name: 'COUNT(*)',
expression: 'COUNT(*)',
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Filter.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Filter.ts
index be420b41a207b..e113a843d468b 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/Filter.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/Filter.ts
@@ -72,6 +72,12 @@ export function isSimpleAdhocFilter(
return filter.expressionType === 'SIMPLE';
}
+export function isFreeFormAdhocFilter(
+ filter: AdhocFilter,
+): filter is FreeFormAdhocFilter {
+ return filter.expressionType === 'SQL';
+}
+
export function isUnaryAdhocFilter(
filter: SimpleAdhocFilter,
): filter is UnaryAdhocFilter {
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts
index 396ccd4c5b5a1..c0f770f9041d4 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts
@@ -17,7 +17,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Maybe } from '../../types';
+import { Maybe, QueryFormMetric } from '../../types';
import { Column } from './Column';
export type Aggregate =
@@ -72,10 +72,24 @@ export interface Metric {
warning_text?: Maybe;
}
-export default {};
+export function isSavedMetric(metric: any): metric is SavedMetric {
+ return typeof metric === 'string';
+}
+
+export function isAdhocMetricSimple(metric: any): metric is AdhocMetricSimple {
+ return typeof metric !== 'string' && metric?.expressionType === 'SIMPLE';
+}
-export function isAdhocMetricSimple(
- metric: AdhocMetric,
-): metric is AdhocMetricSimple {
- return metric.expressionType === 'SIMPLE';
+export function isAdhocMetricSQL(metric: any): metric is AdhocMetricSQL {
+ return typeof metric !== 'string' && metric?.expressionType === 'SQL';
}
+
+export function isQueryFormMetric(metric: any): metric is QueryFormMetric {
+ return (
+ isSavedMetric(metric) ||
+ isAdhocMetricSimple(metric) ||
+ isAdhocMetricSQL(metric)
+ );
+}
+
+export default {};
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Operator.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Operator.ts
index 754766bef262d..10385767614e6 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/Operator.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/Operator.ts
@@ -31,6 +31,7 @@ const BINARY_OPERATORS = [
'ILIKE',
'LIKE',
'REGEX',
+ 'TEMPORAL_RANGE',
] as const;
/** List of operators that require another operand that is a set */
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts b/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts
index 315cdb8456cda..e32eda6a90ac7 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/PostProcessing.ts
@@ -18,7 +18,7 @@
*/
import { JsonObject } from '../../connection';
import { TimeGranularity } from '../../time-format';
-import { RollingType, ComparisionType } from './AdvancedAnalytics';
+import { RollingType, ComparisonType } from './AdvancedAnalytics';
export type NumpyFunction =
| 'average'
@@ -57,7 +57,7 @@ export interface Aggregates {
[colname: string]: {
operator: NumpyFunction;
/**
- * the name of the column to generate aggrates from.
+ * the name of the column to generate aggregates from.
*/
column?: string;
options?: JsonObject;
@@ -111,12 +111,10 @@ interface _PostProcessingPivot {
columns: string[];
combine_value_with_metric?: boolean;
drop_missing_columns?: boolean;
- flatten_columns?: boolean;
index: string[];
marginal_distribution_name?: string;
marginal_distributions?: boolean;
metric_fill_value?: any;
- reset_index?: boolean;
};
}
export type PostProcessingPivot = _PostProcessingPivot | DefaultPostProcessing;
@@ -124,7 +122,7 @@ export type PostProcessingPivot = _PostProcessingPivot | DefaultPostProcessing;
interface _PostProcessingProphet {
operation: 'prophet';
options: {
- time_grain: TimeGranularity;
+ time_grain: TimeGranularity | undefined;
periods: number;
confidence_interval: number;
yearly_seasonality?: boolean | number;
@@ -173,7 +171,7 @@ export interface _PostProcessingCompare {
options: {
source_columns: string[];
compare_columns: string[];
- compare_type: Omit;
+ compare_type: Omit;
drop_original_columns: boolean;
};
}
@@ -184,7 +182,9 @@ export type PostProcessingCompare =
interface _PostProcessingSort {
operation: 'sort';
options: {
- columns: Record;
+ is_sort_index?: boolean;
+ by?: string[] | string;
+ ascending?: boolean[] | boolean;
};
}
export type PostProcessingSort = _PostProcessingSort | DefaultPostProcessing;
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts
index 9354326b1d29e..57377ebb7297c 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts
@@ -32,28 +32,36 @@ import { PostProcessingRule } from './PostProcessing';
import { JsonObject } from '../../connection';
import { TimeGranularity } from '../../time-format';
-export type QueryObjectFilterClause = {
+export type BaseQueryObjectFilterClause = {
col: QueryFormColumn;
grain?: TimeGranularity;
isExtra?: boolean;
-} & (
- | {
- op: BinaryOperator;
- val: string | number | boolean;
- }
- | {
- op: SetOperator;
- val: (string | number | boolean)[];
- }
- | {
- op: UnaryOperator;
- }
-);
+};
+
+export type BinaryQueryObjectFilterClause = BaseQueryObjectFilterClause & {
+ op: BinaryOperator;
+ val: string | number | boolean;
+ formattedVal?: string;
+};
+
+export type SetQueryObjectFilterClause = BaseQueryObjectFilterClause & {
+ op: SetOperator;
+ val: (string | number | boolean)[];
+ formattedVal?: string[];
+};
+
+export type UnaryQueryObjectFilterClause = BaseQueryObjectFilterClause & {
+ op: UnaryOperator;
+ formattedVal?: string;
+};
+
+export type QueryObjectFilterClause =
+ | BinaryQueryObjectFilterClause
+ | SetQueryObjectFilterClause
+ | UnaryQueryObjectFilterClause;
export type QueryObjectExtras = Partial<{
/** HAVING condition for Druid */
- having_druid?: string;
- druid_time_origin?: string;
/** HAVING condition for SQLAlchemy */
having?: string;
relative_start?: string;
@@ -128,12 +136,6 @@ export interface QueryObject
/** The size of bucket by which to group timeseries data (forthcoming) */
time_grain?: string;
- /** Maximum number of timeseries */
- timeseries_limit?: number;
-
- /** The metric used to sort the returned result. */
- timeseries_limit_metric?: Maybe;
-
/** Direction to ordered by */
order_desc?: boolean;
@@ -251,19 +253,40 @@ export const CtasEnum = {
export type QueryColumn = {
name: string;
+ column_name?: string;
type: string | null;
is_dttm: boolean;
};
-export type QueryState =
- | 'stopped'
- | 'failed'
- | 'pending'
- | 'running'
- | 'scheduled'
- | 'success'
- | 'fetching'
- | 'timed_out';
+// Possible states of a query object for processing on the server
+export enum QueryState {
+ STARTED = 'started',
+ STOPPED = 'stopped',
+ FAILED = 'failed',
+ PENDING = 'pending',
+ RUNNING = 'running',
+ SCHEDULED = 'scheduled',
+ SUCCESS = 'success',
+ FETCHING = 'fetching',
+ TIMED_OUT = 'timed_out',
+}
+
+// Inidcates a Query's state is still processing
+export const runningQueryStateList: QueryState[] = [
+ QueryState.RUNNING,
+ QueryState.STARTED,
+ QueryState.PENDING,
+ QueryState.FETCHING,
+ QueryState.SCHEDULED,
+];
+
+// Indicates a Query's state has completed processing regardless of success / failure
+export const concludedQueryStateList: QueryState[] = [
+ QueryState.STOPPED,
+ QueryState.FAILED,
+ QueryState.SUCCESS,
+ QueryState.TIMED_OUT,
+];
export type Query = {
cached: boolean;
@@ -274,6 +297,7 @@ export type Query = {
errorMessage: string | null;
extra: {
progress: string | null;
+ errors?: SupersetError[];
};
id: string;
isDataPreview: boolean;
@@ -305,7 +329,7 @@ export type Query = {
executedSql: string;
output: string | Record;
actions: Record;
- type: DatasourceType.Query;
+ type: DatasourceType;
columns: QueryColumn[];
};
@@ -317,11 +341,13 @@ export type QueryResults = {
expanded_columns: QueryColumn[];
selected_columns: QueryColumn[];
query: { limit: number };
+ query_id?: number;
};
};
export type QueryResponse = Query & QueryResults;
+// todo: move out from typing
export const testQuery: Query = {
id: 'clientId2353',
dbId: 1,
@@ -336,7 +362,7 @@ export const testQuery: Query = {
isDataPreview: false,
progress: 0,
resultsKey: null,
- state: 'success',
+ state: QueryState.SUCCESS,
tempSchema: null,
trackingUrl: null,
templateParams: null,
@@ -360,20 +386,76 @@ export const testQuery: Query = {
columns: [
{
name: 'Column 1',
- type: DatasourceType.Query,
+ type: 'STRING',
is_dttm: false,
},
{
name: 'Column 3',
- type: DatasourceType.Query,
+ type: 'STRING',
is_dttm: false,
},
{
name: 'Column 2',
- type: DatasourceType.Query,
+ type: 'TIMESTAMP',
is_dttm: true,
},
],
};
+export const testQueryResults = {
+ results: {
+ displayLimitReached: false,
+ columns: [
+ {
+ name: 'Column 1',
+ type: 'STRING',
+ is_dttm: false,
+ },
+ {
+ name: 'Column 3',
+ type: 'STRING',
+ is_dttm: false,
+ },
+ {
+ name: 'Column 2',
+ type: 'TIMESTAMP',
+ is_dttm: true,
+ },
+ ],
+ data: [
+ { 'Column 1': 'a', 'Column 2': 'b', 'Column 3': '2014-11-11T00:00:00' },
+ ],
+ expanded_columns: [],
+ selected_columns: [
+ {
+ name: 'Column 1',
+ type: 'STRING',
+ is_dttm: false,
+ },
+ {
+ name: 'Column 3',
+ type: 'STRING',
+ is_dttm: false,
+ },
+ {
+ name: 'Column 2',
+ type: 'TIMESTAMP',
+ is_dttm: true,
+ },
+ ],
+ query: { limit: 6 },
+ },
+};
+
+export const testQueryResponse = { ...testQuery, ...testQueryResults };
+
+export enum ContributionType {
+ Row = 'row',
+ Column = 'column',
+}
+
+export type DatasourceSamplesQuery = {
+ filters?: QueryObjectFilterClause[];
+};
+
export default {};
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/QueryFormData.ts b/superset-frontend/packages/superset-ui-core/src/query/types/QueryFormData.ts
index ac57561d5d2ba..1806f567ec5a4 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/QueryFormData.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/QueryFormData.ts
@@ -122,14 +122,14 @@ export type ExtraFormDataAppend = {
* filter clauses can't be overridden */
export type ExtraFormDataOverrideExtras = Pick<
QueryObjectExtras,
- 'druid_time_origin' | 'relative_start' | 'relative_end' | 'time_grain_sqla'
+ 'relative_start' | 'relative_end' | 'time_grain_sqla'
>;
/** These parameters override those already present in the form data/query object */
export type ExtraFormDataOverrideRegular = Partial<
Pick
> &
- Partial> &
+ Partial> &
Partial> &
Partial>;
@@ -166,13 +166,15 @@ export interface BaseFormData extends TimeRange, FormDataResidual {
extra_form_data?: ExtraFormData;
/** order descending */
order_desc?: boolean;
- /** limit number of time series */
+ /** limit number of time series
+ * deprecated - use series_limit instead */
limit?: number;
/** limit number of row in the results */
row_limit?: string | number | null;
/** row offset for server side pagination */
row_offset?: string | number | null;
- /** The metric used to order timeseries for limiting */
+ /** The metric used to order timeseries for limiting
+ * deprecated - use series_limit_metric instead */
timeseries_limit_metric?: QueryFormMetric;
/** Force refresh */
force?: boolean;
@@ -184,7 +186,7 @@ export interface BaseFormData extends TimeRange, FormDataResidual {
/** limit number of series */
series_columns?: QueryFormColumn[];
series_limit?: number;
- series_limit_metric?: QueryFormColumn;
+ series_limit_metric?: QueryFormMetric;
}
/**
@@ -194,34 +196,16 @@ export interface SqlaFormData extends BaseFormData {
/**
* Name of the Time Column. Time column is optional.
*/
+ granularity?: string;
granularity_sqla?: string;
time_grain_sqla?: TimeGranularity;
having?: string;
}
-/**
- * Form data for Druid datasources.
- */
-export interface DruidFormData extends BaseFormData {
- granularity?: string;
- having_druid?: string;
- druid_time_origin?: string;
-}
-
-export type QueryFormData = DruidFormData | SqlaFormData;
+export type QueryFormData = SqlaFormData;
//---------------------------------------------------
// Type guards
//---------------------------------------------------
-export function isDruidFormData(
- formData: QueryFormData,
-): formData is DruidFormData {
- return 'granularity' in formData;
-}
-
-export function isSavedMetric(metric: QueryFormMetric): metric is SavedMetric {
- return typeof metric === 'string';
-}
-
export default {};
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/QueryResponse.ts b/superset-frontend/packages/superset-ui-core/src/query/types/QueryResponse.ts
index 74d33f6e944b6..e4869805f819d 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/QueryResponse.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/QueryResponse.ts
@@ -83,6 +83,7 @@ export interface ChartDataResponseResult {
export interface TimeseriesChartDataResponseResult
extends ChartDataResponseResult {
data: TimeseriesDataRecord[];
+ label_map: Record;
}
/**
diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Time.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Time.ts
index 820c3f1a3c819..56156166feb76 100644
--- a/superset-frontend/packages/superset-ui-core/src/query/types/Time.ts
+++ b/superset-frontend/packages/superset-ui-core/src/query/types/Time.ts
@@ -30,7 +30,6 @@ export type TimeColumnConfigKey =
| '__time_col'
| '__time_grain'
| '__time_range'
- | '__time_origin'
| '__granularity';
export type AppliedTimeExtras = Partial<
diff --git a/superset-frontend/packages/superset-ui-core/src/style/index.tsx b/superset-frontend/packages/superset-ui-core/src/style/index.tsx
index b20dbc5aa9f5e..ee0b6e10ac419 100644
--- a/superset-frontend/packages/superset-ui-core/src/style/index.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/style/index.tsx
@@ -22,6 +22,7 @@ import createCache from '@emotion/cache';
export {
css,
+ keyframes,
jsx,
ThemeProvider,
CacheProvider as EmotionCacheProvider,
@@ -160,6 +161,7 @@ const defaultTheme = {
},
transitionTiming: 0.3,
gridUnit: 4,
+ brandIconMaxWidth: 37,
};
export type SupersetTheme = typeof defaultTheme;
diff --git a/superset-frontend/packages/superset-ui-core/src/translation/index.ts b/superset-frontend/packages/superset-ui-core/src/translation/index.ts
index 71cb8acbe0ea4..216bf5847595a 100644
--- a/superset-frontend/packages/superset-ui-core/src/translation/index.ts
+++ b/superset-frontend/packages/superset-ui-core/src/translation/index.ts
@@ -22,4 +22,4 @@ export * from './types';
export default {};
-export { default as __hack_reexport_trasnslation } from './types';
+export { default as __hack_reexport_translation } from './types';
diff --git a/superset-frontend/packages/superset-ui-core/src/types/index.ts b/superset-frontend/packages/superset-ui-core/src/types/index.ts
index eaab5c0b49dc7..a1c527afd6f06 100644
--- a/superset-frontend/packages/superset-ui-core/src/types/index.ts
+++ b/superset-frontend/packages/superset-ui-core/src/types/index.ts
@@ -19,3 +19,7 @@
export * from '../query/types';
export type Maybe = T | null;
+
+export type Optional = T | undefined;
+
+export type ValueOf = T[keyof T];
diff --git a/superset-frontend/packages/superset-ui-core/src/ui-overrides/ExtensionsRegistry.ts b/superset-frontend/packages/superset-ui-core/src/ui-overrides/ExtensionsRegistry.ts
new file mode 100644
index 0000000000000..a411c41d08af8
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-core/src/ui-overrides/ExtensionsRegistry.ts
@@ -0,0 +1,97 @@
+/**
+ * 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 { TypedRegistry } from '../models';
+import { makeSingleton } from '../utils';
+
+/**
+ * A function which returns text (or marked-up text)
+ * If what you want is a react component, don't use this. Use React.ComponentType instead.
+ */
+type ReturningDisplayable = (props: P) => string | React.ReactElement;
+
+/**
+ * This type defines all available extensions of Superset's default UI.
+ * Namespace the keys here to follow the form of 'some_domain.functonality.item'.
+ * Take care to name your keys well, as the name describes what this extension point's role is in Superset.
+ *
+ * When defining a new option here, take care to keep any parameters to functions (or components) minimal.
+ * Any removal or alteration to a parameter will be considered a breaking change.
+ */
+
+// from src/views/components/Menu, not imported since this is a separate package
+interface MenuObjectChildProps {
+ label: string;
+ name?: string;
+ icon?: string;
+ index?: number;
+ url?: string;
+ isFrontendRoute?: boolean;
+ perm?: string | boolean;
+ view?: string;
+ disable?: boolean;
+}
+
+export interface SwitchProps {
+ isEditMode: boolean;
+ dbFetched: any;
+ disableSSHTunnelingForEngine?: boolean;
+ useSSHTunneling: boolean;
+ setUseSSHTunneling: React.Dispatch>;
+ setDB: React.Dispatch;
+ isSSHTunneling: boolean;
+}
+
+type ConfigDetailsProps = {
+ embeddedId: string;
+};
+type RightMenuItemIconProps = {
+ menuChild: MenuObjectChildProps;
+};
+
+export type Extensions = Partial<{
+ 'alertsreports.header.icon': React.ComponentType;
+ 'embedded.documentation.configuration_details': React.ComponentType;
+ 'embedded.documentation.description': ReturningDisplayable;
+ 'embedded.documentation.url': string;
+ 'dashboard.nav.right': React.ComponentType;
+ 'navbar.right-menu.item.icon': React.ComponentType;
+ 'navbar.right': React.ComponentType;
+ 'report-modal.dropdown.item.icon': React.ComponentType;
+ 'root.context.provider': React.ComponentType;
+ 'welcome.message': React.ComponentType;
+ 'welcome.banner': React.ComponentType;
+ 'welcome.main.replacement': React.ComponentType;
+ 'ssh_tunnel.form.switch': React.ComponentType;
+}>;
+
+/**
+ * A registry containing extensions which can alter Superset's UI at specific points defined by Superset.
+ * See SIP-87: https://github.com/apache/superset/issues/20615
+ */
+class ExtensionsRegistry extends TypedRegistry {
+ name = 'ExtensionsRegistry';
+}
+
+export const getExtensionsRegistry = makeSingleton(ExtensionsRegistry, {});
+
+// Exporting this under the old name for backwards compatibility.
+// After downstream folks have migrated to `getExtensionsRegistry`, we should remove this.
+export const getUiOverrideRegistry = getExtensionsRegistry;
diff --git a/superset-frontend/packages/superset-ui-core/src/ui-overrides/UiOverrideRegistry.ts b/superset-frontend/packages/superset-ui-core/src/ui-overrides/UiOverrideRegistry.ts
deleted file mode 100644
index fb74ae1ece493..0000000000000
--- a/superset-frontend/packages/superset-ui-core/src/ui-overrides/UiOverrideRegistry.ts
+++ /dev/null
@@ -1,46 +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 { TypedRegistry } from '../models';
-import { makeSingleton } from '../utils';
-
-/** A function (or component) which returns text (or marked-up text) */
-type UiGeneratorText = (props: P) => string | React.ReactElement;
-
-/**
- * This type defines all the UI override options which replace elements of Superset's default UI.
- * Idea with the keys here is generally to namespace following the form of 'domain.functonality.item'
- *
- * When defining a new option here, take care to keep any parameters to functions (or components) minimal.
- * Any removal or alteration to a parameter will be considered a breaking change.
- */
-export type UiOverrides = Partial<{
- 'embedded.documentation.description': UiGeneratorText;
- 'embedded.documentation.url': string;
-}>;
-
-/**
- * A registry containing UI customizations to replace elements of Superset's default UI.
- */
-class UiOverrideRegistry extends TypedRegistry {
- name = 'UiOverrideRegistry';
-}
-
-export const getUiOverrideRegistry = makeSingleton(UiOverrideRegistry, {});
diff --git a/superset-frontend/packages/superset-ui-core/src/ui-overrides/index.tsx b/superset-frontend/packages/superset-ui-core/src/ui-overrides/index.tsx
index d59afc216fb8b..4796ae0fe322d 100644
--- a/superset-frontend/packages/superset-ui-core/src/ui-overrides/index.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/ui-overrides/index.tsx
@@ -17,4 +17,4 @@
* under the License.
*/
-export * from './UiOverrideRegistry';
+export * from './ExtensionsRegistry';
diff --git a/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts b/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts
index d6a1f2097f94f..524d5a6d1c14f 100644
--- a/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts
+++ b/superset-frontend/packages/superset-ui-core/src/utils/featureFlags.ts
@@ -19,42 +19,51 @@
// We can codegen the enum definition based on a list of supported flags that we
// check into source control. We're hardcoding the supported flags for now.
export enum FeatureFlag {
- ALLOW_DASHBOARD_DOMAIN_SHARDING = 'ALLOW_DASHBOARD_DOMAIN_SHARDING',
+ // PLEASE KEEP THE LIST SORTED ALPHABETICALLY
+ ALERTS_ATTACH_REPORTS = 'ALERTS_ATTACH_REPORTS',
ALERT_REPORTS = 'ALERT_REPORTS',
+ ALLOW_DASHBOARD_DOMAIN_SHARDING = 'ALLOW_DASHBOARD_DOMAIN_SHARDING',
+ ALLOW_FULL_CSV_EXPORT = 'ALLOW_FULL_CSV_EXPORT',
CLIENT_CACHE = 'CLIENT_CACHE',
- DYNAMIC_PLUGINS = 'DYNAMIC_PLUGINS',
- ENABLE_ADVANCED_DATA_TYPES = 'ENABLE_ADVANCED_DATA_TYPES',
- SCHEDULED_QUERIES = 'SCHEDULED_QUERIES',
- SQL_VALIDATORS_BY_ENGINE = 'SQL_VALIDATORS_BY_ENGINE',
- ESTIMATE_QUERY_COST = 'ESTIMATE_QUERY_COST',
- SHARE_QUERIES_VIA_KV_STORE = 'SHARE_QUERIES_VIA_KV_STORE',
- SQLLAB_BACKEND_PERSISTENCE = 'SQLLAB_BACKEND_PERSISTENCE',
- THUMBNAILS = 'THUMBNAILS',
- LISTVIEWS_DEFAULT_CARD_VIEW = 'LISTVIEWS_DEFAULT_CARD_VIEW',
- DISABLE_LEGACY_DATASOURCE_EDITOR = 'DISABLE_LEGACY_DATASOURCE_EDITOR',
- DISABLE_DATASET_SOURCE_EDIT = 'DISABLE_DATASET_SOURCE_EDIT',
- DISPLAY_MARKDOWN_HTML = 'DISPLAY_MARKDOWN_HTML',
- ESCAPE_MARKDOWN_HTML = 'ESCAPE_MARKDOWN_HTML',
- DASHBOARD_NATIVE_FILTERS = 'DASHBOARD_NATIVE_FILTERS',
DASHBOARD_CROSS_FILTERS = 'DASHBOARD_CROSS_FILTERS',
- DASHBOARD_NATIVE_FILTERS_SET = 'DASHBOARD_NATIVE_FILTERS_SET',
+ DASHBOARD_EDIT_CHART_IN_NEW_TAB = 'DASHBOARD_EDIT_CHART_IN_NEW_TAB',
DASHBOARD_FILTERS_EXPERIMENTAL = 'DASHBOARD_FILTERS_EXPERIMENTAL',
+ CONFIRM_DASHBOARD_DIFF = 'CONFIRM_DASHBOARD_DIFF',
+ DASHBOARD_NATIVE_FILTERS = 'DASHBOARD_NATIVE_FILTERS',
+ DASHBOARD_NATIVE_FILTERS_SET = 'DASHBOARD_NATIVE_FILTERS_SET',
+ DASHBOARD_VIRTUALIZATION = 'DASHBOARD_VIRTUALIZATION',
+ DASHBOARD_RBAC = 'DASHBOARD_RBAC',
+ DATAPANEL_CLOSED_BY_DEFAULT = 'DATAPANEL_CLOSED_BY_DEFAULT',
+ DISABLE_DATASET_SOURCE_EDIT = 'DISABLE_DATASET_SOURCE_EDIT',
+ DISABLE_LEGACY_DATASOURCE_EDITOR = 'DISABLE_LEGACY_DATASOURCE_EDITOR',
+ DISPLAY_MARKDOWN_HTML = 'DISPLAY_MARKDOWN_HTML',
+ DRILL_TO_DETAIL = 'DRILL_TO_DETAIL',
+ DYNAMIC_PLUGINS = 'DYNAMIC_PLUGINS',
+ EMBEDDABLE_CHARTS = 'EMBEDDABLE_CHARTS',
EMBEDDED_SUPERSET = 'EMBEDDED_SUPERSET',
+ ENABLE_ADVANCED_DATA_TYPES = 'ENABLE_ADVANCED_DATA_TYPES',
+ ENABLE_DND_WITH_CLICK_UX = 'ENABLE_DND_WITH_CLICK_UX',
+ ENABLE_EXPLORE_DRAG_AND_DROP = 'ENABLE_EXPLORE_DRAG_AND_DROP',
ENABLE_FILTER_BOX_MIGRATION = 'ENABLE_FILTER_BOX_MIGRATION',
- VERSIONED_EXPORT = 'VERSIONED_EXPORT',
- GLOBAL_ASYNC_QUERIES = 'GLOBAL_ASYNC_QUERIES',
+ ENABLE_JAVASCRIPT_CONTROLS = 'ENABLE_JAVASCRIPT_CONTROLS',
ENABLE_TEMPLATE_PROCESSING = 'ENABLE_TEMPLATE_PROCESSING',
- ENABLE_EXPLORE_DRAG_AND_DROP = 'ENABLE_EXPLORE_DRAG_AND_DROP',
- ENABLE_DND_WITH_CLICK_UX = 'ENABLE_DND_WITH_CLICK_UX',
- FORCE_DATABASE_CONNECTIONS_SSL = 'FORCE_DATABASE_CONNECTIONS_SSL',
ENABLE_TEMPLATE_REMOVE_FILTERS = 'ENABLE_TEMPLATE_REMOVE_FILTERS',
- ENABLE_JAVASCRIPT_CONTROLS = 'ENABLE_JAVASCRIPT_CONTROLS',
- DASHBOARD_RBAC = 'DASHBOARD_RBAC',
- ALERTS_ATTACH_REPORTS = 'ALERTS_ATTACH_REPORTS',
- ALLOW_FULL_CSV_EXPORT = 'ALLOW_FULL_CSV_EXPORT',
- UX_BETA = 'UX_BETA',
+ ESCAPE_MARKDOWN_HTML = 'ESCAPE_MARKDOWN_HTML',
+ ESTIMATE_QUERY_COST = 'ESTIMATE_QUERY_COST',
+ FORCE_DATABASE_CONNECTIONS_SSL = 'FORCE_DATABASE_CONNECTIONS_SSL',
GENERIC_CHART_AXES = 'GENERIC_CHART_AXES',
+ GLOBAL_ASYNC_QUERIES = 'GLOBAL_ASYNC_QUERIES',
+ HORIZONTAL_FILTER_BAR = 'HORIZONTAL_FILTER_BAR',
+ LISTVIEWS_DEFAULT_CARD_VIEW = 'LISTVIEWS_DEFAULT_CARD_VIEW',
+ SCHEDULED_QUERIES = 'SCHEDULED_QUERIES',
+ SHARE_QUERIES_VIA_KV_STORE = 'SHARE_QUERIES_VIA_KV_STORE',
+ SQLLAB_BACKEND_PERSISTENCE = 'SQLLAB_BACKEND_PERSISTENCE',
+ SQL_VALIDATORS_BY_ENGINE = 'SQL_VALIDATORS_BY_ENGINE',
+ THUMBNAILS = 'THUMBNAILS',
USE_ANALAGOUS_COLORS = 'USE_ANALAGOUS_COLORS',
+ UX_BETA = 'UX_BETA',
+ VERSIONED_EXPORT = 'VERSIONED_EXPORT',
+ SSH_TUNNELING = 'SSH_TUNNELING',
}
export type ScheduleQueriesProps = {
JSONSCHEMA: {
@@ -80,6 +89,11 @@ declare global {
}
}
-export function isFeatureEnabled(feature: FeatureFlag) {
- return window && window.featureFlags && !!window.featureFlags[feature];
+export function isFeatureEnabled(feature: FeatureFlag): boolean {
+ try {
+ return !!window.featureFlags[feature];
+ } catch (error) {
+ console.error(`Failed to query feature flag ${feature}`);
+ }
+ return false;
}
diff --git a/superset-frontend/packages/superset-ui-core/src/utils/index.ts b/superset-frontend/packages/superset-ui-core/src/utils/index.ts
index 1c43663e28ef9..19c5ed586145a 100644
--- a/superset-frontend/packages/superset-ui-core/src/utils/index.ts
+++ b/superset-frontend/packages/superset-ui-core/src/utils/index.ts
@@ -26,6 +26,7 @@ export { default as makeSingleton } from './makeSingleton';
export { default as promiseTimeout } from './promiseTimeout';
export { default as logging } from './logging';
export { default as removeDuplicates } from './removeDuplicates';
+export { lruCache } from './lruCache';
export * from './featureFlags';
export * from './random';
export * from './typedMemo';
diff --git a/superset-frontend/packages/superset-ui-core/src/utils/isDefined.ts b/superset-frontend/packages/superset-ui-core/src/utils/isDefined.ts
index 097115e11c1c2..0cdba14eb6fd3 100644
--- a/superset-frontend/packages/superset-ui-core/src/utils/isDefined.ts
+++ b/superset-frontend/packages/superset-ui-core/src/utils/isDefined.ts
@@ -17,6 +17,6 @@
* under the License.
*/
-export default function isDefined(x: unknown) {
+export default function isDefined(x: T): x is NonNullable {
return x !== null && x !== undefined;
}
diff --git a/superset-frontend/packages/superset-ui-core/src/utils/lruCache.ts b/superset-frontend/packages/superset-ui-core/src/utils/lruCache.ts
new file mode 100644
index 0000000000000..f6785850c22af
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-core/src/utils/lruCache.ts
@@ -0,0 +1,74 @@
+/**
+ * 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.
+ */
+
+class LRUCache {
+ private cache: Map;
+
+ readonly capacity: number;
+
+ constructor(capacity: number) {
+ if (capacity < 1) {
+ throw new Error('The capacity in LRU must be greater than 0.');
+ }
+ this.capacity = capacity;
+ this.cache = new Map();
+ }
+
+ public has(key: string): boolean {
+ return this.cache.has(key);
+ }
+
+ public get(key: string): T | undefined {
+ // Prevent runtime errors
+ if (typeof key !== 'string') {
+ throw new TypeError('The LRUCache key must be string.');
+ }
+
+ if (this.cache.has(key)) {
+ const tmp = this.cache.get(key) as T;
+ this.cache.delete(key);
+ this.cache.set(key, tmp);
+ return tmp;
+ }
+ return undefined;
+ }
+
+ public set(key: string, value: T) {
+ // Prevent runtime errors
+ if (typeof key !== 'string') {
+ throw new TypeError('The LRUCache key must be string.');
+ }
+ if (this.cache.size >= this.capacity) {
+ this.cache.delete(this.cache.keys().next().value);
+ }
+ this.cache.set(key, value);
+ }
+
+ public clear() {
+ this.cache.clear();
+ }
+
+ public get size() {
+ return this.cache.size;
+ }
+}
+
+export function lruCache(capacity = 100) {
+ return new LRUCache(capacity);
+}
diff --git a/superset-frontend/packages/superset-ui-core/test/__mocks__/resize-observer-polyfill.ts b/superset-frontend/packages/superset-ui-core/test/__mocks__/resize-observer-polyfill.ts
index 238e14ab23360..719ad1119f1f4 100644
--- a/superset-frontend/packages/superset-ui-core/test/__mocks__/resize-observer-polyfill.ts
+++ b/superset-frontend/packages/superset-ui-core/test/__mocks__/resize-observer-polyfill.ts
@@ -37,6 +37,11 @@ export default function ResizeObserver(callback: ObserveCallback) {
allCallbacks.push(callback);
}
},
+ unobserve() {
+ if (callback) {
+ allCallbacks.splice(allCallbacks.indexOf(callback), 1);
+ }
+ },
};
}
diff --git a/superset-frontend/packages/superset-ui-core/test/chart/fixtures/formData.ts b/superset-frontend/packages/superset-ui-core/test/chart/fixtures/formData.ts
index 25d27f5e2d0c6..9d926f46131bd 100644
--- a/superset-frontend/packages/superset-ui-core/test/chart/fixtures/formData.ts
+++ b/superset-frontend/packages/superset-ui-core/test/chart/fixtures/formData.ts
@@ -19,13 +19,14 @@
/* eslint sort-keys: 'off' */
/** The form data defined here is based on default visualizations packaged with Apache Superset */
+import { TimeGranularity } from '@superset-ui/core';
export const bigNumberFormData = {
datasource: '3__table',
viz_type: 'big_number',
slice_id: 54,
granularity_sqla: 'ds',
- time_grain_sqla: 'P1D',
+ time_grain_sqla: TimeGranularity.DAY,
time_range: '100 years ago : now',
metric: 'sum__num',
adhoc_filters: [],
diff --git a/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts b/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts
index 91a8f4a3185a7..9e83aaba9a871 100644
--- a/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts
@@ -140,7 +140,7 @@ describe('CategoricalColorScale', () => {
expect(scale2.getColorMap()).toEqual({
cow: 'black',
pig: 'pink',
- horse: 'blue',
+ horse: 'green',
});
});
});
diff --git a/superset-frontend/packages/superset-ui-core/test/color/ColorSchemeRegistry.test.ts b/superset-frontend/packages/superset-ui-core/test/color/ColorSchemeRegistry.test.ts
index 4629828474904..13aa49922f452 100644
--- a/superset-frontend/packages/superset-ui-core/test/color/ColorSchemeRegistry.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/color/ColorSchemeRegistry.test.ts
@@ -18,10 +18,26 @@
*/
import ColorSchemeRegistry from '../../src/color/ColorSchemeRegistry';
+import schemes from '../../src/color/colorSchemes/categorical/d3';
+import CategoricalScheme from '../../src/color/CategoricalScheme';
describe('ColorSchemeRegistry', () => {
it('exists', () => {
expect(ColorSchemeRegistry).toBeDefined();
expect(ColorSchemeRegistry).toBeInstanceOf(Function);
});
+ it('returns undefined', () => {
+ const registry = new ColorSchemeRegistry();
+ expect(registry.get('something')).toBeUndefined();
+ });
+ it('returns default', () => {
+ const registry = new ColorSchemeRegistry();
+ registry.registerValue('SUPERSET_DEFAULT', schemes[0]);
+ expect(registry.get('something')).toBeInstanceOf(CategoricalScheme);
+ });
+ it('returns undefined in strict mode', () => {
+ const registry = new ColorSchemeRegistry();
+ registry.registerValue('SUPERSET_DEFAULT', schemes[0]);
+ expect(registry.get('something', true)).toBeUndefined();
+ });
});
diff --git a/superset-frontend/packages/superset-ui-core/test/color/SharedLabelColorSingleton.test.ts b/superset-frontend/packages/superset-ui-core/test/color/SharedLabelColorSingleton.test.ts
index 86d3ba9c409e1..88610874dbd74 100644
--- a/superset-frontend/packages/superset-ui-core/test/color/SharedLabelColorSingleton.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/color/SharedLabelColorSingleton.test.ts
@@ -23,6 +23,7 @@ import {
getCategoricalSchemeRegistry,
getSharedLabelColor,
SharedLabelColor,
+ SharedLabelColorSource,
} from '@superset-ui/core';
import { getAnalogousColors } from '../../src/color/utils';
@@ -52,6 +53,7 @@ describe('SharedLabelColor', () => {
});
beforeEach(() => {
+ getSharedLabelColor().source = SharedLabelColorSource.dashboard;
getSharedLabelColor().clear();
});
@@ -60,18 +62,48 @@ describe('SharedLabelColor', () => {
});
describe('.addSlice(value, color, sliceId)', () => {
- it('should add to valueSliceMap when first adding label', () => {
+ it('should add to sliceLabelColorMap when first adding label', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
- expect(sharedLabelColor.sliceLabelColorMap).toHaveProperty('1', {
- a: 'red',
- });
+ expect(sharedLabelColor.sliceLabelMap.has(1)).toEqual(true);
+ const labels = sharedLabelColor.sliceLabelMap.get(1);
+ expect(labels?.includes('a')).toEqual(true);
+ const colorMap = sharedLabelColor.getColorMap();
+ expect(Object.fromEntries(colorMap)).toEqual({ a: 'red' });
+ });
+
+ it('should add to sliceLabelColorMap when slice exist', () => {
+ const sharedLabelColor = getSharedLabelColor();
+ sharedLabelColor.addSlice('a', 'red', 1);
+ sharedLabelColor.addSlice('b', 'blue', 1);
+ const labels = sharedLabelColor.sliceLabelMap.get(1);
+ expect(labels?.includes('b')).toEqual(true);
+ const colorMap = sharedLabelColor.getColorMap();
+ expect(Object.fromEntries(colorMap)).toEqual({ a: 'red', b: 'blue' });
+ });
+
+ it('should use last color if adding label repeatedly', () => {
+ const sharedLabelColor = getSharedLabelColor();
+ sharedLabelColor.addSlice('b', 'blue', 1);
+ sharedLabelColor.addSlice('b', 'green', 1);
+ const labels = sharedLabelColor.sliceLabelMap.get(1);
+ expect(labels?.includes('b')).toEqual(true);
+ expect(labels?.length).toEqual(1);
+ const colorMap = sharedLabelColor.getColorMap();
+ expect(Object.fromEntries(colorMap)).toEqual({ b: 'green' });
+ });
+
+ it('should do nothing when source is not dashboard', () => {
+ const sharedLabelColor = getSharedLabelColor();
+ sharedLabelColor.source = SharedLabelColorSource.explore;
+ sharedLabelColor.addSlice('a', 'red');
+ expect(Object.fromEntries(sharedLabelColor.sliceLabelMap)).toEqual({});
});
it('should do nothing when sliceId is undefined', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red');
- expect(sharedLabelColor.sliceLabelColorMap).toEqual({});
+ expect(Object.fromEntries(sharedLabelColor.sliceLabelMap)).toEqual({});
});
});
@@ -80,55 +112,92 @@ describe('SharedLabelColor', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
sharedLabelColor.removeSlice(1);
- expect(sharedLabelColor.sliceLabelColorMap).toEqual({});
+ expect(sharedLabelColor.sliceLabelMap.has(1)).toEqual(false);
});
- });
- describe('.getColorMap(namespace, scheme, updateColorScheme)', () => {
- it('should be undefined when scheme is undefined', () => {
+ it('should update colorMap', () => {
const sharedLabelColor = getSharedLabelColor();
+ sharedLabelColor.addSlice('a', 'red', 1);
+ sharedLabelColor.addSlice('b', 'blue', 2);
+ sharedLabelColor.removeSlice(1);
const colorMap = sharedLabelColor.getColorMap();
- expect(colorMap).toBeUndefined();
+ expect(Object.fromEntries(colorMap)).toEqual({ b: 'blue' });
});
- it('should update color value if passing updateColorScheme', () => {
+ it('should do nothing when source is not dashboard', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
- sharedLabelColor.addSlice('b', 'blue', 2);
- const colorMap = sharedLabelColor.getColorMap('', 'testColors2', true);
- expect(colorMap).toEqual({ a: 'yellow', b: 'yellow' });
+ sharedLabelColor.source = SharedLabelColorSource.explore;
+ sharedLabelColor.removeSlice(1);
+ expect(sharedLabelColor.sliceLabelMap.has(1)).toEqual(true);
});
+ });
- it('should get origin color value if not pass updateColorScheme', () => {
+ describe('.updateColorMap(namespace, scheme)', () => {
+ it('should update color map', () => {
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
- sharedLabelColor.addSlice('b', 'blue', 2);
- const colorMap = sharedLabelColor.getColorMap('', 'testColors');
- expect(colorMap).toEqual({ a: 'red', b: 'blue' });
+ sharedLabelColor.addSlice('b', 'pink', 1);
+ sharedLabelColor.addSlice('b', 'green', 2);
+ sharedLabelColor.addSlice('c', 'blue', 2);
+ sharedLabelColor.updateColorMap('', 'testColors2');
+ const colorMap = sharedLabelColor.getColorMap();
+ expect(Object.fromEntries(colorMap)).toEqual({
+ a: 'yellow',
+ b: 'yellow',
+ c: 'green',
+ });
});
- it('should use recycle colors if shared label exit', () => {
+ it('should use recycle colors', () => {
window.featureFlags = {
[FeatureFlag.USE_ANALAGOUS_COLORS]: false,
};
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
- sharedLabelColor.addSlice('a', 'blue', 2);
- const colorMap = sharedLabelColor.getColorMap('', 'testColors');
- expect(colorMap).not.toEqual({});
+ sharedLabelColor.addSlice('b', 'blue', 2);
+ sharedLabelColor.addSlice('c', 'green', 3);
+ sharedLabelColor.addSlice('d', 'red', 4);
+ sharedLabelColor.updateColorMap('', 'testColors');
+ const colorMap = sharedLabelColor.getColorMap();
+ expect(Object.fromEntries(colorMap)).not.toEqual({});
expect(getAnalogousColors).not.toBeCalled();
});
- it('should use analagous colors if shared label exit', () => {
+ it('should use analagous colors', () => {
window.featureFlags = {
[FeatureFlag.USE_ANALAGOUS_COLORS]: true,
};
const sharedLabelColor = getSharedLabelColor();
sharedLabelColor.addSlice('a', 'red', 1);
- sharedLabelColor.addSlice('a', 'blue', 2);
- const colorMap = sharedLabelColor.getColorMap('', 'testColors');
- expect(colorMap).not.toEqual({});
+ sharedLabelColor.addSlice('b', 'blue', 1);
+ sharedLabelColor.addSlice('c', 'green', 1);
+ sharedLabelColor.addSlice('d', 'red', 1);
+ sharedLabelColor.updateColorMap('', 'testColors');
+ const colorMap = sharedLabelColor.getColorMap();
+ expect(Object.fromEntries(colorMap)).not.toEqual({});
expect(getAnalogousColors).toBeCalled();
});
});
+
+ describe('.getColorMap()', () => {
+ it('should get color map', () => {
+ const sharedLabelColor = getSharedLabelColor();
+ sharedLabelColor.addSlice('a', 'red', 1);
+ sharedLabelColor.addSlice('b', 'blue', 2);
+ const colorMap = sharedLabelColor.getColorMap();
+ expect(Object.fromEntries(colorMap)).toEqual({ a: 'red', b: 'blue' });
+ });
+ });
+
+ describe('.reset()', () => {
+ it('should reset color map', () => {
+ const sharedLabelColor = getSharedLabelColor();
+ sharedLabelColor.addSlice('a', 'red', 1);
+ sharedLabelColor.addSlice('b', 'blue', 2);
+ sharedLabelColor.reset();
+ const colorMap = sharedLabelColor.getColorMap();
+ expect(Object.fromEntries(colorMap)).toEqual({ a: '', b: '' });
+ });
+ });
});
diff --git a/superset-frontend/packages/superset-ui-core/test/connection/SupersetClient.test.ts b/superset-frontend/packages/superset-ui-core/test/connection/SupersetClient.test.ts
index 17a07f3c727e6..caba59f563722 100644
--- a/superset-frontend/packages/superset-ui-core/test/connection/SupersetClient.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/connection/SupersetClient.test.ts
@@ -38,6 +38,7 @@ describe('SupersetClient', () => {
expect(typeof SupersetClient.postForm).toBe('function');
expect(typeof SupersetClient.isAuthenticated).toBe('function');
expect(typeof SupersetClient.reAuthenticate).toBe('function');
+ expect(typeof SupersetClient.getGuestToken).toBe('function');
expect(typeof SupersetClient.request).toBe('function');
expect(typeof SupersetClient.reset).toBe('function');
});
@@ -55,7 +56,7 @@ describe('SupersetClient', () => {
// this also tests that the ^above doesn't throw if configure is called appropriately
it('calls appropriate SupersetClient methods when configured', async () => {
- expect.assertions(15);
+ expect.assertions(16);
const mockGetUrl = '/mock/get/url';
const mockPostUrl = '/mock/post/url';
const mockRequestUrl = '/mock/request/url';
@@ -82,6 +83,10 @@ describe('SupersetClient', () => {
);
const csrfSpy = jest.spyOn(SupersetClientClass.prototype, 'getCSRFToken');
const requestSpy = jest.spyOn(SupersetClientClass.prototype, 'request');
+ const getGuestTokenSpy = jest.spyOn(
+ SupersetClientClass.prototype,
+ 'getGuestToken',
+ );
SupersetClient.configure({});
await SupersetClient.init();
@@ -114,6 +119,9 @@ describe('SupersetClient', () => {
SupersetClient.isAuthenticated();
await SupersetClient.reAuthenticate();
+ SupersetClient.getGuestToken();
+ expect(getGuestTokenSpy).toHaveBeenCalledTimes(1);
+
expect(initSpy).toHaveBeenCalledTimes(2);
expect(deleteSpy).toHaveBeenCalledTimes(1);
expect(putSpy).toHaveBeenCalledTimes(1);
diff --git a/superset-frontend/packages/superset-ui-core/test/connection/SupersetClientClass.test.ts b/superset-frontend/packages/superset-ui-core/test/connection/SupersetClientClass.test.ts
index 4db26b05b4151..56ab3f1baea07 100644
--- a/superset-frontend/packages/superset-ui-core/test/connection/SupersetClientClass.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/connection/SupersetClientClass.test.ts
@@ -329,7 +329,7 @@ describe('SupersetClientClass', () => {
});
it('uses a guest token when provided', async () => {
- expect.assertions(1);
+ expect.assertions(2);
const client = new SupersetClientClass({
protocol,
@@ -337,6 +337,7 @@ describe('SupersetClientClass', () => {
guestToken: 'abc123',
guestTokenHeaderName: 'guestTokenHeader',
});
+ expect(client.getGuestToken()).toBe('abc123');
await client.init();
await client.get({ url: mockGetUrl });
diff --git a/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApi.test.ts b/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApi.test.ts
index 81467ce2393a9..81c8e2d150522 100644
--- a/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApi.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApi.test.ts
@@ -23,6 +23,12 @@ import callApi from '../../../src/connection/callApi/callApi';
import { LOGIN_GLOB } from '../fixtures/constants';
+// missing the toString function causing method to error out when casting to String
+class BadObject {}
+const corruptObject = new BadObject();
+/* @ts-expect-error */
+BadObject.prototype.toString = undefined;
+
describe('callApi()', () => {
beforeAll(() => {
fetchMock.get(LOGIN_GLOB, { result: '1234' });
@@ -178,6 +184,44 @@ describe('callApi()', () => {
expect(jsonRequestBody[key]).toEqual(value);
});
});
+
+ it('removes corrupt value when building formData with stringify = false', async () => {
+ /*
+ There has been a case when 'stringify' is false an object value on one of the
+ attributes was missing a toString function making the cast to String() fail
+ and causing entire method call to fail. The new logic skips corrupt values that fail cast to String()
+ and allows all valid attributes to be added as key / value pairs to the formData
+ instance. This test case replicates a corrupt object missing the .toString method
+ representing a real bug report.
+ */
+ const postPayload = {
+ string: 'value',
+ number: 1237,
+ array: [1, 2, 3],
+ object: { a: 'a', 1: 1 },
+ null: null,
+ emptyString: '',
+ // corruptObject has no toString method and will fail cast to String()
+ corrupt: [corruptObject],
+ };
+ jest.spyOn(console, 'error').mockImplementation();
+
+ await callApi({
+ url: mockPostUrl,
+ method: 'POST',
+ postPayload,
+ stringify: false,
+ });
+
+ const calls = fetchMock.calls(mockPostUrl);
+ expect(calls).toHaveLength(1);
+ const unstringified = (calls[0][1] as RequestInit).body as FormData;
+ const hasCorruptKey = unstringified.has('corrupt');
+ expect(hasCorruptKey).toBeFalsy();
+ // When a corrupt attribute is encountred, a console.error call is made with info about the corrupt attribute
+ // eslint-disable-next-line no-console
+ expect(console.error).toHaveBeenCalledTimes(1);
+ });
});
describe('PUT requests', () => {
diff --git a/superset-frontend/packages/superset-ui-core/test/connection/callApi/parseResponse.test.ts b/superset-frontend/packages/superset-ui-core/test/connection/callApi/parseResponse.test.ts
index d54be27e9c8f1..e13964ecf7300 100644
--- a/superset-frontend/packages/superset-ui-core/test/connection/callApi/parseResponse.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/connection/callApi/parseResponse.test.ts
@@ -137,6 +137,38 @@ describe('parseResponse()', () => {
expect(responseRaw.bodyUsed).toBe(false);
});
+ it('resolves to big number value if `parseMethod=json-bigint`', async () => {
+ const mockBigIntUrl = '/mock/get/bigInt';
+ const mockGetBigIntPayload =
+ '{ "value": 9223372036854775807, "minus": { "value": -483729382918228373892, "str": "something" }, "number": 1234, "floatValue": { "plus": 0.3452211361231223, "minus": -0.3452211361231223 } }';
+ fetchMock.get(mockBigIntUrl, mockGetBigIntPayload);
+ const responseBigNumber = await parseResponse(
+ callApi({ url: mockBigIntUrl, method: 'GET' }),
+ 'json-bigint',
+ );
+ expect(`${responseBigNumber.json.value}`).toEqual('9223372036854775807');
+ expect(`${responseBigNumber.json.minus.value}`).toEqual(
+ '-483729382918228373892',
+ );
+ expect(responseBigNumber.json.number).toEqual(1234);
+ expect(responseBigNumber.json.floatValue.plus).toEqual(0.3452211361231223);
+ expect(responseBigNumber.json.floatValue.minus).toEqual(
+ -0.3452211361231223,
+ );
+ expect(
+ responseBigNumber.json.floatValue.plus +
+ responseBigNumber.json.floatValue.minus,
+ ).toEqual(0);
+ expect(
+ responseBigNumber.json.floatValue.plus /
+ responseBigNumber.json.floatValue.minus,
+ ).toEqual(-1);
+ expect(Math.min(responseBigNumber.json.floatValue.plus, 0)).toEqual(0);
+ expect(Math.abs(responseBigNumber.json.floatValue.minus)).toEqual(
+ responseBigNumber.json.floatValue.plus,
+ );
+ });
+
it('rejects if request.ok=false', async () => {
expect.assertions(3);
const mockNotOkayUrl = '/mock/notokay/url';
diff --git a/superset-frontend/packages/superset-ui-core/test/models/Registry.test.ts b/superset-frontend/packages/superset-ui-core/test/models/Registry.test.ts
index 1c85dc242c90c..11c2e65eb51f4 100644
--- a/superset-frontend/packages/superset-ui-core/test/models/Registry.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/models/Registry.test.ts
@@ -404,7 +404,7 @@ describe('Registry', () => {
expect(listener).toBeCalledWith(['foo']);
});
- it('calls the listener when a value is overriden', () => {
+ it('calls the listener when a value is overridden', () => {
registry.registerValue('foo', 'bar');
listener.mockClear();
registry.registerValue('foo', 'baz');
diff --git a/superset-frontend/packages/superset-ui-core/test/number-format/factories/createD3NumberFormatter.test.ts b/superset-frontend/packages/superset-ui-core/test/number-format/factories/createD3NumberFormatter.test.ts
index c80c72c30868c..951ab039c5a9a 100644
--- a/superset-frontend/packages/superset-ui-core/test/number-format/factories/createD3NumberFormatter.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/number-format/factories/createD3NumberFormatter.test.ts
@@ -62,7 +62,7 @@ describe('createD3NumberFormatter(config)', () => {
});
});
describe('config.description', () => {
- it('set decription if specified', () => {
+ it('set description if specified', () => {
const formatter = createD3NumberFormatter({
description: 'lorem ipsum',
formatString: '.2f',
diff --git a/superset-frontend/packages/superset-ui-core/test/query/DatasourceKey.test.ts b/superset-frontend/packages/superset-ui-core/test/query/DatasourceKey.test.ts
index 6b1d62e6aa135..4a3c8772c372d 100644
--- a/superset-frontend/packages/superset-ui-core/test/query/DatasourceKey.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/query/DatasourceKey.test.ts
@@ -19,18 +19,15 @@
import { DatasourceKey } from '@superset-ui/core';
describe('DatasourceKey', () => {
- const tableKey = '5__table';
- const druidKey = '5__druid';
-
it('should handle table data sources', () => {
- const datasourceKey = new DatasourceKey(tableKey);
- expect(datasourceKey.toString()).toBe(tableKey);
+ const datasourceKey = new DatasourceKey('5__table');
+ expect(datasourceKey.toString()).toBe('5__table');
expect(datasourceKey.toObject()).toEqual({ id: 5, type: 'table' });
});
- it('should handle druid data sources', () => {
- const datasourceKey = new DatasourceKey(druidKey);
- expect(datasourceKey.toString()).toBe(druidKey);
- expect(datasourceKey.toObject()).toEqual({ id: 5, type: 'druid' });
+ it('should handle query data sources', () => {
+ const datasourceKey = new DatasourceKey('5__query');
+ expect(datasourceKey.toString()).toBe('5__query');
+ expect(datasourceKey.toObject()).toEqual({ id: 5, type: 'query' });
});
});
diff --git a/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts b/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts
index 366feeff7a2e1..9d47361e8fdd4 100644
--- a/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts
@@ -17,6 +17,8 @@
* under the License.
*/
import { buildQueryContext } from '@superset-ui/core';
+import * as queryModule from '../../src/query/normalizeTimeColumn';
+import * as getXAxisModule from '../../src/query/getXAxis';
describe('buildQueryContext', () => {
it('should build datasource for table sources and apply defaults', () => {
@@ -31,17 +33,6 @@ describe('buildQueryContext', () => {
expect(queryContext.result_format).toBe('json');
expect(queryContext.result_type).toBe('full');
});
- it('should build datasource for druid sources and set force to true', () => {
- const queryContext = buildQueryContext({
- datasource: '5__druid',
- granularity: 'ds',
- viz_type: 'table',
- force: true,
- });
- expect(queryContext.datasource.id).toBe(5);
- expect(queryContext.datasource.type).toBe('druid');
- expect(queryContext.force).toBe(true);
- });
it('should build datasource for table sources with columns', () => {
const queryContext = buildQueryContext(
{
@@ -108,6 +99,7 @@ describe('buildQueryContext', () => {
]),
);
});
+ // todo(Yongjie): move these test case into buildQueryObject.test.ts
it('should remove undefined value in post_processing', () => {
const queryContext = buildQueryContext(
{
@@ -133,4 +125,43 @@ describe('buildQueryContext', () => {
},
]);
});
+ it('should call normalizeTimeColumn if GENERIC_CHART_AXES is enabled and has x_axis', () => {
+ Object.defineProperty(getXAxisModule, 'hasGenericChartAxes', {
+ value: true,
+ });
+ const spyNormalizeTimeColumn = jest.spyOn(
+ queryModule,
+ 'normalizeTimeColumn',
+ );
+
+ buildQueryContext(
+ {
+ datasource: '5__table',
+ viz_type: 'table',
+ x_axis: 'axis',
+ },
+ () => [{}],
+ );
+ expect(spyNormalizeTimeColumn).toBeCalled();
+ spyNormalizeTimeColumn.mockRestore();
+ });
+ it("shouldn't call normalizeTimeColumn if GENERIC_CHART_AXES is disabled", () => {
+ Object.defineProperty(getXAxisModule, 'hasGenericChartAxes', {
+ value: false,
+ });
+ const spyNormalizeTimeColumn = jest.spyOn(
+ queryModule,
+ 'normalizeTimeColumn',
+ );
+
+ buildQueryContext(
+ {
+ datasource: '5__table',
+ viz_type: 'table',
+ },
+ () => [{}],
+ );
+ expect(spyNormalizeTimeColumn).not.toBeCalled();
+ spyNormalizeTimeColumn.mockRestore();
+ });
});
diff --git a/superset-frontend/packages/superset-ui-core/test/query/buildQueryObject.test.ts b/superset-frontend/packages/superset-ui-core/test/query/buildQueryObject.test.ts
index 321e2a8401776..cdabcff57e0b8 100644
--- a/superset-frontend/packages/superset-ui-core/test/query/buildQueryObject.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/query/buildQueryObject.test.ts
@@ -119,15 +119,26 @@ describe('buildQueryObject', () => {
expect(query.metrics).toEqual(['sum__num', 'avg__num']);
});
- it('should build limit', () => {
- const limit = 2;
+ it('should build series_limit from legacy control', () => {
+ const series_limit = 2;
query = buildQueryObject({
datasource: '5__table',
granularity_sqla: 'ds',
viz_type: 'table',
- limit,
+ limit: series_limit,
});
- expect(query.timeseries_limit).toEqual(limit);
+ expect(query.series_limit).toEqual(series_limit);
+ });
+
+ it('should build series_limit', () => {
+ const series_limit = 2;
+ query = buildQueryObject({
+ datasource: '5__table',
+ granularity_sqla: 'ds',
+ viz_type: 'table',
+ series_limit,
+ });
+ expect(query.series_limit).toEqual(series_limit);
});
it('should build order_desc', () => {
@@ -141,7 +152,7 @@ describe('buildQueryObject', () => {
expect(query.order_desc).toEqual(orderDesc);
});
- it('should build timeseries_limit_metric', () => {
+ it('should build series_limit_metric from legacy control', () => {
const metric = 'country';
query = buildQueryObject({
datasource: '5__table',
@@ -149,7 +160,7 @@ describe('buildQueryObject', () => {
viz_type: 'table',
timeseries_limit_metric: metric,
});
- expect(query.timeseries_limit_metric).toEqual(metric);
+ expect(query.series_limit_metric).toEqual(metric);
});
it('should build series_limit_metric', () => {
@@ -291,6 +302,26 @@ describe('buildQueryObject', () => {
).toBeUndefined();
});
+ it('should populate granularity', () => {
+ const granularity = 'ds';
+ query = buildQueryObject({
+ datasource: '5__table',
+ granularity,
+ viz_type: 'table',
+ });
+ expect(query.granularity).toEqual(granularity);
+ });
+
+ it('should populate granularity from legacy field', () => {
+ const granularity = 'ds';
+ query = buildQueryObject({
+ datasource: '5__table',
+ granularity_sqla: granularity,
+ viz_type: 'table',
+ });
+ expect(query.granularity).toEqual(granularity);
+ });
+
it('should populate custom_params', () => {
const customParams: JsonObject = {
customObject: { id: 137, name: 'C-137' },
diff --git a/superset-frontend/packages/superset-ui-core/test/query/extractExtras.test.ts b/superset-frontend/packages/superset-ui-core/test/query/extractExtras.test.ts
index ca6ab730d1af5..35174f72bd35f 100644
--- a/superset-frontend/packages/superset-ui-core/test/query/extractExtras.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/query/extractExtras.test.ts
@@ -16,13 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { TimeGranularity } from '@superset-ui/core';
import extractExtras from '../../src/query/extractExtras';
describe('extractExtras', () => {
const baseQueryFormData = {
datasource: '1__table',
granularity_sqla: 'ds',
- time_grain_sqla: 'PT1M',
+ time_grain_sqla: TimeGranularity.MINUTE,
viz_type: 'my_viz',
};
diff --git a/superset-frontend/packages/superset-ui-core/test/query/getAxis.test.ts b/superset-frontend/packages/superset-ui-core/test/query/getAxis.test.ts
new file mode 100644
index 0000000000000..010bd9fc67591
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-core/test/query/getAxis.test.ts
@@ -0,0 +1,26 @@
+/**
+ * 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 { isXAxisSet } from '@superset-ui/core';
+
+test('isXAxisSet', () => {
+ expect(isXAxisSet({ datasource: '123', viz_type: 'table' })).not.toBeTruthy();
+ expect(
+ isXAxisSet({ datasource: '123', viz_type: 'table', x_axis: 'axis' }),
+ ).toBeTruthy();
+});
diff --git a/superset-frontend/packages/superset-ui-core/test/query/normalizeOrderBy.test.ts b/superset-frontend/packages/superset-ui-core/test/query/normalizeOrderBy.test.ts
index 57b186a1297ee..564d4aa815bbd 100644
--- a/superset-frontend/packages/superset-ui-core/test/query/normalizeOrderBy.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/query/normalizeOrderBy.test.ts
@@ -29,13 +29,13 @@ describe('normalizeOrderBy', () => {
expect(normalizeOrderBy(query)).toEqual(query);
});
- it('has timeseries_limit_metric in queryObject', () => {
+ it('has series_limit_metric in queryObject', () => {
const query: QueryObject = {
datasource: '5__table',
viz_type: 'table',
time_range: '1 year ago : 2013',
metrics: ['count(*)'],
- timeseries_limit_metric: {
+ series_limit_metric: {
expressionType: 'SIMPLE',
column: {
id: 1,
@@ -46,7 +46,7 @@ describe('normalizeOrderBy', () => {
order_desc: true,
};
const expectedQueryObject = normalizeOrderBy(query);
- expect(expectedQueryObject).not.toHaveProperty('timeseries_limit_metric');
+ expect(expectedQueryObject).not.toHaveProperty('series_limit_metric');
expect(expectedQueryObject).not.toHaveProperty('order_desc');
expect(expectedQueryObject).toEqual({
datasource: '5__table',
@@ -118,7 +118,7 @@ describe('normalizeOrderBy', () => {
order_desc: true,
};
const expectedQueryObject = normalizeOrderBy(query);
- expect(expectedQueryObject).not.toHaveProperty('timeseries_limit_metric');
+ expect(expectedQueryObject).not.toHaveProperty('series_limit_metric');
expect(expectedQueryObject).not.toHaveProperty('order_desc');
expect(expectedQueryObject).toEqual({
datasource: '5__table',
diff --git a/superset-frontend/packages/superset-ui-core/test/query/normalizeTimeColumn.test.ts b/superset-frontend/packages/superset-ui-core/test/query/normalizeTimeColumn.test.ts
new file mode 100644
index 0000000000000..22189b90551c1
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-core/test/query/normalizeTimeColumn.test.ts
@@ -0,0 +1,291 @@
+/**
+ * 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 {
+ normalizeTimeColumn,
+ QueryObject,
+ SqlaFormData,
+} from '@superset-ui/core';
+
+describe('GENERIC_CHART_AXES is disabled', () => {
+ let windowSpy: any;
+
+ beforeAll(() => {
+ // @ts-ignore
+ windowSpy = jest.spyOn(window, 'window', 'get').mockImplementation(() => ({
+ featureFlags: {
+ GENERIC_CHART_AXES: false,
+ },
+ }));
+ });
+
+ afterAll(() => {
+ windowSpy.mockRestore();
+ });
+
+ it('should return original QueryObject if disabled GENERIC_CHART_AXES', () => {
+ const formData: SqlaFormData = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ time_grain_sqla: 'P1Y',
+ time_range: '1 year ago : 2013',
+ columns: ['col1'],
+ metrics: ['count(*)'],
+ };
+ const query: QueryObject = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ extras: {
+ time_grain_sqla: 'P1Y',
+ },
+ time_range: '1 year ago : 2013',
+ orderby: [['count(*)', true]],
+ columns: ['col1'],
+ metrics: ['count(*)'],
+ is_timeseries: true,
+ };
+ expect(normalizeTimeColumn(formData, query)).toEqual(query);
+ });
+
+ it('should return converted QueryObject even though disabled GENERIC_CHART_AXES (x_axis in formData)', () => {
+ const formData: SqlaFormData = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ time_grain_sqla: 'P1Y',
+ time_range: '1 year ago : 2013',
+ columns: ['col1'],
+ metrics: ['count(*)'],
+ x_axis: 'time_column',
+ };
+ const query: QueryObject = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ extras: {
+ time_grain_sqla: 'P1Y',
+ },
+ time_range: '1 year ago : 2013',
+ orderby: [['count(*)', true]],
+ columns: ['time_column', 'col1'],
+ metrics: ['count(*)'],
+ is_timeseries: true,
+ };
+ expect(normalizeTimeColumn(formData, query)).toEqual({
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ extras: {},
+ time_range: '1 year ago : 2013',
+ orderby: [['count(*)', true]],
+ columns: [
+ {
+ timeGrain: 'P1Y',
+ columnType: 'BASE_AXIS',
+ sqlExpression: 'time_column',
+ label: 'time_column',
+ expressionType: 'SQL',
+ },
+ 'col1',
+ ],
+ metrics: ['count(*)'],
+ });
+ });
+});
+
+describe('GENERIC_CHART_AXES is enabled', () => {
+ let windowSpy: any;
+
+ beforeAll(() => {
+ // @ts-ignore
+ windowSpy = jest.spyOn(window, 'window', 'get').mockImplementation(() => ({
+ featureFlags: {
+ GENERIC_CHART_AXES: true,
+ },
+ }));
+ });
+
+ afterAll(() => {
+ windowSpy.mockRestore();
+ });
+
+ it('should return original QueryObject if x_axis is empty', () => {
+ const formData: SqlaFormData = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ time_grain_sqla: 'P1Y',
+ time_range: '1 year ago : 2013',
+ columns: ['col1'],
+ metrics: ['count(*)'],
+ };
+ const query: QueryObject = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ extras: {
+ time_grain_sqla: 'P1Y',
+ },
+ time_range: '1 year ago : 2013',
+ orderby: [['count(*)', true]],
+ columns: ['col1'],
+ metrics: ['count(*)'],
+ is_timeseries: true,
+ };
+ expect(normalizeTimeColumn(formData, query)).toEqual(query);
+ });
+
+ it('should support different columns for x-axis and granularity', () => {
+ const formData: SqlaFormData = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ time_grain_sqla: 'P1Y',
+ time_range: '1 year ago : 2013',
+ x_axis: 'time_column_in_x_axis',
+ columns: ['col1'],
+ metrics: ['count(*)'],
+ };
+ const query: QueryObject = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ extras: {
+ time_grain_sqla: 'P1Y',
+ where: '',
+ having: '',
+ },
+ time_range: '1 year ago : 2013',
+ orderby: [['count(*)', true]],
+ columns: ['time_column_in_x_axis', 'col1'],
+ metrics: ['count(*)'],
+ is_timeseries: true,
+ };
+ expect(normalizeTimeColumn(formData, query)).toEqual({
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ extras: { where: '', having: '' },
+ time_range: '1 year ago : 2013',
+ orderby: [['count(*)', true]],
+ columns: [
+ {
+ timeGrain: 'P1Y',
+ columnType: 'BASE_AXIS',
+ sqlExpression: 'time_column_in_x_axis',
+ label: 'time_column_in_x_axis',
+ expressionType: 'SQL',
+ },
+ 'col1',
+ ],
+ metrics: ['count(*)'],
+ });
+ });
+
+ it('should support custom SQL in x-axis', () => {
+ const formData: SqlaFormData = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ time_grain_sqla: 'P1Y',
+ time_range: '1 year ago : 2013',
+ x_axis: {
+ expressionType: 'SQL',
+ label: 'Order Data + 1 year',
+ sqlExpression: '"Order Date" + interval \'1 year\'',
+ },
+ columns: ['col1'],
+ metrics: ['count(*)'],
+ };
+ const query: QueryObject = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ extras: {
+ time_grain_sqla: 'P1Y',
+ where: '',
+ having: '',
+ },
+ time_range: '1 year ago : 2013',
+ orderby: [['count(*)', true]],
+ columns: [
+ {
+ expressionType: 'SQL',
+ label: 'Order Data + 1 year',
+ sqlExpression: '"Order Date" + interval \'1 year\'',
+ },
+ 'col1',
+ ],
+ metrics: ['count(*)'],
+ is_timeseries: true,
+ };
+ expect(normalizeTimeColumn(formData, query)).toEqual({
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ extras: { where: '', having: '' },
+ time_range: '1 year ago : 2013',
+ orderby: [['count(*)', true]],
+ columns: [
+ {
+ timeGrain: 'P1Y',
+ columnType: 'BASE_AXIS',
+ expressionType: 'SQL',
+ label: 'Order Data + 1 year',
+ sqlExpression: `"Order Date" + interval '1 year'`,
+ },
+ 'col1',
+ ],
+ metrics: ['count(*)'],
+ });
+ });
+
+ it('fallback and invalid columns value', () => {
+ const formData: SqlaFormData = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ time_grain_sqla: 'P1Y',
+ time_range: '1 year ago : 2013',
+ x_axis: {
+ expressionType: 'SQL',
+ label: 'Order Data + 1 year',
+ sqlExpression: '"Order Date" + interval \'1 year\'',
+ },
+ columns: ['col1'],
+ metrics: ['count(*)'],
+ };
+ const query: QueryObject = {
+ datasource: '5__table',
+ viz_type: 'table',
+ granularity: 'time_column',
+ extras: {
+ time_grain_sqla: 'P1Y',
+ where: '',
+ having: '',
+ },
+ time_range: '1 year ago : 2013',
+ orderby: [['count(*)', true]],
+ metrics: ['count(*)'],
+ is_timeseries: true,
+ };
+ expect(normalizeTimeColumn(formData, query)).toEqual(query);
+ });
+});
diff --git a/superset-frontend/packages/superset-ui-core/test/query/processFilters.test.ts b/superset-frontend/packages/superset-ui-core/test/query/processFilters.test.ts
index 151c0363f16f0..0d4fc4cd9f7c7 100644
--- a/superset-frontend/packages/superset-ui-core/test/query/processFilters.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/query/processFilters.test.ts
@@ -28,7 +28,7 @@ describe('processFilters', () => {
}),
).toEqual(
expect.objectContaining({
- extras: { having: '', having_druid: [], where: '' },
+ extras: { having: '', where: '' },
filters: [],
}),
);
@@ -54,12 +54,19 @@ describe('processFilters', () => {
subject: 'gender',
operator: 'IS NOT NULL',
},
+ // ignore simple having filter
+ {
+ expressionType: 'SIMPLE',
+ clause: 'HAVING',
+ subject: 'sum(sales)',
+ operator: '>',
+ comparator: '100',
+ },
],
}),
).toEqual({
extras: {
having: '',
- having_druid: [],
where: '',
},
filters: [
@@ -89,7 +96,6 @@ describe('processFilters', () => {
filters: [],
extras: {
having: '',
- having_druid: [],
where: '(1 = 1)',
},
});
@@ -115,20 +121,6 @@ describe('processFilters', () => {
operator: '==',
comparator: 'almond',
},
- {
- expressionType: 'SIMPLE',
- clause: 'HAVING',
- subject: 'sweetness',
- operator: '>',
- comparator: '0',
- },
- {
- expressionType: 'SIMPLE',
- clause: 'HAVING',
- subject: 'sweetness',
- operator: '<=',
- comparator: '50',
- },
{
expressionType: 'SQL',
clause: 'WHERE',
@@ -154,18 +146,6 @@ describe('processFilters', () => {
).toEqual({
extras: {
having: '(ice = 25 OR ice = 50) AND (waitTime <= 180 -- comment\n)',
- having_druid: [
- {
- col: 'sweetness',
- op: '>',
- val: '0',
- },
- {
- col: 'sweetness',
- op: '<=',
- val: '50',
- },
- ],
where: "(tea = 'jasmine') AND (cup = 'large' -- comment\n)",
},
filters: [
diff --git a/superset-frontend/packages/superset-ui-core/test/query/types/Column.test.ts b/superset-frontend/packages/superset-ui-core/test/query/types/Column.test.ts
new file mode 100644
index 0000000000000..d4391cfd01945
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-core/test/query/types/Column.test.ts
@@ -0,0 +1,63 @@
+/**
+ * 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 {
+ isAdhocColumn,
+ isPhysicalColumn,
+ isQueryFormColumn,
+} from '@superset-ui/core';
+
+const adhocColumn = {
+ expressionType: 'SQL',
+ label: 'country',
+ optionName: 'country',
+ sqlExpression: 'country',
+};
+
+test('isPhysicalColumn returns true', () => {
+ expect(isPhysicalColumn('gender')).toEqual(true);
+});
+
+test('isPhysicalColumn returns false', () => {
+ expect(isPhysicalColumn(adhocColumn)).toEqual(false);
+});
+
+test('isAdhocColumn returns true', () => {
+ expect(isAdhocColumn(adhocColumn)).toEqual(true);
+});
+
+test('isAdhocColumn returns false', () => {
+ expect(isAdhocColumn('hello')).toEqual(false);
+ expect(isAdhocColumn({})).toEqual(false);
+ expect(
+ isAdhocColumn({
+ expressionType: 'SQL',
+ label: 'country',
+ optionName: 'country',
+ }),
+ ).toEqual(false);
+});
+
+test('isQueryFormColumn returns true', () => {
+ expect(isQueryFormColumn('gender')).toEqual(true);
+ expect(isQueryFormColumn(adhocColumn)).toEqual(true);
+});
+
+test('isQueryFormColumn returns false', () => {
+ expect(isQueryFormColumn({})).toEqual(false);
+});
diff --git a/superset-frontend/packages/superset-ui-core/test/query/types/Dashboard.test.ts b/superset-frontend/packages/superset-ui-core/test/query/types/Dashboard.test.ts
index ea6236338c765..f72bff490f116 100644
--- a/superset-frontend/packages/superset-ui-core/test/query/types/Dashboard.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/query/types/Dashboard.test.ts
@@ -21,27 +21,50 @@ import {
isFilterDivider,
Filter,
NativeFilterType,
+ FilterWithDataMask,
+ Divider,
+ isNativeFilterWithDataMask,
} from '@superset-ui/core';
-test('should do native filter type guard', () => {
- const dummyFilter: Filter = {
- cascadeParentIds: [],
- defaultDataMask: {},
- id: 'dummyID',
- name: 'dummyName',
- scope: { rootPath: [], excluded: [] },
- filterType: 'dummyType',
- targets: [{}],
- controlValues: {},
- type: NativeFilterType.NATIVE_FILTER,
- description: 'dummyDesc',
- };
- expect(isNativeFilter(dummyFilter)).toBeTruthy();
- expect(
- isFilterDivider({
- ...dummyFilter,
- type: NativeFilterType.DIVIDER,
- title: 'dummyTitle',
- }),
- ).toBeTruthy();
+const filter: Filter = {
+ cascadeParentIds: [],
+ defaultDataMask: {},
+ id: 'filter_id',
+ name: 'Filter Name',
+ scope: { rootPath: [], excluded: [] },
+ filterType: 'filter_type',
+ targets: [{}],
+ controlValues: {},
+ type: NativeFilterType.NATIVE_FILTER,
+ description: 'Filter description.',
+};
+
+const filterWithDataMask: FilterWithDataMask = {
+ ...filter,
+ dataMask: { id: 'data_mask_id', filterState: { value: 'Filter value' } },
+};
+
+const filterDivider: Divider = {
+ id: 'divider_id',
+ type: NativeFilterType.DIVIDER,
+ title: 'Divider title',
+ description: 'Divider description.',
+};
+
+test('filter type guard', () => {
+ expect(isNativeFilter(filter)).toBeTruthy();
+ expect(isNativeFilter(filterWithDataMask)).toBeTruthy();
+ expect(isNativeFilter(filterDivider)).toBeFalsy();
+});
+
+test('filter with dataMask type guard', () => {
+ expect(isNativeFilterWithDataMask(filter)).toBeFalsy();
+ expect(isNativeFilterWithDataMask(filterWithDataMask)).toBeTruthy();
+ expect(isNativeFilterWithDataMask(filterDivider)).toBeFalsy();
+});
+
+test('filter divider type guard', () => {
+ expect(isFilterDivider(filter)).toBeFalsy();
+ expect(isFilterDivider(filterWithDataMask)).toBeFalsy();
+ expect(isFilterDivider(filterDivider)).toBeTruthy();
});
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/legacySortBy.tsx b/superset-frontend/packages/superset-ui-core/test/query/types/Datasource.test.ts
similarity index 60%
rename from superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/legacySortBy.tsx
rename to superset-frontend/packages/superset-ui-core/test/query/types/Datasource.test.ts
index 3cb882d29ece6..c80f3d6950017 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/legacySortBy.tsx
+++ b/superset-frontend/packages/superset-ui-core/test/query/types/Datasource.test.ts
@@ -16,22 +16,22 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { t } from '@superset-ui/core';
-import { ControlSetRow } from '../types';
+import { DatasourceType, DEFAULT_METRICS } from '@superset-ui/core';
-export const legacySortBy: ControlSetRow[] = [
- ['legacy_order_by'],
- [
+test('DEFAULT_METRICS', () => {
+ expect(DEFAULT_METRICS).toEqual([
{
- name: 'order_desc',
- config: {
- type: 'CheckboxControl',
- label: t('Sort descending'),
- default: true,
- description: t(
- 'Whether to sort descending or ascending. Takes effect only when "Sort by" is set',
- ),
- },
+ metric_name: 'COUNT(*)',
+ expression: 'COUNT(*)',
},
- ],
-];
+ ]);
+});
+
+test('DatasourceType', () => {
+ expect(Object.keys(DatasourceType).length).toBe(5);
+ expect(DatasourceType.Table).toBe('table');
+ expect(DatasourceType.Query).toBe('query');
+ expect(DatasourceType.Dataset).toBe('dataset');
+ expect(DatasourceType.SlTable).toBe('sl_table');
+ expect(DatasourceType.SavedQuery).toBe('saved_query');
+});
diff --git a/superset-frontend/packages/superset-ui-core/test/query/types/Filter.test.ts b/superset-frontend/packages/superset-ui-core/test/query/types/Filter.test.ts
index 3861bb0085b51..4aa4a474159b0 100644
--- a/superset-frontend/packages/superset-ui-core/test/query/types/Filter.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/query/types/Filter.test.ts
@@ -21,6 +21,7 @@ import {
isUnaryAdhocFilter,
isBinaryAdhocFilter,
isSetAdhocFilter,
+ isFreeFormAdhocFilter,
} from '@superset-ui/core';
describe('Filter type guards', () => {
@@ -95,4 +96,26 @@ describe('Filter type guards', () => {
).toEqual(false);
});
});
+ describe('isFreeFormAdhocFilter', () => {
+ it('should return true when it is the correct type', () => {
+ expect(
+ isFreeFormAdhocFilter({
+ expressionType: 'SQL',
+ clause: 'WHERE',
+ sqlExpression: 'gender = "boy"',
+ }),
+ ).toEqual(true);
+ });
+ it('should return false otherwise', () => {
+ expect(
+ isFreeFormAdhocFilter({
+ expressionType: 'SIMPLE',
+ clause: 'WHERE',
+ subject: 'tea',
+ operator: '==',
+ comparator: 'matcha',
+ }),
+ ).toEqual(false);
+ });
+ });
});
diff --git a/superset-frontend/packages/superset-ui-core/test/query/types/Metric.test.ts b/superset-frontend/packages/superset-ui-core/test/query/types/Metric.test.ts
new file mode 100644
index 0000000000000..041e913409583
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-core/test/query/types/Metric.test.ts
@@ -0,0 +1,88 @@
+/**
+ * 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 {
+ isSavedMetric,
+ isAdhocMetricSimple,
+ isAdhocMetricSQL,
+ isQueryFormMetric,
+} from '@superset-ui/core';
+
+const adhocMetricSimple = {
+ expressionType: 'SIMPLE',
+ column: {
+ id: 1,
+ column_name: 'sales',
+ columnName: 'sales',
+ verbose_name: 'sales',
+ },
+ aggregate: 'SUM',
+ label: 'count',
+ optionName: 'count',
+};
+
+const adhocMetricSQL = {
+ expressionType: 'SQL',
+ label: 'count',
+ optionName: 'count',
+ sqlExpression: 'count(*)',
+};
+
+const savedMetric = 'count(*)';
+
+test('isSavedMetric returns true', () => {
+ expect(isSavedMetric(savedMetric)).toEqual(true);
+});
+
+test('isSavedMetric returns false', () => {
+ expect(isSavedMetric(adhocMetricSQL)).toEqual(false);
+ expect(isSavedMetric(null)).toEqual(false);
+ expect(isSavedMetric(undefined)).toEqual(false);
+});
+
+test('isAdhocMetricSimple returns true', () => {
+ expect(isAdhocMetricSimple(adhocMetricSimple)).toEqual(true);
+});
+
+test('isAdhocMetricSimple returns false', () => {
+ expect(isAdhocMetricSimple('hello')).toEqual(false);
+ expect(isAdhocMetricSimple({})).toEqual(false);
+ expect(isAdhocMetricSimple(adhocMetricSQL)).toEqual(false);
+});
+
+test('isAdhocMetricSQL returns true', () => {
+ expect(isAdhocMetricSQL(adhocMetricSQL)).toEqual(true);
+});
+
+test('isAdhocMetricSQL returns false', () => {
+ expect(isAdhocMetricSQL('hello')).toEqual(false);
+ expect(isAdhocMetricSQL({})).toEqual(false);
+ expect(isAdhocMetricSQL(adhocMetricSimple)).toEqual(false);
+});
+
+test('isQueryFormMetric returns true', () => {
+ expect(isQueryFormMetric(adhocMetricSQL)).toEqual(true);
+ expect(isQueryFormMetric(adhocMetricSimple)).toEqual(true);
+ expect(isQueryFormMetric(savedMetric)).toEqual(true);
+});
+
+test('isQueryFormMetric returns false', () => {
+ expect(isQueryFormMetric({})).toEqual(false);
+ expect(isQueryFormMetric(undefined)).toEqual(false);
+ expect(isQueryFormMetric(null)).toEqual(false);
+});
diff --git a/superset-frontend/packages/superset-ui-core/test/query/types/PostProcessing.test.ts b/superset-frontend/packages/superset-ui-core/test/query/types/PostProcessing.test.ts
index 1d7d9f044e5d3..047699fa57415 100644
--- a/superset-frontend/packages/superset-ui-core/test/query/types/PostProcessing.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/query/types/PostProcessing.test.ts
@@ -42,7 +42,7 @@ import {
PostProcessingRolling,
PostProcessingSort,
} from '@superset-ui/core';
-import { ComparisionType, RollingType, TimeGranularity } from '../../../src';
+import { ComparisonType, RollingType, TimeGranularity } from '../../../src';
const AGGREGATES_OPTION: Aggregates = {
bar: {
@@ -74,7 +74,7 @@ const COMPARE_RULE: PostProcessingCompare = {
options: {
source_columns: ['foo'],
compare_columns: ['bar'],
- compare_type: ComparisionType.Percentage,
+ compare_type: ComparisonType.Percentage,
drop_original_columns: false,
},
};
@@ -110,8 +110,6 @@ const PIVOT_RULE: PostProcessingPivot = {
index: ['foo'],
columns: ['bar'],
aggregates: AGGREGATES_OPTION,
- flatten_columns: true,
- reset_index: true,
},
};
@@ -149,7 +147,7 @@ const ROLLING_RULE: PostProcessingRolling = {
const SORT_RULE: PostProcessingSort = {
operation: 'sort',
options: {
- columns: { foo: true },
+ by: 'foo',
},
};
diff --git a/superset-frontend/packages/superset-ui-core/test/ui-overrides/UiOverrideRegistry.test.ts b/superset-frontend/packages/superset-ui-core/test/ui-overrides/ExtensionsRegistry.test.ts
similarity index 81%
rename from superset-frontend/packages/superset-ui-core/test/ui-overrides/UiOverrideRegistry.test.ts
rename to superset-frontend/packages/superset-ui-core/test/ui-overrides/ExtensionsRegistry.test.ts
index 6e440551416dc..e80a3baad4221 100644
--- a/superset-frontend/packages/superset-ui-core/test/ui-overrides/UiOverrideRegistry.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/ui-overrides/ExtensionsRegistry.test.ts
@@ -16,8 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { getUiOverrideRegistry } from '@superset-ui/core';
+import { getExtensionsRegistry } from '@superset-ui/core';
-test('should get instance of getUiOverrideRegistry', () => {
- expect(getUiOverrideRegistry().name).toBe('UiOverrideRegistry');
+test('should get instance of getExtensionsRegistry', () => {
+ expect(getExtensionsRegistry().name).toBe('ExtensionsRegistry');
});
diff --git a/superset-frontend/packages/superset-ui-core/test/utils/featureFlag.test.ts b/superset-frontend/packages/superset-ui-core/test/utils/featureFlag.test.ts
index 52a57909aa386..66c58e79af567 100644
--- a/superset-frontend/packages/superset-ui-core/test/utils/featureFlag.test.ts
+++ b/superset-frontend/packages/superset-ui-core/test/utils/featureFlag.test.ts
@@ -16,20 +16,41 @@
* specific language governing permissions and limitations
* under the License.
*/
+import mockConsole from 'jest-mock-console';
+import { isFeatureEnabled, FeatureFlag } from '@superset-ui/core';
-import { FeatureFlag, isFeatureEnabled } from '@superset-ui/core';
+it('returns false and raises console error if feature flags have not been initialized', () => {
+ mockConsole();
+ Object.defineProperty(window, 'featureFlags', {
+ value: undefined,
+ });
+
+ expect(isFeatureEnabled(FeatureFlag.ALLOW_DASHBOARD_DOMAIN_SHARDING)).toEqual(
+ false,
+ );
+ expect(console.error).toHaveBeenCalled();
+ // @ts-expect-error
+ expect(console.error.mock.calls[0][0]).toEqual(
+ 'Failed to query feature flag ALLOW_DASHBOARD_DOMAIN_SHARDING',
+ );
+});
-describe('isFeatureFlagEnabled', () => {
- window.featureFlags = {
- [FeatureFlag.CLIENT_CACHE]: true,
- };
- it('returns false for unset feature flag', () => {
- expect(
- isFeatureEnabled(FeatureFlag.ALLOW_DASHBOARD_DOMAIN_SHARDING),
- ).toEqual(false);
+it('returns false for unset feature flag', () => {
+ Object.defineProperty(window, 'featureFlags', {
+ value: {},
});
- it('returns true for set feature flag', () => {
- expect(isFeatureEnabled(FeatureFlag.CLIENT_CACHE)).toEqual(true);
+ expect(isFeatureEnabled(FeatureFlag.ALLOW_DASHBOARD_DOMAIN_SHARDING)).toEqual(
+ false,
+ );
+});
+
+it('returns true for set feature flag', () => {
+ Object.defineProperty(window, 'featureFlags', {
+ value: {
+ CLIENT_CACHE: true,
+ },
});
+
+ expect(isFeatureEnabled(FeatureFlag.CLIENT_CACHE)).toEqual(true);
});
diff --git a/superset-frontend/packages/superset-ui-core/test/utils/lruCache.test.ts b/superset-frontend/packages/superset-ui-core/test/utils/lruCache.test.ts
new file mode 100644
index 0000000000000..f8a077eba0318
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-core/test/utils/lruCache.test.ts
@@ -0,0 +1,60 @@
+/*
+ * 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 { lruCache } from '@superset-ui/core';
+
+test('initial LRU', () => {
+ expect(lruCache().capacity).toBe(100);
+ expect(lruCache(10).capacity).toBe(10);
+ expect(lruCache(10).size).toBe(0);
+ expect(() => lruCache(0)).toThrow(Error);
+});
+
+test('LRU operations', () => {
+ const cache = lruCache(3);
+ cache.set('1', 'a');
+ cache.set('2', 'b');
+ cache.set('3', 'c');
+ cache.set('4', 'd');
+ expect(cache.size).toBe(3);
+ expect(cache.has('1')).toBeFalsy();
+ expect(cache.get('1')).toBeUndefined();
+ cache.get('2');
+ cache.set('5', 'e');
+ expect(cache.has('2')).toBeTruthy();
+ expect(cache.has('3')).toBeFalsy();
+ // @ts-expect-error
+ expect(() => cache.set(0)).toThrow(TypeError);
+ // @ts-expect-error
+ expect(() => cache.get(0)).toThrow(TypeError);
+ expect(cache.size).toBe(3);
+ cache.clear();
+ expect(cache.size).toBe(0);
+ expect(cache.capacity).toBe(3);
+});
+
+test('LRU handle null and undefined', () => {
+ const cache = lruCache();
+ cache.set('a', null);
+ cache.set('b', undefined);
+ expect(cache.has('a')).toBeTruthy();
+ expect(cache.has('b')).toBeTruthy();
+ expect(cache.get('a')).toBeNull();
+ expect(cache.get('b')).toBeUndefined();
+});
diff --git a/superset-frontend/packages/superset-ui-core/types/external.d.ts b/superset-frontend/packages/superset-ui-core/types/external.d.ts
index 31b0250bf4455..dcce5fa8823f8 100644
--- a/superset-frontend/packages/superset-ui-core/types/external.d.ts
+++ b/superset-frontend/packages/superset-ui-core/types/external.d.ts
@@ -17,6 +17,6 @@
* under the License.
*/
/**
- * Stub for the untypped jed module.
+ * Stub for the untyped jed module.
*/
declare module 'jed';
diff --git a/superset-frontend/packages/superset-ui-demo/package.json b/superset-frontend/packages/superset-ui-demo/package.json
index bf3da61c12583..a2ff398662dc1 100644
--- a/superset-frontend/packages/superset-ui-demo/package.json
+++ b/superset-frontend/packages/superset-ui-demo/package.json
@@ -41,7 +41,7 @@
"@storybook/addons": "^6.3.12",
"@storybook/react": "^6.3.12",
"@types/react-loadable": "^5.5.3",
- "antd": "^4.9.4",
+ "antd": "4.10.3",
"bootstrap": "^3.4.1",
"core-js": "3.8.3",
"gh-pages": "^3.0.0",
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/shared/components/VerifyCORS.tsx b/superset-frontend/packages/superset-ui-demo/storybook/shared/components/VerifyCORS.tsx
index de0b2ef8aba05..3aa082186981d 100644
--- a/superset-frontend/packages/superset-ui-demo/storybook/shared/components/VerifyCORS.tsx
+++ b/superset-frontend/packages/superset-ui-demo/storybook/shared/components/VerifyCORS.tsx
@@ -23,6 +23,7 @@ import {
Method,
makeApi,
SupersetApiError,
+ t,
} from '@superset-ui/core';
import ErrorMessage from './ErrorMessage';
@@ -121,7 +122,7 @@ export default class VerifyCORS extends React.Component {
className="btn btn-primary btn-sm"
onClick={this.handleVerify}
>
- Verify
+ {t('Verify')}
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/controlsShown.tsx b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/controlsShown.tsx
index fac198e826e84..603ef83b0f5b5 100644
--- a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/controlsShown.tsx
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/controlsShown.tsx
@@ -31,7 +31,7 @@ export const controlsShown = () => (
queriesData={[{ data }]}
formData={{
bottomMargin: 'auto',
- colorCcheme: 'd3Category10',
+ colorScheme: 'd3Category10',
contribution: false,
groupby: ['region'],
lineInterpolation: 'linear',
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/expanded.tsx b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/expanded.tsx
index 8f71fb5289f73..b26e7dfa6dba5 100644
--- a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/expanded.tsx
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/expanded.tsx
@@ -31,7 +31,7 @@ export const expanded = () => (
queriesData={[{ data }]}
formData={{
bottomMargin: 'auto',
- colorCcheme: 'd3Category10',
+ colorScheme: 'd3Category10',
contribution: false,
groupby: ['region'],
lineInterpolation: 'linear',
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/stackedWithBounds.tsx b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/stackedWithBounds.tsx
index d3dccc63d4f67..6f7a19825a2d1 100644
--- a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/stackedWithBounds.tsx
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/legacy-preset-chart-nvd3/Area/stories/stackedWithBounds.tsx
@@ -31,7 +31,7 @@ export const stackedWithYAxisBounds = () => (
queriesData={[{ data }]}
formData={{
bottomMargin: 'auto',
- colorCcheme: 'd3Category10',
+ colorScheme: 'd3Category10',
contribution: false,
groupby: ['region'],
lineInterpolation: 'linear',
@@ -66,7 +66,7 @@ export const stackedWithYAxisBoundsMinOnly = () => (
queriesData={[{ data }]}
formData={{
bottomMargin: 'auto',
- colorCcheme: 'd3Category10',
+ colorScheme: 'd3Category10',
contribution: false,
groupby: ['region'],
lineInterpolation: 'linear',
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/AreaTimeseries/Stories.tsx b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/AreaTimeseries/Stories.tsx
new file mode 100644
index 0000000000000..4cffeabd38a9e
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/AreaTimeseries/Stories.tsx
@@ -0,0 +1,88 @@
+/*
+ * 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, getChartTransformPropsRegistry } from '@superset-ui/core';
+import { boolean, number, select, withKnobs } from '@storybook/addon-knobs';
+import {
+ EchartsAreaChartPlugin,
+ TimeseriesTransformProps,
+} from '@superset-ui/plugin-chart-echarts';
+import data from './data';
+import { withResizableChartDemo } from '../../../../shared/components/ResizableChartDemo';
+
+new EchartsAreaChartPlugin().configure({ key: 'echarts_area' }).register();
+
+getChartTransformPropsRegistry().registerValue(
+ 'echarts_area',
+ TimeseriesTransformProps,
+);
+
+export default {
+ title: 'Chart Plugins/plugin-chart-echarts/Timeseries Area',
+ decorators: [withKnobs, withResizableChartDemo],
+};
+
+export const Timeseries = ({ width, height }) => {
+ const forecastEnabled = boolean('Enable forecast', true);
+ const queryData = data
+ .map(row =>
+ forecastEnabled
+ ? row
+ : {
+ // eslint-disable-next-line no-underscore-dangle
+ __timestamp: row.__timestamp,
+ Boston: row.Boston,
+ California: row.California,
+ WestTexNewMexico: row.WestTexNewMexico,
+ },
+ )
+ .filter(row => forecastEnabled || !!row.Boston);
+ return (
+
+ );
+};
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/AreaTimeseries/data.ts b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/AreaTimeseries/data.ts
new file mode 100644
index 0000000000000..ac6b5d0a5463e
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/AreaTimeseries/data.ts
@@ -0,0 +1,771 @@
+/*
+ * 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.
+ */
+
+export default [
+ {
+ __timestamp: 1419811200000,
+ Boston__yhat: 1.5348466045278903,
+ Boston__yhat_lower: 1.4108696830290821,
+ Boston__yhat_upper: 1.65406759478647,
+ Boston: 1.425,
+ California__yhat: 1.1428578093572317,
+ California__yhat_lower: 0.9954265301846809,
+ California__yhat_upper: 1.285336837473888,
+ California: 1.085,
+ WestTexNewMexico__yhat: 1.2189159706952082,
+ WestTexNewMexico__yhat_lower: 1.04104376708674,
+ WestTexNewMexico__yhat_upper: 1.3729774938431487,
+ WestTexNewMexico: 1.195,
+ },
+ {
+ __timestamp: 1420416000000,
+ Boston__yhat: 1.5183086928032201,
+ Boston__yhat_lower: 1.4051623626305831,
+ Boston__yhat_upper: 1.6373864508998999,
+ Boston: 1.52,
+ California__yhat: 1.1473836815806109,
+ California__yhat_lower: 0.9896908958316125,
+ California__yhat_upper: 1.3074486619072236,
+ California: 1.01,
+ WestTexNewMexico__yhat: 1.2101876636102695,
+ WestTexNewMexico__yhat_lower: 1.0531768381015862,
+ WestTexNewMexico__yhat_upper: 1.3798811980337082,
+ WestTexNewMexico: 1.305,
+ },
+ {
+ __timestamp: 1421020800000,
+ Boston__yhat: 1.5008792239446107,
+ Boston__yhat_lower: 1.3900408734935294,
+ Boston__yhat_upper: 1.6209717523914786,
+ Boston: 1.615,
+ California__yhat: 1.1257411477192287,
+ California__yhat_lower: 0.9647179126679808,
+ California__yhat_upper: 1.2856214776337003,
+ California: 1.13,
+ WestTexNewMexico__yhat: 1.211700721257458,
+ WestTexNewMexico__yhat_lower: 1.0512509758817796,
+ WestTexNewMexico__yhat_upper: 1.3838299538827643,
+ WestTexNewMexico: 1.255,
+ },
+ {
+ __timestamp: 1421625600000,
+ Boston__yhat: 1.493895763520492,
+ Boston__yhat_lower: 1.3819463100452443,
+ Boston__yhat_upper: 1.614560073797367,
+ Boston: 1.59,
+ California__yhat: 1.0914497359848156,
+ California__yhat_lower: 0.9309999613012108,
+ California__yhat_upper: 1.2413000315404008,
+ California: 1.18,
+ WestTexNewMexico__yhat: 1.208627046579019,
+ WestTexNewMexico__yhat_lower: 1.0443728779662684,
+ WestTexNewMexico__yhat_upper: 1.3675637830491076,
+ WestTexNewMexico: 1.215,
+ },
+ {
+ __timestamp: 1422230400000,
+ Boston__yhat: 1.5016078116606606,
+ Boston__yhat_lower: 1.3867245804741557,
+ Boston__yhat_upper: 1.614234955854214,
+ Boston: 1.5,
+ California__yhat: 1.0697859033873383,
+ California__yhat_lower: 0.9250294445931526,
+ California__yhat_upper: 1.227180419756037,
+ California: 0.98,
+ WestTexNewMexico__yhat: 1.1903524073209464,
+ WestTexNewMexico__yhat_lower: 1.0158621722285877,
+ WestTexNewMexico__yhat_upper: 1.3552685059028697,
+ WestTexNewMexico: 1.24,
+ },
+ {
+ __timestamp: 1422835200000,
+ Boston__yhat: 1.5159923617934186,
+ Boston__yhat_lower: 1.3970137282601371,
+ Boston__yhat_upper: 1.6308844178549995,
+ Boston: 1.475,
+ California__yhat: 1.0746946690720922,
+ California__yhat_lower: 0.9113788241318873,
+ California__yhat_upper: 1.2273689220316724,
+ California: 1.13,
+ WestTexNewMexico__yhat: 1.162418169193016,
+ WestTexNewMexico__yhat_lower: 0.984399666972796,
+ WestTexNewMexico__yhat_upper: 1.3286127921414361,
+ WestTexNewMexico: 1.19,
+ },
+ {
+ __timestamp: 1423440000000,
+ Boston__yhat: 1.525604106275286,
+ Boston__yhat_lower: 1.4091552054110317,
+ Boston__yhat_upper: 1.6398544651033324,
+ Boston: 1.555,
+ California__yhat: 1.0983484232374483,
+ California__yhat_lower: 0.9499667479813172,
+ California__yhat_upper: 1.2604622036877084,
+ California: 1.24,
+ WestTexNewMexico__yhat: 1.1407497573494716,
+ WestTexNewMexico__yhat_lower: 0.9682090338277108,
+ WestTexNewMexico__yhat_upper: 1.3110751375528853,
+ WestTexNewMexico: 1.24,
+ },
+ {
+ __timestamp: 1424044800000,
+ Boston__yhat: 1.5285159859188788,
+ Boston__yhat_lower: 1.4151325345500827,
+ Boston__yhat_upper: 1.63898403722097,
+ Boston: 1.485,
+ California__yhat: 1.1215587530856748,
+ California__yhat_lower: 0.9680608180357422,
+ California__yhat_upper: 1.282442960930767,
+ California: 1.185,
+ WestTexNewMexico__yhat: 1.1360040254613264,
+ WestTexNewMexico__yhat_lower: 0.963307750313048,
+ WestTexNewMexico__yhat_upper: 1.2986544671046583,
+ WestTexNewMexico: 1.28,
+ },
+ {
+ __timestamp: 1424649600000,
+ Boston__yhat: 1.5334822003771225,
+ Boston__yhat_lower: 1.4176345632105387,
+ Boston__yhat_upper: 1.6496071238192505,
+ Boston: 1.48,
+ California__yhat: 1.1336412205342397,
+ California__yhat_lower: 0.9743289540694136,
+ California__yhat_upper: 1.2898768461219847,
+ California: 0.995,
+ WestTexNewMexico__yhat: 1.1446754348884136,
+ WestTexNewMexico__yhat_lower: 0.986235125109336,
+ WestTexNewMexico__yhat_upper: 1.307986287217312,
+ WestTexNewMexico: 1.24,
+ },
+ {
+ __timestamp: 1425254400000,
+ Boston__yhat: 1.547848654545939,
+ Boston__yhat_lower: 1.4328177356803633,
+ Boston__yhat_upper: 1.673661661344583,
+ Boston: 1.54,
+ California__yhat: 1.1421270972002817,
+ California__yhat_lower: 0.9880212170643778,
+ California__yhat_upper: 1.298074311825913,
+ California: 1.22,
+ WestTexNewMexico__yhat: 1.1538926041448758,
+ WestTexNewMexico__yhat_lower: 1.0019767923899103,
+ WestTexNewMexico__yhat_upper: 1.3221026377048228,
+ WestTexNewMexico: 1.16,
+ },
+ {
+ __timestamp: 1425859200000,
+ Boston__yhat: 1.5675203083502125,
+ Boston__yhat_lower: 1.4543946077807537,
+ Boston__yhat_upper: 1.6864674764386627,
+ Boston: 1.62,
+ California__yhat: 1.1632572393543539,
+ California__yhat_lower: 0.9970086508003331,
+ California__yhat_upper: 1.3209871054747437,
+ California: 1.29,
+ WestTexNewMexico__yhat: 1.1530029605889838,
+ WestTexNewMexico__yhat_lower: 0.9729319698723828,
+ WestTexNewMexico__yhat_upper: 1.3054475293729533,
+ WestTexNewMexico: 1.285,
+ },
+ {
+ __timestamp: 1426464000000,
+ Boston__yhat: 1.581607931619551,
+ Boston__yhat_lower: 1.4723154031359578,
+ Boston__yhat_upper: 1.7069606387126863,
+ Boston: 1.56,
+ California__yhat: 1.2030769562029524,
+ California__yhat_lower: 1.04933598570031,
+ California__yhat_upper: 1.3591487662881023,
+ California: 1.18,
+ WestTexNewMexico__yhat: 1.1414541767825306,
+ WestTexNewMexico__yhat_lower: 0.9717441065782068,
+ WestTexNewMexico__yhat_upper: 1.312661843170456,
+ WestTexNewMexico: 1.345,
+ },
+ {
+ __timestamp: 1427068800000,
+ Boston__yhat: 1.5853316979769883,
+ Boston__yhat_lower: 1.4708451743058149,
+ Boston__yhat_upper: 1.7117728705026014,
+ Boston: 1.585,
+ California__yhat: 1.2479444775684796,
+ California__yhat_lower: 1.0837411336417548,
+ California__yhat_upper: 1.3998890149965297,
+ California: 1.315,
+ WestTexNewMexico__yhat: 1.1283059431234486,
+ WestTexNewMexico__yhat_lower: 0.9778619797162577,
+ WestTexNewMexico__yhat_upper: 1.2954488963192434,
+ WestTexNewMexico: 1.255,
+ },
+ {
+ __timestamp: 1427673600000,
+ Boston__yhat: 1.5841383828593085,
+ Boston__yhat_lower: 1.4654575751911438,
+ Boston__yhat_upper: 1.6946343035808373,
+ Boston: 1.59,
+ California__yhat: 1.2739437360014318,
+ California__yhat_lower: 1.1100282969104833,
+ California__yhat_upper: 1.428117476226516,
+ California: 1.32,
+ WestTexNewMexico__yhat: 1.1249371539002126,
+ WestTexNewMexico__yhat_lower: 0.9695967792994402,
+ WestTexNewMexico__yhat_upper: 1.287869970682996,
+ WestTexNewMexico: 1.28,
+ },
+ {
+ __timestamp: 1428278400000,
+ Boston__yhat: 1.5839751550296846,
+ Boston__yhat_lower: 1.4658964846078435,
+ Boston__yhat_upper: 1.710402200124056,
+ Boston: 1.56,
+ California__yhat: 1.2665706718822929,
+ California__yhat_lower: 1.1158333765771138,
+ California__yhat_upper: 1.4320483959058965,
+ California: 1.28,
+ WestTexNewMexico__yhat: 1.1355965911503207,
+ WestTexNewMexico__yhat_lower: 0.964066677858961,
+ WestTexNewMexico__yhat_upper: 1.3022575299852956,
+ WestTexNewMexico: 1.21,
+ },
+ {
+ __timestamp: 1428883200000,
+ Boston__yhat: 1.5816178634356794,
+ Boston__yhat_lower: 1.4715929905435854,
+ Boston__yhat_upper: 1.7003122219671367,
+ Boston: 1.545,
+ California__yhat: 1.232881524770783,
+ California__yhat_lower: 1.0767786935430315,
+ California__yhat_upper: 1.3959964303961667,
+ California: 1.285,
+ WestTexNewMexico__yhat: 1.1523828742682716,
+ WestTexNewMexico__yhat_lower: 0.9811195853500172,
+ WestTexNewMexico__yhat_upper: 1.3138046554765905,
+ WestTexNewMexico: 1.15,
+ },
+ {
+ __timestamp: 1429488000000,
+ Boston__yhat: 1.5693505553611033,
+ Boston__yhat_lower: 1.454366551073654,
+ Boston__yhat_upper: 1.672997430777775,
+ Boston: 1.57,
+ California__yhat: 1.1961960021745208,
+ California__yhat_lower: 1.0574856955397094,
+ California__yhat_upper: 1.3527191406913728,
+ California: 1.325,
+ WestTexNewMexico__yhat: 1.1605683040698191,
+ WestTexNewMexico__yhat_lower: 1.0031473604785308,
+ WestTexNewMexico__yhat_upper: 1.3131490159580719,
+ WestTexNewMexico: 1.33,
+ },
+ {
+ __timestamp: 1430092800000,
+ Boston__yhat: 1.548687090028952,
+ Boston__yhat_lower: 1.4345338808929986,
+ Boston__yhat_upper: 1.674291034018414,
+ Boston: 1.495,
+ California__yhat: 1.17944168965866,
+ California__yhat_lower: 1.0208437159576145,
+ California__yhat_upper: 1.3437648164186333,
+ California: 1.18,
+ WestTexNewMexico__yhat: 1.152452251304891,
+ WestTexNewMexico__yhat_lower: 0.9925163021235553,
+ WestTexNewMexico__yhat_upper: 1.33370469389031,
+ WestTexNewMexico: 1.125,
+ },
+ {
+ __timestamp: 1430697600000,
+ Boston__yhat: 1.5339945463021136,
+ Boston__yhat_lower: 1.4131803310322042,
+ Boston__yhat_upper: 1.6534068731295286,
+ Boston: 1.58,
+ California__yhat: 1.1914708975476587,
+ California__yhat_lower: 1.0346943811155895,
+ California__yhat_upper: 1.346918284211045,
+ California: 1.165,
+ WestTexNewMexico__yhat: 1.136951442350726,
+ WestTexNewMexico__yhat_lower: 0.9785853981941628,
+ WestTexNewMexico__yhat_upper: 1.305120499270747,
+ WestTexNewMexico: 1.07,
+ },
+ {
+ __timestamp: 1431302400000,
+ Boston__yhat: 1.538494530655746,
+ Boston__yhat_lower: 1.417157877783077,
+ Boston__yhat_upper: 1.6657402419552576,
+ Boston: 1.585,
+ California__yhat: 1.2250425993396363,
+ California__yhat_lower: 1.0694624006721893,
+ California__yhat_upper: 1.3779793141537178,
+ California: 1.285,
+ WestTexNewMexico__yhat: 1.131850140196041,
+ WestTexNewMexico__yhat_lower: 0.9693152036413223,
+ WestTexNewMexico__yhat_upper: 1.2969371429211514,
+ WestTexNewMexico: 1.06,
+ },
+ {
+ __timestamp: 1431907200000,
+ Boston__yhat: 1.5586586605892516,
+ Boston__yhat_lower: 1.4437718674345732,
+ Boston__yhat_upper: 1.678444300307212,
+ Boston: 1.54,
+ California__yhat: 1.2640228484312774,
+ California__yhat_lower: 1.105695580617842,
+ California__yhat_upper: 1.4262751320209555,
+ California: 1.3,
+ WestTexNewMexico__yhat: 1.14279691969869,
+ WestTexNewMexico__yhat_lower: 0.9744635833347896,
+ WestTexNewMexico__yhat_upper: 1.309843116203469,
+ WestTexNewMexico: 1.065,
+ },
+ {
+ __timestamp: 1432512000000,
+ Boston__yhat: 1.5775197465059267,
+ Boston__yhat_lower: 1.4598708798261923,
+ Boston__yhat_upper: 1.6911276338952719,
+ Boston: 1.6,
+ California__yhat: 1.292475578711032,
+ California__yhat_lower: 1.1228796890918014,
+ California__yhat_upper: 1.4471391733217347,
+ California: 1.24,
+ WestTexNewMexico__yhat: 1.151946670246945,
+ WestTexNewMexico__yhat_lower: 0.9787075088274869,
+ WestTexNewMexico__yhat_upper: 1.3257344034341332,
+ WestTexNewMexico: 1.065,
+ },
+ {
+ __timestamp: 1433116800000,
+ Boston__yhat: 1.5847361491556036,
+ Boston__yhat_lower: 1.469478725883583,
+ Boston__yhat_upper: 1.698200477547973,
+ Boston: 1.625,
+ California__yhat: 1.301640708602741,
+ California__yhat_lower: 1.1448194258091566,
+ California__yhat_upper: 1.4657411831360765,
+ California: 1.325,
+ WestTexNewMexico__yhat: 1.1344270549760207,
+ WestTexNewMexico__yhat_lower: 0.9628949633601395,
+ WestTexNewMexico__yhat_upper: 1.2999364461809975,
+ WestTexNewMexico: 1.08,
+ },
+ {
+ __timestamp: 1433721600000,
+ Boston__yhat: 1.588841301654564,
+ Boston__yhat_lower: 1.4701868286368829,
+ Boston__yhat_upper: 1.708276878629705,
+ Boston: 1.555,
+ California__yhat: 1.2945568932951903,
+ California__yhat_lower: 1.1357913193434988,
+ California__yhat_upper: 1.441658100122194,
+ California: 1.325,
+ WestTexNewMexico__yhat: 1.090609476160724,
+ WestTexNewMexico__yhat_lower: 0.9171628023326979,
+ WestTexNewMexico__yhat_upper: 1.2519104172461586,
+ WestTexNewMexico: 1.125,
+ },
+ {
+ __timestamp: 1434326400000,
+ Boston__yhat: 1.60467809761448,
+ Boston__yhat_lower: 1.4872087156545453,
+ Boston__yhat_upper: 1.7206390174307566,
+ Boston: 1.65,
+ California__yhat: 1.2866911289244536,
+ California__yhat_lower: 1.1223304657283866,
+ California__yhat_upper: 1.4489712765550424,
+ California: 1.38,
+ WestTexNewMexico__yhat: 1.058286202137859,
+ WestTexNewMexico__yhat_lower: 0.8983319008178635,
+ WestTexNewMexico__yhat_upper: 1.2230688588329341,
+ WestTexNewMexico: 1.2,
+ },
+ {
+ __timestamp: 1434931200000,
+ Boston__yhat: 1.6296561292532252,
+ Boston__yhat_lower: 1.5147117985377605,
+ Boston__yhat_upper: 1.7484553862428687,
+ Boston: 1.64,
+ California__yhat: 1.298704180420278,
+ California__yhat_lower: 1.143996831592798,
+ California__yhat_upper: 1.4569530963291766,
+ California: 1.385,
+ WestTexNewMexico__yhat: 1.0837741118769433,
+ WestTexNewMexico__yhat_lower: 0.9165400527844431,
+ WestTexNewMexico__yhat_upper: 1.2633713277285281,
+ WestTexNewMexico: 1.145,
+ },
+ {
+ __timestamp: 1435536000000,
+ Boston__yhat: 1.6387330700540754,
+ Boston__yhat_lower: 1.5214382052884348,
+ Boston__yhat_upper: 1.7593446818133576,
+ Boston: 1.7,
+ California__yhat: 1.3419159537936654,
+ California__yhat_lower: 1.1824389777530346,
+ California__yhat_upper: 1.5077615808876883,
+ California: 1.395,
+ WestTexNewMexico__yhat: 1.1753283438356257,
+ WestTexNewMexico__yhat_lower: 1.0084515427055218,
+ WestTexNewMexico__yhat_upper: 1.3411968014102083,
+ WestTexNewMexico: 1.18,
+ },
+ {
+ __timestamp: 1436140800000,
+ Boston__yhat: 1.6078378110129543,
+ Boston__yhat_lower: 1.4858780410049368,
+ Boston__yhat_upper: 1.7333942938670541,
+ Boston: 1.665,
+ California__yhat: 1.4064610022347392,
+ California__yhat_lower: 1.2518481325894115,
+ California__yhat_upper: 1.5631376401498112,
+ California: 1.465,
+ WestTexNewMexico__yhat: 1.2876812690769497,
+ WestTexNewMexico__yhat_lower: 1.118277996711148,
+ WestTexNewMexico__yhat_upper: 1.453601368173299,
+ WestTexNewMexico: 1.365,
+ },
+ {
+ __timestamp: 1436745600000,
+ Boston__yhat: 1.54126454151401,
+ Boston__yhat_lower: 1.4242640278872807,
+ Boston__yhat_upper: 1.658820938407199,
+ Boston: 1.615,
+ California__yhat: 1.4648637533773619,
+ California__yhat_lower: 1.3165708549095063,
+ California__yhat_upper: 1.6123722518242183,
+ California: 1.535,
+ WestTexNewMexico__yhat: 1.359084635413718,
+ WestTexNewMexico__yhat_lower: 1.1923924916510695,
+ WestTexNewMexico__yhat_upper: 1.5397046826260015,
+ WestTexNewMexico: 1.25,
+ },
+ {
+ __timestamp: 1437350400000,
+ Boston__yhat: 1.4716975989229104,
+ Boston__yhat_lower: 1.3478802335545248,
+ Boston__yhat_upper: 1.5897005348114144,
+ Boston: 1.65,
+ California__yhat: 1.492196708250474,
+ California__yhat_lower: 1.3281011466171584,
+ California__yhat_upper: 1.6482617063876424,
+ California: 1.53,
+ WestTexNewMexico__yhat: 1.3665720856468249,
+ WestTexNewMexico__yhat_lower: 1.1985870084342607,
+ WestTexNewMexico__yhat_upper: 1.540444302838635,
+ WestTexNewMexico: 1.325,
+ },
+ {
+ __timestamp: 1437955200000,
+ Boston__yhat: 1.4316465654883939,
+ Boston__yhat_lower: 1.3151590237205186,
+ Boston__yhat_upper: 1.5502363732881383,
+ Boston: 1.645,
+ California__yhat: 1.486878703643501,
+ California__yhat_lower: 1.3387136764087475,
+ California__yhat_upper: 1.6406538496379224,
+ California: 1.575,
+ WestTexNewMexico__yhat: 1.3430004296140337,
+ WestTexNewMexico__yhat_lower: 1.1696134333274417,
+ WestTexNewMexico__yhat_upper: 1.5143254675484394,
+ WestTexNewMexico: 1.345,
+ },
+ {
+ __timestamp: 1438560000000,
+ Boston__yhat: 1.4271527274427822,
+ Boston__yhat_lower: 1.3009869979033386,
+ Boston__yhat_upper: 1.5444571765505344,
+ Boston: 1.545,
+ California__yhat: 1.4721251850161223,
+ California__yhat_lower: 1.3130424764080704,
+ California__yhat_upper: 1.6322300582937983,
+ California: 1.565,
+ WestTexNewMexico__yhat: 1.3385023304664054,
+ WestTexNewMexico__yhat_lower: 1.169557000507694,
+ WestTexNewMexico__yhat_upper: 1.501423586440048,
+ WestTexNewMexico: 1.3,
+ },
+ {
+ __timestamp: 1439164800000,
+ Boston__yhat: 1.4407299749907534,
+ Boston__yhat_lower: 1.323436292855159,
+ Boston__yhat_upper: 1.5636100946562665,
+ Boston: 1.62,
+ California__yhat: 1.4747274927843579,
+ California__yhat_lower: 1.3090246017944651,
+ California__yhat_upper: 1.6212028571910875,
+ California: 1.535,
+ WestTexNewMexico__yhat: 1.369033029466056,
+ WestTexNewMexico__yhat_lower: 1.2063418855681307,
+ WestTexNewMexico__yhat_upper: 1.5410908830393701,
+ WestTexNewMexico: 1.215,
+ },
+ {
+ __timestamp: 1439769600000,
+ Boston__yhat: 1.4558141240561584,
+ Boston__yhat_lower: 1.3384500860436346,
+ Boston__yhat_upper: 1.5593449899412495,
+ Boston: 1.535,
+ California__yhat: 1.5004588541583503,
+ California__yhat_lower: 1.3525771130800601,
+ California__yhat_upper: 1.6557709189818204,
+ California: 1.515,
+ WestTexNewMexico__yhat: 1.4078705349829708,
+ WestTexNewMexico__yhat_lower: 1.2465576754469605,
+ WestTexNewMexico__yhat_upper: 1.5765990094113416,
+ WestTexNewMexico: 1.205,
+ },
+ {
+ __timestamp: 1440374400000,
+ Boston__yhat: 1.4714837581619955,
+ Boston__yhat_lower: 1.3542849882799493,
+ Boston__yhat_upper: 1.587250053083524,
+ Boston: 1.58,
+ California__yhat: 1.5302322554730527,
+ California__yhat_lower: 1.3712263333300627,
+ California__yhat_upper: 1.6766472256899916,
+ California: 1.53,
+ WestTexNewMexico__yhat: 1.425931627994101,
+ WestTexNewMexico__yhat_lower: 1.2620778981321579,
+ WestTexNewMexico__yhat_upper: 1.5920830784029816,
+ WestTexNewMexico: 1.255,
+ },
+ {
+ __timestamp: 1440979200000,
+ Boston__yhat: 1.491444403016728,
+ Boston__yhat_lower: 1.3719274262306433,
+ Boston__yhat_upper: 1.6081603165448515,
+ Boston: 1.54,
+ California__yhat: 1.5411777460499874,
+ California__yhat_lower: 1.3904365117687372,
+ California__yhat_upper: 1.694546785101698,
+ California: 1.54,
+ WestTexNewMexico__yhat: 1.4320134472163049,
+ WestTexNewMexico__yhat_lower: 1.273365593253299,
+ WestTexNewMexico__yhat_upper: 1.5931974288222444,
+ WestTexNewMexico: 1.29,
+ },
+ {
+ __timestamp: 1441584000000,
+ Boston__yhat: 1.5051820756139245,
+ Boston__yhat_lower: 1.3835553327078385,
+ Boston__yhat_upper: 1.6240589221993718,
+ Boston: 1.515,
+ California__yhat: 1.5313765368007273,
+ California__yhat_lower: 1.3681294180618269,
+ California__yhat_upper: 1.6892153479755334,
+ California: 1.55,
+ WestTexNewMexico__yhat: 1.4638751687570226,
+ WestTexNewMexico__yhat_lower: 1.2864210645323784,
+ WestTexNewMexico__yhat_upper: 1.6187694320540935,
+ WestTexNewMexico: 1.37,
+ },
+ {
+ __timestamp: 1442188800000,
+ Boston__yhat: 1.4894325587299742,
+ Boston__yhat_lower: 1.3727869467332703,
+ Boston__yhat_upper: 1.6084226338870418,
+ Boston: 1.58,
+ California__yhat: 1.522640140138669,
+ California__yhat_lower: 1.3734557489282102,
+ California__yhat_upper: 1.6743091049728624,
+ California: 1.53,
+ WestTexNewMexico__yhat: 1.5400751405380166,
+ WestTexNewMexico__yhat_lower: 1.3774375535282375,
+ WestTexNewMexico__yhat_upper: 1.723050870346822,
+ WestTexNewMexico: 1.485,
+ },
+ {
+ __timestamp: 1442793600000,
+ Boston__yhat: 1.4322083667601824,
+ Boston__yhat_lower: 1.3101390870258312,
+ Boston__yhat_upper: 1.5571183048764867,
+ Boston: 1.535,
+ California__yhat: 1.5378925480202739,
+ California__yhat_lower: 1.3886019658089772,
+ California__yhat_upper: 1.6978496884233474,
+ California: 1.445,
+ WestTexNewMexico__yhat: 1.6287478669084643,
+ WestTexNewMexico__yhat_lower: 1.478287058860101,
+ WestTexNewMexico__yhat_upper: 1.795633152002224,
+ WestTexNewMexico: 1.275,
+ },
+ {
+ __timestamp: 1443398400000,
+ Boston__yhat: 1.351816621968265,
+ Boston__yhat_lower: 1.2376540378452352,
+ Boston__yhat_upper: 1.4729299390946764,
+ Boston: 1.175,
+ California__yhat: 1.5759661525657334,
+ California__yhat_lower: 1.4231456717732236,
+ California__yhat_upper: 1.733586091013307,
+ California: 1.51,
+ WestTexNewMexico__yhat: 1.6721603417638533,
+ WestTexNewMexico__yhat_lower: 1.508503941330916,
+ WestTexNewMexico__yhat_upper: 1.8462459308936394,
+ WestTexNewMexico: 1.43,
+ },
+ {
+ __timestamp: 1444003200000,
+ Boston__yhat: 1.286486461072129,
+ Boston__yhat_lower: 1.1680220690052265,
+ Boston__yhat_upper: 1.4035977590666622,
+ Boston: 1.2,
+ California__yhat: 1.6097139361369517,
+ California__yhat_lower: 1.4449082988736466,
+ California__yhat_upper: 1.7603053272180196,
+ California: 1.575,
+ WestTexNewMexico__yhat: 1.639290251177639,
+ WestTexNewMexico__yhat_lower: 1.473164681029519,
+ WestTexNewMexico__yhat_upper: 1.8064957246654998,
+ WestTexNewMexico: 1.47,
+ },
+ {
+ __timestamp: 1444608000000,
+ Boston__yhat: 1.2630051620190224,
+ Boston__yhat_lower: 1.1467376145041555,
+ Boston__yhat_upper: 1.377446221614078,
+ Boston: 1.1,
+ California__yhat: 1.6098713751752662,
+ California__yhat_lower: 1.4600843147210683,
+ California__yhat_upper: 1.763955521152191,
+ California: 1.54,
+ WestTexNewMexico__yhat: 1.5551952931382806,
+ WestTexNewMexico__yhat_lower: 1.3962129897996904,
+ WestTexNewMexico__yhat_upper: 1.726357454658797,
+ WestTexNewMexico: 1.415,
+ },
+ {
+ __timestamp: 1445212800000,
+ Boston__yhat: 1.276278781347193,
+ Boston__yhat_lower: 1.1580450205542776,
+ Boston__yhat_upper: 1.3920651070329326,
+ Boston: 1.145,
+ California__yhat: 1.571148844853862,
+ California__yhat_lower: 1.4083378535887405,
+ California__yhat_upper: 1.733966017882931,
+ California: 1.38,
+ WestTexNewMexico__yhat: 1.4722932415830279,
+ WestTexNewMexico__yhat_lower: 1.3050378331324088,
+ WestTexNewMexico__yhat_upper: 1.6418924805303612,
+ WestTexNewMexico: 1.41,
+ },
+ {
+ __timestamp: 1445817600000,
+ Boston__yhat: 1.2991073481696098,
+ Boston__yhat_lower: 1.1878452793959065,
+ Boston__yhat_upper: 1.424293199867907,
+ Boston: 1.18,
+ California__yhat: 1.5150187954091354,
+ California__yhat_lower: 1.3476318997481405,
+ California__yhat_upper: 1.677657858675358,
+ California: 1.275,
+ WestTexNewMexico__yhat: 1.4199561158957161,
+ WestTexNewMexico__yhat_lower: 1.263080331712721,
+ WestTexNewMexico__yhat_upper: 1.5718996342613911,
+ WestTexNewMexico: 1.36,
+ },
+ {
+ __timestamp: 1446422400000,
+ Boston__yhat: 1.308880887797368,
+ Boston__yhat_lower: 1.1862924735231104,
+ Boston__yhat_upper: 1.4168025454442827,
+ Boston: 1.13,
+ California__yhat: 1.467196455991084,
+ California__yhat_lower: 1.31469058277437,
+ California__yhat_upper: 1.6266140472626818,
+ California: 1.32,
+ WestTexNewMexico__yhat: 1.385809818488925,
+ WestTexNewMexico__yhat_lower: 1.2178231659097734,
+ WestTexNewMexico__yhat_upper: 1.5529990050614997,
+ WestTexNewMexico: 1.37,
+ },
+ {
+ __timestamp: 1447027200000,
+ Boston__yhat: 1.3030202507313675,
+ Boston__yhat_lower: 1.1871331759675903,
+ Boston__yhat_upper: 1.4220034213332513,
+ Boston: 1.1,
+ California__yhat: 1.432710953584346,
+ California__yhat_lower: 1.2824951329265597,
+ California__yhat_upper: 1.586661603708675,
+ California: 1.21,
+ WestTexNewMexico__yhat: 1.3404954026443072,
+ WestTexNewMexico__yhat_lower: 1.1821733202392815,
+ WestTexNewMexico__yhat_upper: 1.5011656305912942,
+ WestTexNewMexico: 1.315,
+ },
+ {
+ __timestamp: 1447632000000,
+ Boston__yhat: 1.2921088188147662,
+ Boston__yhat_lower: 1.1728345442847379,
+ Boston__yhat_upper: 1.4033407585022522,
+ Boston: 1.17,
+ California__yhat: 1.3931387239731783,
+ California__yhat_lower: 1.2432214745880616,
+ California__yhat_upper: 1.5498822030297323,
+ California: 1.26,
+ WestTexNewMexico__yhat: 1.276766317307663,
+ WestTexNewMexico__yhat_lower: 1.0999844956570386,
+ WestTexNewMexico__yhat_upper: 1.446687228788756,
+ WestTexNewMexico: 1.375,
+ },
+ {
+ __timestamp: 1448236800000,
+ Boston__yhat: 1.2844900175902454,
+ Boston__yhat_lower: 1.1751725419028316,
+ Boston__yhat_upper: 1.4071918419152338,
+ Boston: 1.235,
+ California__yhat: 1.3280733170736323,
+ California__yhat_lower: 1.168686173676362,
+ California__yhat_upper: 1.4828349526176714,
+ California: 1.33,
+ WestTexNewMexico__yhat: 1.2150153206911025,
+ WestTexNewMexico__yhat_lower: 1.0575514264315589,
+ WestTexNewMexico__yhat_upper: 1.3738174939464802,
+ WestTexNewMexico: 1.445,
+ },
+ {
+ __timestamp: 1448841600000,
+ Boston__yhat: 1.2805251906155837,
+ Boston__yhat_lower: 1.1707757707707065,
+ Boston__yhat_upper: 1.3999312395395147,
+ Boston: 1.155,
+ California__yhat: 1.2392981370779044,
+ California__yhat_lower: 1.0733806154601595,
+ California__yhat_upper: 1.4014509402239486,
+ California: 1.13,
+ WestTexNewMexico__yhat: 1.1770436607980383,
+ WestTexNewMexico__yhat_lower: 0.993583553273554,
+ WestTexNewMexico__yhat_upper: 1.333422820891247,
+ WestTexNewMexico: 0.74,
+ },
+ {
+ __timestamp: 1449446400000,
+ Boston__yhat: 1.279267142574869,
+ Boston__yhat_lower: 1.1585705827510129,
+ Boston__yhat_upper: 1.3983536869495787,
+ Boston: 1.28,
+ California__yhat: 1.1539951545645342,
+ California__yhat_lower: 0.9889501465743559,
+ California__yhat_upper: 1.3053289212843744,
+ California: 1.13,
+ WestTexNewMexico__yhat: 1.162380614252356,
+ WestTexNewMexico__yhat_lower: 0.9965272411245537,
+ WestTexNewMexico__yhat_upper: 1.3253180367221955,
+ WestTexNewMexico: 1.29,
+ },
+];
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/Stories.tsx b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/Stories.tsx
new file mode 100644
index 0000000000000..7742f1ecfe537
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/Stories.tsx
@@ -0,0 +1,57 @@
+/*
+ * 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, getChartTransformPropsRegistry } from '@superset-ui/core';
+import { boolean, withKnobs } from '@storybook/addon-knobs';
+import {
+ EchartsSunburstChartPlugin,
+ SunburstTransformProps,
+} from '@superset-ui/plugin-chart-echarts';
+import { withResizableChartDemo } from '../../../../shared/components/ResizableChartDemo';
+import data from './data';
+
+new EchartsSunburstChartPlugin()
+ .configure({ key: 'echarts-sunburst' })
+ .register();
+
+getChartTransformPropsRegistry().registerValue(
+ 'echarts-sunburst',
+ SunburstTransformProps,
+);
+
+export default {
+ title: 'Chart Plugins/plugin-chart-echarts/Sunburst',
+ decorators: [withKnobs, withResizableChartDemo],
+};
+
+export const Sunburst = ({ width, height }) => (
+
+);
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/data.ts b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/data.ts
new file mode 100644
index 0000000000000..35675465dfddb
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Sunburst/data.ts
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+export default [
+ { genre: 'Adventure', platform: 'Wii', count: 84 },
+ { genre: 'Adventure', platform: 'N64', count: 14 },
+ { genre: 'Adventure', platform: 'XOne', count: 12 },
+ { genre: 'Adventure', platform: 'PS4', count: 19 },
+ { genre: 'Strategy', platform: 'Wii', count: 25 },
+ { genre: 'Strategy', platform: 'PS4', count: 15 },
+ { genre: 'Strategy', platform: 'N64', count: 29 },
+ { genre: 'Strategy', platform: 'XOne', count: 23 },
+ { genre: 'Simulation', platform: 'PS4', count: 15 },
+ { genre: 'Simulation', platform: 'XOne', count: 36 },
+ { genre: 'Simulation', platform: 'N64', count: 20 },
+ { genre: 'Simulation', platform: 'Wii', count: 50 },
+];
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Timeseries/Stories.tsx b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Timeseries/Stories.tsx
index b44ba252ea5d4..342db6f5bad41 100644
--- a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Timeseries/Stories.tsx
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Timeseries/Stories.tsx
@@ -26,6 +26,8 @@ import {
} from '@superset-ui/plugin-chart-echarts';
import data from './data';
import negativeNumData from './negativeNumData';
+import confbandData from './confbandData';
+import stackWithNullsData from './stackWithNulls';
import { withResizableChartDemo } from '../../../../shared/components/ResizableChartDemo';
new EchartsTimeseriesChartPlugin()
@@ -66,26 +68,26 @@ export const Timeseries = ({ width, height }) => {
{ data: queryData, colnames: ['__timestamp'], coltypes: [2] },
]}
formData={{
- contributionMode: undefined,
forecastEnabled,
- colorScheme: 'supersetColors',
+ color_scheme: 'supersetColors',
seriesType: select(
'Line type',
['line', 'scatter', 'smooth', 'bar', 'start', 'middle', 'end'],
'line',
),
logAxis: boolean('Log axis', false),
- yAxisFormat: 'SMART_NUMBER',
+ y_axis_format: 'SMART_NUMBER',
stack: boolean('Stack', false),
- showValue: boolean('Show Values', false),
- onlyTotal: boolean('Only Total', false),
- percentageThreshold: number('Percentage Threshold', 0),
+ show_value: boolean('Show Values', false),
+ only_total: boolean('Only Total', false),
+ percentage_threshold: number('Percentage Threshold', 0),
area: boolean('Area chart', false),
markerEnabled: boolean('Enable markers', false),
markerSize: number('Marker Size', 6),
minorSplitLine: boolean('Minor splitline', false),
opacity: number('Opacity', 0.2),
zoomable: boolean('Zoomable', false),
+ x_axis: '__timestamp',
}}
/>
);
@@ -100,23 +102,71 @@ export const WithNegativeNumbers = ({ width, height }) => (
{ data: negativeNumData, colnames: ['__timestamp'], coltypes: [2] },
]}
formData={{
- contributionMode: undefined,
- colorScheme: 'supersetColors',
+ color_scheme: 'supersetColors',
seriesType: select(
'Line type',
['line', 'scatter', 'smooth', 'bar', 'start', 'middle', 'end'],
'line',
),
- yAxisFormat: '$,.2f',
+ y_axis_format: '$,.2f',
stack: boolean('Stack', true),
- showValue: true,
- showLegend: true,
- onlyTotal: boolean('Only Total', true),
+ show_value: true,
+ show_legend: true,
+ only_total: boolean('Only Total', true),
orientation: select(
'Orientation',
['vertical', 'horizontal'],
'vertical',
),
+ x_axis: '__timestamp',
+ }}
+ />
+);
+
+export const ConfidenceBand = ({ width, height }) => (
+
+);
+
+export const StackWithNulls = ({ width, height }) => (
+
);
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Timeseries/confbandData.ts b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Timeseries/confbandData.ts
new file mode 100644
index 0000000000000..46529e59b1eee
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Timeseries/confbandData.ts
@@ -0,0 +1,329 @@
+/*
+ * 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.
+ */
+
+export default [
+ {
+ ds: -157766400000,
+ 'SUM(num)': 173161,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: -126230400000,
+ 'SUM(num)': 173777,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: -94694400000,
+ 'SUM(num)': 178221,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: -63158400000,
+ 'SUM(num)': 176779,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: -31536000000,
+ 'SUM(num)': 184113,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 0,
+ 'SUM(num)': 188343,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 31536000000,
+ 'SUM(num)': 178441,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 63072000000,
+ 'SUM(num)': 169507,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 94694400000,
+ 'SUM(num)': 156783,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 126230400000,
+ 'SUM(num)': 157434,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 157766400000,
+ 'SUM(num)': 153606,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 189302400000,
+ 'SUM(num)': 150937,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 220924800000,
+ 'SUM(num)': 154361,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 252460800000,
+ 'SUM(num)': 154515,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 283996800000,
+ 'SUM(num)': 159885,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 315532800000,
+ 'SUM(num)': 159087,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 347155200000,
+ 'SUM(num)': 159061,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 378691200000,
+ 'SUM(num)': 167242,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 410227200000,
+ 'SUM(num)': 165944,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 441763200000,
+ 'SUM(num)': 165662,
+ 'SUM(num)__yhat': null,
+ 'SUM(num)__yhat_lower': null,
+ 'SUM(num)__yhat_upper': null,
+ },
+ {
+ ds: 473385600000,
+ 'SUM(num)': 162578,
+ 'SUM(num)__yhat': 162578,
+ 'SUM(num)__yhat_lower': 162578,
+ 'SUM(num)__yhat_upper': 162578,
+ },
+ {
+ ds: 504921600000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 157613,
+ 'SUM(num)__yhat_lower': 147613,
+ 'SUM(num)__yhat_upper': 167613,
+ },
+ {
+ ds: 536457600000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 154580,
+ 'SUM(num)__yhat_lower': 134580,
+ 'SUM(num)__yhat_upper': 174580,
+ },
+ {
+ ds: 567993600000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 152134,
+ 'SUM(num)__yhat_lower': 122134,
+ 'SUM(num)__yhat_upper': 182134,
+ },
+ {
+ ds: 599616000000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 153577,
+ 'SUM(num)__yhat_lower': 113577,
+ 'SUM(num)__yhat_upper': 193577,
+ },
+ {
+ ds: 631152000000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 151121,
+ 'SUM(num)__yhat_lower': 101121,
+ 'SUM(num)__yhat_upper': 201121,
+ },
+ {
+ ds: 662688000000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 138102,
+ 'SUM(num)__yhat_lower': 78102,
+ 'SUM(num)__yhat_upper': 208102,
+ },
+ {
+ ds: 694224000000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 125030,
+ 'SUM(num)__yhat_lower': 45030,
+ 'SUM(num)__yhat_upper': 205030,
+ },
+ {
+ ds: 725846400000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 114647,
+ 'SUM(num)__yhat_lower': 24647,
+ 'SUM(num)__yhat_upper': 204647,
+ },
+ {
+ ds: 757382400000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 103968,
+ 'SUM(num)__yhat_lower': 13968,
+ 'SUM(num)__yhat_upper': 193968,
+ },
+ {
+ ds: 788918400000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 97006,
+ 'SUM(num)__yhat_lower': 7006,
+ 'SUM(num)__yhat_upper': 187006,
+ },
+ {
+ ds: 820454400000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 92213,
+ 'SUM(num)__yhat_lower': 2213,
+ 'SUM(num)__yhat_upper': 182213,
+ },
+ {
+ ds: 852076800000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 88462,
+ 'SUM(num)__yhat_lower': -1538,
+ 'SUM(num)__yhat_upper': 178462,
+ },
+ {
+ ds: 883612800000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 84424,
+ 'SUM(num)__yhat_lower': -5576,
+ 'SUM(num)__yhat_upper': 174424,
+ },
+ {
+ ds: 915148800000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 79787,
+ 'SUM(num)__yhat_lower': -10213,
+ 'SUM(num)__yhat_upper': 169787,
+ },
+ {
+ ds: 946684800000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 76610,
+ 'SUM(num)__yhat_lower': -13390,
+ 'SUM(num)__yhat_upper': 166610,
+ },
+ {
+ ds: 978307200000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 72073,
+ 'SUM(num)__yhat_lower': -17927,
+ 'SUM(num)__yhat_upper': 162073,
+ },
+ {
+ ds: 1009843200000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 68487,
+ 'SUM(num)__yhat_lower': -21513,
+ 'SUM(num)__yhat_upper': 158487,
+ },
+ {
+ ds: 1041379200000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 66381,
+ 'SUM(num)__yhat_lower': -23619,
+ 'SUM(num)__yhat_upper': 156381,
+ },
+ {
+ ds: 1072915200000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 63472,
+ 'SUM(num)__yhat_lower': -26528,
+ 'SUM(num)__yhat_upper': 153472,
+ },
+ {
+ ds: 1104537600000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 60885,
+ 'SUM(num)__yhat_lower': -29115,
+ 'SUM(num)__yhat_upper': 150885,
+ },
+ {
+ ds: 1136073600000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 59682,
+ 'SUM(num)__yhat_lower': -30318,
+ 'SUM(num)__yhat_upper': 149682,
+ },
+ {
+ ds: 1167609600000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 59191,
+ 'SUM(num)__yhat_lower': -30809,
+ 'SUM(num)__yhat_upper': 149191,
+ },
+ {
+ ds: 1199145600000,
+ 'SUM(num)': null,
+ 'SUM(num)__yhat': 54091,
+ 'SUM(num)__yhat_lower': -35909,
+ 'SUM(num)__yhat_upper': 144091,
+ },
+];
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Timeseries/stackWithNulls.ts b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Timeseries/stackWithNulls.ts
new file mode 100644
index 0000000000000..8416fa09def21
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-echarts/Timeseries/stackWithNulls.ts
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+export default [
+ {
+ ds: 1293840000000,
+ '1': 2,
+ '2': 1,
+ },
+ {
+ ds: 1325376000000,
+ '1': null,
+ '2': null,
+ },
+ {
+ ds: 1356998400000,
+ '1': null,
+ '2': 1,
+ },
+];
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-pivot-table/PivotTableStories.tsx b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-pivot-table/PivotTableStories.tsx
new file mode 100644
index 0000000000000..54903c013aa25
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-pivot-table/PivotTableStories.tsx
@@ -0,0 +1,74 @@
+/*
+ * 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 { withKnobs } from '@storybook/addon-knobs';
+import { SuperChart } from '@superset-ui/core';
+import { PivotTableChartPlugin } from '@superset-ui/plugin-chart-pivot-table';
+import { basicFormData, basicData } from './testData';
+import { withResizableChartDemo } from '../../../shared/components/ResizableChartDemo';
+
+export default {
+ title: 'Chart Plugins/plugin-chart-pivot-table',
+ decorators: [withKnobs, withResizableChartDemo],
+};
+
+new PivotTableChartPlugin().configure({ key: 'pivot_table_v2' }).register();
+
+export const basic = ({ width, height }) => (
+
+);
+basic.story = {
+ parameters: {
+ initialSize: {
+ width: 680,
+ height: 420,
+ },
+ },
+};
+
+export const MaximumAggregation = ({ width, height }) => (
+
+);
+basic.story = {
+ parameters: {
+ initialSize: {
+ width: 680,
+ height: 420,
+ },
+ },
+};
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-pivot-table/testData.ts b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-pivot-table/testData.ts
new file mode 100644
index 0000000000000..0e6457d0c122e
--- /dev/null
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-pivot-table/testData.ts
@@ -0,0 +1,126 @@
+/**
+ * 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.
+ */
+export const basicFormData = {
+ datasource: '1__table',
+ viz_type: 'pivot_table_v2',
+ granularity_sqla: 'ts',
+ groupbyColumns: ['location'],
+ groupbyRows: ['program_language'],
+ metrics: [
+ {
+ expressionType: 'SIMPLE',
+ column: {
+ id: 1,
+ column_name: 'count',
+ description: null,
+ expression: null,
+ groupby: true,
+ is_dttm: false,
+ python_date_format: null,
+ type: 'BIGINT',
+ type_generic: 0,
+ },
+ aggregate: 'SUM',
+ sqlExpression: null,
+ isNew: false,
+ hasCustomLabel: true,
+ label: 'Count',
+ },
+ {
+ expressionType: 'SIMPLE',
+ column: {
+ id: 2,
+ column_name: 'ts',
+ description: null,
+ expression: "DATE_PARSE(ds || ' ' || hr, '%Y-%m-%d %H')",
+ groupby: true,
+ is_dttm: true,
+ type: 'TIMESTAMP',
+ type_generic: 2,
+ python_date_format: null,
+ },
+ aggregate: 'MAX',
+ sqlExpression: null,
+ isNew: false,
+ hasCustomLabel: true,
+ label: 'Most Recent Data',
+ },
+ ],
+ metricsLayout: 'COLUMNS',
+ order_desc: true,
+ aggregateFunction: 'Sum',
+ valueFormat: '~g',
+ date_format: 'smart_date',
+ rowOrder: 'key_a_to_z',
+ colOrder: 'key_a_to_z',
+};
+
+export const basicData = {
+ cache_key: 'f2cd2a37b6977e3619ce6c07d0027972',
+ cached_dttm: '2022-07-27T17:42:39',
+ cache_timeout: 129600,
+ applied_template_filters: [],
+ annotation_data: {},
+ error: null,
+ is_cached: true,
+ query: 'SELECT \nFROM\nWHERE',
+ status: 'success',
+ stacktrace: null,
+ rowcount: 5,
+ from_dttm: 1658426268000,
+ to_dttm: 1659031068000,
+ colnames: ['location', 'program_language', 'Count', 'Most Recent Data'],
+ indexnames: [0, 1, 2, 3, 4],
+ coltypes: [1, 1, 0, 1],
+ data: [
+ {
+ location: 'AMEA',
+ program_language: 'Javscript',
+ Count: 134,
+ 'Most Recent Data': '2022-07-25 13:00:00.000',
+ },
+ {
+ location: 'ASIA',
+ program_language: 'python',
+ Count: 19,
+ 'Most Recent Data': '2022-07-25 16:00:00.000',
+ },
+ {
+ location: 'ASIA',
+ program_language: 'Java',
+ Count: 7,
+ 'Most Recent Data': '2022-07-25 15:00:00.000',
+ },
+ {
+ location: 'ASIA',
+ program_language: 'C++',
+ Count: 1,
+ 'Most Recent Data': '2022-07-25 02:00:00.000',
+ },
+ {
+ location: 'ASIA',
+ program_language: 'PHP',
+ Count: 1,
+ 'Most Recent Data': '2022-07-24 00:00:00.000',
+ },
+ ],
+ result_format: 'json',
+ applied_filters: [],
+ rejected_filters: [],
+};
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-table/birthNames.json b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-table/birthNames.json
index cecb37e02627e..c35d3a80665fc 100644
--- a/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-table/birthNames.json
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/plugins/plugin-chart-table/birthNames.json
@@ -13,7 +13,6 @@
"id": 1,
"name": "examples",
"backend": "postgresql",
- "allow_multi_schema_metadata_fetch": false,
"allows_subquery": true,
"allows_cost_estimate": null,
"allows_virtual_table_explore": true,
diff --git a/superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-chart/SuperChartStories.tsx b/superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-chart/SuperChartStories.tsx
index 490f498ec1370..d2eb5dae4445c 100644
--- a/superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-chart/SuperChartStories.tsx
+++ b/superset-frontend/packages/superset-ui-demo/storybook/stories/superset-ui-chart/SuperChartStories.tsx
@@ -110,7 +110,7 @@ export const fixedHeight100Width = () => {
};
fixedHeight100Width.story = { name: 'fixed height, 100% width' };
-export const withErrorBoundar = () => {
+export const withErrorBoundary = () => {
const width = text('Vis width', '500');
const height = text('Vis height', '300');
diff --git a/superset-frontend/packages/superset-ui-switchboard/package.json b/superset-frontend/packages/superset-ui-switchboard/package.json
index f7e6c69a1b508..1e49d7eb9cde0 100644
--- a/superset-frontend/packages/superset-ui-switchboard/package.json
+++ b/superset-frontend/packages/superset-ui-switchboard/package.json
@@ -1,6 +1,6 @@
{
"name": "@superset-ui/switchboard",
- "version": "0.18.26-0",
+ "version": "0.18.26-1",
"description": "Switchboard is a library to make it easier to communicate across browser windows using the MessageChannel API",
"sideEffects": false,
"main": "lib/index.js",
diff --git a/superset-frontend/packages/superset-ui-switchboard/src/index.ts b/superset-frontend/packages/superset-ui-switchboard/src/index.ts
index adbd7450fc035..8e6bef5ff7e4f 100644
--- a/superset-frontend/packages/superset-ui-switchboard/src/index.ts
+++ b/superset-frontend/packages/superset-ui-switchboard/src/index.ts
@@ -17,4 +17,7 @@
* under the License.
*/
+import Switchboard from './switchboard';
+
export * from './switchboard';
+export default Switchboard;
diff --git a/superset-frontend/packages/superset-ui-switchboard/src/switchboard.test.ts b/superset-frontend/packages/superset-ui-switchboard/src/switchboard.test.ts
index fb77ab90f8a42..9e36f541e1cd9 100644
--- a/superset-frontend/packages/superset-ui-switchboard/src/switchboard.test.ts
+++ b/superset-frontend/packages/superset-ui-switchboard/src/switchboard.test.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { Switchboard } from './switchboard';
+import SingletonSwitchboard, { Switchboard } from './switchboard';
type EventHandler = (event: MessageEvent) => void;
@@ -105,13 +105,16 @@ describe('comms', () => {
let originalConsoleError: any = null;
beforeAll(() => {
- global.MessageChannel = FakeMessageChannel; // yolo
+ Object.defineProperty(global, 'MessageChannel', {
+ value: FakeMessageChannel,
+ });
originalConsoleDebug = console.debug;
originalConsoleError = console.error;
});
beforeEach(() => {
console.debug = jest.fn(); // silencio bruno
+ console.error = jest.fn();
});
afterEach(() => {
@@ -126,6 +129,30 @@ describe('comms', () => {
expect(sb).toHaveProperty('debugMode');
});
+ it('singleton', async () => {
+ SingletonSwitchboard.start();
+ expect(console.error).toHaveBeenCalledWith(
+ '[]',
+ 'Switchboard not initialised',
+ );
+ SingletonSwitchboard.emit('someEvent', 42);
+ expect(console.error).toHaveBeenCalledWith(
+ '[]',
+ 'Switchboard not initialised',
+ );
+ await expect(SingletonSwitchboard.get('failing')).rejects.toThrow(
+ 'Switchboard not initialised',
+ );
+ SingletonSwitchboard.init({ port: new MessageChannel().port1 });
+ expect(SingletonSwitchboard).toHaveProperty('name');
+ expect(SingletonSwitchboard).toHaveProperty('debugMode');
+ SingletonSwitchboard.init({ port: new MessageChannel().port1 });
+ expect(console.error).toHaveBeenCalledWith(
+ '[switchboard]',
+ 'already initialized',
+ );
+ });
+
describe('emit', () => {
it('triggers the method', async () => {
const channel = new MessageChannel();
diff --git a/superset-frontend/packages/superset-ui-switchboard/src/switchboard.ts b/superset-frontend/packages/superset-ui-switchboard/src/switchboard.ts
index f12c9b6482c3d..ab19d462f1b23 100644
--- a/superset-frontend/packages/superset-ui-switchboard/src/switchboard.ts
+++ b/superset-frontend/packages/superset-ui-switchboard/src/switchboard.ts
@@ -90,7 +90,7 @@ function isError(message: Message): message is ErrorMessage {
export class Switchboard {
port: MessagePort;
- name: string;
+ name = '';
methods: Record> = {};
@@ -99,7 +99,23 @@ export class Switchboard {
debugMode: boolean;
- constructor({ port, name = 'switchboard', debug = false }: Params) {
+ private isInitialised: boolean;
+
+ constructor(params?: Params) {
+ if (!params) {
+ return;
+ }
+ this.init(params);
+ }
+
+ init(params: Params) {
+ if (this.isInitialised) {
+ this.logError('already initialized');
+ return;
+ }
+
+ const { port, name = 'switchboard', debug = false } = params;
+
this.port = port;
this.name = name;
this.debugMode = debug;
@@ -122,6 +138,8 @@ export class Switchboard {
}
}
});
+
+ this.isInitialised = true;
}
private async getMethodResult({
@@ -173,11 +191,15 @@ export class Switchboard {
* Instead of an arguments list, arguments are supplied as a map.
*
* @param method the name of the method to call
- * @param args arguments that will be supplied. Must be serializable, no functions or other nonense.
+ * @param args arguments that will be supplied. Must be serializable, no functions or other nonsense.
* @returns whatever is returned from the method
*/
get(method: string, args: unknown = undefined): Promise {
return new Promise((resolve, reject) => {
+ if (!this.isInitialised) {
+ reject(new Error('Switchboard not initialised'));
+ return;
+ }
// In order to "call a method" on the other side of the port,
// we will send a message with a unique id
const messageId = this.getNewMessageId();
@@ -215,6 +237,10 @@ export class Switchboard {
* @param args
*/
emit(method: string, args: unknown = undefined) {
+ if (!this.isInitialised) {
+ this.logError('Switchboard not initialised');
+ return;
+ }
const message: EmitMessage = {
switchboardAction: Actions.EMIT,
method,
@@ -224,6 +250,10 @@ export class Switchboard {
}
start() {
+ if (!this.isInitialised) {
+ this.logError('Switchboard not initialised');
+ return;
+ }
this.port.start();
}
@@ -242,3 +272,5 @@ export class Switchboard {
return `m_${this.name}_${this.incrementor++}`;
}
}
+
+export default new Switchboard();
diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js
index 0417ea3e8b5af..d97557a77b506 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js
+++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/Calendar.js
@@ -19,7 +19,7 @@
import PropTypes from 'prop-types';
import { extent as d3Extent, range as d3Range } from 'd3-array';
import { select as d3Select } from 'd3-selection';
-import { getSequentialSchemeRegistry } from '@superset-ui/core';
+import { getSequentialSchemeRegistry, t } from '@superset-ui/core';
import CalHeatMap from './vendor/cal-heatmap';
const propTypes = {
@@ -85,10 +85,12 @@ function Calendar(element, props) {
const metricsData = data.data;
+ const METRIC_TEXT = t('Metric');
+
Object.keys(metricsData).forEach(metric => {
const calContainer = div.append('div');
if (showMetricName) {
- calContainer.text(`Metric: ${verboseMap[metric] || metric}`);
+ calContainer.text(`${METRIC_TEXT}: ${verboseMap[metric] || metric}`);
}
const timestamps = metricsData[metric];
const extents = d3Extent(Object.keys(timestamps), key => timestamps[key]);
diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/controlPanel.ts b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/controlPanel.ts
index 2787687b06159..9071a278eebae 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/controlPanel.ts
+++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/controlPanel.ts
@@ -21,7 +21,7 @@ import {
ControlPanelConfig,
D3_FORMAT_DOCS,
D3_TIME_FORMAT_OPTIONS,
- formatSelectOptions,
+ getStandardizedControls,
sections,
} from '@superset-ui/chart-controls';
@@ -39,13 +39,13 @@ const config: ControlPanelConfig = {
type: 'SelectControl',
label: t('Domain'),
default: 'month',
- choices: formatSelectOptions([
- 'hour',
- 'day',
- 'week',
- 'month',
- 'year',
- ]),
+ choices: [
+ ['hour', t('hour')],
+ ['day', t('day')],
+ ['week', t('week')],
+ ['month', t('month')],
+ ['year', t('year')],
+ ],
description: t('The time unit used for the grouping of blocks'),
},
},
@@ -55,13 +55,13 @@ const config: ControlPanelConfig = {
type: 'SelectControl',
label: t('Subdomain'),
default: 'day',
- choices: formatSelectOptions([
- 'min',
- 'hour',
- 'day',
- 'week',
- 'month',
- ]),
+ choices: [
+ ['min', t('min')],
+ ['hour', t('hour')],
+ ['day', t('day')],
+ ['week', t('week')],
+ ['month', t('month')],
+ ],
description: t(
'The time unit for each block. Should be a smaller unit than ' +
'domain_granularity. Should be larger or equal to Time Grain',
@@ -191,6 +191,10 @@ const config: ControlPanelConfig = {
label: t('Number Format'),
},
},
+ formDataOverrides: formData => ({
+ ...formData,
+ metrics: getStandardizedControls().popAllMetrics(),
+ }),
};
export default config;
diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/images/example.jpg b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/images/example.jpg
new file mode 100644
index 0000000000000..0bbf24ae6ffa8
Binary files /dev/null and b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/images/example.jpg differ
diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/index.js b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/index.js
index 5c0a9425ef126..926d98421d31a 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/index.js
+++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/index.js
@@ -18,6 +18,7 @@
*/
import { t, ChartMetadata, ChartPlugin } from '@superset-ui/core';
import transformProps from './transformProps';
+import example from './images/example.jpg';
import controlPanel from './controlPanel';
import thumbnail from './images/thumbnail.png';
@@ -27,6 +28,7 @@ const metadata = new ChartMetadata({
description: t(
"Visualizes how a metric has changed over a time using a color scale and a calendar view. Gray values are used to indicate missing values and the linear color scheme is used to encode the magnitude of each day's value.",
),
+ exampleGallery: [{ url: example }],
name: t('Calendar Heatmap'),
tags: [
t('Business'),
diff --git a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js
index 3320693f5cc63..760bf0ce2b0c4 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js
+++ b/superset-frontend/plugins/legacy-plugin-chart-calendar/src/vendor/cal-heatmap.js
@@ -9,7 +9,7 @@
/* eslint-disable */
import d3tip from 'd3-tip';
-import { getContrastingColor } from '@superset-ui/core';
+import { getContrastingColor, t } from '@superset-ui/core';
var d3 = typeof require === 'function' ? require('d3') : window.d3;
@@ -256,9 +256,9 @@ var CalHeatMap = function () {
// Formatting of the title displayed when hovering a legend cell
legendTitleFormat: {
- lower: 'less than {min} {name}',
- inner: 'between {down} and {up} {name}',
- upper: 'more than {max} {name}',
+ lower: t('less than {min} {name}'),
+ inner: t('between {down} and {up} {name}'),
+ upper: t('more than {max} {name}'),
},
// Animation duration, in ms
diff --git a/superset-frontend/plugins/legacy-plugin-chart-chord/src/controlPanel.ts b/superset-frontend/plugins/legacy-plugin-chart-chord/src/controlPanel.ts
index c2559a7b0d57f..5a58b567a7a32 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-chord/src/controlPanel.ts
+++ b/superset-frontend/plugins/legacy-plugin-chart-chord/src/controlPanel.ts
@@ -16,8 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { t, validateNonEmpty } from '@superset-ui/core';
-import { ControlPanelConfig, sections } from '@superset-ui/chart-controls';
+import { ensureIsArray, t, validateNonEmpty } from '@superset-ui/core';
+import {
+ ControlPanelConfig,
+ getStandardizedControls,
+ sections,
+} from '@superset-ui/chart-controls';
const config: ControlPanelConfig = {
controlPanelSections: [
@@ -69,6 +73,16 @@ const config: ControlPanelConfig = {
description: t('Choose a target'),
},
},
+ formDataOverrides: formData => {
+ const groupby = getStandardizedControls()
+ .popAllColumns()
+ .filter(col => !ensureIsArray(formData.columns).includes(col));
+ return {
+ ...formData,
+ groupby,
+ metric: getStandardizedControls().shiftMetric(),
+ };
+ },
};
export default config;
diff --git a/superset-frontend/plugins/legacy-plugin-chart-country-map/scripts/Country Map GeoJSON Generator.ipynb b/superset-frontend/plugins/legacy-plugin-chart-country-map/scripts/Country Map GeoJSON Generator.ipynb
index e91d20ed6a3c7..4908a5e6d4571 100644
--- a/superset-frontend/plugins/legacy-plugin-chart-country-map/scripts/Country Map GeoJSON Generator.ipynb
+++ b/superset-frontend/plugins/legacy-plugin-chart-country-map/scripts/Country Map GeoJSON Generator.ipynb
@@ -34,7 +34,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 35,
"metadata": {},
"outputs": [
{
@@ -93,7 +93,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": 36,
"metadata": {},
"outputs": [],
"source": [
@@ -109,7 +109,7 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 37,
"metadata": {},
"outputs": [
{
@@ -118,12 +118,12 @@
"Index(['featurecla', 'scalerank', 'adm1_code', 'diss_me', 'iso_3166_2',\n",
" 'wikipedia', 'iso_a2', 'adm0_sr', 'name', 'name_alt',\n",
" ...\n",
- " 'FCLASS_TR', 'FCLASS_ID', 'FCLASS_PL', 'FCLASS_GR', 'FCLASS_IT',\n",
- " 'FCLASS_NL', 'FCLASS_SE', 'FCLASS_BD', 'FCLASS_UA', 'geometry'],\n",
- " dtype='object', length=121)"
+ " 'FCLASS_ID', 'FCLASS_PL', 'FCLASS_GR', 'FCLASS_IT', 'FCLASS_NL',\n",
+ " 'FCLASS_SE', 'FCLASS_BD', 'FCLASS_UA', 'FCLASS_TLC', 'geometry'],\n",
+ " dtype='object', length=122)"
]
},
- "execution_count": 3,
+ "execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
@@ -134,21 +134,21 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": 38,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Index(['featurecla', 'scalerank', 'labelrank', 'sovereignt', 'sov_a3',\n",
- " 'adm0_dif', 'level', 'type', 'admin', 'adm0_a3',\n",
+ " 'adm0_dif', 'level', 'type', 'tlc', 'admin',\n",
" ...\n",
" 'fclass_tr', 'fclass_id', 'fclass_pl', 'fclass_gr', 'fclass_it',\n",
" 'fclass_nl', 'fclass_se', 'fclass_bd', 'fclass_ua', 'geometry'],\n",
- " dtype='object', length=162)"
+ " dtype='object', length=169)"
]
},
- "execution_count": 4,
+ "execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
@@ -160,7 +160,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": 39,
"metadata": {},
"outputs": [
{
@@ -195,7 +195,6 @@
" name \n",
" name_alt \n",
" ... \n",
- " FCLASS_TR \n",
" FCLASS_ID \n",
" FCLASS_PL \n",
" FCLASS_GR \n",
@@ -204,6 +203,7 @@
" FCLASS_SE \n",
" FCLASS_BD \n",
" FCLASS_UA \n",
+ " FCLASS_TLC \n",
" geometry \n",
" \n",
" \n",
@@ -450,7 +450,7 @@
" \n",
" \n",
"\n",
- "9 rows × 120 columns
\n",
+ "9 rows × 121 columns
\n",
"