mirror of
https://github.com/apache/superset.git
synced 2024-09-19 20:19:37 -04:00
feat(legacy-table-chart): add query mode switch (#609)
This commit is contained in:
parent
92ac471216
commit
aade5ef42c
@ -28,6 +28,7 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@superset-ui/color": "^0.14.0",
|
"@superset-ui/color": "^0.14.0",
|
||||||
"@superset-ui/query": "^0.14.0",
|
"@superset-ui/query": "^0.14.0",
|
||||||
|
"@superset-ui/style": "^0.14.0",
|
||||||
"@superset-ui/translation": "^0.14.0",
|
"@superset-ui/translation": "^0.14.0",
|
||||||
"@superset-ui/validator": "^0.14.0",
|
"@superset-ui/validator": "^0.14.0",
|
||||||
"react": "^16.13.1"
|
"react": "^16.13.1"
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* 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, { ReactText, ReactNode, MouseEvent, useCallback } from 'react';
|
||||||
|
import styled from '@superset-ui/style';
|
||||||
|
import { InfoTooltipWithTrigger } from './InfoTooltipWithTrigger';
|
||||||
|
|
||||||
|
export interface RadioButtonOption {
|
||||||
|
label: string;
|
||||||
|
value: ReactText;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RadioButtonControlProps {
|
||||||
|
label?: ReactNode;
|
||||||
|
description?: string;
|
||||||
|
options: RadioButtonOption[];
|
||||||
|
hovered?: boolean;
|
||||||
|
value?: string;
|
||||||
|
onChange: (opt: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Styles = styled.div`
|
||||||
|
.btn:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.control-label + .btn-group {
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
.btn-group .btn.active {
|
||||||
|
background: ${({ theme }) => theme.colors.secondary.light5};
|
||||||
|
box-shadow: none;
|
||||||
|
font-weight: ${({ theme }) => theme.typography.weights.bold};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function RadioButtonControl({
|
||||||
|
label: controlLabel,
|
||||||
|
description,
|
||||||
|
value: initialValue,
|
||||||
|
hovered,
|
||||||
|
options,
|
||||||
|
onChange,
|
||||||
|
}: RadioButtonControlProps) {
|
||||||
|
const currentValue = initialValue || options[0].value;
|
||||||
|
const onClick = useCallback(
|
||||||
|
(e: MouseEvent<HTMLButtonElement>) => {
|
||||||
|
onChange(e.currentTarget.value);
|
||||||
|
},
|
||||||
|
[onChange],
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Styles>
|
||||||
|
{controlLabel && (
|
||||||
|
<div className="control-label">
|
||||||
|
{controlLabel}{' '}
|
||||||
|
{hovered && description && (
|
||||||
|
<InfoTooltipWithTrigger tooltip={description} placement="top" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="btn-group btn-group-sm">
|
||||||
|
{options.map(({ label, value }, i) => (
|
||||||
|
<button
|
||||||
|
key={value}
|
||||||
|
type="button"
|
||||||
|
className={`btn btn-default ${options[i].value === currentValue ? 'active' : ''}`}
|
||||||
|
value={value}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Styles>
|
||||||
|
);
|
||||||
|
}
|
@ -12,10 +12,14 @@ export const sections = sectionModules;
|
|||||||
export { D3_FORMAT_DOCS, D3_FORMAT_OPTIONS, D3_TIME_FORMAT_OPTIONS } from './utils/D3Formatting';
|
export { D3_FORMAT_DOCS, D3_FORMAT_OPTIONS, D3_TIME_FORMAT_OPTIONS } from './utils/D3Formatting';
|
||||||
export { formatSelectOptions, formatSelectOptionsForRange } from './utils/selectOptions';
|
export { formatSelectOptions, formatSelectOptionsForRange } from './utils/selectOptions';
|
||||||
export * from './utils/mainMetric';
|
export * from './utils/mainMetric';
|
||||||
|
export * from './utils/expandControlConfig';
|
||||||
|
|
||||||
export * from './components/InfoTooltipWithTrigger';
|
export * from './components/InfoTooltipWithTrigger';
|
||||||
export * from './components/ColumnOption';
|
export * from './components/ColumnOption';
|
||||||
export * from './components/ColumnTypeLabel';
|
export * from './components/ColumnTypeLabel';
|
||||||
export * from './components/MetricOption';
|
export * from './components/MetricOption';
|
||||||
|
|
||||||
|
// React control components
|
||||||
|
export * from './components/RadioButtonControl';
|
||||||
|
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 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 RadioButtonControl from '../components/RadioButtonControl';
|
||||||
|
|
||||||
|
export * from '../components/RadioButtonControl';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aliases for Control Components
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
RadioButtonControl,
|
||||||
|
};
|
@ -42,17 +42,17 @@ import {
|
|||||||
} from '@superset-ui/color';
|
} from '@superset-ui/color';
|
||||||
import { legacyValidateInteger, validateNonEmpty } from '@superset-ui/validator';
|
import { legacyValidateInteger, validateNonEmpty } from '@superset-ui/validator';
|
||||||
|
|
||||||
import { formatSelectOptions } from './utils/selectOptions';
|
import { formatSelectOptions } from '../utils/selectOptions';
|
||||||
import { mainMetric, Metric } from './utils/mainMetric';
|
import { mainMetric, Metric } from '../utils/mainMetric';
|
||||||
import { TIME_FILTER_LABELS } from './constants';
|
import { TIME_FILTER_LABELS } from '../constants';
|
||||||
import {
|
import {
|
||||||
SharedControlConfig,
|
SharedControlConfig,
|
||||||
ColumnMeta,
|
ColumnMeta,
|
||||||
DatasourceMeta,
|
DatasourceMeta,
|
||||||
ExtraControlProps,
|
ExtraControlProps,
|
||||||
SelectControlConfig,
|
SelectControlConfig,
|
||||||
} from './types';
|
} from '../types';
|
||||||
import { ColumnOption } from './components/ColumnOption';
|
import { ColumnOption } from '../components/ColumnOption';
|
||||||
|
|
||||||
const categoricalSchemeRegistry = getCategoricalSchemeRegistry();
|
const categoricalSchemeRegistry = getCategoricalSchemeRegistry();
|
||||||
const sequentialSchemeRegistry = getSequentialSchemeRegistry();
|
const sequentialSchemeRegistry = getSequentialSchemeRegistry();
|
@ -20,13 +20,19 @@
|
|||||||
import React, { ReactNode, ReactText, ReactElement } from 'react';
|
import React, { ReactNode, ReactText, ReactElement } from 'react';
|
||||||
import { QueryFormData } from '@superset-ui/query';
|
import { QueryFormData } from '@superset-ui/query';
|
||||||
import sharedControls from './shared-controls';
|
import sharedControls from './shared-controls';
|
||||||
|
import sharedControlComponents from './shared-controls/components';
|
||||||
|
|
||||||
type AnyDict = Record<string, unknown>;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
type AnyDict = Record<string, any>;
|
||||||
interface Action {
|
interface Action {
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
interface AnyAction extends Action, AnyDict {}
|
interface AnyAction extends Action, AnyDict {}
|
||||||
|
|
||||||
|
export type SharedControls = typeof sharedControls;
|
||||||
|
export type SharedControlAlias = keyof typeof sharedControls;
|
||||||
|
export type SharedControlComponents = typeof sharedControlComponents;
|
||||||
|
|
||||||
/** ----------------------------------------------
|
/** ----------------------------------------------
|
||||||
* Input data/props while rendering
|
* Input data/props while rendering
|
||||||
* ---------------------------------------------*/
|
* ---------------------------------------------*/
|
||||||
@ -77,10 +83,11 @@ export interface ControlPanelActionDispatchers {
|
|||||||
export type ExtraControlProps = AnyDict;
|
export type ExtraControlProps = AnyDict;
|
||||||
|
|
||||||
// Ref:superset-frontend/src/explore/store.js
|
// Ref:superset-frontend/src/explore/store.js
|
||||||
export type ControlState<
|
export type ControlState<T = ControlType, O extends SelectOption = SelectOption> = ControlConfig<
|
||||||
T extends InternalControlType | unknown = InternalControlType,
|
T,
|
||||||
O extends SelectOption = SelectOption
|
O
|
||||||
> = ControlConfig<T, O> & ExtraControlProps;
|
> &
|
||||||
|
ExtraControlProps;
|
||||||
|
|
||||||
export interface ControlStateMapping {
|
export interface ControlStateMapping {
|
||||||
[key: string]: ControlState;
|
[key: string]: ControlState;
|
||||||
@ -125,11 +132,11 @@ export type InternalControlType =
|
|||||||
| 'FilterBoxItemControl'
|
| 'FilterBoxItemControl'
|
||||||
| 'MetricsControlVerifiedOptions'
|
| 'MetricsControlVerifiedOptions'
|
||||||
| 'SelectControlVerifiedOptions'
|
| 'SelectControlVerifiedOptions'
|
||||||
| 'AdhocFilterControlVerifiedOptions';
|
| 'AdhocFilterControlVerifiedOptions'
|
||||||
|
| keyof SharedControlComponents; // expanded in `expandControlConfig`
|
||||||
|
|
||||||
export interface ControlValueValidator<T = unknown, O extends SelectOption = SelectOption> {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(value: T, state: ControlState<O>): boolean | string;
|
export type ControlType = InternalControlType | React.ComponentType<any>;
|
||||||
}
|
|
||||||
|
|
||||||
export type TabOverride = 'data' | boolean;
|
export type TabOverride = 'data' | boolean;
|
||||||
|
|
||||||
@ -160,13 +167,17 @@ export type TabOverride = 'data' | boolean;
|
|||||||
* - visibility: a function that uses control panel props to check whether a control should
|
* - visibility: a function that uses control panel props to check whether a control should
|
||||||
* be visibile.
|
* be visibile.
|
||||||
*/
|
*/
|
||||||
export interface BaseControlConfig<T = unknown> {
|
export interface BaseControlConfig<
|
||||||
|
T extends ControlType = ControlType,
|
||||||
|
O extends SelectOption = SelectOption,
|
||||||
|
V = unknown
|
||||||
|
> extends AnyDict {
|
||||||
type: T;
|
type: T;
|
||||||
label?: ReactNode;
|
label?: ReactNode;
|
||||||
description?: ReactNode;
|
description?: ReactNode;
|
||||||
default?: unknown;
|
default?: V;
|
||||||
renderTrigger?: boolean;
|
renderTrigger?: boolean;
|
||||||
validators?: ControlValueValidator[];
|
validators?: ControlValueValidator<T, O, V>[];
|
||||||
warning?: ReactNode;
|
warning?: ReactNode;
|
||||||
error?: ReactNode;
|
error?: ReactNode;
|
||||||
// override control panel state props
|
// override control panel state props
|
||||||
@ -177,7 +188,14 @@ export interface BaseControlConfig<T = unknown> {
|
|||||||
) => ExtraControlProps;
|
) => ExtraControlProps;
|
||||||
tabOverride?: TabOverride;
|
tabOverride?: TabOverride;
|
||||||
visibility?: (props: ControlPanelsContainerProps) => boolean;
|
visibility?: (props: ControlPanelsContainerProps) => boolean;
|
||||||
[key: string]: unknown;
|
}
|
||||||
|
|
||||||
|
export interface ControlValueValidator<
|
||||||
|
T = ControlType,
|
||||||
|
O extends SelectOption = SelectOption,
|
||||||
|
V = unknown
|
||||||
|
> {
|
||||||
|
(value: V, state: ControlState<T, O>): boolean | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** --------------------------------------------
|
/** --------------------------------------------
|
||||||
@ -207,7 +225,7 @@ interface FilterOption<T extends SelectOption> {
|
|||||||
export interface SelectControlConfig<
|
export interface SelectControlConfig<
|
||||||
O extends SelectOption = SelectOption,
|
O extends SelectOption = SelectOption,
|
||||||
T extends SelectControlType = SelectControlType
|
T extends SelectControlType = SelectControlType
|
||||||
> extends BaseControlConfig<T> {
|
> extends BaseControlConfig<T, O> {
|
||||||
clearable?: boolean;
|
clearable?: boolean;
|
||||||
freeForm?: boolean;
|
freeForm?: boolean;
|
||||||
multi?: boolean;
|
multi?: boolean;
|
||||||
@ -227,24 +245,26 @@ export type SharedControlConfig<
|
|||||||
/** --------------------------------------------
|
/** --------------------------------------------
|
||||||
* Custom controls
|
* Custom controls
|
||||||
* --------------------------------------------- */
|
* --------------------------------------------- */
|
||||||
export type CustomComponentControlConfig<P = unknown> = BaseControlConfig<
|
export type CustomControlConfig<P = {}> = BaseControlConfig<React.ComponentType<P>> &
|
||||||
InternalControlType | React.ComponentType<P>
|
// two run-time properties from superset-frontend/src/explore/components/Control.jsx
|
||||||
> &
|
Omit<P, 'onChange' | 'hovered'>;
|
||||||
Omit<P, 'onChange' | 'hovered'>; // two run-time properties from superset-frontend/src/explore/components/Control.jsx
|
|
||||||
|
|
||||||
// Catch-all ControlConfig
|
// Catch-all ControlConfig
|
||||||
// - if T == known control types, return SharedControlConfig,
|
// - if T is known control types, return SharedControlConfig,
|
||||||
|
// - if T is object, assume a CustomComponent
|
||||||
// - otherwise assume it's a custom component control
|
// - otherwise assume it's a custom component control
|
||||||
export type ControlConfig<
|
export type ControlConfig<
|
||||||
T extends InternalControlType | unknown = InternalControlType,
|
T = AnyDict,
|
||||||
O extends SelectOption = SelectOption
|
O extends SelectOption = SelectOption
|
||||||
> = T extends InternalControlType ? SharedControlConfig<T, O> : CustomComponentControlConfig<T>;
|
> = T extends InternalControlType
|
||||||
|
? SharedControlConfig<T, O>
|
||||||
|
: T extends object
|
||||||
|
? CustomControlConfig<T> // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
: CustomControlConfig<any>;
|
||||||
|
|
||||||
/** --------------------------------------------
|
/** ===========================================================
|
||||||
* Chart plugin control panel config
|
* Chart plugin control panel config
|
||||||
* --------------------------------------------- */
|
* ========================================================= */
|
||||||
export type SharedControlAlias = keyof typeof sharedControls;
|
|
||||||
|
|
||||||
export type SharedSectionAlias =
|
export type SharedSectionAlias =
|
||||||
| 'annotations'
|
| 'annotations'
|
||||||
| 'colorScheme'
|
| 'colorScheme'
|
||||||
@ -253,28 +273,23 @@ export type SharedSectionAlias =
|
|||||||
| 'sqlaTimeSeries'
|
| 'sqlaTimeSeries'
|
||||||
| 'NVD3TimeSeries';
|
| 'NVD3TimeSeries';
|
||||||
|
|
||||||
export interface OverrideSharedControlItem {
|
export interface OverrideSharedControlItem<A extends SharedControlAlias = SharedControlAlias> {
|
||||||
name: SharedControlAlias;
|
name: A;
|
||||||
override: Partial<SharedControlConfig>;
|
override: Partial<SharedControls[A]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
export type CustomControlItem = {
|
||||||
export interface CustomControlItem<P = any> {
|
|
||||||
name: string;
|
name: string;
|
||||||
config: CustomComponentControlConfig<P>;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
}
|
config: BaseControlConfig<any, any, any>;
|
||||||
|
};
|
||||||
export type ControlSetItem =
|
|
||||||
| SharedControlAlias
|
|
||||||
| OverrideSharedControlItem
|
|
||||||
| CustomControlItem
|
|
||||||
// use ReactElement instead of ReactNode because `string`, `number`, etc. may
|
|
||||||
// interfere with other ControlSetItem types
|
|
||||||
| ReactElement
|
|
||||||
| null;
|
|
||||||
|
|
||||||
|
// use ReactElement instead of ReactNode because `string`, `number`, etc. may
|
||||||
|
// interfere with other ControlSetItem types
|
||||||
export type ExpandedControlItem = CustomControlItem | ReactElement | null;
|
export type ExpandedControlItem = CustomControlItem | ReactElement | null;
|
||||||
|
|
||||||
|
export type ControlSetItem = SharedControlAlias | OverrideSharedControlItem | ExpandedControlItem;
|
||||||
|
|
||||||
export type ControlSetRow = ControlSetItem[];
|
export type ControlSetRow = ControlSetItem[];
|
||||||
|
|
||||||
// Ref:
|
// Ref:
|
||||||
@ -296,7 +311,7 @@ export interface ControlPanelConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ControlOverrides = {
|
export type ControlOverrides = {
|
||||||
[P in SharedControlAlias]?: Partial<ControlConfig>;
|
[P in SharedControlAlias]?: Partial<SharedControls[P]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SectionOverrides = {
|
export type SectionOverrides = {
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* 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, { ReactElement } from 'react';
|
||||||
|
import sharedControls from '../shared-controls';
|
||||||
|
import sharedControlComponents from '../shared-controls/components';
|
||||||
|
import { ControlType, ControlSetItem, ExpandedControlItem, ControlOverrides } from '../types';
|
||||||
|
|
||||||
|
export function expandControlType(controlType: ControlType) {
|
||||||
|
if (typeof controlType === 'string' && controlType in sharedControlComponents) {
|
||||||
|
return sharedControlComponents[controlType as keyof typeof sharedControlComponents];
|
||||||
|
}
|
||||||
|
return controlType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand a shorthand control config item to full config in the format of
|
||||||
|
* {
|
||||||
|
* name: ...,
|
||||||
|
* config: {
|
||||||
|
* type: ...,
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
export function expandControlConfig(
|
||||||
|
control: ControlSetItem,
|
||||||
|
controlOverrides: ControlOverrides = {},
|
||||||
|
): ExpandedControlItem {
|
||||||
|
// one of the named shared controls
|
||||||
|
if (typeof control === 'string' && control in sharedControls) {
|
||||||
|
const name = control;
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
config: {
|
||||||
|
...sharedControls[name],
|
||||||
|
...controlOverrides[name],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// JSX/React element or NULL
|
||||||
|
if (!control || typeof control === 'string' || React.isValidElement(control)) {
|
||||||
|
return control as ReactElement;
|
||||||
|
}
|
||||||
|
// already fully expanded control config
|
||||||
|
if ('name' in control && 'config' in control) {
|
||||||
|
return {
|
||||||
|
...control,
|
||||||
|
config: {
|
||||||
|
...control.config,
|
||||||
|
type: expandControlType(control.config.type as ControlType),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// apply overrides with shared controls
|
||||||
|
if ('override' in control && control.name in sharedControls) {
|
||||||
|
const { name, override } = control;
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
config: {
|
||||||
|
...sharedControls[name],
|
||||||
|
...override,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
@ -31,9 +31,9 @@ export function formatSelectOptions<T extends Formattable>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* outputs array of arrays
|
* Outputs array of arrays
|
||||||
* formatSelectOptionsForRange(1, 5)
|
* >> formatSelectOptionsForRange(1, 5)
|
||||||
* returns [[1,'1'], [2,'2'], [3,'3'], [4,'4'], [5,'5']]
|
* >> [[1,'1'], [2,'2'], [3,'3'], [4,'4'], [5,'5']]
|
||||||
*/
|
*/
|
||||||
export function formatSelectOptionsForRange(start: number, end: number) {
|
export function formatSelectOptionsForRange(start: number, end: number) {
|
||||||
const options: Formatted[] = [];
|
const options: Formatted[] = [];
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* 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 { expandControlConfig, sharedControls } from '../../src';
|
||||||
|
|
||||||
|
describe('expandControlConfig()', () => {
|
||||||
|
it('expands shared control alias', () => {
|
||||||
|
expect(expandControlConfig('metrics')).toEqual({
|
||||||
|
name: 'metrics',
|
||||||
|
config: sharedControls.metrics,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('expands control with overrides', () => {
|
||||||
|
expect(
|
||||||
|
expandControlConfig({
|
||||||
|
name: 'metrics',
|
||||||
|
override: {
|
||||||
|
label: 'Custom Metric',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toEqual({
|
||||||
|
name: 'metrics',
|
||||||
|
config: {
|
||||||
|
...sharedControls.metrics,
|
||||||
|
label: 'Custom Metric',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('leave full control untouched', () => {
|
||||||
|
const input = {
|
||||||
|
name: 'metrics',
|
||||||
|
config: {
|
||||||
|
type: 'SelectControl',
|
||||||
|
label: 'Custom Metric',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(expandControlConfig(input)).toEqual(input);
|
||||||
|
});
|
||||||
|
it('leave NULL and ReactElement untouched', () => {
|
||||||
|
expect(expandControlConfig(null)).toBeNull();
|
||||||
|
const input = <h1>Test</h1>;
|
||||||
|
expect(expandControlConfig(input)).toBe(input);
|
||||||
|
});
|
||||||
|
it('leave unknown text untouched', () => {
|
||||||
|
const input = 'superset-ui';
|
||||||
|
expect(expandControlConfig(input as never)).toBe(input);
|
||||||
|
});
|
||||||
|
});
|
@ -38,6 +38,7 @@
|
|||||||
"@superset-ui/chart-controls": "^0.14.0",
|
"@superset-ui/chart-controls": "^0.14.0",
|
||||||
"@superset-ui/number-format": "^0.14.0",
|
"@superset-ui/number-format": "^0.14.0",
|
||||||
"@superset-ui/query": "^0.14.0",
|
"@superset-ui/query": "^0.14.0",
|
||||||
|
"@superset-ui/style": "^0.14.0",
|
||||||
"@superset-ui/time-format": "^0.14.0",
|
"@superset-ui/time-format": "^0.14.0",
|
||||||
"@superset-ui/translation": "^0.14.0",
|
"@superset-ui/translation": "^0.14.0",
|
||||||
"@superset-ui/validator": "^0.14.0",
|
"@superset-ui/validator": "^0.14.0",
|
||||||
|
@ -22,41 +22,168 @@ import { t } from '@superset-ui/translation';
|
|||||||
import {
|
import {
|
||||||
formatSelectOptions,
|
formatSelectOptions,
|
||||||
D3_TIME_FORMAT_OPTIONS,
|
D3_TIME_FORMAT_OPTIONS,
|
||||||
|
ControlConfig,
|
||||||
ColumnOption,
|
ColumnOption,
|
||||||
|
ControlStateMapping,
|
||||||
ControlPanelConfig,
|
ControlPanelConfig,
|
||||||
|
ControlPanelsContainerProps,
|
||||||
|
sharedControls,
|
||||||
} from '@superset-ui/chart-controls';
|
} from '@superset-ui/chart-controls';
|
||||||
import { validateNonEmpty } from '@superset-ui/validator';
|
import { validateNonEmpty } from '@superset-ui/validator';
|
||||||
|
import { smartDateFormatter } from '@superset-ui/time-format';
|
||||||
|
|
||||||
|
export const PAGE_SIZE_OPTIONS = formatSelectOptions<number>([[0, t('All')], 10, 20, 50, 100, 200]);
|
||||||
|
|
||||||
|
export enum QueryMode {
|
||||||
|
aggregate = 'aggregate',
|
||||||
|
raw = 'raw',
|
||||||
|
}
|
||||||
|
|
||||||
|
const QueryModeLabel = {
|
||||||
|
[QueryMode.aggregate]: t('Aggregate'),
|
||||||
|
[QueryMode.raw]: t('Raw Records'),
|
||||||
|
};
|
||||||
|
|
||||||
|
function getQueryMode(controls: ControlStateMapping): QueryMode {
|
||||||
|
const mode = controls?.query_mode?.value;
|
||||||
|
if (mode === QueryMode.aggregate || mode === QueryMode.raw) {
|
||||||
|
return mode as QueryMode;
|
||||||
|
}
|
||||||
|
const groupby = controls?.groupby?.value;
|
||||||
|
const hasGroupBy = groupby && (groupby as string[])?.length > 0;
|
||||||
|
return hasGroupBy ? QueryMode.aggregate : QueryMode.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visibility check
|
||||||
|
*/
|
||||||
|
function isQueryMode(mode: QueryMode) {
|
||||||
|
return ({ controls }: ControlPanelsContainerProps) => {
|
||||||
|
return getQueryMode(controls) === mode;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAggMode = isQueryMode(QueryMode.aggregate);
|
||||||
|
const isRawMode = isQueryMode(QueryMode.raw);
|
||||||
|
|
||||||
|
const queryMode: ControlConfig<'RadioButtonControl'> = {
|
||||||
|
type: 'RadioButtonControl',
|
||||||
|
label: t('Query Mode'),
|
||||||
|
default: QueryMode.aggregate,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: QueryModeLabel[QueryMode.aggregate],
|
||||||
|
value: QueryMode.aggregate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: QueryModeLabel[QueryMode.raw],
|
||||||
|
value: QueryMode.raw,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
mapStateToProps: ({ controls }) => {
|
||||||
|
return { value: getQueryMode(controls) };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const all_columns: typeof sharedControls.groupby = {
|
||||||
|
type: 'SelectControl',
|
||||||
|
label: t('Columns'),
|
||||||
|
description: t('Columns to display'),
|
||||||
|
multi: true,
|
||||||
|
freeForm: true,
|
||||||
|
allowAll: true,
|
||||||
|
commaChoosesOption: false,
|
||||||
|
default: [],
|
||||||
|
optionRenderer: c => <ColumnOption showType column={c} />,
|
||||||
|
valueRenderer: c => <ColumnOption column={c} />,
|
||||||
|
valueKey: 'column_name',
|
||||||
|
mapStateToProps: ({ datasource, controls }) => ({
|
||||||
|
options: datasource?.columns || [],
|
||||||
|
queryMode: getQueryMode(controls),
|
||||||
|
}),
|
||||||
|
visibility: isRawMode,
|
||||||
|
};
|
||||||
|
|
||||||
|
const percent_metrics: typeof sharedControls.metrics = {
|
||||||
|
type: 'MetricsControl',
|
||||||
|
label: t('Percentage Metrics'),
|
||||||
|
description: t('Metrics for which percentage of total are to be displayed'),
|
||||||
|
multi: true,
|
||||||
|
visibility: isAggMode,
|
||||||
|
mapStateToProps: ({ datasource, controls }) => {
|
||||||
|
return {
|
||||||
|
columns: datasource?.columns || [],
|
||||||
|
savedMetrics: datasource?.metrics || [],
|
||||||
|
datasourceType: datasource?.type,
|
||||||
|
queryMode: getQueryMode(controls),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
validators: [],
|
||||||
|
};
|
||||||
|
|
||||||
const config: ControlPanelConfig = {
|
const config: ControlPanelConfig = {
|
||||||
controlPanelSections: [
|
controlPanelSections: [
|
||||||
{
|
{
|
||||||
label: t('GROUP BY'),
|
label: t('Query'),
|
||||||
description: t('Use this section if you want a query that aggregates'),
|
|
||||||
expanded: true,
|
expanded: true,
|
||||||
controlSetRows: [
|
controlSetRows: [
|
||||||
['groupby'],
|
|
||||||
['metrics'],
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: 'percent_metrics',
|
name: 'query_mode',
|
||||||
config: {
|
config: queryMode,
|
||||||
type: 'MetricsControl',
|
},
|
||||||
multi: true,
|
],
|
||||||
mapStateToProps: ({ datasource }) => {
|
[
|
||||||
return {
|
{
|
||||||
columns: datasource?.columns || [],
|
name: 'groupby',
|
||||||
savedMetrics: datasource?.metrics || [],
|
override: {
|
||||||
datasourceType: datasource?.type,
|
visibility: isAggMode,
|
||||||
};
|
|
||||||
},
|
|
||||||
default: [],
|
|
||||||
label: t('Percentage Metrics'),
|
|
||||||
validators: [],
|
|
||||||
description: t('Metrics for which percentage of total are to be displayed'),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
['timeseries_limit_metric', 'row_limit'],
|
[
|
||||||
|
{
|
||||||
|
name: 'metrics',
|
||||||
|
override: {
|
||||||
|
validators: [],
|
||||||
|
visibility: isAggMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'all_columns',
|
||||||
|
config: all_columns,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'percent_metrics',
|
||||||
|
config: percent_metrics,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'timeseries_limit_metric',
|
||||||
|
override: {
|
||||||
|
visibility: isAggMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'order_by_cols',
|
||||||
|
config: {
|
||||||
|
type: 'SelectControl',
|
||||||
|
label: t('Ordering'),
|
||||||
|
description: t('One or many metrics to display'),
|
||||||
|
multi: true,
|
||||||
|
default: [],
|
||||||
|
mapStateToProps: ({ datasource }) => ({
|
||||||
|
choices: datasource?.order_by_choices || [],
|
||||||
|
}),
|
||||||
|
visibility: isRawMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
['row_limit'],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: 'include_time',
|
name: 'include_time',
|
||||||
@ -67,6 +194,7 @@ const config: ControlPanelConfig = {
|
|||||||
'Whether to include the time granularity as defined in the time section',
|
'Whether to include the time granularity as defined in the time section',
|
||||||
),
|
),
|
||||||
default: false,
|
default: false,
|
||||||
|
visibility: isAggMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -76,60 +204,13 @@ const config: ControlPanelConfig = {
|
|||||||
label: t('Sort Descending'),
|
label: t('Sort Descending'),
|
||||||
default: true,
|
default: true,
|
||||||
description: t('Whether to sort descending or ascending'),
|
description: t('Whether to sort descending or ascending'),
|
||||||
|
visibility: isAggMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
['adhoc_filters'],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: t('NOT GROUPED BY'),
|
|
||||||
description: t('Use this section if you want to query atomic rows'),
|
|
||||||
expanded: true,
|
|
||||||
controlSetRows: [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'all_columns',
|
|
||||||
config: {
|
|
||||||
type: 'SelectControl',
|
|
||||||
multi: true,
|
|
||||||
label: t('Columns'),
|
|
||||||
default: [],
|
|
||||||
description: t('Columns to display'),
|
|
||||||
optionRenderer: (c: never) => <ColumnOption showType column={c} />,
|
|
||||||
valueRenderer: (c: never) => <ColumnOption column={c} />,
|
|
||||||
valueKey: 'column_name',
|
|
||||||
allowAll: true,
|
|
||||||
mapStateToProps: ({ datasource }) => ({
|
|
||||||
options: datasource?.columns || [],
|
|
||||||
}),
|
|
||||||
commaChoosesOption: false,
|
|
||||||
freeForm: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'order_by_cols',
|
|
||||||
config: {
|
|
||||||
type: 'SelectControl',
|
|
||||||
multi: true,
|
|
||||||
label: t('Ordering'),
|
|
||||||
default: [],
|
|
||||||
description: t('One or many metrics to display'),
|
|
||||||
mapStateToProps: ({ datasource }) => ({
|
|
||||||
choices: datasource?.order_by_choices || [],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
['row_limit', null],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('Query'),
|
|
||||||
expanded: true,
|
|
||||||
controlSetRows: [['adhoc_filters']],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: t('Options'),
|
label: t('Options'),
|
||||||
expanded: true,
|
expanded: true,
|
||||||
@ -141,7 +222,7 @@ const config: ControlPanelConfig = {
|
|||||||
type: 'SelectControl',
|
type: 'SelectControl',
|
||||||
freeForm: true,
|
freeForm: true,
|
||||||
label: t('Table Timestamp Format'),
|
label: t('Table Timestamp Format'),
|
||||||
default: '%Y-%m-%d %H:%M:%S',
|
default: smartDateFormatter.id,
|
||||||
renderTrigger: true,
|
renderTrigger: true,
|
||||||
validators: [validateNonEmpty],
|
validators: [validateNonEmpty],
|
||||||
clearable: false,
|
clearable: false,
|
||||||
@ -158,8 +239,8 @@ const config: ControlPanelConfig = {
|
|||||||
freeForm: true,
|
freeForm: true,
|
||||||
renderTrigger: true,
|
renderTrigger: true,
|
||||||
label: t('Page Length'),
|
label: t('Page Length'),
|
||||||
default: 0,
|
default: null,
|
||||||
choices: formatSelectOptions([0, 10, 25, 40, 50, 75, 100, 150, 200]),
|
choices: PAGE_SIZE_OPTIONS,
|
||||||
description: t('Rows per page, 0 means no pagination'),
|
description: t('Rows per page, 0 means no pagination'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -225,11 +306,6 @@ const config: ControlPanelConfig = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
controlOverrides: {
|
|
||||||
metrics: {
|
|
||||||
validators: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sectionOverrides: {
|
sectionOverrides: {
|
||||||
druidTimeSeries: {
|
druidTimeSeries: {
|
||||||
controlSetRows: [['granularity', 'druid_time_origin'], ['time_range']],
|
controlSetRows: [['granularity', 'druid_time_origin'], ['time_range']],
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
@ -21,45 +22,168 @@ import { t } from '@superset-ui/translation';
|
|||||||
import {
|
import {
|
||||||
formatSelectOptions,
|
formatSelectOptions,
|
||||||
D3_TIME_FORMAT_OPTIONS,
|
D3_TIME_FORMAT_OPTIONS,
|
||||||
|
ControlConfig,
|
||||||
ColumnOption,
|
ColumnOption,
|
||||||
|
ControlStateMapping,
|
||||||
|
ControlPanelConfig,
|
||||||
|
ControlPanelsContainerProps,
|
||||||
|
sharedControls,
|
||||||
} from '@superset-ui/chart-controls';
|
} from '@superset-ui/chart-controls';
|
||||||
import { validateNonEmpty } from '@superset-ui/validator';
|
import { validateNonEmpty } from '@superset-ui/validator';
|
||||||
import { smartDateFormatter } from '@superset-ui/time-format';
|
import { smartDateFormatter } from '@superset-ui/time-format';
|
||||||
|
|
||||||
export const PAGE_SIZE_OPTIONS = formatSelectOptions<number>([[0, t('All')], 10, 20, 50, 100, 200]);
|
export const PAGE_SIZE_OPTIONS = formatSelectOptions<number>([[0, t('All')], 10, 20, 50, 100, 200]);
|
||||||
|
|
||||||
export default {
|
export enum QueryMode {
|
||||||
|
aggregate = 'aggregate',
|
||||||
|
raw = 'raw',
|
||||||
|
}
|
||||||
|
|
||||||
|
const QueryModeLabel = {
|
||||||
|
[QueryMode.aggregate]: t('Aggregate'),
|
||||||
|
[QueryMode.raw]: t('Raw Records'),
|
||||||
|
};
|
||||||
|
|
||||||
|
function getQueryMode(controls: ControlStateMapping): QueryMode {
|
||||||
|
const mode = controls?.query_mode?.value;
|
||||||
|
if (mode === QueryMode.aggregate || mode === QueryMode.raw) {
|
||||||
|
return mode as QueryMode;
|
||||||
|
}
|
||||||
|
const groupby = controls?.groupby?.value;
|
||||||
|
const hasGroupBy = groupby && (groupby as string[])?.length > 0;
|
||||||
|
return hasGroupBy ? QueryMode.aggregate : QueryMode.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visibility check
|
||||||
|
*/
|
||||||
|
function isQueryMode(mode: QueryMode) {
|
||||||
|
return ({ controls }: ControlPanelsContainerProps) => {
|
||||||
|
return getQueryMode(controls) === mode;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAggMode = isQueryMode(QueryMode.aggregate);
|
||||||
|
const isRawMode = isQueryMode(QueryMode.raw);
|
||||||
|
|
||||||
|
const queryMode: ControlConfig<'RadioButtonControl'> = {
|
||||||
|
type: 'RadioButtonControl',
|
||||||
|
label: t('Query Mode'),
|
||||||
|
default: QueryMode.aggregate,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: QueryModeLabel[QueryMode.aggregate],
|
||||||
|
value: QueryMode.aggregate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: QueryModeLabel[QueryMode.raw],
|
||||||
|
value: QueryMode.raw,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
mapStateToProps: ({ controls }) => {
|
||||||
|
return { value: getQueryMode(controls) };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const all_columns: typeof sharedControls.groupby = {
|
||||||
|
type: 'SelectControl',
|
||||||
|
label: t('Columns'),
|
||||||
|
description: t('Columns to display'),
|
||||||
|
multi: true,
|
||||||
|
freeForm: true,
|
||||||
|
allowAll: true,
|
||||||
|
commaChoosesOption: false,
|
||||||
|
default: [],
|
||||||
|
optionRenderer: c => <ColumnOption showType column={c} />,
|
||||||
|
valueRenderer: c => <ColumnOption column={c} />,
|
||||||
|
valueKey: 'column_name',
|
||||||
|
mapStateToProps: ({ datasource, controls }) => ({
|
||||||
|
options: datasource?.columns || [],
|
||||||
|
queryMode: getQueryMode(controls),
|
||||||
|
}),
|
||||||
|
visibility: isRawMode,
|
||||||
|
};
|
||||||
|
|
||||||
|
const percent_metrics: typeof sharedControls.metrics = {
|
||||||
|
type: 'MetricsControl',
|
||||||
|
label: t('Percentage Metrics'),
|
||||||
|
description: t('Metrics for which percentage of total are to be displayed'),
|
||||||
|
multi: true,
|
||||||
|
visibility: isAggMode,
|
||||||
|
mapStateToProps: ({ datasource, controls }) => {
|
||||||
|
return {
|
||||||
|
columns: datasource?.columns || [],
|
||||||
|
savedMetrics: datasource?.metrics || [],
|
||||||
|
datasourceType: datasource?.type,
|
||||||
|
queryMode: getQueryMode(controls),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
default: [],
|
||||||
|
validators: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const config: ControlPanelConfig = {
|
||||||
controlPanelSections: [
|
controlPanelSections: [
|
||||||
{
|
{
|
||||||
label: t('GROUP BY'),
|
label: t('Query'),
|
||||||
description: t('Use this section if you want a query that aggregates'),
|
|
||||||
expanded: true,
|
expanded: true,
|
||||||
controlSetRows: [
|
controlSetRows: [
|
||||||
['groupby'],
|
|
||||||
['metrics'],
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: 'percent_metrics',
|
name: 'query_mode',
|
||||||
config: {
|
config: queryMode,
|
||||||
type: 'MetricsControl',
|
},
|
||||||
multi: true,
|
],
|
||||||
mapStateToProps: (state: never) => {
|
[
|
||||||
const { datasource } = state;
|
{
|
||||||
const { columns, metrics, type } = datasource;
|
name: 'groupby',
|
||||||
return {
|
override: {
|
||||||
columns: datasource ? columns : [],
|
visibility: isAggMode,
|
||||||
savedMetrics: datasource ? metrics : [],
|
|
||||||
datasourceType: datasource && type,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
default: [],
|
|
||||||
label: t('Percentage Metrics'),
|
|
||||||
validators: [],
|
|
||||||
description: t('Metrics for which percentage of total are to be displayed'),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
['timeseries_limit_metric', 'row_limit'],
|
[
|
||||||
|
{
|
||||||
|
name: 'metrics',
|
||||||
|
override: {
|
||||||
|
validators: [],
|
||||||
|
visibility: isAggMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'all_columns',
|
||||||
|
config: all_columns,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'percent_metrics',
|
||||||
|
config: percent_metrics,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'timeseries_limit_metric',
|
||||||
|
override: {
|
||||||
|
visibility: isAggMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'order_by_cols',
|
||||||
|
config: {
|
||||||
|
type: 'SelectControl',
|
||||||
|
label: t('Ordering'),
|
||||||
|
description: t('One or many metrics to display'),
|
||||||
|
multi: true,
|
||||||
|
default: [],
|
||||||
|
mapStateToProps: ({ datasource }) => ({
|
||||||
|
choices: datasource?.order_by_choices || [],
|
||||||
|
}),
|
||||||
|
visibility: isRawMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
['row_limit'],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: 'include_time',
|
name: 'include_time',
|
||||||
@ -70,6 +194,7 @@ export default {
|
|||||||
'Whether to include the time granularity as defined in the time section',
|
'Whether to include the time granularity as defined in the time section',
|
||||||
),
|
),
|
||||||
default: false,
|
default: false,
|
||||||
|
visibility: isAggMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -79,61 +204,13 @@ export default {
|
|||||||
label: t('Sort Descending'),
|
label: t('Sort Descending'),
|
||||||
default: true,
|
default: true,
|
||||||
description: t('Whether to sort descending or ascending'),
|
description: t('Whether to sort descending or ascending'),
|
||||||
|
visibility: isAggMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
['adhoc_filters'],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: t('NOT GROUPED BY'),
|
|
||||||
description: t('Use this section if you want to query atomic rows'),
|
|
||||||
expanded: true,
|
|
||||||
controlSetRows: [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'all_columns',
|
|
||||||
config: {
|
|
||||||
type: 'SelectControl',
|
|
||||||
multi: true,
|
|
||||||
label: t('Columns'),
|
|
||||||
default: [],
|
|
||||||
description: t('Columns to display'),
|
|
||||||
optionRenderer: (c: never) => <ColumnOption showType column={c} />,
|
|
||||||
valueRenderer: (c: never) => <ColumnOption column={c} />,
|
|
||||||
valueKey: 'column_name',
|
|
||||||
allowAll: true,
|
|
||||||
mapStateToProps: (state: { datasource: { columns: unknown } }) => ({
|
|
||||||
options: state.datasource ? state.datasource.columns : [],
|
|
||||||
}),
|
|
||||||
commaChoosesOption: false,
|
|
||||||
freeForm: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'order_by_cols',
|
|
||||||
config: {
|
|
||||||
type: 'SelectControl',
|
|
||||||
multi: true,
|
|
||||||
label: t('Ordering'),
|
|
||||||
default: [],
|
|
||||||
description: t('One or many metrics to display'),
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
mapStateToProps: (state: { datasource: { order_by_choices: never } }) => ({
|
|
||||||
choices: state.datasource ? state.datasource.order_by_choices : [],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
['row_limit', null],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('Query'),
|
|
||||||
expanded: true,
|
|
||||||
controlSetRows: [['adhoc_filters']],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: t('Options'),
|
label: t('Options'),
|
||||||
expanded: true,
|
expanded: true,
|
||||||
@ -164,9 +241,7 @@ export default {
|
|||||||
label: t('Page Length'),
|
label: t('Page Length'),
|
||||||
default: null,
|
default: null,
|
||||||
choices: PAGE_SIZE_OPTIONS,
|
choices: PAGE_SIZE_OPTIONS,
|
||||||
description: t(
|
description: t('Rows per page, 0 means no pagination'),
|
||||||
'Rows per page, 0 means no pagination. Leave empty to automatically add pagination for large tables.',
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
@ -231,11 +306,6 @@ export default {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
controlOverrides: {
|
|
||||||
metrics: {
|
|
||||||
validators: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sectionOverrides: {
|
sectionOverrides: {
|
||||||
druidTimeSeries: {
|
druidTimeSeries: {
|
||||||
controlSetRows: [['granularity', 'druid_time_origin'], ['time_range']],
|
controlSetRows: [['granularity', 'druid_time_origin'], ['time_range']],
|
||||||
@ -245,3 +315,5 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
@ -144,7 +144,10 @@ const processColumns = memoizeOne(function processColumns(props: TableChartProps
|
|||||||
] as [typeof metrics, typeof percentMetrics, typeof columns];
|
] as [typeof metrics, typeof percentMetrics, typeof columns];
|
||||||
}, isEqualColumns);
|
}, isEqualColumns);
|
||||||
|
|
||||||
const getDefaultPageSize = (
|
/**
|
||||||
|
* Automatically set page size based on number of cells.
|
||||||
|
*/
|
||||||
|
const getPageSize = (
|
||||||
pageSize: number | string | null | undefined,
|
pageSize: number | string | null | undefined,
|
||||||
numRecords: number,
|
numRecords: number,
|
||||||
numColumns: number,
|
numColumns: number,
|
||||||
@ -194,7 +197,7 @@ export default function transformProps(chartProps: TableChartProps): TableChartT
|
|||||||
showCellBars,
|
showCellBars,
|
||||||
sortDesc,
|
sortDesc,
|
||||||
includeSearch,
|
includeSearch,
|
||||||
pageSize: getDefaultPageSize(pageSize, data.length, columns.length),
|
pageSize: getPageSize(pageSize, data.length, columns.length),
|
||||||
filters,
|
filters,
|
||||||
emitFilter: tableFilter === true,
|
emitFilter: tableFilter === true,
|
||||||
onChangeFilter,
|
onChangeFilter,
|
||||||
|
Loading…
Reference in New Issue
Block a user