-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(explore): Frontend implementation of dataset creation from infobox #19855
Changes from 47 commits
6487ee4
2675ccb
0bbb69f
a5688f5
8a3e4aa
3e1f913
d4afdfe
c32412b
83d2173
3430551
2bdd51a
fe7085a
29f74e2
7aeba6b
1d49ef5
4c35710
ff63e6a
d28edf4
5ec5eed
65da6ac
2ef4ad4
1729fe7
8a45aef
e4fc404
106609a
4028043
a9212ff
eab9fc0
9b14992
8558460
c1fb8a6
9d225c4
383ec2e
a0c771b
bc38438
91f06ea
55bb379
dab90b4
c560456
30f4508
6ee52ea
1773954
20ede6d
ccd3e3e
a585546
5c93394
b9db9d2
cea65ce
90c20b2
faac391
ac671a4
cf12f1d
55c3a79
379d9a9
23923f4
430e43b
054ee97
5514305
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,11 +20,14 @@ | |
import { | ||
FeatureFlag, | ||
isFeatureEnabled, | ||
QueryColumn, | ||
QueryResponse, | ||
t, | ||
validateNonEmpty, | ||
} from '@superset-ui/core'; | ||
import { ExtraControlProps, SharedControlConfig } from '../types'; | ||
import { TIME_COLUMN_OPTION, TIME_FILTER_LABELS } from '../constants'; | ||
import { ExtraControlProps, SharedControlConfig, Dataset } from '../types'; | ||
import { DATASET_TIME_COLUMN_OPTION, TIME_FILTER_LABELS } from '../constants'; | ||
import { QUERY_TIME_COLUMN_OPTION, savedMetricsTypeCheck } from '..'; | ||
|
||
export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { | ||
type: 'DndColumnSelect', | ||
|
@@ -36,15 +39,25 @@ export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { | |
), | ||
mapStateToProps(state, { includeTime }) { | ||
const newState: ExtraControlProps = {}; | ||
if (state.datasource) { | ||
const options = state.datasource.columns.filter(c => c.groupby); | ||
const { datasource } = state; | ||
if (datasource?.columns[0]?.hasOwnProperty('groupby')) { | ||
const options = (datasource as Dataset).columns.filter(c => c.groupby); | ||
if (includeTime) { | ||
options.unshift(TIME_COLUMN_OPTION); | ||
options.unshift(DATASET_TIME_COLUMN_OPTION); | ||
} | ||
newState.options = Object.fromEntries( | ||
options.map(option => [option.column_name, option]), | ||
); | ||
newState.savedMetrics = state.datasource.metrics || []; | ||
newState.savedMetrics = (datasource as Dataset).metrics || []; | ||
} else { | ||
const options = datasource?.columns; | ||
if (includeTime) { | ||
(options as QueryColumn[])?.unshift(QUERY_TIME_COLUMN_OPTION); | ||
} | ||
newState.options = Object.fromEntries( | ||
(options as QueryColumn[])?.map(option => [option.name, option]), | ||
); | ||
newState.options = datasource?.columns; | ||
} | ||
return newState; | ||
}, | ||
|
@@ -83,8 +96,10 @@ export const dnd_adhoc_filters: SharedControlConfig<'DndFilterSelect'> = { | |
default: [], | ||
description: '', | ||
mapStateToProps: ({ datasource, form_data }) => ({ | ||
columns: datasource?.columns.filter(c => c.filterable) || [], | ||
savedMetrics: datasource?.metrics || [], | ||
columns: datasource?.columns[0]?.hasOwnProperty('filterable') | ||
? (datasource as Dataset)?.columns.filter(c => c.filterable) | ||
: datasource?.columns || [], | ||
savedMetrics: savedMetricsTypeCheck(datasource), | ||
// current active adhoc metrics | ||
selectedMetrics: | ||
form_data.metrics || (form_data.metric ? [form_data.metric] : []), | ||
|
@@ -99,8 +114,8 @@ export const dnd_adhoc_metrics: SharedControlConfig<'DndMetricSelect'> = { | |
label: t('Metrics'), | ||
validators: [validateNonEmpty], | ||
mapStateToProps: ({ datasource }) => ({ | ||
columns: datasource ? datasource.columns : [], | ||
savedMetrics: datasource ? datasource.metrics : [], | ||
columns: datasource?.columns || [], | ||
savedMetrics: savedMetricsTypeCheck(datasource), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice |
||
datasource, | ||
datasourceType: datasource?.type, | ||
}), | ||
|
@@ -130,7 +145,7 @@ export const dnd_sort_by: SharedControlConfig<'DndMetricSelect'> = { | |
), | ||
mapStateToProps: ({ datasource }) => ({ | ||
columns: datasource?.columns || [], | ||
savedMetrics: datasource?.metrics || [], | ||
savedMetrics: savedMetricsTypeCheck(datasource), | ||
datasource, | ||
datasourceType: datasource?.type, | ||
}), | ||
|
@@ -178,14 +193,30 @@ export const dnd_granularity_sqla: typeof dndGroupByControl = { | |
: 'Drop temporal column here', | ||
), | ||
mapStateToProps: ({ datasource }) => { | ||
const temporalColumns = datasource?.columns.filter(c => c.is_dttm) ?? []; | ||
if (datasource?.columns[0]?.hasOwnProperty('column_name')) { | ||
const temporalColumns = | ||
(datasource as Dataset)?.columns.filter(c => c.is_dttm) ?? []; | ||
const options = Object.fromEntries( | ||
temporalColumns.map(option => [option.column_name, option]), | ||
); | ||
return { | ||
options, | ||
default: | ||
(datasource as Dataset)?.main_dttm_col || | ||
temporalColumns[0]?.column_name || | ||
null, | ||
isTemporal: true, | ||
}; | ||
} | ||
|
||
const temporalColumns = | ||
(datasource as QueryResponse)?.columns.filter(c => c.is_dttm) ?? []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the one where let's show all the columns but put the is_dttm ones at the top, since users can't turn on/ off the is_dttm property. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ohhh yes that's right. I added a sort for this in |
||
const options = Object.fromEntries( | ||
temporalColumns.map(option => [option.column_name, option]), | ||
temporalColumns.map(option => [option.name, option]), | ||
); | ||
return { | ||
options, | ||
default: | ||
datasource?.main_dttm_col || temporalColumns[0]?.column_name || null, | ||
default: temporalColumns[0]?.name || null, | ||
isTemporal: true, | ||
}; | ||
}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,6 +45,8 @@ import { | |
legacyValidateInteger, | ||
validateNonEmpty, | ||
ComparisionType, | ||
QueryResponse, | ||
QueryColumn, | ||
} from '@superset-ui/core'; | ||
|
||
import { | ||
|
@@ -55,14 +57,16 @@ import { | |
D3_TIME_FORMAT_DOCS, | ||
DEFAULT_TIME_FORMAT, | ||
DEFAULT_NUMBER_FORMAT, | ||
savedMetricsTypeCheck, | ||
} from '../utils'; | ||
import { TIME_FILTER_LABELS, TIME_COLUMN_OPTION } from '../constants'; | ||
import { TIME_FILTER_LABELS, DATASET_TIME_COLUMN_OPTION } from '../constants'; | ||
import { | ||
Metric, | ||
SharedControlConfig, | ||
ColumnMeta, | ||
ExtraControlProps, | ||
SelectControlConfig, | ||
Dataset, | ||
} from '../types'; | ||
import { ColumnOption } from '../components/ColumnOption'; | ||
|
||
|
@@ -131,13 +135,14 @@ const groupByControl: SharedControlConfig<'SelectControl', ColumnMeta> = { | |
promptTextCreator: (label: unknown) => label, | ||
mapStateToProps(state, { includeTime }) { | ||
const newState: ExtraControlProps = {}; | ||
if (state.datasource) { | ||
const options = state.datasource.columns.filter(c => c.groupby); | ||
const { datasource } = state; | ||
if (datasource?.columns[0]?.hasOwnProperty('groupby')) { | ||
const options = (datasource as Dataset).columns.filter(c => c.groupby); | ||
if (includeTime) { | ||
options.unshift(TIME_COLUMN_OPTION); | ||
options.unshift(DATASET_TIME_COLUMN_OPTION); | ||
} | ||
newState.options = options; | ||
} | ||
} else newState.options = datasource?.columns; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should still do lines 141 and 142 if this is a query. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh good catch, thank you! Fixed in |
||
return newState; | ||
}, | ||
commaChoosesOption: false, | ||
|
@@ -149,8 +154,8 @@ const metrics: SharedControlConfig<'MetricsControl'> = { | |
label: t('Metrics'), | ||
validators: [validateNonEmpty], | ||
mapStateToProps: ({ datasource }) => ({ | ||
columns: datasource ? datasource.columns : [], | ||
savedMetrics: datasource ? datasource.metrics : [], | ||
columns: datasource?.columns || [], | ||
savedMetrics: savedMetricsTypeCheck(datasource), | ||
datasource, | ||
datasourceType: datasource?.type, | ||
}), | ||
|
@@ -292,15 +297,21 @@ const granularity_sqla: SharedControlConfig<'SelectControl', ColumnMeta> = { | |
valueRenderer: c => <ColumnOption column={c} />, | ||
valueKey: 'column_name', | ||
mapStateToProps: state => { | ||
const props: Partial<SelectControlConfig<ColumnMeta>> = {}; | ||
if (state.datasource) { | ||
props.options = state.datasource.columns.filter(c => c.is_dttm); | ||
const props: Partial<SelectControlConfig<ColumnMeta | QueryColumn>> = {}; | ||
const { datasource } = state; | ||
const dataset = datasource as Dataset; | ||
const query = datasource as QueryResponse; | ||
if (datasource?.columns[0]?.hasOwnProperty('main_dttm_col')) { | ||
props.options = dataset.columns.filter((c: ColumnMeta) => c.is_dttm); | ||
props.default = null; | ||
if (state.datasource.main_dttm_col) { | ||
props.default = state.datasource.main_dttm_col; | ||
} else if (props.options && props.options.length > 0) { | ||
props.default = props.options[0].column_name; | ||
if (dataset.main_dttm_col) { | ||
props.default = dataset.main_dttm_col; | ||
} else if (props?.options) { | ||
props.default = (props.options[0] as ColumnMeta).column_name; | ||
} | ||
} else { | ||
props.options = query?.columns.filter((c: QueryColumn) => c.is_dttm); | ||
if (props?.options) props.default = props.options[0].name; | ||
} | ||
return props; | ||
}, | ||
|
@@ -318,7 +329,7 @@ const time_grain_sqla: SharedControlConfig<'SelectControl'> = { | |
'engine basis in the Superset source code.', | ||
), | ||
mapStateToProps: ({ datasource }) => ({ | ||
choices: datasource?.time_grain_sqla || null, | ||
choices: (datasource as Dataset)?.time_grain_sqla || null, | ||
}), | ||
}; | ||
|
||
|
@@ -335,7 +346,7 @@ const time_range: SharedControlConfig<'DateFilterControl'> = { | |
"using the engine's local timezone. Note one can explicitly set the timezone " + | ||
'per the ISO 8601 format if specifying either the start and/or end time.', | ||
), | ||
mapStateToProps: ({ datasource, form_data }) => ({ | ||
mapStateToProps: ({ datasource }) => ({ | ||
datasource, | ||
}), | ||
}; | ||
|
@@ -401,7 +412,7 @@ const sort_by: SharedControlConfig<'MetricsControl'> = { | |
), | ||
mapStateToProps: ({ datasource }) => ({ | ||
columns: datasource?.columns || [], | ||
savedMetrics: datasource?.metrics || [], | ||
savedMetrics: savedMetricsTypeCheck(datasource), | ||
datasource, | ||
datasourceType: datasource?.type, | ||
}), | ||
|
@@ -493,8 +504,10 @@ const adhoc_filters: SharedControlConfig<'AdhocFilterControl'> = { | |
default: [], | ||
description: '', | ||
mapStateToProps: ({ datasource, form_data }) => ({ | ||
columns: datasource?.columns.filter(c => c.filterable) || [], | ||
savedMetrics: datasource?.metrics || [], | ||
columns: datasource?.columns[0]?.hasOwnProperty('filterable') | ||
? (datasource as Dataset)?.columns.filter(c => c.filterable) | ||
: datasource?.columns || [], | ||
savedMetrics: savedMetricsTypeCheck(datasource), | ||
// current active adhoc metrics | ||
selectedMetrics: | ||
form_data.metrics || (form_data.metric ? [form_data.metric] : []), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ import type { | |
JsonValue, | ||
Metric, | ||
QueryFormData, | ||
QueryResponse, | ||
} from '@superset-ui/core'; | ||
import { sharedControls } from './shared-controls'; | ||
import sharedControlComponents from './shared-controls/components'; | ||
|
@@ -51,7 +52,7 @@ export type ColumnMeta = Omit<Column, 'id'> & { | |
id?: number; | ||
} & AnyDict; | ||
|
||
export interface DatasourceMeta { | ||
export interface Dataset { | ||
id: number; | ||
type: DatasourceType; | ||
columns: ColumnMeta[]; | ||
|
@@ -69,7 +70,7 @@ export interface DatasourceMeta { | |
|
||
export interface ControlPanelState { | ||
form_data: QueryFormData; | ||
datasource: DatasourceMeta | null; | ||
datasource: Dataset | QueryResponse | null; | ||
controls: ControlStateMapping; | ||
} | ||
|
||
|
@@ -88,7 +89,7 @@ export interface ActionDispatcher< | |
* Mapping of action dispatchers | ||
*/ | ||
export interface ControlPanelActionDispatchers { | ||
setDatasource: ActionDispatcher<[DatasourceMeta]>; | ||
setDatasource: ActionDispatcher<[Dataset]>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will need to be Query too in the future |
||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* eslint-disable camelcase */ | ||
/** | ||
* 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 { QueryResponse } from '@superset-ui/core'; | ||
import { Dataset } from '../types'; | ||
import { DEFAULT_METRICS } from '..'; | ||
|
||
export const savedMetricsTypeCheck = ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to be more specific, I would say that this function is either fetching/defining or creating metrics. The type checking is just a side effect, if you wanted to rename this to something that explains what it does a little more succinctly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea! I changed it to |
||
datasource: Dataset | QueryResponse | null, | ||
) => | ||
datasource?.hasOwnProperty('metrics') | ||
? (datasource as Dataset)?.metrics || [] | ||
: DEFAULT_METRICS; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lyndsiWilliams do you think you can dry up lines 45-60 more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually, looks like we'll have to wait for the column name property to be the same first. 👍