diff --git a/src/ui/components/ControlComponents/Sections/AppearanceSection/Rows/InnerTitleRow/InnerTitleRow.tsx b/src/ui/components/ControlComponents/Sections/AppearanceSection/Rows/InnerTitleRow/InnerTitleRow.tsx index f4c653e86a..a108a89b98 100644 --- a/src/ui/components/ControlComponents/Sections/AppearanceSection/Rows/InnerTitleRow/InnerTitleRow.tsx +++ b/src/ui/components/ControlComponents/Sections/AppearanceSection/Rows/InnerTitleRow/InnerTitleRow.tsx @@ -7,14 +7,13 @@ import {I18n} from 'i18n'; import {useDispatch, useSelector} from 'react-redux'; import {ControlQA, DashTabItemControlSourceType, DialogControlQa} from 'shared'; import {setSelectorDialogItem} from 'ui/store/actions/controlDialog'; +import {ELEMENT_TYPE} from 'ui/store/constants/controlDialog'; import { selectIsControlConfigurationDisabled, selectSelectorDialog, } from 'ui/store/selectors/controlDialog'; import type {SelectorSourceType} from 'ui/store/typings/controlDialog'; -import {ELEMENT_TYPE} from '../../../../../../units/dash/containers/Dialogs/Control/constants'; - import '../../AppearanceSection.scss'; const b = block('control2-appearance-section'); diff --git a/src/ui/components/ControlComponents/Sections/AppearanceSection/Rows/TitlePlacementRow/TitlePlacementRow.tsx b/src/ui/components/ControlComponents/Sections/AppearanceSection/Rows/TitlePlacementRow/TitlePlacementRow.tsx index 08b5af2d05..ce142656a6 100644 --- a/src/ui/components/ControlComponents/Sections/AppearanceSection/Rows/TitlePlacementRow/TitlePlacementRow.tsx +++ b/src/ui/components/ControlComponents/Sections/AppearanceSection/Rows/TitlePlacementRow/TitlePlacementRow.tsx @@ -7,11 +7,11 @@ import {I18n} from 'i18n'; import {useDispatch, useSelector} from 'react-redux'; import {TitlePlacementOption} from 'shared/types/dash'; import {setSelectorDialogItem} from 'ui/store/actions/controlDialog'; +import {ELEMENT_TYPE} from 'ui/store/constants/controlDialog'; import { selectIsControlConfigurationDisabled, selectSelectorDialog, } from 'ui/store/selectors/controlDialog'; -import {ELEMENT_TYPE} from 'ui/units/dash/containers/Dialogs/Control/constants'; import '../../AppearanceSection.scss'; diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/CommonSettingsSection.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/CommonSettingsSection.tsx index 5bb8edd7a7..c765cd7ae2 100644 --- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/CommonSettingsSection.tsx +++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/CommonSettingsSection.tsx @@ -10,17 +10,42 @@ import {ExternalSelectorSettings} from './ExternalSelectorSettings/ExternalSelec import {InputSettings} from './InputSettings/InputSettings'; // TODO: Remove hideCommonFields and related fields after enabling DLPROJECTS-93 -export const CommonSettingsSection = ({hideCommonFields}: {hideCommonFields?: boolean}) => { +export const CommonSettingsSection = ({ + hideCommonFields, + navigationPath, + changeNavigationPath, +}: { + navigationPath: string | null; + changeNavigationPath: (newNavigationPath: string) => void; + hideCommonFields?: boolean; +}) => { const {sourceType} = useSelector(selectSelectorDialog); switch (sourceType) { case DashTabItemControlSourceType.External: - return ; + return ( + + ); case DashTabItemControlSourceType.Manual: return ; case DashTabItemControlSourceType.Connection: - return ; + return ( + + ); default: - return ; + return ( + + ); } }; diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ConnectionSettings/ConnectionSettings.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ConnectionSettings/ConnectionSettings.tsx index 2de7066fce..521d57f2f9 100644 --- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ConnectionSettings/ConnectionSettings.tsx +++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ConnectionSettings/ConnectionSettings.tsx @@ -14,12 +14,23 @@ import {QueryTypeControl} from './components/QueryTypeControl/QueryTypeControl'; const i18n = I18n.keyset('dash.control-dialog.edit'); -export const ConnectionSettings = ({hideCommonFields}: {hideCommonFields?: boolean}) => { +export const ConnectionSettings = ({ + hideCommonFields, + navigationPath, + changeNavigationPath, +}: { + hideCommonFields?: boolean; + navigationPath: string | null; + changeNavigationPath: (newNavigationPath: string) => void; +}) => { const {connectionQueryTypes} = useSelector(selectSelectorDialog); return ( - + {connectionQueryTypes && connectionQueryTypes.length > 0 && ( diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ConnectionSettings/components/ConnectionSelector/ConnectionSelector.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ConnectionSettings/components/ConnectionSelector/ConnectionSelector.tsx index d61eb8ad0f..794e2347c7 100644 --- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ConnectionSettings/components/ConnectionSelector/ConnectionSelector.tsx +++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ConnectionSettings/components/ConnectionSelector/ConnectionSelector.tsx @@ -3,23 +3,24 @@ import React from 'react'; import {I18n} from 'i18n'; import {useDispatch, useSelector} from 'react-redux'; import {EntryScope} from 'shared'; -import {setSelectorDialogItem} from 'ui/store/actions/controlDialog'; -import {selectSelectorDialog} from 'ui/store/selectors/controlDialog'; +import {setLastUsedConnectionId, setSelectorDialogItem} from 'ui/store/actions/controlDialog'; +import {ELEMENT_TYPE} from 'ui/store/constants/controlDialog'; +import {selectOpenedItemMeta, selectSelectorDialog} from 'ui/store/selectors/controlDialog'; import logger from '../../../../../../../libs/logger'; import {getSdk} from '../../../../../../../libs/schematic-sdk'; -import {ELEMENT_TYPE} from '../../../../../../../units/dash/containers/Dialogs/Control/constants'; -import {setLastUsedConnectionId} from '../../../../../../../units/dash/store/actions/dashTyped'; -import {selectDashWorkbookId} from '../../../../../../../units/dash/store/selectors/dashTypedSelectors'; import {EntrySelector} from '../../../EntrySelector/EntrySelector'; import {prepareConnectionData} from './helpers'; const i18n = I18n.keyset('dash.control-dialog.edit'); -export const ConnectionSelector = () => { +export const ConnectionSelector = (props: { + navigationPath: string | null; + changeNavigationPath: (newNavigationPath: string) => void; +}) => { const dispatch = useDispatch(); const {connectionId} = useSelector(selectSelectorDialog); - const workbookId = useSelector(selectDashWorkbookId); + const {workbookId} = useSelector(selectOpenedItemMeta); const [isInvalid, setIsInvalid] = React.useState(false); const [unsupportedConnectionError, setUnsupportedConnectionError] = React.useState< @@ -91,6 +92,8 @@ export const ConnectionSelector = () => { isInvalid={isEntryInvalid} errorText={unsupportedConnectionError} workbookId={workbookId} + navigationPath={props.navigationPath} + changeNavigationPath={props.changeNavigationPath} /> ); }; diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/DatasetSelector/DatasetSelector.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/DatasetSelector/DatasetSelector.tsx index 3b68942de4..13539f4197 100644 --- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/DatasetSelector/DatasetSelector.tsx +++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/DatasetSelector/DatasetSelector.tsx @@ -13,13 +13,11 @@ import { EntryScope, } from 'shared'; import logger from 'ui/libs/logger'; -import {setSelectorDialogItem} from 'ui/store/actions/controlDialog'; -import {selectSelectorDialog} from 'ui/store/selectors/controlDialog'; +import {setLastUsedDatasetId, setSelectorDialogItem} from 'ui/store/actions/controlDialog'; +import {ELEMENT_TYPE} from 'ui/store/constants/controlDialog'; +import {selectOpenedItemMeta, selectSelectorDialog} from 'ui/store/selectors/controlDialog'; import type {SelectorElementType, SetSelectorDialogItemArgs} from 'ui/store/typings/controlDialog'; -import {setLastUsedDatasetId} from 'units/dash/store/actions/dashTyped'; -import {selectDashWorkbookId} from 'units/dash/store/selectors/dashTypedSelectors'; -import {ELEMENT_TYPE} from '../../../../../units/dash/containers/Dialogs/Control/constants'; import {DatasetField} from '../../Switchers/DatasetField/DatasetField'; import {EntrySelector} from '../EntrySelector/EntrySelector'; @@ -27,11 +25,14 @@ const i18n = I18n.keyset('dash.control-dialog.edit'); const getDatasetLink = (entryId: string) => `/datasets/${entryId}`; -function DatasetSelector() { +function DatasetSelector(props: { + navigationPath: string | null; + changeNavigationPath: (newNavigationPath: string) => void; +}) { const dispatch = useDispatch(); const {datasetId, datasetFieldId, isManualTitle, title, fieldType, validation} = useSelector(selectSelectorDialog); - const workbookId = useSelector(selectDashWorkbookId); + const {workbookId} = useSelector(selectOpenedItemMeta); const [isInvalid, setIsInvalid] = React.useState(false); const fetchDataset = React.useCallback((entryId: string) => { @@ -139,6 +140,8 @@ function DatasetSelector() { isInvalid={isInvalid} workbookId={workbookId} getEntryLink={getDatasetLink} + navigationPath={props.navigationPath} + changeNavigationPath={props.changeNavigationPath} /> diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/DatasetSettings/DatasetSettings.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/DatasetSettings/DatasetSettings.tsx index d6d112f961..f1e50093dc 100644 --- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/DatasetSettings/DatasetSettings.tsx +++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/DatasetSettings/DatasetSettings.tsx @@ -6,10 +6,21 @@ import {ValueSelector} from '../../ValueSelector/ValueSelector'; import {DatasetSelector} from '../DatasetSelector/DatasetSelector'; import {InputTypeSelector} from '../InputTypeSelector/InputTypeSelector'; -const DatasetSettings = ({hideCommonFields}: {hideCommonFields?: boolean}) => { +const DatasetSettings = ({ + hideCommonFields, + navigationPath, + changeNavigationPath, +}: { + hideCommonFields?: boolean; + navigationPath: string | null; + changeNavigationPath: (newNavigationPath: string) => void; +}) => { return ( - + {!hideCommonFields && ( diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/EntrySelector/EntrySelector.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/EntrySelector/EntrySelector.tsx index 0e0b6a8ba3..c00bf8e1c9 100644 --- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/EntrySelector/EntrySelector.tsx +++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/EntrySelector/EntrySelector.tsx @@ -2,12 +2,9 @@ import React from 'react'; import {FormRow} from '@gravity-ui/components'; import block from 'bem-cn-lite'; -import {useDispatch, useSelector} from 'react-redux'; import type {StringParams} from 'shared/types'; import NavigationInput from 'ui/units/dash/components/NavigationInput/NavigationInput'; import type {EntryTypeNode} from 'ui/units/dash/modules/constants'; -import {changeNavigationPath} from 'ui/units/dash/store/actions/dashTyped'; -import {selectNavigationPath} from 'ui/units/dash/store/selectors/dashTypedSelectors'; import {FieldWrapper} from '../../../../FieldWrapper/FieldWrapper'; @@ -33,6 +30,8 @@ type EntrySelectorProps = { workbookId?: string | null; includeClickableType?: EntryTypeNode; getEntryLink?: (entryId: string) => string; + navigationPath: string | null; + changeNavigationPath: (newNavigationPath: string) => void; }; export const EntrySelector: React.FC = (props: EntrySelectorProps) => { @@ -46,15 +45,10 @@ export const EntrySelector: React.FC = (props: EntrySelector isInvalid, getEntryLink, includeClickableType, + navigationPath, + changeNavigationPath, } = props; - const dispatch = useDispatch(); - const navigationPath = useSelector(selectNavigationPath); - - const handleChangeNavigationPath = (newNavigationPath: string) => { - dispatch(changeNavigationPath(newNavigationPath)); - }; - return ( @@ -69,7 +63,7 @@ export const EntrySelector: React.FC = (props: EntrySelector getEntryLink={getEntryLink} includeClickableType={includeClickableType} navigationPath={navigationPath} - changeNavigationPath={handleChangeNavigationPath} + changeNavigationPath={changeNavigationPath} /> diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ExternalSelectorSettings/ExternalSelectorSettings.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ExternalSelectorSettings/ExternalSelectorSettings.tsx index e7e33d99b1..1bd1ef1bba 100644 --- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ExternalSelectorSettings/ExternalSelectorSettings.tsx +++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/ExternalSelectorSettings/ExternalSelectorSettings.tsx @@ -28,7 +28,10 @@ imm.extend('$auto', (value, object) => { return object ? update(object, value) : update({}, value); }); -const ExternalSelectorSettings = () => { +const ExternalSelectorSettings: React.FC<{ + navigationPath: string | null; + changeNavigationPath: (newNavigationPath: string) => void; +}> = (props) => { const dispatch = useDispatch(); const {autoHeight, chartId, title, selectorParameters, validation, selectorParametersGroup} = useSelector(selectSelectorDialog); @@ -98,6 +101,8 @@ const ExternalSelectorSettings = () => { entryId={chartId} onChange={handleChartIdChange} includeClickableType={EntryTypeNode.CONTROL_NODE} + navigationPath={props.navigationPath} + changeNavigationPath={props.changeNavigationPath} /> diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/InputTypeSelector/InputTypeSelector.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/InputTypeSelector/InputTypeSelector.tsx index 03ead48dd3..5930f2aab7 100644 --- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/InputTypeSelector/InputTypeSelector.tsx +++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/InputTypeSelector/InputTypeSelector.tsx @@ -13,6 +13,7 @@ import { } from 'shared'; import {SelectOptionWithIcon} from 'ui/components/SelectComponents/components/SelectOptionWithIcon/SelectOptionWithIcon'; import {setSelectorDialogItem} from 'ui/store/actions/controlDialog'; +import {ELEMENT_TYPE} from 'ui/store/constants/controlDialog'; import { selectIsControlConfigurationDisabled, selectSelectorControlType, @@ -20,7 +21,6 @@ import { } from 'ui/store/selectors/controlDialog'; import type {SelectorElementType} from 'ui/store/typings/controlDialog'; -import {ELEMENT_TYPE} from '../../../../../units/dash/containers/Dialogs/Control/constants'; import {getElementOptions} from '../helpers/input-type-select'; const i18n = I18n.keyset('dash.control-dialog.edit'); diff --git a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/helpers/input-type-select.tsx b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/helpers/input-type-select.tsx index df7861651d..dee8104b6a 100644 --- a/src/ui/components/ControlComponents/Sections/CommonSettingsSection/helpers/input-type-select.tsx +++ b/src/ui/components/ControlComponents/Sections/CommonSettingsSection/helpers/input-type-select.tsx @@ -5,8 +5,7 @@ import type {SelectOption} from '@gravity-ui/uikit'; import {Icon} from '@gravity-ui/uikit'; import {I18n} from 'i18n'; import {DialogControlQa} from 'shared/constants/qa'; - -import {ELEMENT_TYPE} from '../../../../../units/dash/containers/Dialogs/Control/constants'; +import {ELEMENT_TYPE} from 'ui/store/constants/controlDialog'; const i18n = I18n.keyset('dash.control-dialog.edit'); diff --git a/src/ui/components/ControlComponents/Sections/Date/Acceptable/Acceptable.tsx b/src/ui/components/ControlComponents/Sections/Date/Acceptable/Acceptable.tsx index 4140769f5f..39d0cba517 100644 --- a/src/ui/components/ControlComponents/Sections/Date/Acceptable/Acceptable.tsx +++ b/src/ui/components/ControlComponents/Sections/Date/Acceptable/Acceptable.tsx @@ -4,8 +4,8 @@ import block from 'bem-cn-lite'; import {i18n} from 'i18n'; import moment from 'moment'; import {registry} from 'ui/registry'; +import {DATE_FORMAT} from 'ui/store/constants/controlDialog'; -import {DATE_FORMAT} from '../../../../../units/dash/containers/Dialogs/Control/constants'; import Dialog from '../../../Dialog/Dialog'; import Button from '../../Switchers/Button/Button'; diff --git a/src/ui/components/ControlComponents/Sections/Date/Default/Default.tsx b/src/ui/components/ControlComponents/Sections/Date/Default/Default.tsx index f6c72c2217..be545a90b7 100644 --- a/src/ui/components/ControlComponents/Sections/Date/Default/Default.tsx +++ b/src/ui/components/ControlComponents/Sections/Date/Default/Default.tsx @@ -13,11 +13,8 @@ import { getParsedRelativeDate, } from 'shared'; import {RelativeDatesPicker} from 'ui'; +import {DATETIME_FORMAT, DATE_FORMAT} from 'ui/store/constants/controlDialog'; -import { - DATETIME_FORMAT, - DATE_FORMAT, -} from '../../../../../units/dash/containers/Dialogs/Control/constants'; import Dialog from '../../../Dialog/Dialog'; import './Default.scss'; diff --git a/src/ui/components/ControlComponents/Sections/ValueSelector/RequiredValueCheckbox/RequiredValueCheckbox.tsx b/src/ui/components/ControlComponents/Sections/ValueSelector/RequiredValueCheckbox/RequiredValueCheckbox.tsx index 70af6dbed2..514428983f 100644 --- a/src/ui/components/ControlComponents/Sections/ValueSelector/RequiredValueCheckbox/RequiredValueCheckbox.tsx +++ b/src/ui/components/ControlComponents/Sections/ValueSelector/RequiredValueCheckbox/RequiredValueCheckbox.tsx @@ -7,14 +7,13 @@ import {I18n} from 'i18n'; import {useDispatch, useSelector} from 'react-redux'; import {DialogControlQa} from 'shared'; import {setSelectorDialogItem} from 'ui/store/actions/controlDialog'; +import {ELEMENT_TYPE} from 'ui/store/constants/controlDialog'; import { selectIsControlConfigurationDisabled, selectSelectorControlType, selectSelectorRequired, } from 'ui/store/selectors/controlDialog'; -import {ELEMENT_TYPE} from '../../../../../units/dash/containers/Dialogs/Control/constants'; - import '../ValueSelector.scss'; const i18n = I18n.keyset('dash.control-dialog.edit'); diff --git a/src/ui/components/ControlComponents/Sections/ValueSelector/ValueSelector.tsx b/src/ui/components/ControlComponents/Sections/ValueSelector/ValueSelector.tsx index 465d525e80..860ef699c0 100644 --- a/src/ui/components/ControlComponents/Sections/ValueSelector/ValueSelector.tsx +++ b/src/ui/components/ControlComponents/Sections/ValueSelector/ValueSelector.tsx @@ -10,6 +10,7 @@ import {FieldWrapper} from 'ui/components/FieldWrapper/FieldWrapper'; import {VIEW_MODES} from 'ui/components/Select/hooks/useSelectRenderFilter/useSelectRenderFilter'; import {registry} from 'ui/registry'; import {setSelectorDialogItem} from 'ui/store/actions/controlDialog'; +import {CheckboxControlValue} from 'ui/store/constants/controlDialog'; import { selectIsControlConfigurationDisabled, selectSelectorControlType, @@ -21,7 +22,6 @@ import {selectWorkbookId} from 'ui/units/workbooks/store/selectors'; import type {FilterValue} from '../../../../../shared/modules'; import {DATASET_FIELD_TYPES, DashTabItemControlSourceType} from '../../../../../shared/types'; -import {CheckboxControlValue} from '../../../../units/dash/containers/Dialogs/Control/constants'; import {getDistinctsByTypedQuery} from '../CommonSettingsSection/ConnectionSettings/helpers/get-distincts-by-typed-query'; import { DEFAULT_PAGE_SIZE, diff --git a/src/ui/units/dash/components/TwoColumnDialog/TwoColumnDialog.scss b/src/ui/components/ControlComponents/TwoColumnDialog/TwoColumnDialog.scss similarity index 100% rename from src/ui/units/dash/components/TwoColumnDialog/TwoColumnDialog.scss rename to src/ui/components/ControlComponents/TwoColumnDialog/TwoColumnDialog.scss diff --git a/src/ui/units/dash/components/TwoColumnDialog/TwoColumnDialog.tsx b/src/ui/components/ControlComponents/TwoColumnDialog/TwoColumnDialog.tsx similarity index 100% rename from src/ui/units/dash/components/TwoColumnDialog/TwoColumnDialog.tsx rename to src/ui/components/ControlComponents/TwoColumnDialog/TwoColumnDialog.tsx diff --git a/src/ui/components/DialogChartWidget/DialogChartWidget.tsx b/src/ui/components/DialogChartWidget/DialogChartWidget.tsx index 0e2e985364..71072c27ff 100644 --- a/src/ui/components/DialogChartWidget/DialogChartWidget.tsx +++ b/src/ui/components/DialogChartWidget/DialogChartWidget.tsx @@ -30,12 +30,12 @@ import { updateParamValue, validateParamTitle, } from '../../units/dash/components/ParamsSettings/helpers'; -import TwoColumnDialog from '../../units/dash/components/TwoColumnDialog/TwoColumnDialog'; import {PaletteBackground} from '../../units/dash/containers/Dialogs/components/PaletteBackground/PaletteBackground'; import {isEntryTypeWithFiltering} from '../../units/dash/containers/Dialogs/utils'; import {DASH_WIDGET_TYPES, EntryTypeNode} from '../../units/dash/modules/constants'; import type {SetItemDataArgs} from '../../units/dash/store/actions/dashTyped'; import Utils from '../../utils'; +import TwoColumnDialog from '../ControlComponents/TwoColumnDialog/TwoColumnDialog'; import {TabMenu} from './TabMenu/TabMenu'; import type {UpdateState} from './TabMenu/types'; @@ -79,7 +79,14 @@ function Line(props: LineProps) { type AfterSettingsWidgetCallback = ((selectedWidgetType: WidgetKind) => void) | null; -export interface DialogChartWidgetProps { +export interface DialogChartWidgetFeatureProps { + withoutSidebar?: boolean; + + enableAutoheight?: boolean; + enableBackgroundColor?: boolean; + enableFilteringSetting?: boolean; +} +export interface DialogChartWidgetProps extends DialogChartWidgetFeatureProps { openedItemId: string | null; openedItemData: DashTabItemWidget['data']; dialogIsVisible: boolean; @@ -92,12 +99,6 @@ export interface DialogChartWidgetProps { [key: string]: string; }; - withoutSidebar?: boolean; - - enableAutoheight?: boolean; - enableBackgroundColor?: boolean; - enableFilteringSetting?: boolean; - changeNavigationPath: (newNavigationPath: string) => void; closeDialog: () => void; setItemData: (newItemData: SetItemDataArgs) => void; diff --git a/src/ui/components/DialogEditItem/DialogEditItem.tsx b/src/ui/components/DialogEditItem/DialogEditItem.tsx new file mode 100644 index 0000000000..a0105a9440 --- /dev/null +++ b/src/ui/components/DialogEditItem/DialogEditItem.tsx @@ -0,0 +1,246 @@ +import React from 'react'; + +import {useDispatch, useSelector} from 'react-redux'; +import type { + DashTabItem, + DashTabItemGroupControl, + DashTabItemImage, + DashTabItemText, + DashTabItemTitle, + DashTabItemWidget, + EntryScope, + StringParams, + WidgetType, +} from 'shared'; +import {DashTabItemType} from 'shared'; +import {ITEM_TYPE} from 'ui/constants/dialogs'; +import {useEffectOnce} from 'ui/hooks'; +import {initControlDialog, resetControlDialog} from 'ui/store/actions/controlDialog'; +import {selectOpenedDialogType} from 'ui/store/selectors/controlDialog'; + +import type {DialogChartWidgetFeatureProps} from '../DialogChartWidget/DialogChartWidget'; +import DialogChartWidget from '../DialogChartWidget/DialogChartWidget'; +import DialogExternalControl from '../DialogExternalControl/DialogExternalControl'; +import {DialogGroupControl} from '../DialogGroupControl/DialogGroupControl'; +import {DialogImageWidget} from '../DialogImageWidget'; +import {DialogTextWidgetWrapper} from '../DialogTextWidget'; +import type {DialogTextWidgetFeatureProps} from '../DialogTextWidget/DialogTextWidget'; +import type {DialogTitleWidgetFeatureProps} from '../DialogTitleWidget/DialogTitleWidget'; +import DialogTitleWidget from '../DialogTitleWidget/DialogTitleWidget'; + +type DialogEditTitleProps = { + type: typeof ITEM_TYPE.TITLE; + openedItemData: React.ComponentProps['openedItemData']; + setItemData: React.ComponentProps['setItemData']; + widgetType: any; + widgetsCurrentTab: any; + openedItemDefaults: any; +}; + +type DialogEditTextProps = { + type: typeof ITEM_TYPE.TEXT; + openedItemData: React.ComponentProps['openedItemData']; + setItemData: React.ComponentProps['setItemData']; + widgetType: any; + widgetsCurrentTab: any; + openedItemDefaults: any; +}; + +type DialogEditChartProps = { + type: typeof ITEM_TYPE.WIDGET; + openedItemData: React.ComponentProps['openedItemData']; + widgetType: WidgetType; + setItemData: React.ComponentProps['setItemData']; + widgetsCurrentTab: { + [key: string]: string; + }; + openedItemDefaults: any; +}; + +type DialogEditGroupControlProps = { + type: typeof ITEM_TYPE.GROUP_CONTROL; + openedItemData: React.ComponentProps['openedItemData']; + setItemData: React.ComponentProps['setItemData']; + widgetType: any; + widgetsCurrentTab: any; + openedItemDefaults: any; +}; + +type DialogEditExternalControlProps = { + type: typeof ITEM_TYPE.CONTROL; + setItemData: React.ComponentProps['setItemData']; + openedItemData: any; + widgetType: any; + widgetsCurrentTab: any; + openedItemDefaults: StringParams; +}; + +type DialogEditImageProps = { + type: typeof ITEM_TYPE.IMAGE; + openedItemData: React.ComponentProps['openedItemData']; + setItemData: React.ComponentProps['onApply']; + widgetType: any; + widgetsCurrentTab: any; + openedItemDefaults: any; +}; + +export type DialogEditItemFeaturesProp = { + [DashTabItemType.Title]?: DialogTitleWidgetFeatureProps; + [DashTabItemType.Text]?: DialogTextWidgetFeatureProps; + [DashTabItemType.Widget]?: DialogChartWidgetFeatureProps; +}; + +export type DialogEditItemProps = { + entryId: string | null; + scope: EntryScope; + openedItemId: string | null; + currentTabId: string | null; + workbookId: string | null; + closeDialog: () => void; + navigationPath: string | null; + openedItemNamespace: string; + changeNavigationPath: (newNavigationPath: string) => void; + features?: DialogEditItemFeaturesProp; + openedItemData: DashTabItem['data']; +} & ( + | DialogEditTitleProps + | DialogEditTextProps + | DialogEditChartProps + | DialogEditGroupControlProps + | DialogEditExternalControlProps + | DialogEditImageProps +); + +export const isDialogEditItemType = (type: string): type is DashTabItemType => + Object.values(ITEM_TYPE).includes(type as DashTabItemType); + +export const DialogEditItem: React.FC = (props) => { + const { + features, + scope, + entryId, + type, + openedItemId, + openedItemNamespace, + openedItemDefaults, + currentTabId, + openedItemData, + widgetType, + widgetsCurrentTab, + workbookId, + navigationPath, + changeNavigationPath, + closeDialog, + setItemData, + } = props; + + const dispatch = useDispatch(); + const openedDialog = useSelector(selectOpenedDialogType); + + const [isOpenedDialog, setOpenedDialog] = React.useState(false); + + useEffectOnce(() => { + dispatch( + initControlDialog({ + type, + id: openedItemId, + data: openedItemData, + defaults: openedItemDefaults, + openedItemMeta: { + scope, + entryId, + namespace: openedItemNamespace, + currentTabId, + workbookId, + }, + }), + ); + + return () => { + dispatch(resetControlDialog()); + }; + }); + + React.useEffect(() => { + setOpenedDialog(Boolean(openedDialog)); + }, [dispatch, openedDialog]); + + switch (openedDialog) { + case ITEM_TYPE.TITLE: + return ( + + ); + case ITEM_TYPE.TEXT: { + return ( + + ); + } + case ITEM_TYPE.WIDGET: + return ( + + ); + case ITEM_TYPE.CONTROL: + return ( + + ); + case ITEM_TYPE.GROUP_CONTROL: + return ( + + ); + + case ITEM_TYPE.IMAGE: + return ( + + ); + default: { + return null; + } + } +}; diff --git a/src/ui/components/DialogExternalControl/DialogExternalControl.tsx b/src/ui/components/DialogExternalControl/DialogExternalControl.tsx index db2d4e2782..89ccb32f1f 100644 --- a/src/ui/components/DialogExternalControl/DialogExternalControl.tsx +++ b/src/ui/components/DialogExternalControl/DialogExternalControl.tsx @@ -6,7 +6,6 @@ import {I18n} from 'i18n'; import type {DatalensGlobalState} from 'index'; import {Utils} from 'index'; import {connect} from 'react-redux'; -import type {Dispatch} from 'redux'; import {bindActionCreators} from 'redux'; import {ControlQA, DashTabItemControlSourceType, Feature} from 'shared'; import {AppearanceSection} from 'ui/components/ControlComponents/Sections/AppearanceSection/AppearanceSection'; @@ -15,11 +14,16 @@ import {ParametersSection} from 'ui/components/ControlComponents/Sections/Parame import {SelectorPreview} from 'ui/components/ControlComponents/SelectorPreview/SelectorPreview'; import {SelectorTypeSelect} from 'ui/components/ControlComponents/SelectorTypeSelect/SelectorTypeSelect'; import {SectionWrapper} from 'ui/components/SectionWrapper/SectionWrapper'; +import type {AppDispatch} from 'ui/store'; +import { + applyExternalControlDialog, + closeExternalControlDialog, +} from 'ui/store/actions/controlDialog'; import { selectIsParametersSectionAvailable, selectSelectorDialog, } from 'ui/store/selectors/controlDialog'; -import {applyControl2Dialog, closeControl2Dialog} from 'units/dash/store/actions/dashTyped'; +import type {SetItemDataArgs} from 'ui/units/dash/store/actions/dashTyped'; import './DialogExternalControl.scss'; @@ -29,7 +33,13 @@ const dashI18n = I18n.keyset('dash.main.view'); type StateProps = ReturnType; type DispatchProps = ReturnType; -type OwnProps = {}; +type OwnProps = { + dialogIsVisible: boolean; + closeDialog: () => void; + setItemData: (newItemData: SetItemDataArgs) => void; + navigationPath: string | null; + changeNavigationPath: (newNavigationPath: string) => void; +}; type Props = OwnProps & DispatchProps & StateProps; @@ -37,17 +47,15 @@ const b = block('dl-dialog-add-control'); class DialogExternalControl extends React.Component { render() { - const {isEdit, validation} = this.props; + const {isEdit, validation, dialogIsVisible} = this.props; const textButtonApply = isEdit ? controlI18n('button_save') : controlI18n('button_add'); //TODO: raname 'label_control' after enabling feature flag - const caption = Utils.isEnabledFeature(Feature.GroupControls) - ? dashI18n('button_edit-panel-editor-selector') - : controlI18n('label_control'); + const caption = dashI18n('button_edit-panel-editor-selector'); return ( { private renderBody() { const showTypeSelect = !Utils.isEnabledFeature(Feature.GroupControls); const showParametersSection = this.props.isParametersSectionAvailable; + const {navigationPath, changeNavigationPath} = this.props; return ( @@ -88,7 +97,10 @@ class DialogExternalControl extends React.Component { )}
- +
{showParametersSection && ( @@ -116,11 +128,11 @@ class DialogExternalControl extends React.Component { } private handleClose = () => { - this.props.actions.closeControl2Dialog(); + this.props.actions.closeDialog(); }; private handleApply = () => { - this.props.actions.applyControl2Dialog(); + this.props.actions.applyChanges(); }; } @@ -134,12 +146,19 @@ const mapStateToProps = (state: DatalensGlobalState) => { }; }; -const mapDispatchToProps = (dispatch: Dispatch) => { +const mapDispatchToProps = (dispatch: AppDispatch, props: OwnProps) => { return { actions: bindActionCreators( { - applyControl2Dialog, - closeControl2Dialog, + closeDialog: () => + closeExternalControlDialog({ + closeDialog: props.closeDialog, + }), + applyChanges: () => + applyExternalControlDialog({ + closeDialog: props.closeDialog, + setItemData: props.setItemData, + }), }, dispatch, ), diff --git a/src/ui/components/DialogGroupControl/DialogGroupControl.tsx b/src/ui/components/DialogGroupControl/DialogGroupControl.tsx index 883c366277..46a4814de3 100644 --- a/src/ui/components/DialogGroupControl/DialogGroupControl.tsx +++ b/src/ui/components/DialogGroupControl/DialogGroupControl.tsx @@ -3,12 +3,13 @@ import React from 'react'; import block from 'bem-cn-lite'; import {I18n} from 'i18n'; import {useDispatch, useSelector} from 'react-redux'; +import type {DashTabItemGroupControl} from 'shared'; import {ControlQA} from 'shared/constants/qa'; +import {applyGroupControlDialog, copyControlToStorage} from 'ui/store/actions/controlDialog'; import {selectSelectorDialog} from 'ui/store/selectors/controlDialog'; -import {applyGroupControlDialog} from 'units/dash/store/actions/controls/actions'; +import type {SetItemDataArgs} from 'ui/units/dash/store/actions/dashTyped'; -import TwoColumnDialog from '../../units/dash/components/TwoColumnDialog/TwoColumnDialog'; -import {closeDialog} from '../../units/dash/store/actions/dialogs/actions'; +import TwoColumnDialog from '../ControlComponents/TwoColumnDialog/TwoColumnDialog'; import {GroupControlBody} from './GroupControlBody/GroupControlBody'; import {GroupControlFooter} from './GroupControlFooter/GroupControlFooter'; @@ -19,28 +20,61 @@ import './DialogGroupControl.scss'; const b = block('group-control-dialog'); const i18n = I18n.keyset('dash.group-controls-dialog.edit'); -export const DialogGroupControl = () => { +export type DialogGroupControlProps = { + openedItemData: DashTabItemGroupControl['data']; + dialogIsVisible: boolean; + closeDialog: () => void; + setItemData: (newItemData: SetItemDataArgs) => void; + navigationPath: string | null; + changeNavigationPath: (newNavigationPath: string) => void; +}; + +export const DialogGroupControl: React.FC = ({ + dialogIsVisible, + closeDialog, + setItemData, + navigationPath, + changeNavigationPath, +}) => { const {id, draftId} = useSelector(selectSelectorDialog); + const dispatch = useDispatch(); + const handleApply = React.useCallback(() => { + dispatch( + applyGroupControlDialog({ + setItemData, + closeDialog, + }), + ); + }, [closeDialog, dispatch, setItemData]); - const handleClose = () => { - dispatch(closeDialog()); - }; + const handleClose = React.useCallback(() => { + closeDialog(); + }, [closeDialog]); - const handleApply = () => { - dispatch(applyGroupControlDialog()); - }; + const handleCopyItem = React.useCallback( + (itemIndex: number) => { + dispatch(copyControlToStorage(itemIndex)); + }, + [dispatch], + ); return ( } + sidebar={} bodyHeader={i18n('label_selector-settings')} // key for rerendering form on tab change - body={} + body={ + + } footer={} contentClassMixin={b('content')} bodyClassMixin={b('body')} diff --git a/src/ui/components/DialogGroupControl/GroupControlBody/GroupControlBody.tsx b/src/ui/components/DialogGroupControl/GroupControlBody/GroupControlBody.tsx index 6476db97e8..bbda2954a4 100644 --- a/src/ui/components/DialogGroupControl/GroupControlBody/GroupControlBody.tsx +++ b/src/ui/components/DialogGroupControl/GroupControlBody/GroupControlBody.tsx @@ -15,17 +15,19 @@ import {OperationSelector} from 'ui/components/ControlComponents/Sections/Operat import {RequiredValueCheckbox} from 'ui/components/ControlComponents/Sections/ValueSelector/RequiredValueCheckbox/RequiredValueCheckbox'; import {ValueSelector} from 'ui/components/ControlComponents/Sections/ValueSelector/ValueSelector'; import {SelectorTypeSelect} from 'ui/components/ControlComponents/SelectorTypeSelect/SelectorTypeSelect'; +import {ELEMENT_TYPE} from 'ui/store/constants/controlDialog'; import {selectSelectorControlType} from 'ui/store/selectors/controlDialog'; import Utils from 'ui/utils/utils'; -import {ELEMENT_TYPE} from '../../../units/dash/containers/Dialogs/Control/constants'; - import '../DialogGroupControl.scss'; const b = block('group-control-dialog'); const i18n = I18n.keyset('dash.group-controls-dialog.edit'); -export const GroupControlBody = () => { +export const GroupControlBody: React.FC<{ + navigationPath: string | null; + changeNavigationPath: (newNavigationPath: string) => void; +}> = (props) => { const elementType = useSelector(selectSelectorControlType); const isTypeNotCheckbox = elementType !== ELEMENT_TYPE.CHECKBOX; @@ -36,7 +38,11 @@ export const GroupControlBody = () => {
- +
diff --git a/src/ui/components/DialogGroupControl/GroupControlSidebar/GroupControlSidebar.tsx b/src/ui/components/DialogGroupControl/GroupControlSidebar/GroupControlSidebar.tsx index 1e7b3ab93e..41fc33ffaa 100644 --- a/src/ui/components/DialogGroupControl/GroupControlSidebar/GroupControlSidebar.tsx +++ b/src/ui/components/DialogGroupControl/GroupControlSidebar/GroupControlSidebar.tsx @@ -28,7 +28,6 @@ import {selectActiveSelectorIndex, selectSelectorsGroup} from 'ui/store/selector import type {SelectorDialogState, SelectorsGroupDialogState} from 'ui/store/typings/controlDialog'; import type {CopiedConfigData} from 'ui/units/dash/modules/helpers'; import {isItemPasteAllowed} from 'ui/units/dash/modules/helpers'; -import {copyControlToStorage} from 'ui/units/dash/store/actions/controls/actions'; import {TabMenu} from '../../DialogChartWidget/TabMenu/TabMenu'; import type {TabMenuItemData, UpdateState} from '../../DialogChartWidget/TabMenu/types'; @@ -79,7 +78,9 @@ const handlePasteItems = (pasteConfig: CopiedConfigData | null) => { return pasteItems as TabMenuItemData[]; }; -export const GroupControlSidebar = () => { +export const GroupControlSidebar: React.FC<{handleCopyItem: (itemIndex: number) => void}> = ( + props, +) => { const selectorsGroup = useSelector(selectSelectorsGroup); const activeSelectorIndex = useSelector(selectActiveSelectorIndex); @@ -182,10 +183,6 @@ export const GroupControlSidebar = () => { ); }; - const handleCopyItem = (itemIndex: number) => { - dispatch(copyControlToStorage(itemIndex)); - }; - const handleUpdateItem = (title: string) => { dispatch( setSelectorDialogItem({ @@ -217,7 +214,7 @@ export const GroupControlSidebar = () => { onPasteItems={handlePasteItems} canPasteItems={canPasteItems} addButtonView="outlined" - onCopyItem={handleCopyItem} + onCopyItem={props.handleCopyItem} onUpdateItem={handleUpdateItem} />
diff --git a/src/ui/components/DialogTextWidget/DialogTextWidget.tsx b/src/ui/components/DialogTextWidget/DialogTextWidget.tsx index f4dfb899cf..2d64199b13 100644 --- a/src/ui/components/DialogTextWidget/DialogTextWidget.tsx +++ b/src/ui/components/DialogTextWidget/DialogTextWidget.tsx @@ -14,13 +14,15 @@ import './DialogTextWidget.scss'; const b = block('dialog-text'); -export interface DialogTextWidgetProps { +export interface DialogTextWidgetFeatureProps { + enableAutoheight?: boolean; +} + +export interface DialogTextWidgetProps extends DialogTextWidgetFeatureProps { openedItemId: string | null; openedItemData: DashTabItemText['data']; dialogIsVisible: boolean; - enableAutoheight?: boolean; - closeDialog: () => void; setItemData: (newItemData: SetItemDataArgs) => void; } diff --git a/src/ui/components/DialogTitleWidget/DialogTitleWidget.tsx b/src/ui/components/DialogTitleWidget/DialogTitleWidget.tsx index 0efc0448e0..7de97b07bc 100644 --- a/src/ui/components/DialogTitleWidget/DialogTitleWidget.tsx +++ b/src/ui/components/DialogTitleWidget/DialogTitleWidget.tsx @@ -40,7 +40,11 @@ interface DialogTitleWidgetState { backgroundColor?: string; } -interface DialogTitleWidgetProps { +export interface DialogTitleWidgetFeatureProps { + enableAutoheight?: boolean; + enableShowInTOC?: boolean; +} +interface DialogTitleWidgetProps extends DialogTitleWidgetFeatureProps { openedItemId: string | null; openedItemData: DashTabItemTitle['data']; dialogIsVisible: boolean; diff --git a/src/ui/index.ts b/src/ui/index.ts index 3eb626d51e..35fecb9541 100644 --- a/src/ui/index.ts +++ b/src/ui/index.ts @@ -6,7 +6,9 @@ import type {LandingState} from 'store/reducers/landing'; import type {AsideHeaderState} from 'store/typings/asideHeader'; import type {EntryGlobalState} from 'store/typings/entryContent'; import type {UserState} from 'store/typings/user'; +import type {ControlDialogState} from 'ui/store/reducers/controlDialog'; import type {CopyEntriesToWorkbookState} from 'ui/store/reducers/copyEntriesToWorkbook'; +import type {EditHistoryState} from 'ui/store/reducers/editHistory'; import type {MigrationToWorkbookState} from 'ui/store/reducers/migrationToWorkbook'; import type {CollectionsState} from 'units/collections/store/reducers'; import type {ConnectionsReduxState} from 'units/connections/store/typings'; @@ -18,7 +20,6 @@ import type {QLState} from 'units/ql/store/typings'; import type {WizardGlobalState} from 'units/wizard/reducers'; import type {WorkbooksState} from 'units/workbooks/store/reducers'; -import type {EditHistoryState} from './store/reducers/editHistory'; import type {CollectionsNavigationState} from './units/collections-navigation/store/reducers'; export {default as ActionPanel} from './components/ActionPanel/ActionPanel'; @@ -58,6 +59,7 @@ export type DatalensGlobalState = { dash: DashState; dataset: DatasetReduxState; ql: QLState; + controlDialog: ControlDialogState; collections: CollectionsState; collectionsStructure: CollectionsStructureState; workbooks: WorkbooksState; diff --git a/src/ui/libs/DatalensChartkit/components/Control/Items/Items.js b/src/ui/libs/DatalensChartkit/components/Control/Items/Items.js index fc52b19df6..f11b910926 100644 --- a/src/ui/libs/DatalensChartkit/components/Control/Items/Items.js +++ b/src/ui/libs/DatalensChartkit/components/Control/Items/Items.js @@ -7,7 +7,7 @@ import PropTypes from 'prop-types'; import {ControlQA, TitlePlacementOption, resolveIntervalDate, resolveRelativeDate} from 'shared'; import {DL} from 'ui/constants/common'; import {registry} from 'ui/registry'; -import {CheckboxControlValue} from 'ui/units/dash/containers/Dialogs/Control/constants'; +import {CheckboxControlValue} from 'ui/store/constants/controlDialog'; import {MOBILE_SIZE} from 'ui/utils/mobile'; import {YCSelect} from '../../../../../components/common/YCSelect/YCSelect'; diff --git a/src/ui/store/actions/controlDialog.ts b/src/ui/store/actions/controlDialog.ts index b19c7e8895..a8225a4bc0 100644 --- a/src/ui/store/actions/controlDialog.ts +++ b/src/ui/store/actions/controlDialog.ts @@ -1,4 +1,78 @@ +import {DashTabItemType, TitlePlacementOption} from 'shared'; +import type { + DashTabItem, + DashTabItemControlData, + DashTabItemGroupControl, + StringParams, +} from 'shared'; import type {SelectorsGroupDialogState, SetSelectorDialogItemArgs} from '../typings/controlDialog'; +import type {AppDispatch} from '..'; +import { + getControlDefaultsForField, + getControlValidation, + getItemDataSource, +} from '../utils/controlDialog'; +import isEmpty from 'lodash/isEmpty'; +import { + getBeforeCloseDialogItemAction, + getExtendedItemDataAction, +} from 'ui/units/dash/store/actions/helpers'; +import {showToast} from './toaster'; +import {I18n} from 'i18n'; +import type {ControlDialogStateItemMeta} from '../reducers/controlDialog'; +import {getGroupSelectorDialogInitialState} from '../reducers/controlDialog'; +import {DEFAULT_CONTROL_LAYOUT} from 'ui/components/DashKit/constants'; +import {COPIED_WIDGET_STORAGE_KEY} from 'ui/constants'; +import type {ConfigItemGroup} from '@gravity-ui/dashkit/helpers'; +import {DEFAULT_NAMESPACE} from '@gravity-ui/dashkit/helpers'; +import {CONTROLS_PLACEMENT_MODE} from 'ui/constants/dialogs'; +import type {PreparedCopyItemOptions} from '@gravity-ui/dashkit'; +import {getPreparedCopyItemOptions, type CopiedConfigContext} from 'ui/units/dash/modules/helpers'; +import type {SetItemDataArgs} from 'ui/units/dash/store/actions/dashTyped'; +import type {DatalensGlobalState} from 'ui/index'; +import { + selectActiveSelectorIndex, + selectIsControlSourceTypeHasChanged, + selectOpenedItemData, + selectOpenedItemId, + selectOpenedItemMeta, + selectSelectorDialog, + selectSelectorsGroup, +} from '../selectors/controlDialog'; + +const dialogI18n = I18n.keyset('dash.group-controls-dialog.edit'); + +export const INIT_DIALOG = Symbol('controlDialog/INIT_DIALOG'); + +export type InitDialogAction = { + type: typeof INIT_DIALOG; + payload: { + id: string | null; + data: DashTabItem['data']; + type: DashTabItemType; + defaults?: StringParams | null; + openedItemMeta: ControlDialogStateItemMeta; + }; +}; + +export const initControlDialog = (payload: InitDialogAction['payload']): InitDialogAction => { + return { + type: INIT_DIALOG, + payload, + }; +}; + +export const RESET_DIALOG = Symbol('controlDialog/RESET_DIALOG'); + +export type ResetDialogAction = { + type: typeof RESET_DIALOG; +}; + +export const resetControlDialog = (): ResetDialogAction => { + return { + type: RESET_DIALOG, + }; +}; export const ADD_SELECTOR_TO_GROUP = Symbol('controlDialog/ADD_SELECTOR_TO_GROUP'); @@ -65,3 +139,270 @@ export const setSelectorDialogItem = ( payload, }; }; + +export const SET_LAST_USED_DATASET_ID = Symbol('controlDialog/SET_LAST_USED_DATASET_ID'); +export type SetLastUsedDatasetIdAction = { + type: typeof SET_LAST_USED_DATASET_ID; + payload: string; +}; + +export const setLastUsedDatasetId = (datasetId: string): SetLastUsedDatasetIdAction => ({ + type: SET_LAST_USED_DATASET_ID, + payload: datasetId, +}); + +export const SET_LAST_USED_CONNECTION_ID = Symbol('controlDialog/SET_LAST_USED_CONNECTION_ID'); +export type SetLastUsedConnectionIdAction = { + type: typeof SET_LAST_USED_CONNECTION_ID; + payload: string; +}; + +export const setLastUsedConnectionId = (connectionId: string): SetLastUsedConnectionIdAction => ({ + type: SET_LAST_USED_CONNECTION_ID, + payload: connectionId, +}); + +export const applyGroupControlDialog = ({ + setItemData, + closeDialog, +}: { + closeDialog: () => void; + setItemData: (newItemData: SetItemDataArgs) => void; +}) => { + return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => { + const state = getState(); + const selectorsGroup = selectSelectorsGroup(state); + const activeSelectorIndex = selectActiveSelectorIndex(state); + const openedItemData = selectOpenedItemData(state); + const openedItemId = selectOpenedItemId(state); + + let firstInvalidIndex: number | null = null; + const groupFieldNames: Record = {}; + selectorsGroup.group.forEach((groupItem) => { + if (groupItem.fieldName) { + const itemName = groupItem.title; + if (groupFieldNames[groupItem.fieldName] && itemName) { + groupFieldNames[groupItem.fieldName].push(itemName); + } + + if (!groupFieldNames[groupItem.fieldName] && itemName) { + groupFieldNames[groupItem.fieldName] = [itemName]; + } + } + }); + + const validatedSelectorsGroup = Object.assign({}, selectorsGroup); + + // check validation for every control + for (let i = 0; i < validatedSelectorsGroup.group.length; i += 1) { + const validation = getControlValidation( + validatedSelectorsGroup.group[i], + groupFieldNames, + ); + + if (!isEmpty(validation) && firstInvalidIndex === null) { + firstInvalidIndex = i; + } + + validatedSelectorsGroup.group[i].validation = validation; + } + + if (firstInvalidIndex !== null) { + const activeSelectorValidation = + validatedSelectorsGroup.group[activeSelectorIndex].validation; + dispatch(updateSelectorsGroup(validatedSelectorsGroup)); + + if (!isEmpty(activeSelectorValidation)) { + dispatch( + setSelectorDialogItem({ + validation: activeSelectorValidation, + }), + ); + return; + } + dispatch(setActiveSelectorIndex({activeSelectorIndex: firstInvalidIndex})); + return; + } + + const isSingleControl = selectorsGroup.group.length === 1; + const autoHeight = + !isSingleControl || + selectorsGroup.buttonApply || + selectorsGroup.buttonReset || + selectorsGroup.group[0].titlePlacement === TitlePlacementOption.Top + ? selectorsGroup.autoHeight + : false; + const updateControlsOnChange = + !isSingleControl && selectorsGroup.buttonApply + ? selectorsGroup.updateControlsOnChange + : false; + + const data = { + autoHeight, + buttonApply: selectorsGroup.buttonApply, + buttonReset: selectorsGroup.buttonReset, + updateControlsOnChange, + group: selectorsGroup.group.map((selector) => { + let hasChangedSourceType = false; + if (openedItemId) { + const configSelectorItem = ( + openedItemData as DashTabItemGroupControl['data'] + ).group?.find(({id}) => id === selector.id); + + // we check changing of sourceType only if selector was already saved and it's not the old one + hasChangedSourceType = configSelectorItem + ? configSelectorItem.sourceType !== selector.sourceType + : false; + } + + return { + id: selector.id, + title: selector.title, + sourceType: selector.sourceType, + source: getItemDataSource(selector) as DashTabItemControlData['source'], + placementMode: isSingleControl ? 'auto' : selector.placementMode, + width: isSingleControl ? '' : selector.width, + defaults: getControlDefaultsForField(selector, hasChangedSourceType), + namespace: selector.namespace, + }; + }), + }; + + const getExtendedItemData = getExtendedItemDataAction(); + const itemData = dispatch(getExtendedItemData({data})); + + setItemData({...itemData, type: DashTabItemType.GroupControl}); + closeDialog(); + }; +}; + +export const copyControlToStorage = (controlIndex: number) => { + return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => { + const state = getState(); + const selectorsGroup = selectSelectorsGroup(state); + const activeSelectorIndex = selectActiveSelectorIndex(state); + const { + workbookId, + scope, + entryId, + namespace, + currentTabId: tabId, + } = selectOpenedItemMeta(state); + const validation = getControlValidation(selectorsGroup.group[controlIndex]); + + if (!scope) { + return; + } + + if (!isEmpty(validation)) { + if (activeSelectorIndex !== controlIndex) { + dispatch(setActiveSelectorIndex({activeSelectorIndex: controlIndex})); + } + + dispatch( + setSelectorDialogItem({ + validation, + }), + ); + + dispatch( + showToast({ + type: 'danger', + title: dialogI18n('label_copy-invalid-control'), + }), + ); + + return; + } + + // logic is copied from dashkit + const selectorToCopy = selectorsGroup.group[controlIndex]; + + const copiedItem = { + id: selectorToCopy.id, + title: selectorToCopy.title, + sourceType: selectorToCopy.sourceType, + source: getItemDataSource(selectorToCopy) as DashTabItemControlData['source'], + defaults: getControlDefaultsForField(selectorToCopy), + namespace: namespace || DEFAULT_NAMESPACE, + width: '', + placementMode: CONTROLS_PLACEMENT_MODE.AUTO, + }; + + const options: PreparedCopyItemOptions = { + timestamp: Date.now(), + data: { + ...getGroupSelectorDialogInitialState(), + group: [copiedItem as unknown as ConfigItemGroup], + }, + type: DashTabItemType.GroupControl, + defaults: copiedItem.defaults, + namespace: copiedItem.namespace, + layout: DEFAULT_CONTROL_LAYOUT, + targetId: selectorToCopy.id, + }; + + const preparedOptions = getPreparedCopyItemOptions(options, null, { + workbookId: workbookId ?? null, + fromScope: scope, + targetDashTabId: tabId, + targetEntryId: entryId, + }); + + localStorage.setItem(COPIED_WIDGET_STORAGE_KEY, JSON.stringify(preparedOptions)); + // https://stackoverflow.com/questions/35865481/storage-event-not-firing + window.dispatchEvent(new Event('storage')); + }; +}; + +export const applyExternalControlDialog = ({ + closeDialog, + setItemData, +}: { + closeDialog: () => void; + setItemData: (newItemData: SetItemDataArgs) => void; +}) => { + return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => { + const state = getState(); + const selectorDialog = selectSelectorDialog(state); + const {title, sourceType, autoHeight} = selectorDialog; + + const validation = getControlValidation(selectorDialog); + + if (!isEmpty(validation)) { + dispatch( + setSelectorDialogItem({ + validation, + }), + ); + return; + } + + const hasChangedSourceType = selectIsControlSourceTypeHasChanged(state); + const defaults = getControlDefaultsForField(selectorDialog, hasChangedSourceType); + + const data = { + title, + sourceType, + autoHeight, + source: getItemDataSource(selectorDialog), + }; + const getExtendedItemData = getExtendedItemDataAction(); + const itemData = dispatch(getExtendedItemData({data, defaults})); + + setItemData({ + data: itemData.data, + type: DashTabItemType.Control, + defaults: itemData.defaults, + }); + closeDialog(); + }; +}; + +export const closeExternalControlDialog = ({closeDialog}: {closeDialog: () => void}) => { + return (dispatch: AppDispatch) => { + const beforeCloseDialogItem = getBeforeCloseDialogItemAction(); + dispatch(beforeCloseDialogItem()); + closeDialog(); + }; +}; diff --git a/src/ui/store/constants/controlDialog.ts b/src/ui/store/constants/controlDialog.ts index a26f9914a5..edaa165b20 100644 --- a/src/ui/store/constants/controlDialog.ts +++ b/src/ui/store/constants/controlDialog.ts @@ -36,3 +36,24 @@ export const DATEPICKER_OPERATIONS = [...BASE_EQUALITY_OPERATIONS, ...BASE_COMPA export const DATEPICKER_RANGE_OPERATIONS = [ ...BASE_DATE_OPERATIONS.map((item) => ({...item, range: true})), ]; + +export const ELEMENT_TYPE: { + SELECT: 'select'; + DATE: 'date'; + INPUT: 'input'; + CHECKBOX: 'checkbox'; +} = { + SELECT: 'select', + DATE: 'date', + INPUT: 'input', + CHECKBOX: 'checkbox', +}; + +export const DATE_FORMAT = 'DD.MM.YYYY'; + +export const DATETIME_FORMAT = 'DD.MM.YYYY HH:mm:ss'; + +export enum CheckboxControlValue { + TRUE = 'true', + FALSE = 'false', +} diff --git a/src/ui/store/reducers/controlDialog.ts b/src/ui/store/reducers/controlDialog.ts index 8dfcb14f50..03240316ad 100644 --- a/src/ui/store/reducers/controlDialog.ts +++ b/src/ui/store/reducers/controlDialog.ts @@ -1,18 +1,64 @@ -import type {DashTabItemControlData, DashTabItemGroupControlData, StringParams} from 'shared'; +import type { + DashTabItem, + DashTabItemControlData, + DashTabItemGroupControlData, + EntryScope, + StringParams, +} from 'shared'; import {DashTabItemControlSourceType, DashTabItemType, TitlePlacementOption} from 'shared'; import type {SelectorDialogState, SelectorsGroupDialogState} from '../typings/controlDialog'; import {getRandomKey} from 'ui/libs/DatalensChartkit/helpers/helpers'; -import {ELEMENT_TYPE} from 'units/dash/containers/Dialogs/Control/constants'; import {CONTROLS_PLACEMENT_MODE} from 'ui/constants/dialogs'; import {extractTypedQueryParams} from 'shared/modules/typed-query-api/helpers/parameters'; +import { + ADD_SELECTOR_TO_GROUP, + INIT_DIALOG, + RESET_DIALOG, + SET_ACTIVE_SELECTOR_INDEX, + SET_SELECTOR_DIALOG_ITEM, + UPDATE_SELECTORS_GROUP, + SET_LAST_USED_DATASET_ID, + SET_LAST_USED_CONNECTION_ID, +} from '../actions/controlDialog'; +import type { + SetLastUsedDatasetIdAction, + SetLastUsedConnectionIdAction, + InitDialogAction, + ResetDialogAction, + SetSelectorDialogItemAction, + SetActiveSelectorIndexAction, + UpdateSelectorsGroupAction, + AddSelectorToGroupAction, +} from '../actions/controlDialog'; +import {getActualUniqueFieldNameValidation, getInitialDefaultValue} from '../utils/controlDialog'; +import {I18n} from 'i18n'; +import {ELEMENT_TYPE} from '../constants/controlDialog'; + +const i18n = I18n.keyset('dash.store.view'); + +export type ControlDialogStateItemMeta = { + scope: EntryScope; + currentTabId: string | null; + entryId: string | null; + workbookId: string | null; + namespace: string | null; +}; export interface ControlDialogState { activeSelectorIndex: number; selectorsGroup: SelectorsGroupDialogState; selectorDialog: SelectorDialogState; + openedDialog: DashTabItemType | null; + openedItemId: string | null; + openedItemData: DashTabItem['data'] | null; + openedItemMeta: ControlDialogStateItemMeta | null; + + lastUsedDatasetId?: string; + lastUsedConnectionId?: string; } export function getSelectorDialogInitialState( args: { + title?: string | null; lastUsedDatasetId?: string; lastUsedConnectionId?: string; openedDialog?: DashTabItemType.Control | DashTabItemType.GroupControl; @@ -37,6 +83,7 @@ export function getSelectorDialogInitialState( required: false, showHint: false, draftId: getRandomKey(), + ...(args.title ? {title: args.title} : {}), }; } @@ -52,16 +99,16 @@ export function getGroupSelectorDialogInitialState(): SelectorsGroupDialogState export function getSelectorDialogFromData( data: DashTabItemControlData & {source: {[key: string]: any}}, - defaults: StringParams = {}, + defaults?: StringParams | null, ): SelectorDialogState { let selectorParameters; switch (data.sourceType) { case DashTabItemControlSourceType.Connection: - selectorParameters = extractTypedQueryParams(defaults, data.source.fieldName); + selectorParameters = extractTypedQueryParams(defaults ?? {}, data.source.fieldName); break; case DashTabItemControlSourceType.External: - selectorParameters = defaults; + selectorParameters = defaults ?? {}; break; default: selectorParameters = {}; @@ -71,7 +118,7 @@ export function getSelectorDialogFromData( validation: {}, isManualTitle: true, - defaults, + defaults: defaults ?? {}, title: data.title, sourceType: data.sourceType, @@ -112,6 +159,7 @@ export function getSelectorDialogFromData( export function getSelectorGroupDialogFromData(data: DashTabItemGroupControlData) { return { + updateControlsOnChange: false, ...data, group: data.group.map((item) => getSelectorDialogFromData(item)), }; @@ -121,8 +169,238 @@ const getInitialState = (): ControlDialogState => ({ activeSelectorIndex: 0, selectorsGroup: getGroupSelectorDialogInitialState(), selectorDialog: getSelectorDialogInitialState(), + openedDialog: null, + openedItemId: null, + openedItemData: null, + openedItemMeta: null, }); -export function controlDialog(state: ControlDialogState = getInitialState()): ControlDialogState { - return state; +// eslint-disable-next-line complexity +export function controlDialog( + state: ControlDialogState = getInitialState(), + action: + | InitDialogAction + | ResetDialogAction + | SetSelectorDialogItemAction + | SetActiveSelectorIndexAction + | UpdateSelectorsGroupAction + | AddSelectorToGroupAction + | SetLastUsedDatasetIdAction + | SetLastUsedConnectionIdAction, +): ControlDialogState { + const {type} = action; + switch (type) { + case INIT_DIALOG: { + const payload = action.payload; + const {id: openedItemId, type: openedDialog, data, defaults, openedItemMeta} = payload; + + const newState = { + ...state, + openedItemData: {...data}, + openedDialog, + openedItemId, + openedItemMeta, + activeSelectorIndex: 0, + }; + + if ( + openedItemId === null && + (openedDialog === DashTabItemType.Control || + openedDialog === DashTabItemType.GroupControl) + ) { + newState.selectorDialog = getSelectorDialogInitialState({ + title: + openedDialog === DashTabItemType.GroupControl + ? i18n('label_selector-dialog') + : null, + lastUsedDatasetId: state.lastUsedDatasetId, + lastUsedConnectionId: state.lastUsedConnectionId, + openedDialog, + }); + newState.selectorsGroup = { + ...getGroupSelectorDialogInitialState(), + group: [newState.selectorDialog], + }; + } else if ( + openedDialog === DashTabItemType.Control && + (data as unknown as DashTabItemControlData).sourceType !== 'external' + ) { + const selectorDialog = getSelectorDialogFromData( + data as unknown as DashTabItemControlData, + ); + + // migration forward to group + newState.openedDialog = DashTabItemType.GroupControl; + newState.selectorsGroup = { + ...getGroupSelectorDialogInitialState(), + group: [selectorDialog], + }; + newState.selectorDialog = selectorDialog; + } else if (openedDialog === DashTabItemType.GroupControl) { + newState.selectorsGroup = getSelectorGroupDialogFromData( + data as unknown as DashTabItemGroupControlData, + ); + newState.selectorDialog = newState.selectorsGroup.group[0]; + } else if (openedDialog === DashTabItemType.Control) { + newState.selectorDialog = getSelectorDialogFromData( + data as unknown as DashTabItemControlData, + defaults, + ); + } + + return newState; + } + + case SET_SELECTOR_DIALOG_ITEM: { + const {selectorDialog, selectorsGroup, activeSelectorIndex} = state; + const {payload} = action; + + const elementTypeChanged = + payload.elementType && selectorDialog.elementType !== payload.elementType; + const defaultValue = elementTypeChanged + ? getInitialDefaultValue(payload.elementType!) + : selectorDialog.defaultValue; + const isElementTypeWithoutRequired = + elementTypeChanged && payload.elementType === ELEMENT_TYPE.CHECKBOX; + const required = isElementTypeWithoutRequired ? false : selectorDialog.required; + + const validation: SelectorDialogState['validation'] = { + title: + selectorDialog.title === payload.title + ? selectorDialog.validation.title + : undefined, + uniqueFieldName: + selectorDialog.fieldName === payload.fieldName + ? getActualUniqueFieldNameValidation( + selectorsGroup.group, + payload.fieldName, + selectorDialog.validation.fieldName, + ) + : undefined, + fieldName: + selectorDialog.fieldName === payload.fieldName + ? selectorDialog.validation.fieldName + : undefined, + datasetFieldId: + selectorDialog.datasetFieldId === payload.datasetFieldId + ? selectorDialog.validation.datasetFieldId + : undefined, + defaultValue: + !isElementTypeWithoutRequired && + selectorDialog.defaultValue === payload.defaultValue + ? selectorDialog.validation.defaultValue + : undefined, + }; + + const newSelectorState = { + ...state.selectorDialog, + defaultValue, + validation, + required, + ...payload, + }; + + const newSelectorsGroupState = { + ...selectorsGroup, + }; + + if (state.selectorsGroup.group.length) { + newSelectorsGroupState.group = [...selectorsGroup.group]; + newSelectorsGroupState.group[activeSelectorIndex] = newSelectorState; + } + + return { + ...state, + selectorDialog: newSelectorState, + selectorsGroup: newSelectorsGroupState, + }; + } + + case ADD_SELECTOR_TO_GROUP: { + const {payload} = action; + const newSelector = getSelectorDialogInitialState( + state.lastUsedDatasetId + ? { + lastUsedDatasetId: state.lastUsedDatasetId, + } + : {}, + ); + + // if current length is 1, the added selector will be the second so we enable autoHeight + const autoHeight = + state.selectorsGroup.group.length === 1 ? true : state.selectorsGroup.autoHeight; + + return { + ...state, + selectorsGroup: { + ...state.selectorsGroup, + group: [...state.selectorsGroup.group, {...newSelector, title: payload.title}], + autoHeight, + }, + }; + } + + case UPDATE_SELECTORS_GROUP: { + const {selectorsGroup} = state; + const {group, autoHeight, buttonApply, buttonReset, updateControlsOnChange} = + action.payload; + + // if the number of selectors has increased from 1 to several, we enable autoHeight + const updatedAutoHeight = + selectorsGroup.group.length === 1 && group.length > 1 ? true : autoHeight; + + return { + ...state, + selectorsGroup: { + ...selectorsGroup, + group, + autoHeight: updatedAutoHeight, + buttonApply, + buttonReset, + updateControlsOnChange, + }, + }; + } + + case SET_ACTIVE_SELECTOR_INDEX: { + const newCurrentSelector = + state.selectorsGroup.group[action.payload.activeSelectorIndex]; + + return { + ...state, + activeSelectorIndex: action.payload.activeSelectorIndex, + selectorDialog: { + ...newCurrentSelector, + validation: { + ...newCurrentSelector.validation, + // check if validation with non-unique uniqueFieldName is still valid + uniqueFieldName: getActualUniqueFieldNameValidation( + state.selectorsGroup.group, + newCurrentSelector.fieldName, + newCurrentSelector.validation.uniqueFieldName, + ), + }, + }, + }; + } + + case RESET_DIALOG: { + return getInitialState(); + } + + case SET_LAST_USED_DATASET_ID: + return { + ...state, + lastUsedDatasetId: action.payload, + }; + + case SET_LAST_USED_CONNECTION_ID: + return { + ...state, + lastUsedConnectionId: action.payload, + }; + + default: + return state; + } } diff --git a/src/ui/store/reducers/index.tsx b/src/ui/store/reducers/index.tsx index 26f82de96a..eb6bd7f03a 100644 --- a/src/ui/store/reducers/index.tsx +++ b/src/ui/store/reducers/index.tsx @@ -8,8 +8,8 @@ import colorPaletteEditor from 'store/reducers/colorPaletteEditor'; import {collectionsStructure} from 'store/reducers/collectionsStructure'; import {migrationToWorkbook} from 'ui/store/reducers/migrationToWorkbook'; import {copyEntriesToWorkbook} from 'ui/store/reducers/copyEntriesToWorkbook'; - -import {editHistory} from './editHistory'; +import {editHistory} from 'ui/store/reducers/editHistory'; +import {controlDialog} from 'ui/store/reducers/controlDialog'; export default { user, @@ -20,6 +20,7 @@ export default { entryContent, colorPaletteEditor, collectionsStructure, + controlDialog, migrationToWorkbook, copyEntriesToWorkbook, editHistory, diff --git a/src/ui/store/selectors/controlDialog.ts b/src/ui/store/selectors/controlDialog.ts index d39e861ff1..ac9050cecd 100644 --- a/src/ui/store/selectors/controlDialog.ts +++ b/src/ui/store/selectors/controlDialog.ts @@ -2,7 +2,7 @@ import type {Operation} from 'components/DialogFilter/constants'; import {BOOLEAN_OPERATIONS} from 'components/DialogFilter/constants'; import {getAvailableOperations} from 'components/DialogFilter/utils'; import {getFilterOperations} from 'libs/datasetHelper'; -import type {Operations} from 'shared'; +import type {DashTabItemControlData, Operations} from 'shared'; import {DashTabItemControlSourceType, DATASET_FIELD_TYPES} from 'shared'; import type {DatalensGlobalState} from 'ui/index'; import { @@ -14,38 +14,73 @@ import { MULTISELECT_OPERATIONS, SELECTOR_OPERATIONS, } from '../constants/controlDialog'; +import {createSelector} from 'reselect'; -export const selectSelectorsGroup = (state: DatalensGlobalState) => state.dash.selectorsGroup; +export const selectOpenedDialogType = (state: DatalensGlobalState) => + state.controlDialog.openedDialog; + +export const selectControlDialogState = (state: DatalensGlobalState) => state.controlDialog; + +export const selectSelectorsGroup = (state: DatalensGlobalState) => + selectControlDialogState(state).selectorsGroup; + +export const selectOpenedItemMeta = (state: DatalensGlobalState) => + selectControlDialogState(state).openedItemMeta || { + scope: null, + workbookId: null, + entryId: null, + currentTabId: null, + namespace: null, + }; + +export const selectOpenedItemData = (state: DatalensGlobalState) => + selectControlDialogState(state).openedItemData || {}; + +export const selectOpenedItemId = (state: DatalensGlobalState) => + selectControlDialogState(state).openedItemId; export const selectActiveSelectorIndex = (state: DatalensGlobalState) => - state.dash.activeSelectorIndex || 0; + selectControlDialogState(state).activeSelectorIndex || 0; -export const selectSelectorDialog = (state: DatalensGlobalState) => state.dash.selectorDialog; +export const selectSelectorDialog = (state: DatalensGlobalState) => + selectControlDialogState(state).selectorDialog; export const selectSelectorSourceType = (state: DatalensGlobalState) => - state.dash.selectorDialog.sourceType; + selectControlDialogState(state).selectorDialog.sourceType; export const selectSelectorControlType = (state: DatalensGlobalState) => - state.dash.selectorDialog.elementType; + selectControlDialogState(state).selectorDialog.elementType; export const selectSelectorDefaultValue = (state: DatalensGlobalState) => - state.dash.selectorDialog.defaultValue; + selectControlDialogState(state).selectorDialog.defaultValue; export const selectSelectorRequired = (state: DatalensGlobalState) => - state.dash.selectorDialog.required; + selectControlDialogState(state).selectorDialog.required; export const selectSelectorValidation = (state: DatalensGlobalState) => - state.dash.selectorDialog.validation; + selectControlDialogState(state).selectorDialog.validation; export const getDatasetField = (state: DatalensGlobalState) => { - const {dataset, datasetFieldId} = state.dash.selectorDialog; + const {dataset, datasetFieldId} = selectControlDialogState(state).selectorDialog; return (dataset?.dataset?.result_schema || dataset?.result_schema || [])?.find( (item) => item.guid === datasetFieldId, ); }; +export const selectIsControlSourceTypeHasChanged = createSelector( + [selectOpenedItemData, selectSelectorSourceType], + (openedItemData, sourceType) => { + // New item + if (!openedItemData) { + return false; + } + + return (openedItemData as DashTabItemControlData).sourceType !== sourceType; + }, +); + export const selectIsControlConfigurationDisabled = (state: DatalensGlobalState) => { - const selectorDialog = state.dash.selectorDialog; + const selectorDialog = selectControlDialogState(state).selectorDialog; switch (selectorDialog.sourceType) { case DashTabItemControlSourceType.Dataset: @@ -58,7 +93,8 @@ export const selectIsControlConfigurationDisabled = (state: DatalensGlobalState) }; export const selectIsParametersSectionAvailable = (state: DatalensGlobalState): boolean => { - const {sourceType, connectionId, connectionQueryTypes} = state.dash.selectorDialog; + const {sourceType, connectionId, connectionQueryTypes} = + selectControlDialogState(state).selectorDialog; switch (sourceType) { case DashTabItemControlSourceType.Connection: @@ -73,7 +109,7 @@ export const selectIsParametersSectionAvailable = (state: DatalensGlobalState): export const selectAvailableOperationsDict = ( state: DatalensGlobalState, ): Record | undefined => { - const {dataset, datasetFieldId} = state.dash.selectorDialog; + const {dataset, datasetFieldId} = selectControlDialogState(state).selectorDialog; if (!dataset) { return undefined; @@ -104,7 +140,7 @@ export const selectAvailableOperationsDict = ( export const selectInputOperations = (state: DatalensGlobalState) => { const {multiselectable, isRange, elementType, fieldType, sourceType, datasetFieldId} = - state.dash.selectorDialog; + selectControlDialogState(state).selectorDialog; if (sourceType !== 'dataset' && elementType === 'checkbox') { return BOOLEAN_OPERATIONS; diff --git a/src/ui/store/utils/controlDialog.ts b/src/ui/store/utils/controlDialog.ts index 31a51e2fe7..e57ec8669b 100644 --- a/src/ui/store/utils/controlDialog.ts +++ b/src/ui/store/utils/controlDialog.ts @@ -5,11 +5,13 @@ import type { ItemDataSource, SelectorDialogState, SelectorDialogValidation, + SelectorElementType, } from 'ui/store/typings/controlDialog'; import {validateParamTitleOnlyUnderscore} from 'ui/units/dash/components/ParamsSettings/helpers'; import {addOperationForValue} from 'ui/units/dash/modules/helpers'; -import {ELEMENT_TYPE} from 'units/dash/containers/Dialogs/Control/constants'; +import {CheckboxControlValue} from 'ui/store/constants/controlDialog'; import type {DashTab, DashTabItemGroupControl} from 'shared/types'; +import {ELEMENT_TYPE} from '../constants/controlDialog'; export const getActualUniqueFieldNameValidation = ( group: SelectorDialogState[], @@ -286,3 +288,12 @@ export const getItemDataSource = (selectorDialog: SelectorDialogState): ItemData return source; }; + +export const getInitialDefaultValue = (elementType: SelectorElementType) => { + switch (elementType) { + case ELEMENT_TYPE.CHECKBOX: + return CheckboxControlValue.FALSE; + default: + return undefined; + } +}; diff --git a/src/ui/units/dash/containers/Dialogs/Control/constants.tsx b/src/ui/units/dash/containers/Dialogs/Control/constants.tsx deleted file mode 100644 index f1440cd30d..0000000000 --- a/src/ui/units/dash/containers/Dialogs/Control/constants.tsx +++ /dev/null @@ -1,20 +0,0 @@ -export const ELEMENT_TYPE: { - SELECT: 'select'; - DATE: 'date'; - INPUT: 'input'; - CHECKBOX: 'checkbox'; -} = { - SELECT: 'select', - DATE: 'date', - INPUT: 'input', - CHECKBOX: 'checkbox', -}; - -export enum CheckboxControlValue { - TRUE = 'true', - FALSE = 'false', -} - -export const DATE_FORMAT = 'DD.MM.YYYY'; - -export const DATETIME_FORMAT = 'DD.MM.YYYY HH:mm:ss'; diff --git a/src/ui/units/dash/containers/Dialogs/Dialogs.js b/src/ui/units/dash/containers/Dialogs/Dialogs.js index 4130c9ca4d..4fe488a428 100644 --- a/src/ui/units/dash/containers/Dialogs/Dialogs.js +++ b/src/ui/units/dash/containers/Dialogs/Dialogs.js @@ -1,12 +1,10 @@ import React from 'react'; +import {DEFAULT_NAMESPACE} from '@gravity-ui/dashkit/helpers'; import {useDispatch, useSelector} from 'react-redux'; +import {EntryScope} from 'shared'; import {useEffectOnce} from 'ui'; -import DialogChartWidget from 'ui/components/DialogChartWidget/DialogChartWidget'; -import DialogExternalControl from 'ui/components/DialogExternalControl/DialogExternalControl'; -import {DialogGroupControl} from 'ui/components/DialogGroupControl/DialogGroupControl'; -import {DialogTextWidgetWrapper} from 'ui/components/DialogTextWidget'; -import DialogTitleWidget from 'ui/components/DialogTitleWidget/DialogTitleWidget'; +import {DialogEditItem, isDialogEditItemType} from 'ui/components/DialogEditItem/DialogEditItem'; import {registry} from 'ui/registry'; import {DIALOG_TYPE} from '../../../../constants/dialogs'; @@ -15,7 +13,9 @@ import {closeDialog} from '../../store/actions/dialogs/actions'; import { selectCurrentTabId, selectDashWorkbookId, - selectIsDialogVisible, + selectEntryId, + selectNavigationPath, + selectOpenedItem, selectOpenedItemData, selectWidgetsCurrentTab, } from '../../store/selectors/dashTypedSelectors'; @@ -30,24 +30,16 @@ import Tabs from './Tabs/Tabs'; export function Dialogs() { const dispatch = useDispatch(); + const entryId = useSelector(selectEntryId); const openedDialog = useSelector((state) => state.dash.openedDialog); const widgetType = useSelector((state) => state.dash.openedItemWidgetType); const openedItemId = useSelector((state) => state.dash.openedItemId); - const openedItemData = useSelector((state) => selectOpenedItemData(state)); - const currentTabId = useSelector((state) => selectCurrentTabId(state)); - const workbookId = useSelector((state) => selectDashWorkbookId(state)); - const widgetsCurrentTab = useSelector((state) => selectWidgetsCurrentTab(state)); - const navigationPath = useSelector((state) => state.dash.navigationPath); - - const dialogTextIsVisible = useSelector((state) => - selectIsDialogVisible(state, DIALOG_TYPE.TEXT), - ); - const dialogTitleIsVisible = useSelector((state) => - selectIsDialogVisible(state, DIALOG_TYPE.TITLE), - ); - const dialogChartIsVisible = useSelector((state) => - selectIsDialogVisible(state, DIALOG_TYPE.WIDGET), - ); + const openedItemData = useSelector(selectOpenedItemData); + const openedItem = useSelector(selectOpenedItem); + const currentTabId = useSelector(selectCurrentTabId); + const workbookId = useSelector(selectDashWorkbookId); + const widgetsCurrentTab = useSelector(selectWidgetsCurrentTab); + const navigationPath = useSelector(selectNavigationPath); useEffectOnce(() => { return () => { @@ -55,60 +47,51 @@ export function Dialogs() { }; }); + const setItemDataHandle = React.useCallback( + (newItemData) => { + dispatch(setItemData(newItemData)); + }, + [dispatch], + ); + + const closeDialogHandle = React.useCallback(() => { + dispatch(closeDialog()); + }, [dispatch]); + + const changeNavigationPathHandle = React.useCallback( + (newNavigationPath) => { + dispatch(changeNavigationPath(newNavigationPath)); + }, + [dispatch], + ); + + if (openedDialog && isDialogEditItemType(openedDialog)) { + return ( + + ); + } + switch (openedDialog) { case DIALOG_TYPE.CONNECTIONS: return ; case DIALOG_TYPE.TABS: return ; - case DIALOG_TYPE.TITLE: - return ( - dispatch(closeDialog())} - setItemData={(newItemData) => { - dispatch(setItemData(newItemData)); - }} - /> - ); - case DIALOG_TYPE.TEXT: { - return ( - dispatch(closeDialog())} - setItemData={(newItemData) => { - dispatch(setItemData(newItemData)); - }} - /> - ); - } - case DIALOG_TYPE.WIDGET: - return ( - dispatch(closeDialog())} - setItemData={(newItemData) => { - dispatch(setItemData(newItemData)); - }} - navigationPath={navigationPath} - changeNavigationPath={(newNavigationPath) => { - dispatch(changeNavigationPath(newNavigationPath)); - }} - /> - ); - case DIALOG_TYPE.CONTROL: - return ; - case DIALOG_TYPE.GROUP_CONTROL: - return ; case DIALOG_TYPE.SETTINGS: return ; case DIALOG_TYPE.SELECT_STATE: { diff --git a/src/ui/units/dash/store/actions/controls/actions.ts b/src/ui/units/dash/store/actions/controls/actions.ts deleted file mode 100644 index f1a1d5deab..0000000000 --- a/src/ui/units/dash/store/actions/controls/actions.ts +++ /dev/null @@ -1,224 +0,0 @@ -import type {PreparedCopyItemOptions} from '@gravity-ui/dashkit'; -import {type ConfigItemGroup, DEFAULT_NAMESPACE} from '@gravity-ui/dashkit/helpers'; -import {I18n} from 'i18n'; -import isEmpty from 'lodash/isEmpty'; -import type {DashTabItemControlData, DashTabItemGroupControlData} from 'shared/types'; -import {DashTabItemType, TitlePlacementOption} from 'shared/types'; -import {DEFAULT_CONTROL_LAYOUT} from 'ui/components/DashKit/constants'; -import {COPIED_WIDGET_STORAGE_KEY, type DatalensGlobalState} from 'ui/index'; -import type {AppDispatch} from 'ui/store'; -import { - setActiveSelectorIndex, - setSelectorDialogItem, - updateSelectorsGroup, -} from 'ui/store/actions/controlDialog'; -import {showToast} from 'ui/store/actions/toaster'; -import {getGroupSelectorDialogInitialState} from 'ui/store/reducers/controlDialog'; -import { - getControlDefaultsForField, - getControlValidation, - getItemDataSource, -} from 'ui/store/utils/controlDialog'; -import type {CopiedConfigContext} from 'ui/units/dash/modules/helpers'; -import {getPreparedCopyItemOptions} from 'ui/units/dash/modules/helpers'; - -import {CONTROLS_PLACEMENT_MODE} from '../../../../../constants/dialogs'; -import { - selectCurrentTabId, - selectOpenedItem, - selectOpenedItemData, -} from '../../selectors/dashTypedSelectors'; -import {setItemData} from '../dashTyped'; -import {closeDialog as closeDashDialog} from '../dialogs/actions'; -import {getExtendedItemDataAction} from '../helpers'; - -const dialogI18n = I18n.keyset('dash.group-controls-dialog.edit'); - -export const copyControlToStorage = (controlIndex: number) => { - return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => { - const state = getState(); - const { - selectorsGroup, - activeSelectorIndex, - entry: {workbookId, scope, entryId}, - } = state.dash; - const openedItem = selectOpenedItem(state); - const tabId = selectCurrentTabId(state); - - const validation = getControlValidation(selectorsGroup.group[controlIndex]); - - if (!isEmpty(validation)) { - if (activeSelectorIndex !== controlIndex) { - dispatch(setActiveSelectorIndex({activeSelectorIndex: controlIndex})); - } - - dispatch( - setSelectorDialogItem({ - validation, - }), - ); - - dispatch( - showToast({ - type: 'danger', - title: dialogI18n('label_copy-invalid-control'), - }), - ); - - return; - } - - // logic is copied from dashkit - const selectorToCopy = selectorsGroup.group[controlIndex]; - - const copiedItem = { - id: selectorToCopy.id, - title: selectorToCopy.title, - sourceType: selectorToCopy.sourceType, - source: getItemDataSource(selectorToCopy) as DashTabItemControlData['source'], - defaults: getControlDefaultsForField(selectorToCopy), - namespace: openedItem?.namespace || DEFAULT_NAMESPACE, - width: '', - placementMode: CONTROLS_PLACEMENT_MODE.AUTO, - }; - - const options: PreparedCopyItemOptions = { - timestamp: Date.now(), - data: { - ...getGroupSelectorDialogInitialState(), - group: [copiedItem as unknown as ConfigItemGroup], - }, - type: DashTabItemType.GroupControl, - defaults: copiedItem.defaults, - namespace: copiedItem.namespace, - layout: DEFAULT_CONTROL_LAYOUT, - targetId: selectorToCopy.id, - }; - - const preparedOptions = getPreparedCopyItemOptions(options, null, { - workbookId: workbookId ?? null, - fromScope: scope, - targetDashTabId: tabId, - targetEntryId: entryId, - }); - - localStorage.setItem(COPIED_WIDGET_STORAGE_KEY, JSON.stringify(preparedOptions)); - // https://stackoverflow.com/questions/35865481/storage-event-not-firing - window.dispatchEvent(new Event('storage')); - }; -}; - -export const applyGroupControlDialog = () => { - return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => { - const state = getState(); - const {selectorsGroup, openedItemId, activeSelectorIndex} = state.dash; - - let firstInvalidIndex: number | null = null; - const groupFieldNames: Record = {}; - selectorsGroup.group.forEach((groupItem) => { - if (groupItem.fieldName) { - const itemName = groupItem.title; - if (groupFieldNames[groupItem.fieldName] && itemName) { - groupFieldNames[groupItem.fieldName].push(itemName); - } - - if (!groupFieldNames[groupItem.fieldName] && itemName) { - groupFieldNames[groupItem.fieldName] = [itemName]; - } - } - }); - - const validatedSelectorsGroup = Object.assign({}, selectorsGroup); - - // check validation for every control - for (let i = 0; i < validatedSelectorsGroup.group.length; i += 1) { - const validation = getControlValidation( - validatedSelectorsGroup.group[i], - groupFieldNames, - ); - - if (!isEmpty(validation) && firstInvalidIndex === null) { - firstInvalidIndex = i; - } - - validatedSelectorsGroup.group[i].validation = validation; - } - - if (firstInvalidIndex !== null) { - const activeSelectorValidation = - validatedSelectorsGroup.group[activeSelectorIndex].validation; - dispatch(updateSelectorsGroup(validatedSelectorsGroup)); - - if (!isEmpty(activeSelectorValidation)) { - dispatch( - setSelectorDialogItem({ - validation: activeSelectorValidation, - }), - ); - return; - } - dispatch(setActiveSelectorIndex({activeSelectorIndex: firstInvalidIndex})); - return; - } - - const isSingleControl = selectorsGroup.group.length === 1; - const autoHeight = - !isSingleControl || - selectorsGroup.buttonApply || - selectorsGroup.buttonReset || - selectorsGroup.group[0].titlePlacement === TitlePlacementOption.Top - ? selectorsGroup.autoHeight - : false; - const updateControlsOnChange = - !isSingleControl && selectorsGroup.buttonApply - ? selectorsGroup.updateControlsOnChange - : false; - - const data = { - autoHeight, - buttonApply: selectorsGroup.buttonApply, - buttonReset: selectorsGroup.buttonReset, - updateControlsOnChange, - group: selectorsGroup.group.map((selector) => { - let hasChangedSourceType = false; - if (openedItemId) { - const openedItemData = selectOpenedItemData( - state, - ) as DashTabItemGroupControlData; - - const configSelectorItem = openedItemData.group?.find( - ({id}) => id === selector.id, - ); - - // we check changing of sourceType only if selector was already saved and it's not the old one - hasChangedSourceType = configSelectorItem - ? configSelectorItem.sourceType !== selector.sourceType - : false; - } - - return { - id: selector.id, - title: selector.title, - sourceType: selector.sourceType, - source: getItemDataSource(selector) as DashTabItemControlData['source'], - placementMode: isSingleControl ? 'auto' : selector.placementMode, - width: isSingleControl ? '' : selector.width, - defaults: getControlDefaultsForField(selector, hasChangedSourceType), - namespace: selector.namespace, - }; - }), - }; - - const getExtendedItemData = getExtendedItemDataAction(); - const itemData = dispatch(getExtendedItemData({data})); - - dispatch( - setItemData({ - data: itemData.data, - type: DashTabItemType.GroupControl, - }), - ); - - dispatch(closeDashDialog()); - }; -}; diff --git a/src/ui/units/dash/store/actions/dashTyped.ts b/src/ui/units/dash/store/actions/dashTyped.ts index 3ae4be2c82..0961088a5e 100644 --- a/src/ui/units/dash/store/actions/dashTyped.ts +++ b/src/ui/units/dash/store/actions/dashTyped.ts @@ -19,19 +19,15 @@ import type { DashSettings, DashTab, DashTabItem, + DashTabItemControl, + DashTabItemGroupControl, DashTabItemImage, DashTabItemWidget, RecursivePartial, } from 'shared'; -import {DashTabItemType, EntryScope, EntryUpdateMode} from 'shared'; +import {EntryScope, EntryUpdateMode} from 'shared'; import type {AppDispatch} from 'ui/store'; -import {setSelectorDialogItem} from 'ui/store/actions/controlDialog'; -import type {ItemDataSource, SelectorDialogState} from 'ui/store/typings/controlDialog'; -import { - getControlDefaultsForField, - getControlValidation, - getItemDataSource, -} from 'ui/store/utils/controlDialog'; +import type {ItemDataSource} from 'ui/store/typings/controlDialog'; import {getLoginOrIdFromLockedError, isEntryIsLockedError} from 'utils/errors/errorByCode'; import {setLockedTextInfo} from '../../../../components/RevisionsPanel/RevisionsPanel'; @@ -55,16 +51,10 @@ import { selectDashData, selectDashEntry, selectEntryId, - selectIsControlSourceTypeHasChanged, } from '../selectors/dashTypedSelectors'; import {save} from './base/actions'; -import {closeDialog as closeDashDialog} from './dialogs/actions'; -import { - getBeforeCloseDialogItemAction, - getExtendedItemDataAction, - migrateDataSettings, -} from './helpers'; +import {migrateDataSettings} from './helpers'; import type {DashDispatch} from './index'; @@ -324,28 +314,6 @@ export const toggleTableOfContent = (expanded?: boolean): ToggleTableOfContentAc payload: expanded, }); -export const SET_LAST_USED_DATASET_ID = Symbol('dash/SET_LAST_USED_DATASET_ID'); -export type SetLastUsedDatasetIdAction = { - type: typeof SET_LAST_USED_DATASET_ID; - payload: string; -}; - -export const setLastUsedDatasetId = (datasetId: string): SetLastUsedDatasetIdAction => ({ - type: SET_LAST_USED_DATASET_ID, - payload: datasetId, -}); - -export const SET_LAST_USED_CONNECTION_ID = Symbol('dash/SET_LAST_USED_CONNECTION_ID'); -export type SetLastUsedConnectionIdAction = { - type: typeof SET_LAST_USED_CONNECTION_ID; - payload: string; -}; - -export const setLastUsedConnectionId = (connectionId: string): SetLastUsedConnectionIdAction => ({ - type: SET_LAST_USED_CONNECTION_ID, - payload: connectionId, -}); - type SetItemDataBase = { title?: string; sourceType?: string; @@ -354,11 +322,13 @@ type SetItemDataBase = { }; export type SetItemDataText = RecursivePartial & SetItemDataBase; export type SetItemDataTitle = RecursivePartial & SetItemDataBase; +export type SetItemDataGroupControl = Partial & SetItemDataBase; +export type SetItemDataExternalControl = Partial & SetItemDataBase; export type SetItemDataImage = DashTabItemImage['data']; export type SetItemDataDefaults = Record; export type SetItemDataArgs = { - data: SetItemDataText | SetItemDataTitle | SetItemDataImage; + data: SetItemDataText | SetItemDataTitle | SetItemDataImage | SetItemDataGroupControl; defaults?: SetItemDataDefaults; type?: string; namespace?: string; @@ -380,47 +350,6 @@ export const setItemData = (data: SetItemDataArgs) => { }; }; -export const applyControl2Dialog = () => { - return (dispatch: AppDispatch, getState: () => DatalensGlobalState) => { - const state = getState(); - const selectorDialog = state.dash.selectorDialog as SelectorDialogState; - const {title, sourceType, autoHeight} = selectorDialog; - - const validation = getControlValidation(selectorDialog); - - if (!isEmpty(validation)) { - dispatch( - setSelectorDialogItem({ - validation, - }), - ); - return; - } - - const hasChangedSourceType = selectIsControlSourceTypeHasChanged(state); - const defaults = getControlDefaultsForField(selectorDialog, hasChangedSourceType); - - const data = { - title, - sourceType, - autoHeight, - source: getItemDataSource(selectorDialog), - }; - const getExtendedItemData = getExtendedItemDataAction(); - const itemData = dispatch(getExtendedItemData({data, defaults})); - - dispatch( - setItemData({ - data: itemData.data, - type: DashTabItemType.Control, - defaults: itemData.defaults, - }), - ); - - dispatch(closeDashDialog()); - }; -}; - export const SET_DASH_VIEW_MODE = Symbol('dash/SET_DASH_VIEW_MODE'); export type SetViewModeAction = { type: typeof SET_DASH_VIEW_MODE; @@ -927,14 +856,6 @@ export const setWidgetCurrentTab = ( payload, }); -export const closeControl2Dialog = () => { - return (dispatch: AppDispatch) => { - const beforeCloseDialogItem = getBeforeCloseDialogItemAction(); - dispatch(beforeCloseDialogItem()); - dispatch(closeDashDialog()); - }; -}; - export function updateDeprecatedDashConfig() { return async (dispatch: DashDispatch, getState: () => DatalensGlobalState) => { const data = selectDashData(getState()); diff --git a/src/ui/units/dash/store/actions/index.ts b/src/ui/units/dash/store/actions/index.ts index 1eb31a38ff..c9ed11ad82 100644 --- a/src/ui/units/dash/store/actions/index.ts +++ b/src/ui/units/dash/store/actions/index.ts @@ -23,8 +23,6 @@ import type { SetHashStateAction, SetInitialPageTabsItemsAction, SetItemDataAction, - SetLastUsedConnectionIdAction, - SetLastUsedDatasetIdAction, SetLoadingEditModeAction, SetPageDefaultTabItemsAction, SetPageTabAction, @@ -55,7 +53,6 @@ export type DashAction = | SetStateHashIdAction | SetErrorModeAction | ToggleTableOfContentAction - | SetLastUsedDatasetIdAction | SetSelectorDialogItemAction | AddSelectorToGroupAction | UpdateSelectorsGroupAction @@ -79,7 +76,6 @@ export type DashAction = | CloseDialogAction | SaveDashSuccessAction | SaveDashErrorAction - | SetLastUsedConnectionIdAction | SetSettingsAction; export type DashDispatch = ThunkDispatch; diff --git a/src/ui/units/dash/store/reducers/dash.js b/src/ui/units/dash/store/reducers/dash.js index 7d9b283ae9..656690acec 100644 --- a/src/ui/units/dash/store/reducers/dash.js +++ b/src/ui/units/dash/store/reducers/dash.js @@ -1,18 +1,10 @@ import {DashKit} from '@gravity-ui/dashkit'; import {generateUniqId} from '@gravity-ui/dashkit/helpers'; -import {I18n} from 'i18n'; import update from 'immutability-helper'; import pick from 'lodash/pick'; -import {DashTabItemType, Feature} from 'shared'; -import { - getGroupSelectorDialogInitialState, - getSelectorDialogFromData, - getSelectorDialogInitialState, - getSelectorGroupDialogFromData, -} from 'ui/store/reducers/controlDialog'; +import {DashTabItemType} from 'shared'; import {migrateConnectionsForGroupControl} from 'ui/store/utils/controlDialog'; import {getUpdatedConnections} from 'ui/utils/copyItems'; -import Utils from 'utils'; import {EMBEDDED_MODE} from '../../../../constants/embedded'; import {Mode} from '../../modules/constants'; @@ -21,8 +13,6 @@ import * as actionTypes from '../constants/dashActionTypes'; import {dashTypedReducer} from './dashTypedReducer'; -const i18n = I18n.keyset('dash.store.view'); - export const TAB_PROPERTIES = [ 'id', 'title', @@ -63,11 +53,6 @@ const initialState = { error: null, isFullscreenMode: new URLSearchParams(window.location.search).get('mode') === EMBEDDED_MODE.TV, - lastUsedDatasetId: null, - - selectorDialog: getSelectorDialogInitialState(), - selectorsGroup: getGroupSelectorDialogInitialState(), - activeSelectorIndex: 0, skipReload: false, @@ -87,40 +72,23 @@ function dash(state = initialState, action) { case actionTypes.CLOSE_DIALOG: { return { ...state, - selectorsGroup: getGroupSelectorDialogInitialState(), ...action.payload, dragOperationProps: null, }; } - case actionTypes.OPEN_DIALOG: { - const selectorGroup = getGroupSelectorDialogInitialState(); - const selectorDialog = - action.payload?.openedDialog === DashTabItemType.Control || - action.payload?.openedDialog === DashTabItemType.GroupControl - ? getSelectorDialogInitialState({ - lastUsedDatasetId: state.lastUsedDatasetId, - lastUsedConnectionId: state.lastUsedConnectionId, - openedDialog: action.payload?.openedDialog, - }) - : state.selectorDialog; - - if ( - Utils.isEnabledFeature(Feature.GroupControls) && - action.payload?.openedDialog === DashTabItemType.GroupControl - ) { - // TODO: move to getSelectorDialogInitialState after the release of the feature - selectorDialog.title = i18n('label_selector-dialog'); - } + case actionTypes.OPEN_ITEM_DIALOG: { + const payload = action.payload; + return { + ...state, + openedItemId: payload.id ?? null, + openedDialog: payload.type, + }; + } + case actionTypes.OPEN_DIALOG: { return { ...state, ...action.payload, - selectorDialog, - selectorsGroup: { - ...selectorGroup, - group: [selectorDialog], - }, - activeSelectorIndex: 0, dragOperationProps: action.payload.dragOperationProps ?? null, }; } @@ -306,8 +274,6 @@ function dash(state = initialState, action) { type: action.payload.type || state.openedDialog, data: action.payload.data, namespace: action.payload.namespace, - operation: action.payload.operation, - layout: state.dragOperationProps?.itemLayout, ...(action.payload.defaults ? {defaults: action.payload.defaults} : null), }, config: {...tab, salt: data.salt, counter: data.counter}, @@ -342,42 +308,6 @@ function dash(state = initialState, action) { }), }; } - case actionTypes.OPEN_ITEM_DIALOG: { - const payload = action.payload; - const {id: openedItemId, data, defaults} = payload; - let {type: openedDialog} = tab.items.find(({id}) => id === openedItemId); - - const newState = { - ...state, - openedItemId, - activeSelectorIndex: 0, - }; - - if ( - Utils.isEnabledFeature(Feature.GroupControls) && - openedDialog === DashTabItemType.Control && - data.sourceType !== 'external' - ) { - const selectorDialog = getSelectorDialogFromData(data); - - // migration forward to group - openedDialog = DashTabItemType.GroupControl; - newState.selectorsGroup = { - ...getGroupSelectorDialogInitialState(), - group: [selectorDialog], - }; - newState.selectorDialog = selectorDialog; - } else if (openedDialog === DashTabItemType.GroupControl) { - newState.selectorsGroup = getSelectorGroupDialogFromData(data); - newState.selectorDialog = newState.selectorsGroup.group[0]; - } else if (openedDialog === DashTabItemType.Control) { - newState.selectorDialog = getSelectorDialogFromData(data, defaults); - } - - newState.openedDialog = openedDialog; - - return newState; - } default: return dashTypedReducer(state, action); diff --git a/src/ui/units/dash/store/reducers/dashTypedReducer.ts b/src/ui/units/dash/store/reducers/dashTypedReducer.ts index d8f9b38109..5688f69c2f 100644 --- a/src/ui/units/dash/store/reducers/dashTypedReducer.ts +++ b/src/ui/units/dash/store/reducers/dashTypedReducer.ts @@ -4,17 +4,7 @@ import type {DashKit} from '@gravity-ui/dashkit'; import update from 'immutability-helper'; import {cloneDeep, pick} from 'lodash'; import type {DashData, DashDragOptions, DashEntry, Permissions, WidgetType} from 'shared'; -import { - ADD_SELECTOR_TO_GROUP, - SET_ACTIVE_SELECTOR_INDEX, - SET_SELECTOR_DIALOG_ITEM, - UPDATE_SELECTORS_GROUP, -} from 'ui/store/actions/controlDialog'; -import {getSelectorDialogInitialState} from 'ui/store/reducers/controlDialog'; -import type {SelectorDialogState, SelectorsGroupDialogState} from 'ui/store/typings/controlDialog'; -import {getActualUniqueFieldNameValidation} from 'ui/store/utils/controlDialog'; - -import {ELEMENT_TYPE} from '../../containers/Dialogs/Control/constants'; + import {Mode} from '../../modules/constants'; import type {DashUpdateStatus} from '../../typings/dash'; import type {TabsHashStates} from '../actions/dashTyped'; @@ -32,8 +22,6 @@ import { SET_ERROR_MODE, SET_HASH_STATE, SET_INITIAL_PAGE_TABS_ITEMS, - SET_LAST_USED_CONNECTION_ID, - SET_LAST_USED_DATASET_ID, SET_LOADING_EDIT_MODE, SET_PAGE_DEFAULT_TAB_ITEMS, SET_PAGE_TAB, @@ -47,7 +35,6 @@ import { } from '../actions/dashTyped'; import type {DashAction} from '../actions/index'; import {SET_NEW_RELATIONS} from '../constants/dashActionTypes'; -import {getInitialDefaultValue} from '../utils'; import {TAB_PROPERTIES} from './dash'; @@ -64,7 +51,6 @@ export type DashState = { openedDialog: null; // TODO: clarify types openedItemId: string | null; showTableOfContent: boolean; - lastUsedDatasetId: null | string; lastUsedConnectionId: undefined | string; entry: DashEntry; data: DashData; @@ -73,9 +59,6 @@ export type DashState = { permissions?: Permissions; lockToken: string | null; isFullscreenMode?: boolean; - selectorDialog: SelectorDialogState; - selectorsGroup: SelectorsGroupDialogState; - activeSelectorIndex: number; isLoadingEditMode: boolean; isNewRelationsOpened?: boolean; skipReload?: boolean; @@ -231,151 +214,6 @@ export function dashTypedReducer( action.payload === undefined ? !state.showTableOfContent : action.payload, }; - case SET_LAST_USED_DATASET_ID: - return { - ...state, - lastUsedDatasetId: action.payload, - }; - - case SET_LAST_USED_CONNECTION_ID: - return { - ...state, - lastUsedConnectionId: action.payload, - }; - - case SET_SELECTOR_DIALOG_ITEM: { - const {selectorDialog, selectorsGroup, activeSelectorIndex} = state; - const {payload} = action; - - const elementTypeChanged = - payload.elementType && selectorDialog.elementType !== payload.elementType; - const defaultValue = elementTypeChanged - ? getInitialDefaultValue(payload.elementType!) - : selectorDialog.defaultValue; - const isElementTypeWithoutRequired = - elementTypeChanged && payload.elementType === ELEMENT_TYPE.CHECKBOX; - const required = isElementTypeWithoutRequired ? false : selectorDialog.required; - - const validation: SelectorDialogState['validation'] = { - title: - selectorDialog.title === payload.title - ? selectorDialog.validation.title - : undefined, - uniqueFieldName: - selectorDialog.fieldName === payload.fieldName - ? getActualUniqueFieldNameValidation( - selectorsGroup.group, - payload.fieldName, - selectorDialog.validation.fieldName, - ) - : undefined, - fieldName: - selectorDialog.fieldName === payload.fieldName - ? selectorDialog.validation.fieldName - : undefined, - datasetFieldId: - selectorDialog.datasetFieldId === payload.datasetFieldId - ? selectorDialog.validation.datasetFieldId - : undefined, - defaultValue: - !isElementTypeWithoutRequired && - selectorDialog.defaultValue === payload.defaultValue - ? selectorDialog.validation.defaultValue - : undefined, - }; - - const newSelectorState = { - ...state.selectorDialog, - defaultValue, - validation, - required, - ...payload, - }; - - const newSelectorsGroupState = { - ...selectorsGroup, - }; - - if (state.selectorsGroup.group.length) { - newSelectorsGroupState.group = [...selectorsGroup.group]; - newSelectorsGroupState.group[activeSelectorIndex] = newSelectorState; - } - - return { - ...state, - selectorDialog: newSelectorState, - selectorsGroup: newSelectorsGroupState, - }; - } - - case ADD_SELECTOR_TO_GROUP: { - const {payload} = action; - const newSelector = getSelectorDialogInitialState( - state.lastUsedDatasetId - ? { - lastUsedDatasetId: state.lastUsedDatasetId, - } - : {}, - ); - - // if current length is 1, the added selector will be the second so we enable autoHeight - const autoHeight = - state.selectorsGroup.group.length === 1 ? true : state.selectorsGroup.autoHeight; - - return { - ...state, - selectorsGroup: { - ...state.selectorsGroup, - group: [...state.selectorsGroup.group, {...newSelector, title: payload.title}], - autoHeight, - }, - }; - } - - case UPDATE_SELECTORS_GROUP: { - const {selectorsGroup} = state; - const {group, autoHeight, buttonApply, buttonReset, updateControlsOnChange} = - action.payload; - - // if the number of selectors has increased from 1 to several, we enable autoHeight - const updatedAutoHeight = - selectorsGroup.group.length === 1 && group.length > 1 ? true : autoHeight; - - return { - ...state, - selectorsGroup: { - ...selectorsGroup, - group, - autoHeight: updatedAutoHeight, - buttonApply, - buttonReset, - updateControlsOnChange, - }, - }; - } - - case SET_ACTIVE_SELECTOR_INDEX: { - const newCurrentSelector = - state.selectorsGroup.group[action.payload.activeSelectorIndex]; - - return { - ...state, - activeSelectorIndex: action.payload.activeSelectorIndex, - selectorDialog: { - ...newCurrentSelector, - validation: { - ...newCurrentSelector.validation, - // check if validation with non-unique uniqueFieldName is still valid - uniqueFieldName: getActualUniqueFieldNameValidation( - state.selectorsGroup.group, - newCurrentSelector.fieldName, - newCurrentSelector.validation.uniqueFieldName, - ), - }, - }, - }; - } - case SET_DASH_VIEW_MODE: { const entryData = state.convertedEntryData || state.entry.data; diff --git a/src/ui/units/dash/store/selectors/dashTypedSelectors.ts b/src/ui/units/dash/store/selectors/dashTypedSelectors.ts index d0c6ff8ace..159b2ad493 100644 --- a/src/ui/units/dash/store/selectors/dashTypedSelectors.ts +++ b/src/ui/units/dash/store/selectors/dashTypedSelectors.ts @@ -1,13 +1,7 @@ import type {DatalensGlobalState} from 'index'; import isEqual from 'lodash/isEqual'; import {createSelector} from 'reselect'; -import type { - DashTabItem, - DashTabItemControlData, - DashTabItemWidget, - DashTabItemWidgetTab, -} from 'shared'; -import {selectSelectorSourceType} from 'ui/store/selectors/controlDialog'; +import type {DashTabItem, DashTabItemWidget, DashTabItemWidgetTab} from 'shared'; import {ITEM_TYPE} from '../../../../constants/dialogs'; import {isOrderIdsChanged} from '../../containers/Dialogs/Tabs/PopupWidgetsOrder/helpers'; @@ -186,18 +180,6 @@ export const selectOpenedItem = createSelector( }, ); -export const selectIsControlSourceTypeHasChanged = createSelector( - [selectOpenedItemData, selectSelectorSourceType], - (openedItemData, sourceType) => { - // New item - if (!openedItemData) { - return false; - } - - return (openedItemData as DashTabItemControlData).sourceType !== sourceType; - }, -); - export const selectCurrentTabConnectableItems = createSelector([selectCurrentTab], (currentTab) => { if (!currentTab) { return undefined; @@ -264,4 +246,4 @@ export const selectCurrentTabAliases = createSelector( (currentTab) => currentTab?.aliases || null, ); -export const selectNavigationPath = (state: DatalensGlobalState) => state.dash.navigationPath; +export const selectNavigationPath = (state: DatalensGlobalState) => state.dash?.navigationPath; diff --git a/src/ui/units/dash/store/utils.ts b/src/ui/units/dash/store/utils.ts index 71355ad379..d483c1ed01 100644 --- a/src/ui/units/dash/store/utils.ts +++ b/src/ui/units/dash/store/utils.ts @@ -3,10 +3,8 @@ import {I18n} from 'i18n'; import type {FakeDashData} from 'shared/types/dash'; import {DashLoadPriority} from 'shared/types/dash'; import {DL, URL_QUERY} from 'ui/constants'; -import type {SelectorElementType} from 'ui/store/typings/controlDialog'; import Utils from 'ui/utils'; -import {CheckboxControlValue, ELEMENT_TYPE} from '../containers/Dialogs/Control/constants'; import {Mode} from '../modules/constants'; const storeI18n = I18n.keyset('dash.store.view'); @@ -72,12 +70,3 @@ export const getFakeDashEntry = (workbookId?: string) => { navigationPath: Utils.getNavigationPathFromKey(initialKey), }; }; - -export const getInitialDefaultValue = (elementType: SelectorElementType) => { - switch (elementType) { - case ELEMENT_TYPE.CHECKBOX: - return CheckboxControlValue.FALSE; - default: - return undefined; - } -};