feat: Dynamic dashboard component (#17208)

* fix:fix get permission function

* feat: dynamic loading of dashboard components

* fix: revert image

* fix: fix py

* fix: fix py

* fix: pass state to dynamic component

* lint: add typing

* lint: fix lint

* lint: fix lint

* refactor: re-run pipeline

* fix: fix CR notes

* fix: fix CR notes

* move types and interfaces to core

* reorder exports

* rename Scope and Target

Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com>
This commit is contained in:
simcha90 2022-02-09 17:44:40 +02:00 committed by GitHub
parent 5ee070c402
commit bcad1acec2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 803 additions and 218 deletions

View File

@ -16,14 +16,5 @@
* specific language governing permissions and limitations
* under the License.
*/
import { DataMask } from '@superset-ui/core';
export enum DataMaskType {
NativeFilters = 'nativeFilters',
CrossFilters = 'crossFilters',
}
export type DataMaskState = { [id: string]: DataMask };
export type DataMaskWithId = { id: string } & DataMask;
export type DataMaskStateWithId = { [filterId: string]: DataMaskWithId };
export * from './types/Base';

View File

@ -19,20 +19,20 @@
import { AdhocFilter, DataMask } from '@superset-ui/core';
export interface Column {
export interface NativeFilterColumn {
name: string;
displayName?: string;
}
export interface Scope {
export interface NativeFilterScope {
rootPath: string[];
excluded: number[];
}
/** The target of a filter is the datasource/column being filtered */
export interface Target {
export interface NativeFilterTarget {
datasetId: number;
column: Column;
column: NativeFilterColumn;
// maybe someday support this?
// show values from these columns in the filter options selector
@ -44,16 +44,37 @@ export enum NativeFilterType {
DIVIDER = 'DIVIDER',
}
export enum DataMaskType {
NativeFilters = 'nativeFilters',
CrossFilters = 'crossFilters',
}
export type DataMaskState = { [id: string]: DataMask };
export type DataMaskWithId = { id: string } & DataMask;
export type DataMaskStateWithId = { [filterId: string]: DataMaskWithId };
export type FilterSet = {
id: number;
name: string;
nativeFilters: Filters;
dataMask: DataMaskStateWithId;
};
export type FilterSets = {
[filtersSetId: string]: FilterSet;
};
export interface Filter {
cascadeParentIds: string[];
defaultDataMask: DataMask;
id: string; // randomly generated at filter creation
name: string;
scope: Scope;
scope: NativeFilterScope;
filterType: string;
// for now there will only ever be one target
// when multiple targets are supported, change this to Target[]
targets: [Partial<Target>];
targets: [Partial<NativeFilterTarget>];
controlValues: {
[key: string]: any;
};
@ -70,6 +91,7 @@ export interface Filter {
type: typeof NativeFilterType.NATIVE_FILTER;
description: string;
}
export interface Divider {
id: string;
title: string;
@ -78,3 +100,18 @@ export interface Divider {
}
export type FilterConfiguration = Array<Filter | Divider>;
export type Filters = {
[filterId: string]: Filter;
};
export type NativeFiltersState = {
filters: Filters;
filterSets: FilterSets;
focusedFilterId?: string;
};
export type DashboardComponentMetadata = {
nativeFilters: NativeFiltersState;
dataMask: DataMaskStateWithId;
};

View File

@ -16,11 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
export * from './models';
export * from './utils';
export * from './types';
export * from './translation';
export * from './connection';
export * from './dashboard';
export * from './dynamic-plugins';
export * from './query';
export * from './number-format';

View File

@ -16,10 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
import { ExtraFormData } from '@superset-ui/core';
import { NativeFilterType } from 'src/dashboard/components/nativeFilters/types';
import { NativeFiltersState } from 'src/dashboard/reducers/types';
import { DataMaskStateWithId } from '../../src/dataMask/types';
import {
DataMaskStateWithId,
ExtraFormData,
NativeFiltersState,
NativeFilterType,
} from '@superset-ui/core';
export const nativeFilters: NativeFiltersState = {
filterSets: {},

View File

@ -17,21 +17,21 @@
* under the License.
*/
import { makeApi } from '@superset-ui/core';
import {
FilterConfiguration,
Filters,
FilterSet,
FilterSets,
makeApi,
} from '@superset-ui/core';
import { Dispatch } from 'redux';
import { FilterConfiguration } from 'src/dashboard/components/nativeFilters/types';
import {
SET_DATA_MASK_FOR_FILTER_CONFIG_FAIL,
setDataMaskForFilterConfigComplete,
} from 'src/dataMask/actions';
import { HYDRATE_DASHBOARD } from './hydrate';
import { dashboardInfoChanged } from './dashboardInfo';
import {
Filters,
FilterSet,
FilterSetFullData,
FilterSets,
} from '../reducers/types';
import { FilterSetFullData } from '../reducers/types';
import { DashboardInfo, RootState } from '../types';
export const SET_FILTER_CONFIG_BEGIN = 'SET_FILTER_CONFIG_BEGIN';

View File

@ -31,6 +31,8 @@ import NewRow from './gridComponents/new/NewRow';
import NewTabs from './gridComponents/new/NewTabs';
import NewMarkdown from './gridComponents/new/NewMarkdown';
import SliceAdder from '../containers/SliceAdder';
import dashboardComponents from '../../visualizations/presets/dashboardComponents';
import NewDynamicComponent from './gridComponents/new/NewDynamicComponent';
export interface BCPProps {
isStandalone: boolean;
@ -106,6 +108,14 @@ const BuilderComponentPane: React.FC<BCPProps> = ({
<NewHeader />
<NewMarkdown />
<NewDivider />
{dashboardComponents
.getAll()
.map(({ key: componentKey, metadata }) => (
<NewDynamicComponent
metadata={metadata}
componentKey={componentKey}
/>
))}
</Tabs.TabPane>
<Tabs.TabPane
key={2}

View File

@ -18,15 +18,15 @@
*/
import React, { FC } from 'react';
import { FormInstance } from 'antd/lib/form';
import { NativeFilterScope } from '@superset-ui/core';
import FilterScope from 'src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FilterScope/FilterScope';
import { setCrossFilterFieldValues } from 'src/dashboard/components/CrossFilterScopingModal/utils';
import { Scope } from 'src/dashboard/components/nativeFilters/types';
import { useForceUpdate } from 'src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/utils';
import { CrossFilterScopingFormType } from 'src/dashboard/components/CrossFilterScopingModal/types';
type CrossFilterScopingFormProps = {
chartId: number;
scope: Scope;
scope: NativeFilterScope;
form: FormInstance<CrossFilterScopingFormType>;
};

View File

@ -17,8 +17,8 @@
* under the License.
*/
import { Scope } from '../nativeFilters/types';
import { NativeFilterScope } from '@superset-ui/core';
export type CrossFilterScopingFormType = {
scope: Scope;
scope: NativeFilterScope;
};

View File

@ -20,7 +20,7 @@
// when its container size changes, due to e.g., builder side panel opening
import React, { FC, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { FeatureFlag, isFeatureEnabled } from '@superset-ui/core';
import { FeatureFlag, Filters, isFeatureEnabled } from '@superset-ui/core';
import { ParentSize } from '@vx/responsive';
import Tabs from 'src/components/Tabs';
import DashboardGrid from 'src/dashboard/containers/DashboardGrid';
@ -31,7 +31,6 @@ import {
DASHBOARD_ROOT_DEPTH,
} from 'src/dashboard/util/constants';
import { getRootLevelTabIndex, getRootLevelTabsComponent } from './utils';
import { Filters } from '../../reducers/types';
import { getChartIdsInFilterScope } from '../../util/activeDashboardFilters';
import findTabIndexByComponentId from '../../util/findTabIndexByComponentId';
import { findTabsWithChartsInScope } from '../nativeFilters/utils';

View File

@ -17,6 +17,7 @@
* under the License.
*/
import { useSelector } from 'react-redux';
import { Filter } from '@superset-ui/core';
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
import { useCallback, useEffect, useState, useContext } from 'react';
import { URL_PARAMS } from 'src/constants';
@ -27,7 +28,6 @@ import {
useFilters,
useNativeFiltersDataMask,
} from '../nativeFilters/FilterBar/state';
import { Filter } from '../nativeFilters/types';
// eslint-disable-next-line import/prefer-default-export
export const useNativeFilters = () => {

View File

@ -20,9 +20,9 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { uniqWith } from 'lodash';
import cx from 'classnames';
import { DataMaskStateWithId, Filters } from '@superset-ui/core';
import Icons from 'src/components/Icons';
import { usePrevious } from 'src/hooks/usePrevious';
import { DataMaskStateWithId } from 'src/dataMask/types';
import DetailsPanelPopover from './DetailsPanel';
import { Pill } from './Styles';
import {
@ -38,7 +38,6 @@ import {
DashboardLayout,
RootState,
} from '../../types';
import { Filters } from '../../reducers/types';
export interface FiltersBadgeProps {
chartId: number;

View File

@ -17,19 +17,21 @@
* under the License.
*/
import {
DataMaskStateWithId,
DataMaskType,
ensureIsArray,
FeatureFlag,
Filters,
FilterState,
isFeatureEnabled,
NativeFilterType,
} from '@superset-ui/core';
import { NO_TIME_RANGE, TIME_FILTER_MAP } from 'src/explore/constants';
import { getChartIdsInFilterScope } from 'src/dashboard/util/activeDashboardFilters';
import { ChartConfiguration, Filters } from 'src/dashboard/reducers/types';
import { DataMaskStateWithId, DataMaskType } from 'src/dataMask/types';
import { ChartConfiguration } from 'src/dashboard/reducers/types';
import { areObjectsEqual } from 'src/reduxUtils';
import { Layout } from '../../types';
import { getTreeCheckedItems } from '../nativeFilters/FiltersConfigModal/FiltersConfigForm/FilterScope/utils';
import { NativeFilterType } from '../nativeFilters/types';
export enum IndicatorStatus {
Unset = 'UNSET',

View File

@ -0,0 +1,177 @@
/**
* 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, { FC, Suspense } from 'react';
import { DashboardComponentMetadata, JsonObject, t } from '@superset-ui/core';
import backgroundStyleOptions from 'src/dashboard/util/backgroundStyleOptions';
import cx from 'classnames';
import { useSelector } from 'react-redux';
import DragDroppable from '../dnd/DragDroppable';
import { COLUMN_TYPE, ROW_TYPE } from '../../util/componentTypes';
import WithPopoverMenu from '../menu/WithPopoverMenu';
import ResizableContainer from '../resizable/ResizableContainer';
import {
BACKGROUND_TRANSPARENT,
GRID_BASE_UNIT,
GRID_MIN_COLUMN_COUNT,
} from '../../util/constants';
import HoverMenu from '../menu/HoverMenu';
import DeleteComponentButton from '../DeleteComponentButton';
import BackgroundStyleDropdown from '../menu/BackgroundStyleDropdown';
import dashboardComponents from '../../../visualizations/presets/dashboardComponents';
import { RootState } from '../../types';
type FilterSummaryType = {
component: JsonObject;
parentComponent: JsonObject;
index: number;
depth: number;
handleComponentDrop: (...args: any[]) => any;
editMode: boolean;
columnWidth: number;
availableColumnCount: number;
onResizeStart: Function;
onResizeStop: Function;
onResize: Function;
deleteComponent: Function;
updateComponents: Function;
parentId: number;
id: number;
};
const DynamicComponent: FC<FilterSummaryType> = ({
component,
parentComponent,
index,
depth,
handleComponentDrop,
editMode,
columnWidth,
availableColumnCount,
onResizeStart,
onResizeStop,
onResize,
deleteComponent,
parentId,
updateComponents,
id,
}) => {
// inherit the size of parent columns
const widthMultiple =
parentComponent.type === COLUMN_TYPE
? parentComponent.meta.width || GRID_MIN_COLUMN_COUNT
: component.meta.width || GRID_MIN_COLUMN_COUNT;
const handleDeleteComponent = () => {
deleteComponent(id, parentId);
};
const rowStyle = backgroundStyleOptions.find(
opt => opt.value === (component.meta.background || BACKGROUND_TRANSPARENT),
);
const updateMeta = (metaKey: string, nextValue: string | number) => {
updateComponents({
[component.id]: {
...component,
meta: {
...component.meta,
[metaKey]: nextValue,
},
},
});
};
const { Component } = dashboardComponents.get(component.meta.componentKey);
const dashboardData = useSelector<RootState, DashboardComponentMetadata>(
({ nativeFilters, dataMask }) => ({
nativeFilters,
dataMask,
}),
);
return (
<DragDroppable
// @ts-ignore
component={component}
// @ts-ignore
parentComponent={parentComponent}
orientation={parentComponent.type === ROW_TYPE ? 'column' : 'row'}
index={index}
depth={depth}
onDrop={handleComponentDrop}
editMode={editMode}
>
{({ dropIndicatorProps, dragSourceRef }) => (
<WithPopoverMenu
menuItems={[
<BackgroundStyleDropdown
id={`${component.id}-background`}
value={component.meta.background}
onChange={value => updateMeta('background', value)}
/>,
]}
editMode={editMode}
>
<div
data-test={`dashboard-${component.componentKey}`}
className={cx(
'dashboard-component',
`dashboard-${component.componentKey}`,
rowStyle?.className,
)}
id={component.id}
>
<ResizableContainer
id={component.id}
adjustableWidth={parentComponent.type === ROW_TYPE}
widthStep={columnWidth}
widthMultiple={widthMultiple}
heightStep={GRID_BASE_UNIT}
adjustableHeight={false}
heightMultiple={component.meta.height}
minWidthMultiple={GRID_MIN_COLUMN_COUNT}
minHeightMultiple={GRID_MIN_COLUMN_COUNT}
maxWidthMultiple={availableColumnCount + widthMultiple}
onResizeStart={onResizeStart}
onResize={onResize}
onResizeStop={onResizeStop}
>
<div
ref={dragSourceRef}
className="dashboard-component"
data-test="dashboard-component-chart-holder"
>
{editMode && (
<HoverMenu position="top">
<DeleteComponentButton onDelete={handleDeleteComponent} />
</HoverMenu>
)}
<Suspense fallback={<div>{t('Loading...')}</div>}>
<Component dashboardData={dashboardData} />
</Suspense>
</div>
</ResizableContainer>
</div>
{dropIndicatorProps && <div {...dropIndicatorProps} />}
</WithPopoverMenu>
)}
</DragDroppable>
);
};
export default DynamicComponent;

View File

@ -25,6 +25,7 @@ import {
ROW_TYPE,
TAB_TYPE,
TABS_TYPE,
DYNAMIC_TYPE,
} from '../../util/componentTypes';
import ChartHolder from './ChartHolder';
@ -35,6 +36,7 @@ import Header from './Header';
import Row from './Row';
import Tab from './Tab';
import TabsConnected from './Tabs';
import DynamicComponent from './DynamicComponent';
export { default as ChartHolder } from './ChartHolder';
export { default as Markdown } from './Markdown';
@ -44,6 +46,7 @@ export { default as Header } from './Header';
export { default as Row } from './Row';
export { default as Tab } from './Tab';
export { default as Tabs } from './Tabs';
export { default as DynamicComponent } from './DynamicComponent';
export const componentLookup = {
[CHART_TYPE]: ChartHolder,
@ -54,4 +57,5 @@ export const componentLookup = {
[ROW_TYPE]: Row,
[TAB_TYPE]: Tab,
[TABS_TYPE]: TabsConnected,
[DYNAMIC_TYPE]: DynamicComponent,
};

View File

@ -37,10 +37,10 @@ const defaultProps = {
export default class DraggableNewComponent extends React.PureComponent {
render() {
const { label, id, type, className } = this.props;
const { label, id, type, className, meta } = this.props;
return (
<DragDroppable
component={{ type, id }}
component={{ type, id, meta }}
parentComponent={{
id: NEW_COMPONENTS_SOURCE_ID,
type: NEW_COMPONENT_SOURCE_TYPE,

View File

@ -0,0 +1,43 @@
/**
* 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, { FC } from 'react';
import DraggableNewComponent from './DraggableNewComponent';
import { DYNAMIC_TYPE } from '../../../util/componentTypes';
import { NEW_DYNAMIC_COMPONENT } from '../../../util/constants';
import { DashboardComponentsRegistryMetadata } from '../../../../visualizations/dashboardComponents/DashboardComponentsRegistry';
type DraggableNewDynamicComponent = {
componentKey: string;
metadata: DashboardComponentsRegistryMetadata;
};
const DraggableNewDynamicComponent: FC<DraggableNewDynamicComponent> = ({
componentKey,
metadata,
}) => (
<DraggableNewComponent
id={NEW_DYNAMIC_COMPONENT}
type={DYNAMIC_TYPE}
label={metadata.name}
meta={{ metadata, componentKey }}
className={`fa fa-${metadata.iconName}`}
/>
);
export default DraggableNewDynamicComponent;

View File

@ -17,11 +17,14 @@
* under the License.
*/
import React, { RefObject } from 'react';
import { styled, DataMask } from '@superset-ui/core';
import {
DataMaskStateWithId,
Filter,
styled,
DataMask,
} from '@superset-ui/core';
import FilterControl from 'src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl';
import { CascadeFilter } from 'src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/types';
import { Filter } from 'src/dashboard/components/nativeFilters/types';
import { DataMaskStateWithId } from 'src/dataMask/types';
export interface CascadeFilterControlProps {
dataMaskSelected?: DataMaskStateWithId;

View File

@ -23,15 +23,21 @@ import React, {
useState,
useRef,
} from 'react';
import { styled, t, DataMask, css, SupersetTheme } from '@superset-ui/core';
import {
css,
DataMask,
DataMaskStateWithId,
Filter,
styled,
SupersetTheme,
t,
} from '@superset-ui/core';
import Popover from 'src/components/Popover';
import Icons from 'src/components/Icons';
import { Pill } from 'src/dashboard/components/FiltersBadge/Styles';
import { DataMaskStateWithId } from 'src/dataMask/types';
import FilterControl from 'src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl';
import CascadeFilterControl from 'src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadeFilterControl';
import { CascadeFilter } from 'src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/types';
import { Filter } from 'src/dashboard/components/nativeFilters/types';
interface CascadePopoverProps {
dataMaskSelected: DataMaskStateWithId;

View File

@ -17,8 +17,7 @@
* under the License.
*/
import { DataMask } from '@superset-ui/core';
import { Filter } from '../../types';
import { DataMask, Filter } from '@superset-ui/core';
export type CascadeFilter = Filter & { dataMask?: DataMask } & {
cascadeChildren: CascadeFilter[];

View File

@ -20,8 +20,7 @@ import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { setFilterConfiguration } from 'src/dashboard/actions/nativeFilters';
import Button from 'src/components/Button';
import { styled } from '@superset-ui/core';
import { FilterConfiguration } from 'src/dashboard/components/nativeFilters/types';
import { FilterConfiguration, styled } from '@superset-ui/core';
import { FiltersConfigModal } from 'src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal';
import { getFilterBarTestId } from '..';

View File

@ -18,22 +18,24 @@
*/
import React, { FC, useCallback, useMemo, useState } from 'react';
import { css } from '@emotion/react';
import { DataMask, styled, t } from '@superset-ui/core';
import {
DataMask,
DataMaskStateWithId,
Filter,
NativeFilterType,
styled,
t,
} from '@superset-ui/core';
import {
createHtmlPortalNode,
InPortal,
OutPortal,
} from 'react-reverse-portal';
import { Collapse } from 'src/common/components';
import { DataMaskStateWithId } from 'src/dataMask/types';
import {
useDashboardHasTabs,
useSelectFiltersInScope,
} from 'src/dashboard/components/nativeFilters/state';
import {
Filter,
NativeFilterType,
} from 'src/dashboard/components/nativeFilters/types';
import CascadePopover from '../CascadeFilters/CascadePopover';
import { useFilters } from '../state';
import { buildCascadeFiltersTree } from './utils';

View File

@ -18,9 +18,11 @@
*/
import { useMemo } from 'react';
import { useSelector } from 'react-redux';
import { NativeFiltersState } from 'src/dashboard/reducers/types';
import { DataMaskStateWithId } from 'src/dataMask/types';
import { ExtraFormData } from '@superset-ui/core';
import {
DataMaskStateWithId,
ExtraFormData,
NativeFiltersState,
} from '@superset-ui/core';
import { mergeExtraFormData } from '../../utils';
// eslint-disable-next-line import/prefer-default-export

View File

@ -17,9 +17,7 @@
* under the License.
*/
import React, { RefObject } from 'react';
import { DataMask } from '@superset-ui/core';
import { DataMaskStateWithId } from 'src/dataMask/types';
import { Filter } from '../../types';
import { DataMask, DataMaskStateWithId, Filter } from '@superset-ui/core';
export interface FilterProps {
dataMaskSelected?: DataMaskStateWithId;

View File

@ -18,11 +18,11 @@
*/
import { debounce } from 'lodash';
import { Dispatch } from 'react';
import { Filter, NativeFilterType, Divider } from '@superset-ui/core';
import {
setFocusedNativeFilter,
unsetFocusedNativeFilter,
} from 'src/dashboard/actions/nativeFilters';
import { Filter, NativeFilterType, Divider } from '../../types';
import { CascadeFilter } from '../CascadeFilters/types';
import { mapParentFiltersToChildren } from '../utils';

View File

@ -17,12 +17,11 @@
* under the License.
*/
import React, { FC, useMemo, useState } from 'react';
import { HandlerFunction, styled, t } from '@superset-ui/core';
import { DataMaskState, HandlerFunction, styled, t } from '@superset-ui/core';
import { Typography, Tooltip } from 'src/common/components';
import { useDispatch } from 'react-redux';
import Button from 'src/components/Button';
import { updateFilterSet } from 'src/dashboard/actions/nativeFilters';
import { DataMaskState } from 'src/dataMask/types';
import { WarningOutlined } from '@ant-design/icons';
import { ActionButtons } from './Footer';
import { useNativeFiltersDataMask, useFilters, useFilterSets } from '../state';

View File

@ -18,10 +18,15 @@
*/
import { Typography, Dropdown, Menu } from 'src/common/components';
import React, { FC } from 'react';
import { FilterSet } from 'src/dashboard/reducers/types';
import { DataMaskState } from 'src/dataMask/types';
import {
DataMaskState,
FilterSet,
HandlerFunction,
styled,
supersetTheme,
t,
} from '@superset-ui/core';
import { CheckOutlined, EllipsisOutlined } from '@ant-design/icons';
import { HandlerFunction, styled, supersetTheme, t } from '@superset-ui/core';
import Button from 'src/components/Button';
import { Tooltip } from 'src/components/Tooltip';
import FiltersHeader from './FiltersHeader';

View File

@ -17,16 +17,20 @@
* under the License.
*/
import React, { FC } from 'react';
import { styled, t, useTheme } from '@superset-ui/core';
import {
DataMaskState,
FilterSet,
NativeFilterType,
styled,
t,
useTheme,
} from '@superset-ui/core';
import { Collapse, Typography, Tooltip } from 'src/common/components';
import { DataMaskState } from 'src/dataMask/types';
import Icons from 'src/components/Icons';
import { areObjectsEqual } from 'src/reduxUtils';
import { FilterSet } from 'src/dashboard/reducers/types';
import { getFilterValueForDisplay } from './utils';
import { useFilters } from '../state';
import { getFilterBarTestId } from '../index';
import { NativeFilterType } from '../../types';
const FilterHeader = styled.div`
display: flex;

View File

@ -18,18 +18,25 @@
*/
import React, { useEffect, useState } from 'react';
import { DataMask, HandlerFunction, styled, t } from '@superset-ui/core';
import {
DataMask,
DataMaskState,
DataMaskWithId,
Filter,
Filters,
FilterSet,
HandlerFunction,
styled,
t,
} from '@superset-ui/core';
import { useDispatch } from 'react-redux';
import { DataMaskState, DataMaskWithId } from 'src/dataMask/types';
import {
createFilterSet,
deleteFilterSet,
updateFilterSet,
} from 'src/dashboard/actions/nativeFilters';
import { Filters, FilterSet } from 'src/dashboard/reducers/types';
import { areObjectsEqual } from 'src/reduxUtils';
import { findExistingFilterSet } from './utils';
import { Filter } from '../../types';
import { useFilters, useNativeFiltersDataMask, useFilterSets } from '../state';
import Footer from './Footer';
import FilterSetUnit from './FilterSetUnit';

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { FilterSet } from 'src/dashboard/reducers/types';
import { FilterSet } from '@superset-ui/core';
import { findExistingFilterSet } from '.';
const createDataMaskSelected = () => ({

View File

@ -18,10 +18,8 @@
*/
import shortid from 'shortid';
import { t } from '@superset-ui/core';
import { DataMaskState, FilterSet, t } from '@superset-ui/core';
import { areObjectsEqual } from 'src/reduxUtils';
import { DataMaskState } from 'src/dataMask/types';
import { FilterSet } from 'src/dashboard/reducers/types';
export const generateFiltersSetId = () => `FILTERS_SET-${shortid.generate()}`;

View File

@ -17,15 +17,20 @@
* under the License.
*/
/* eslint-disable no-param-reassign */
import { styled, t, useTheme } from '@superset-ui/core';
import {
DataMaskState,
DataMaskStateWithId,
Filter,
styled,
t,
useTheme,
} from '@superset-ui/core';
import React, { FC } from 'react';
import Icons from 'src/components/Icons';
import Button from 'src/components/Button';
import { useSelector } from 'react-redux';
import { DataMaskState, DataMaskStateWithId } from 'src/dataMask/types';
import FilterConfigurationLink from 'src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink';
import { useFilters } from 'src/dashboard/components/nativeFilters/FilterBar/state';
import { Filter } from 'src/dashboard/components/nativeFilters/types';
import { getFilterBarTestId } from '..';
import { RootState } from '../../../../types';

View File

@ -18,7 +18,16 @@
*/
/* eslint-disable no-param-reassign */
import { DataMask, HandlerFunction, styled, t } from '@superset-ui/core';
import {
DataMaskStateWithId,
DataMaskWithId,
Filter,
NativeFilterType,
DataMask,
HandlerFunction,
styled,
t,
} from '@superset-ui/core';
import React, { useEffect, useState, useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import cx from 'classnames';
@ -28,14 +37,9 @@ import { useHistory } from 'react-router-dom';
import { usePrevious } from 'src/hooks/usePrevious';
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
import { updateDataMask, clearDataMask } from 'src/dataMask/actions';
import { DataMaskStateWithId, DataMaskWithId } from 'src/dataMask/types';
import { useImmer } from 'use-immer';
import { isEmpty, isEqual } from 'lodash';
import { testWithId } from 'src/utils/testUtils';
import {
Filter,
NativeFilterType,
} from 'src/dashboard/components/nativeFilters/types';
import Loading from 'src/components/Loading';
import { getInitialDataMask } from 'src/dataMask/reducer';
import { URL_PARAMS } from 'src/constants';

View File

@ -19,20 +19,18 @@
/* eslint-disable no-param-reassign */
import { useSelector } from 'react-redux';
import { filter, keyBy } from 'lodash';
import {
Filters,
FilterSets as FilterSetsType,
} from 'src/dashboard/reducers/types';
import {
DataMaskState,
DataMaskStateWithId,
DataMaskWithId,
} from 'src/dataMask/types';
Filter,
Filters,
FilterSets as FilterSetsType,
} from '@superset-ui/core';
import { useContext, useEffect, useMemo, useState } from 'react';
import { ChartsState, RootState } from 'src/dashboard/types';
import { MigrationContext } from 'src/dashboard/containers/DashboardPage';
import { FILTER_BOX_MIGRATION_STATES } from 'src/explore/constants';
import { Filter } from 'src/dashboard/components/nativeFilters/types';
import { NATIVE_FILTER_PREFIX } from '../FiltersConfigModal/utils';
export const useFilterSets = () =>

View File

@ -17,10 +17,13 @@
* under the License.
*/
import { DataMaskStateWithId } from 'src/dataMask/types';
import { areObjectsEqual } from 'src/reduxUtils';
import { FilterState } from '@superset-ui/core';
import { Filter, Divider } from '../types';
import {
DataMaskStateWithId,
Filter,
FilterState,
Divider,
} from '@superset-ui/core';
export enum TabIds {
AllFilters = 'allFilters',

View File

@ -19,8 +19,7 @@
import React from 'react';
import { FormItem } from 'src/components/Form';
import { Input, TextArea } from 'src/common/components';
import { styled, t } from '@superset-ui/core';
import { NativeFilterType } from '../types';
import { NativeFilterType, styled, t } from '@superset-ui/core';
interface Props {
componentId: string;

View File

@ -16,9 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { styled } from '@superset-ui/core';
import { NativeFilterType, styled } from '@superset-ui/core';
import React from 'react';
import { NativeFilterType } from '../types';
import FilterTitlePane from './FilterTitlePane';
import { FilterRemoval } from './types';

View File

@ -16,10 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
import { styled, t, useTheme } from '@superset-ui/core';
import { NativeFilterType, styled, t, useTheme } from '@superset-ui/core';
import React from 'react';
import { Dropdown, MainNav as Menu } from 'src/common/components';
import { NativeFilterType } from '../types';
import FilterTitleContainer from './FilterTitleContainer';
import { FilterRemoval } from './types';

View File

@ -18,11 +18,10 @@
*/
import React, { FC, useCallback, useState } from 'react';
import { t, styled } from '@superset-ui/core';
import { NativeFilterScope, styled, t } from '@superset-ui/core';
import { Radio } from 'src/components/Radio';
import { Form, Typography } from 'src/common/components';
import { useComponentDidUpdate } from 'src/hooks/useComponentDidUpdate/useComponentDidUpdate';
import { Scope } from '../../../types';
import { ScopingType } from './types';
import ScopingTree from './ScopingTree';
import { getDefaultScopeValue, isScopingAll } from './utils';
@ -30,9 +29,9 @@ import { getDefaultScopeValue, isScopingAll } from './utils';
type FilterScopeProps = {
pathToFormValue?: string[];
updateFormValues: (values: any) => void;
formFilterScope?: Scope;
formFilterScope?: NativeFilterScope;
forceUpdate: Function;
filterScope?: Scope;
filterScope?: NativeFilterScope;
formScopingType?: ScopingType;
chartId?: number;
initiallyExcludedCharts?: number[];

View File

@ -18,19 +18,19 @@
*/
import React, { FC, useMemo, useState } from 'react';
import { NativeFilterScope } from '@superset-ui/core';
import { Tree } from 'src/common/components';
import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
import { Tooltip } from 'src/components/Tooltip';
import Icons from 'src/components/Icons';
import { useFilterScopeTree } from './state';
import { findFilterScope, getTreeCheckedItems } from './utils';
import { Scope } from '../../../types';
type ScopingTreeProps = {
forceUpdate: Function;
updateFormValues: (values: any) => void;
formScope?: Scope;
initialScope: Scope;
formScope?: NativeFilterScope;
initialScope: NativeFilterScope;
chartId?: number;
initiallyExcludedCharts?: number[];
};

View File

@ -23,9 +23,8 @@ import {
TAB_TYPE,
} from 'src/dashboard/util/componentTypes';
import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
import { t } from '@superset-ui/core';
import { NativeFilterScope, t } from '@superset-ui/core';
import { BuildTreeLeafTitle, TreeItem } from './types';
import { Scope } from '../../../types';
export const isShowTypeInTree = ({ type, meta }: LayoutItem, charts?: Charts) =>
(type === TAB_TYPE || type === CHART_TYPE || type === DASHBOARD_ROOT_TYPE) &&
@ -114,7 +113,10 @@ const checkTreeItem = (
});
};
export const getTreeCheckedItems = (scope: Scope, layout: Layout) => {
export const getTreeCheckedItems = (
scope: NativeFilterScope,
layout: Layout,
) => {
const checkedItems: string[] = [];
checkTreeItem(checkedItems, layout, [...scope.rootPath], [...scope.excluded]);
return [...new Set(checkedItems)];
@ -124,7 +126,7 @@ export const getTreeCheckedItems = (scope: Scope, layout: Layout) => {
export const findFilterScope = (
checkedKeys: string[],
layout: Layout,
): Scope => {
): NativeFilterScope => {
if (!checkedKeys.length) {
return {
rootPath: [],
@ -170,14 +172,14 @@ export const findFilterScope = (
export const getDefaultScopeValue = (
chartId?: number,
initiallyExcludedCharts: number[] = [],
): Scope => ({
): NativeFilterScope => ({
rootPath: [DASHBOARD_ROOT_ID],
excluded: chartId
? [chartId, ...initiallyExcludedCharts]
: initiallyExcludedCharts,
});
export const isScopingAll = (scope: Scope, chartId?: number) =>
export const isScopingAll = (scope: NativeFilterScope, chartId?: number) =>
!scope ||
(scope.rootPath[0] === DASHBOARD_ROOT_ID &&
!scope.excluded.filter(item => item !== chartId).length);

View File

@ -27,9 +27,11 @@ import {
Behavior,
ChartDataResponseResult,
Column,
Filter,
GenericDataType,
getChartMetadataRegistry,
JsonResponse,
NativeFilterType,
styled,
SupersetApiError,
SupersetClient,
@ -71,10 +73,6 @@ import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
import { waitForAsyncData } from 'src/middleware/asyncEvent';
import { cacheWrapper } from 'src/utils/cacheWrapper';
import { ClientErrorObject } from 'src/utils/getClientErrorObject';
import {
Filter,
NativeFilterType,
} from 'src/dashboard/components/nativeFilters/types';
import { SingleValueType } from 'src/filters/components/Range/SingleValueType';
import { getFormData } from 'src/dashboard/components/nativeFilters/utils';
import {

View File

@ -18,12 +18,9 @@
*/
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Filter, NativeFilterType } from '@superset-ui/core';
import { render, screen } from 'spec/helpers/testing-library';
import { FormInstance } from 'src/common/components';
import {
Filter,
NativeFilterType,
} from 'src/dashboard/components/nativeFilters/types';
import getControlItemsMap, { ControlItemsProps } from './getControlItemsMap';
import { getControlItems, setNativeFilterFieldValues } from './utils';

View File

@ -23,7 +23,12 @@ import {
import React from 'react';
import { Checkbox } from 'src/common/components';
import { FormInstance } from 'antd/lib/form';
import { getChartControlPanelRegistry, styled, t } from '@superset-ui/core';
import {
Filter,
getChartControlPanelRegistry,
styled,
t,
} from '@superset-ui/core';
import { Tooltip } from 'src/components/Tooltip';
import { FormItem } from 'src/components/Form';
import {
@ -37,7 +42,6 @@ import {
StyledLabel,
StyledRowFormItem,
} from './FiltersConfigForm';
import { Filter } from '../../types';
import { ColumnSelect } from './ColumnSelect';
export interface ControlItemsProps {

View File

@ -18,10 +18,9 @@
*/
import { useEffect, useState } from 'react';
import { FormInstance } from 'antd/lib/form';
import { t } from '@superset-ui/core';
import { Filter, t } from '@superset-ui/core';
import { NativeFiltersForm, NativeFiltersFormItem } from '../types';
import { setNativeFilterFieldValues, useForceUpdate } from './utils';
import { Filter } from '../../types';
// When some fields in form changed we need re-fetch data for Filter defaultValue
// eslint-disable-next-line import/prefer-default-export

View File

@ -24,18 +24,20 @@ import React, {
useRef,
} from 'react';
import { uniq, isEqual, sortBy, debounce } from 'lodash';
import { t, styled, SLOW_DEBOUNCE } from '@superset-ui/core';
import { Form } from 'src/common/components';
import ErrorBoundary from 'src/components/ErrorBoundary';
import { StyledModal } from 'src/components/Modal';
import { testWithId } from 'src/utils/testUtils';
import { useFilterConfigMap, useFilterConfiguration } from '../state';
import {
Filter,
FilterConfiguration,
NativeFilterType,
Divider,
} from '../types';
styled,
SLOW_DEBOUNCE,
t,
} from '@superset-ui/core';
import { Form } from 'src/common/components';
import ErrorBoundary from 'src/components/ErrorBoundary';
import { StyledModal } from 'src/components/Modal';
import { testWithId } from 'src/utils/testUtils';
import { useFilterConfigMap, useFilterConfiguration } from '../state';
import FiltureConfigurePane from './FilterConfigurePane';
import FiltersConfigForm, {
FilterPanels,

View File

@ -1,7 +1,7 @@
import { useEffect } from 'react';
import { usePrevious } from 'src/hooks/usePrevious';
import { NativeFilterType } from '@superset-ui/core';
import { FilterRemoval } from './types';
import { NativeFilterType } from '../types';
/**
* Licensed to the Apache Software Foundation (ASF) under one

View File

@ -16,11 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
import { AdhocFilter, DataMask } from '@superset-ui/core';
import { NativeFilterType, Scope } from '../types';
import {
AdhocFilter,
DataMask,
NativeFilterType,
NativeFilterScope,
} from '@superset-ui/core';
export interface NativeFiltersFormItem {
scope: Scope;
scope: NativeFilterScope;
name: string;
filterType: string;
dataset: {

View File

@ -20,7 +20,14 @@ import { FormInstance } from 'antd/lib/form';
import shortid from 'shortid';
import { getInitialDataMask } from 'src/dataMask/reducer';
import { t } from '@superset-ui/core';
import {
Filter,
FilterConfiguration,
NativeFilterType,
Divider,
t,
NativeFilterTarget,
} from '@superset-ui/core';
import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
import {
FilterRemoval,
@ -28,13 +35,6 @@ import {
FilterHierarchy,
FilterHierarchyNode,
} from './types';
import {
Filter,
FilterConfiguration,
NativeFilterType,
Divider,
Target,
} from '../types';
export const REMOVAL_DELAY_SECS = 5;
@ -156,7 +156,7 @@ export const createHandleSave =
description: formInputs.description,
};
}
const target: Partial<Target> = {};
const target: Partial<NativeFilterTarget> = {};
if (formInputs.dataset) {
target.datasetId = formInputs.dataset.value;
}

View File

@ -23,7 +23,7 @@ import {
FilterConfiguration,
NativeFilterType,
Divider,
} from './types';
} from '@superset-ui/core';
import { ActiveTabs, DashboardLayout, RootState } from '../../types';
import { TAB_TYPE } from '../../util/componentTypes';
import { CascadeFilter } from './FilterBar/CascadeFilters/types';

View File

@ -17,21 +17,21 @@
* under the License.
*/
import {
ExtraFormData,
QueryFormData,
getChartMetadataRegistry,
AdhocFilter,
Behavior,
DataMaskStateWithId,
EXTRA_FORM_DATA_APPEND_KEYS,
EXTRA_FORM_DATA_OVERRIDE_KEYS,
AdhocFilter,
ExtraFormData,
FeatureFlag,
Filter,
getChartMetadataRegistry,
QueryFormData,
} from '@superset-ui/core';
import { Charts, DashboardLayout } from 'src/dashboard/types';
import { RefObject } from 'react';
import { DataMaskStateWithId } from 'src/dataMask/types';
import extractUrlParams from 'src/dashboard/util/extractUrlParams';
import { isFeatureEnabled } from 'src/featureFlags';
import { Filter } from './types';
import { CHART_TYPE, TAB_TYPE } from '../../util/componentTypes';
import { DASHBOARD_GRID_ID, DASHBOARD_ROOT_ID } from '../../util/constants';

View File

@ -16,9 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
import { NativeFilterType } from 'src/dashboard/components/nativeFilters/types';
import { NativeFiltersState } from 'src/dashboard/reducers/types';
import { DataMaskStateWithId } from 'src/dataMask/types';
import {
DataMaskStateWithId,
NativeFiltersState,
NativeFilterType,
} from '@superset-ui/core';
export const mockDataMaskInfo: DataMaskStateWithId = {
DefaultsID: {

View File

@ -24,8 +24,11 @@ import {
SET_FOCUSED_NATIVE_FILTER,
UNSET_FOCUSED_NATIVE_FILTER,
} from 'src/dashboard/actions/nativeFilters';
import { FilterSet, NativeFiltersState } from './types';
import { FilterConfiguration } from '../components/nativeFilters/types';
import {
FilterSet,
FilterConfiguration,
NativeFiltersState,
} from '@superset-ui/core';
import { HYDRATE_DASHBOARD } from '../actions/hydrate';
export function getInitialState({

View File

@ -18,9 +18,7 @@
*/
import componentTypes from 'src/dashboard/util/componentTypes';
import { DataMaskStateWithId } from 'src/dataMask/types';
import { JsonObject } from '@superset-ui/core';
import { Filter, Scope } from '../components/nativeFilters/types';
import { NativeFilterScope, JsonObject } from '@superset-ui/core';
export enum Scoping {
All = 'All',
@ -31,7 +29,7 @@ export type ChartConfiguration = {
[chartId: number]: {
id: number;
crossFilters: {
scope: Scope;
scope: NativeFilterScope;
};
};
};
@ -82,13 +80,6 @@ export type LayoutItem = {
};
};
export type FilterSet = {
id: number;
name: string;
nativeFilters: Filters;
dataMask: DataMaskStateWithId;
};
export type FilterSetFullData = {
changed_by_fk: string | null;
changed_on: string | null;
@ -101,17 +92,3 @@ export type FilterSetFullData = {
owner_type: string;
params: JsonObject;
};
export type FilterSets = {
[filtersSetId: string]: FilterSet;
};
export type Filters = {
[filterId: string]: Filter;
};
export type NativeFiltersState = {
filters: Filters;
filterSets: FilterSets;
focusedFilterId?: string;
};

View File

@ -18,17 +18,17 @@
*/
import {
ChartProps,
DataMaskStateWithId,
ExtraFormData,
GenericDataType,
JsonObject,
NativeFiltersState,
} from '@superset-ui/core';
import { DatasourceMeta } from '@superset-ui/chart-controls';
import { chart } from 'src/chart/chartReducer';
import componentTypes from 'src/dashboard/util/componentTypes';
import { User } from 'src/types/bootstrapTypes';
import { DataMaskStateWithId } from '../dataMask/types';
import { NativeFiltersState } from './reducers/types';
import { ChartState } from '../explore/types';
export { Dashboard } from 'src/types/Dashboard';

View File

@ -16,12 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
import { DataMaskStateWithId } from 'src/dataMask/types';
import { JsonObject } from '@superset-ui/core';
import {
DataMaskStateWithId,
Filters,
JsonObject,
NativeFilterScope,
} from '@superset-ui/core';
import { CHART_TYPE } from './componentTypes';
import { Scope } from '../components/nativeFilters/types';
import { ActiveFilters, Layout, LayoutItem } from '../types';
import { ChartConfiguration, Filters } from '../reducers/types';
import { ChartConfiguration } from '../reducers/types';
import { DASHBOARD_ROOT_ID } from './constants';
// Looking for affected chart scopes and values
@ -35,7 +38,7 @@ export const findAffectedCharts = ({
}: {
child: string;
layout: { [key: string]: LayoutItem };
scope: Scope;
scope: NativeFilterScope;
activeFilters: ActiveFilters;
filterId: string;
extraFormData: any;

View File

@ -16,13 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
import { DataRecordFilters, JsonObject } from '@superset-ui/core';
import {
DataMaskStateWithId,
DataRecordFilters,
JsonObject,
NativeFiltersState,
} from '@superset-ui/core';
import { ChartQueryPayload, Charts, LayoutItem } from 'src/dashboard/types';
import { getExtraFormData } from 'src/dashboard/components/nativeFilters/utils';
import { DataMaskStateWithId } from 'src/dataMask/types';
import { areObjectsEqual } from 'src/reduxUtils';
import getEffectiveExtraFilters from './getEffectiveExtraFilters';
import { ChartConfiguration, NativeFiltersState } from '../../reducers/types';
import { ChartConfiguration } from '../../reducers/types';
import { getAllActiveFilters } from '../activeAllDashboardFilters';
// We cache formData objects so that our connected container components don't always trigger

View File

@ -16,8 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
import { COLUMN_TYPE, CHART_TYPE, MARKDOWN_TYPE } from './componentTypes';
import {
COLUMN_TYPE,
CHART_TYPE,
MARKDOWN_TYPE,
DYNAMIC_TYPE,
} from './componentTypes';
export default function componentIsResizable(entity: { type: string }) {
return [COLUMN_TYPE, CHART_TYPE, MARKDOWN_TYPE].indexOf(entity.type) > -1;
return (
[COLUMN_TYPE, CHART_TYPE, MARKDOWN_TYPE, DYNAMIC_TYPE].indexOf(
entity.type,
) > -1
);
}

View File

@ -28,6 +28,8 @@ export const NEW_COMPONENT_SOURCE_TYPE = 'NEW_COMPONENT_SOURCE';
export const ROW_TYPE = 'ROW';
export const TABS_TYPE = 'TABS';
export const TAB_TYPE = 'TAB';
// Dynamic type proposes lazy loading of custom dashboard components that can be added in separate repository
export const DYNAMIC_TYPE = 'DYNAMIC';
export default {
CHART_TYPE,
@ -42,4 +44,5 @@ export default {
ROW_TYPE,
TABS_TYPE,
TAB_TYPE,
DYNAMIC_TYPE,
};

View File

@ -31,6 +31,7 @@ export const NEW_MARKDOWN_ID = 'NEW_MARKDOWN_ID';
export const NEW_ROW_ID = 'NEW_ROW_ID';
export const NEW_TAB_ID = 'NEW_TAB_ID';
export const NEW_TABS_ID = 'NEW_TABS_ID';
export const NEW_DYNAMIC_COMPONENT = 'NEW_DYNAMIC_COMPONENT';
// grid constants
export const DASHBOARD_ROOT_DEPTH = 0;

View File

@ -19,17 +19,13 @@
import shortid from 'shortid';
import { find, isEmpty } from 'lodash';
import {
Filter,
NativeFilterType,
} from 'src/dashboard/components/nativeFilters/types';
import {
FILTER_CONFIG_ATTRIBUTES,
TIME_FILTER_LABELS,
TIME_FILTER_MAP,
} from 'src/explore/constants';
import { DASHBOARD_FILTER_SCOPE_GLOBAL } from 'src/dashboard/reducers/dashboardFilters';
import { TimeGranularity } from '@superset-ui/core';
import { Filter, NativeFilterType, TimeGranularity } from '@superset-ui/core';
import { getChartIdsInFilterScope } from './activeDashboardFilters';
import getFilterConfigsFromFormdata from './getFilterConfigsFromFormdata';

View File

@ -23,6 +23,7 @@ import {
COLUMN_TYPE,
MARKDOWN_TYPE,
CHART_TYPE,
DYNAMIC_TYPE,
} from './componentTypes';
function getTotalChildWidth({ id, components }) {
@ -84,6 +85,7 @@ export default function getDetailedComponentWidth({
}
});
} else if (
component.type === DYNAMIC_TYPE ||
component.type === MARKDOWN_TYPE ||
component.type === CHART_TYPE
) {

View File

@ -16,11 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
import { Filter } from '@superset-ui/core';
import getFormDataWithExtraFilters, {
GetFormDataWithExtraFiltersArguments,
} from 'src/dashboard/util/charts/getFormDataWithExtraFilters';
import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
import { Filter } from 'src/dashboard/components/nativeFilters/types';
import { LayoutItem } from 'src/dashboard/types';
import { dashboardLayout } from 'spec/fixtures/mockDashboardLayout';
import { sliceId as chartId } from 'spec/fixtures/mockChartQueries';

View File

@ -16,9 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
import { CHART_TYPE, MARKDOWN_TYPE } from './componentTypes';
import { CHART_TYPE, MARKDOWN_TYPE, DYNAMIC_TYPE } from './componentTypes';
const USER_CONTENT_COMPONENT_TYPE: string[] = [CHART_TYPE, MARKDOWN_TYPE];
const USER_CONTENT_COMPONENT_TYPE: string[] = [
CHART_TYPE,
MARKDOWN_TYPE,
DYNAMIC_TYPE,
];
export default function isDashboardEmpty(layout: any): boolean {
// has at least one chart or markdown component
return !Object.values(layout).some(

View File

@ -43,6 +43,7 @@ import {
ROW_TYPE,
TABS_TYPE,
TAB_TYPE,
DYNAMIC_TYPE,
} from './componentTypes';
import { DASHBOARD_ROOT_DEPTH as rootDepth } from './constants';
@ -62,6 +63,7 @@ const parentMaxDepthLookup = {
[DASHBOARD_GRID_TYPE]: {
[CHART_TYPE]: depthOne,
[DYNAMIC_TYPE]: depthOne,
[MARKDOWN_TYPE]: depthOne,
[COLUMN_TYPE]: depthOne,
[DIVIDER_TYPE]: depthOne,
@ -72,6 +74,7 @@ const parentMaxDepthLookup = {
[ROW_TYPE]: {
[CHART_TYPE]: depthFour,
[DYNAMIC_TYPE]: depthFour,
[MARKDOWN_TYPE]: depthFour,
[COLUMN_TYPE]: depthFour,
},
@ -82,6 +85,7 @@ const parentMaxDepthLookup = {
[TAB_TYPE]: {
[CHART_TYPE]: depthFive,
[DYNAMIC_TYPE]: depthFive,
[MARKDOWN_TYPE]: depthFive,
[COLUMN_TYPE]: depthThree,
[DIVIDER_TYPE]: depthFive,
@ -101,6 +105,7 @@ const parentMaxDepthLookup = {
// these have no valid children
[CHART_TYPE]: {},
[DYNAMIC_TYPE]: {},
[DIVIDER_TYPE]: {},
[HEADER_TYPE]: {},
[MARKDOWN_TYPE]: {},

View File

@ -28,12 +28,14 @@ import {
ROW_TYPE,
TABS_TYPE,
TAB_TYPE,
DYNAMIC_TYPE,
} from './componentTypes';
import {
MEDIUM_HEADER,
BACKGROUND_TRANSPARENT,
GRID_DEFAULT_CHART_WIDTH,
GRID_COLUMN_COUNT,
} from './constants';
const typeToDefaultMetaData = {
@ -56,6 +58,10 @@ const typeToDefaultMetaData = {
defaultText: t('Tab title'),
placeholder: t('Tab title'),
},
[DYNAMIC_TYPE]: {
width: GRID_COLUMN_COUNT,
background: BACKGROUND_TRANSPARENT,
},
};
function uuid(type) {

View File

@ -16,10 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { DataMask } from '@superset-ui/core';
import { FilterConfiguration } from '../dashboard/components/nativeFilters/types';
import { DataMask, FilterConfiguration, Filters } from '@superset-ui/core';
import { FeatureFlag, isFeatureEnabled } from '../featureFlags';
import { Filters } from '../dashboard/reducers/types';
import { getInitialDataMask } from './reducer';
export const CLEAR_DATA_MASK_STATE = 'CLEAR_DATA_MASK_STATE';

View File

@ -20,23 +20,25 @@
/* eslint-disable no-param-reassign */
// <- When we work with Immer, we need reassign, so disabling lint
import produce from 'immer';
import { DataMask, FeatureFlag } from '@superset-ui/core';
import {
DataMask,
DataMaskStateWithId,
DataMaskWithId,
FeatureFlag,
Filter,
FilterConfiguration,
Filters,
} from '@superset-ui/core';
import { NATIVE_FILTER_PREFIX } from 'src/dashboard/components/nativeFilters/FiltersConfigModal/utils';
import { HYDRATE_DASHBOARD } from 'src/dashboard/actions/hydrate';
import { isFeatureEnabled } from 'src/featureFlags';
import { DataMaskStateWithId, DataMaskWithId } from './types';
import {
AnyDataMaskAction,
CLEAR_DATA_MASK_STATE,
SET_DATA_MASK_FOR_FILTER_CONFIG_COMPLETE,
UPDATE_DATA_MASK,
} from './actions';
import {
Filter,
FilterConfiguration,
} from '../dashboard/components/nativeFilters/types';
import { areObjectsEqual } from '../reduxUtils';
import { Filters } from '../dashboard/reducers/types';
export function getInitialDataMask(
id?: string | number,

View File

@ -25,6 +25,7 @@ import { merge } from 'lodash';
import setupClient from './setup/setupClient';
import setupColors from './setup/setupColors';
import setupFormatters from './setup/setupFormatters';
import setupDashboardComponents from './setup/setupDasboardComponents';
if (process.env.WEBPACK_MODE === 'development') {
setHotLoaderConfig({ logLevel: 'debug', trackTailUpdates: false });
@ -60,6 +61,8 @@ setupColors(
// Setup number formatters
setupFormatters();
setupDashboardComponents();
export const theme = merge(
supersetTheme,
bootstrapData?.common?.theme_overrides ?? {},

View File

@ -0,0 +1,31 @@
/**
* 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.
*/
/*
This file can be overridden from outside by custom config, it will add/delete new components to existing config in
superset-frontend/src/visualizations/presets/dashboardComponents.ts file
*/
// import dashboardComponentsRegistry from '../visualizations/presets/dashboardComponents';
// import example from '../visualizations/dashboardComponents/ExampleComponent';
export default function setupDashboardComponents() {
// Add custom dashboard components here. Example:
// dashboardComponentsRegistry.set('example', example);
}

View File

@ -0,0 +1,83 @@
/**
* 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, { ComponentType } from 'react';
import { JsonObject } from '@superset-ui/core';
export interface RegistryMetadata {
description: string;
name: string;
}
export interface ComponentItem<Metadata = RegistryMetadata> {
metadata: Metadata;
loadComponent: () => Promise<{ default: ComponentType<any> }>;
}
export interface ComponentRegistry<Metadata = RegistryMetadata> {
metadata: Metadata;
Component: ComponentType<any>;
}
export type FunctionalRegistryState<RegistryT> = {
registry: { [key: string]: RegistryT & { key: string } };
registryKeys: string[];
};
export const registryGetAll =
<RegistryT>({ registryKeys, registry }: FunctionalRegistryState<RegistryT>) =>
() =>
registryKeys.map(key => registry[key]);
export const registryDelete =
<RegistryT>({ registryKeys, registry }: FunctionalRegistryState<RegistryT>) =>
(keyToDelete: string) => {
// eslint-disable-next-line no-param-reassign
registryKeys = registryKeys.filter(key => key !== keyToDelete);
// eslint-disable-next-line no-param-reassign
delete registry[keyToDelete];
};
export const registryGet =
<RegistryT>({ registry }: FunctionalRegistryState<RegistryT>) =>
(key: string) =>
registry[key];
export const registrySet =
({ registryKeys, registry }: FunctionalRegistryState<JsonObject>) =>
(key: string, item: JsonObject) => {
registryKeys.push(key);
// eslint-disable-next-line no-param-reassign
registry[key] = {
key,
...item,
};
};
export const registrySetComponent =
({ registryKeys, registry }: FunctionalRegistryState<ComponentRegistry>) =>
(key: string, item: ComponentItem) => {
registryKeys.push(key);
// eslint-disable-next-line no-param-reassign
registry[key] = {
key,
metadata: item.metadata,
Component: React.lazy(item.loadComponent),
};
};

View File

@ -0,0 +1,62 @@
/**
* 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 {
ComponentItem,
ComponentRegistry,
FunctionalRegistryState,
registryDelete,
registryGet,
registryGetAll,
RegistryMetadata,
registrySetComponent,
} from '../../utils/functionalRegistry';
export interface DashboardComponentsRegistryMetadata extends RegistryMetadata {
iconName: string;
}
/*
This is registry that contains list of dynamic dashboard components that can be added in addition to main components
*/
const DashboardComponentsRegistry = (
initComponents: { key: string; item: ComponentItem }[] = [],
) => {
const state: FunctionalRegistryState<
ComponentRegistry<DashboardComponentsRegistryMetadata>
> = {
registry: {},
registryKeys: [],
};
const set = registrySetComponent(state);
initComponents.forEach(({ key, item }) => {
set(key, item);
});
return {
set,
get: registryGet(state),
delete: registryDelete(state),
getAll: registryGetAll(state),
};
};
export default DashboardComponentsRegistry;

View File

@ -0,0 +1,33 @@
/**
* 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 { DashboardComponentMetadata, t } from '@superset-ui/core';
// TODO: POC only component can be removed after PR approved
const ExampleComponent = ({
metadata,
}: {
metadata: DashboardComponentMetadata;
}) => (
<div>
{t('We have the following keys: %s', Object.keys(metadata).join(', '))}
</div>
);
export default ExampleComponent;

View File

@ -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.
*/
// TODO: POC only component can be removed after PR approved
export default {
metadata: {
name: 'Example',
description: 'Example description',
iconName: 'filter',
},
loadComponent: () => import('./ExampleComponent'),
};

View File

@ -0,0 +1,30 @@
/**
* 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.
*/
/*
Preset dashboard components (in addition to basic components like row, column, chart, etc...)
*/
import DashboardComponentsRegistry from '../dashboardComponents/DashboardComponentsRegistry';
const dashboardComponents = DashboardComponentsRegistry([
// Here can be added default dashboard components
]);
export default dashboardComponents;