diff --git a/src/ui/libs/DatalensChartkit/menu/MenuItems.tsx b/src/ui/libs/DatalensChartkit/menu/MenuItems.tsx index d6772e8ed9..4f23712fc4 100644 --- a/src/ui/libs/DatalensChartkit/menu/MenuItems.tsx +++ b/src/ui/libs/DatalensChartkit/menu/MenuItems.tsx @@ -26,7 +26,8 @@ import Inspector from '../components/ChartKitBase/components/Header/components/M import type {ChartKitDataProvider} from '../components/ChartKitBase/types'; import ChartKitIcon from '../components/ChartKitIcon/ChartKitIcon'; import type DatalensChartkitCustomError from '../modules/datalens-chartkit-custom-error/datalens-chartkit-custom-error'; -import type {LoadedWidget, Widget as TWidget, WidgetData} from '../types'; +import type {LoadedWidget, Widget as TWidget} from '../types'; +import type {AlertsActionArgs} from '../types/menu'; import type {MenuItemConfig, MenuItemModalProps, MenuLoadedData} from './Menu'; @@ -94,8 +95,8 @@ export const getAlertsMenuItem = ({ (loadedData.isNewWizard || loadedData.type === CHARTKIT_WIDGET_TYPE.GRAPH) ); }, - action: ({loadedData}: {loadedData: WidgetData}) => { - const menuAction = (options: {loadedData: WidgetData}) => { + action: ({loadedData}: AlertsActionArgs) => { + const menuAction = (options: AlertsActionArgs) => { const {AlertDialog} = registry.chart.components.getAll(); return (props: MenuItemModalProps) => ( diff --git a/src/ui/libs/DatalensChartkit/types/menu.ts b/src/ui/libs/DatalensChartkit/types/menu.ts index 43ab97c24d..545d053b66 100644 --- a/src/ui/libs/DatalensChartkit/types/menu.ts +++ b/src/ui/libs/DatalensChartkit/types/menu.ts @@ -6,7 +6,7 @@ import type {MenuItemsIds, StringParams} from 'shared'; import type {ChartWidgetDataRef} from '../../../components/Widgets/Chart/types'; import type DatalensChartkitCustomError from '../modules/datalens-chartkit-custom-error/datalens-chartkit-custom-error'; -import type {Widget as TWidget} from './widget'; +import type {Widget as TWidget, WidgetData} from './widget'; export interface MenuItem { id: MenuItemsIds; @@ -45,3 +45,7 @@ export type MenuItems = MenuI TProviderData, TProviderProps >[]; + +export type AlertsActionArgs = { + loadedData: WidgetData; +}; diff --git a/src/ui/units/ql/containers/PanePreview/PanePreview.tsx b/src/ui/units/ql/containers/PanePreview/PanePreview.tsx index ba5cfd4420..cfd1f08659 100644 --- a/src/ui/units/ql/containers/PanePreview/PanePreview.tsx +++ b/src/ui/units/ql/containers/PanePreview/PanePreview.tsx @@ -5,20 +5,33 @@ import {i18n} from 'i18n'; import _ from 'lodash'; import {connect} from 'react-redux'; import {compose} from 'recompose'; +import type {QlConfig} from 'shared'; +import {EntryUpdateMode, MenuItemsIds} from 'shared'; import type {DatalensGlobalState} from 'ui'; import {Utils} from 'ui'; import {PlaceholderIllustration} from 'ui/components/PlaceholderIllustration/PlaceholderIllustration'; +import type {ChartProviderPropsWithRefProps} from 'ui/components/Widgets/Chart/types'; +import {openDialogSaveChartConfirm} from 'ui/store/actions/dialog'; +import {getCustomExportActionWrapperWithSave} from 'ui/utils/custom-export-menu-item'; import {ChartWrapper} from '../../../../components/Widgets/Chart/ChartWidgetWithProvider'; import type {ChartKitWrapperOnLoadProps} from '../../../../libs/DatalensChartkit/components/ChartKitBase/types'; import {VisualizationStatus} from '../../constants'; +import {prepareChartDataBeforeSave} from '../../modules/helpers'; import type {SetQueryMetadataProps} from '../../store/actions/ql'; import { setQueryMetadata, setTablePreviewData, setVisualizationStatus, + updateChart, } from '../../store/actions/ql'; -import {getChart, getConnection, getEntry} from '../../store/reducers/ql'; +import { + getChart, + getConnection, + getEntry, + getEntryCanBeSaved, + getPreviewData, +} from '../../store/reducers/ql'; import type { QLChart, QLChartConfig, @@ -44,10 +57,14 @@ interface PreviewProps { mode: 'preview' | 'chart'; entry: QLEntry | null; connection: QLConnectionEntry | null; + entryCanBeSaved: boolean; + previewData: QlConfig | null; setQueryMetadata: typeof setQueryMetadata; setTablePreviewData: typeof setTablePreviewData; setVisualizationStatus: typeof setVisualizationStatus; + openDialogSaveChartConfirm: typeof openDialogSaveChartConfirm; + updateChart: typeof updateChart; } interface PreviewState { @@ -122,11 +139,33 @@ class Preview extends React.PureComponent { menuType={this.getMenuType()} forwardedRef={this.chartKitRef} workbookId={entry?.workbookId} + customMenuOptions={this.getCustomMenuOptions()} /> ); } + getCustomMenuOptions() { + return { + [MenuItemsIds.EXPORT]: { + actionWrapper: getCustomExportActionWrapperWithSave.bind(null, { + message: i18n('wizard', 'confirm_chart-save_message'), + canBeSaved: this.props.entryCanBeSaved, + onApply: async () => { + const {previewData} = this.props; + + if (!previewData) { + return; + } + + const preparedChartData = prepareChartDataBeforeSave(previewData); + await this.props.updateChart(preparedChartData, EntryUpdateMode.Publish); + }, + }), + }, + } as unknown as ChartProviderPropsWithRefProps['customMenuOptions']; + } + onChartLoad = ({data}: ChartKitWrapperOnLoadProps) => { if (data.loadedData && data.loadedData.data && !_.isEmpty(data.loadedData.data)) { if (Array.isArray(data.loadedData.data)) { @@ -245,6 +284,8 @@ const makeMapStateToProps = (state: DatalensGlobalState) => { chartData: getChart(state), connection: getConnection(state), entry: getEntry(state), + entryCanBeSaved: getEntryCanBeSaved(state), + previewData: getPreviewData(state), }; }; @@ -252,6 +293,8 @@ const mapDispatchToProps = { setQueryMetadata, setTablePreviewData, setVisualizationStatus, + openDialogSaveChartConfirm, + updateChart, }; export default connect( diff --git a/src/ui/units/ql/store/reducers/ql.ts b/src/ui/units/ql/store/reducers/ql.ts index b6f92f2826..bf15c76518 100644 --- a/src/ui/units/ql/store/reducers/ql.ts +++ b/src/ui/units/ql/store/reducers/ql.ts @@ -417,7 +417,7 @@ export const getPreviewData = createSelector( params, placeholdersContent, order, - ): any | null => { + ): QlConfig | null => { if (chartType && connection && visualization) { const result: QlConfig = { type: 'ql', diff --git a/src/ui/units/wizard/actions/widget.ts b/src/ui/units/wizard/actions/widget.ts index 244d09bb51..d48ea54bb9 100644 --- a/src/ui/units/wizard/actions/widget.ts +++ b/src/ui/units/wizard/actions/widget.ts @@ -140,17 +140,9 @@ export function updateWizardWidget(args: UpdateWizardWidgetArgs) { }; } -type UpdateWizardWidgetAndDoActionArgs = { - updateWizardWidgetArguments: UpdateWizardWidgetArgs; - actionAfterReceiveWidgetUpdate: () => void; -}; - -export function updateWizardWidgetAndDoAction({ - updateWizardWidgetArguments, - actionAfterReceiveWidgetUpdate, -}: UpdateWizardWidgetAndDoActionArgs) { +export function updateWizardWidgetAndUpdateConfig(args: UpdateWizardWidgetArgs) { return async function (dispatch: WizardDispatch) { - await dispatch(updateWizardWidget(updateWizardWidgetArguments)); + await dispatch(updateWizardWidget(args)); dispatch( updateClientChartsConfig({ @@ -158,8 +150,6 @@ export function updateWizardWidgetAndDoAction({ withoutRerender: true, }), ); - - actionAfterReceiveWidgetUpdate(); }; } diff --git a/src/ui/units/wizard/containers/Wizard/SectionPreview/SectionPreview.tsx b/src/ui/units/wizard/containers/Wizard/SectionPreview/SectionPreview.tsx index b73a4b7031..e931525d29 100644 --- a/src/ui/units/wizard/containers/Wizard/SectionPreview/SectionPreview.tsx +++ b/src/ui/units/wizard/containers/Wizard/SectionPreview/SectionPreview.tsx @@ -9,6 +9,7 @@ import {MenuItemsIds, WizardPageQa} from 'shared'; import type {DatalensGlobalState} from 'ui'; import {Utils} from 'ui'; import {PlaceholderIllustration} from 'ui/components/PlaceholderIllustration/PlaceholderIllustration'; +import {getCustomExportActionWrapperWithSave} from 'ui/utils/custom-export-menu-item'; import {setDrillDownLevel} from 'units/wizard/actions/visualization'; import {selectDatasetError} from 'units/wizard/selectors/dataset'; import { @@ -31,13 +32,12 @@ import type { ChartKitWrapperLoadSuccess, ChartKitWrapperOnLoadProps, } from '../../../../../libs/DatalensChartkit/components/ChartKitBase/types'; -import type {MenuItemConfig} from '../../../../../libs/DatalensChartkit/menu/Menu'; import type {ConfigNode} from '../../../../../libs/DatalensChartkit/modules/data-provider/charts'; import {openDialogSaveChartConfirm} from '../../../../../store/actions/dialog'; import {reloadRevisionsOnSave} from '../../../../../store/actions/entryContent'; import type {HighchartsWidget} from '../../../actions/preview'; import {setHighchartsWidget} from '../../../actions/preview'; -import {updateWizardWidgetAndDoAction} from '../../../actions/widget'; +import {updateWizardWidgetAndUpdateConfig} from '../../../actions/widget'; import {selectWizardWorkbookId} from '../../../selectors/settings'; import {selectWidget} from '../../../selectors/widget'; import {shouldComponentUpdateWithDeepComparison} from '../../../utils/helpers'; @@ -67,56 +67,38 @@ class SectionPreview extends Component { } getCustomMenuOptions() { + const {isChartSaved, widget, configForSaving} = this.props; + + const args = { + canBeSaved: !isChartSaved, + onApply: async () => { + if (configForSaving) { + await this.props.updateWizardWidgetAndUpdateConfig({ + config: configForSaving, + entry: widget, + }); + + this.props.reloadRevisionsOnSave(); + } + }, + }; + return { [MenuItemsIds.EXPORT]: { - actionWrapper: this.wrapChartKitMenuSaveChartAction.bind( - null, - i18n('wizard', 'confirm_chart-save_message'), - ), + actionWrapper: getCustomExportActionWrapperWithSave.bind(null, { + message: i18n('wizard', 'confirm_chart-save_message'), + ...args, + }), }, [MenuItemsIds.ALERTS]: { - actionWrapper: this.wrapChartKitMenuSaveChartAction.bind( - null, - i18n('wizard', 'confirm_chart-save_message_alerts'), - ), + actionWrapper: getCustomExportActionWrapperWithSave.bind(null, { + message: i18n('wizard', 'confirm_chart-save_message_alerts'), + ...args, + }), }, } as unknown as ChartProviderPropsWithRefProps['customMenuOptions']; } - wrapChartKitMenuSaveChartAction = ( - confirmText: string, - originalAction: MenuItemConfig['action'], - ) => { - return (...originalActionArgs: any) => { - const {widget, isChartSaved, configForSaving} = this.props; - - return new Promise((resolve) => { - if (isChartSaved) { - resolve(originalAction.apply(this, originalActionArgs)); - } else { - const onApply = () => { - if (configForSaving) { - this.props.updateWizardWidgetAndDoAction({ - updateWizardWidgetArguments: { - config: configForSaving, - entry: widget, - }, - actionAfterReceiveWidgetUpdate: () => { - resolve(originalAction.apply(this, originalActionArgs)); - this.props.reloadRevisionsOnSave(); - }, - }); - } - }; - this.props.openDialogSaveChartConfirm({ - onApply, - message: confirmText, - }); - } - }); - }; - }; - handleLoad = ( result: | ChartKitBaseOnLoadProps @@ -227,7 +209,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => { { setHighchartsWidget, openDialogSaveChartConfirm, - updateWizardWidgetAndDoAction, + updateWizardWidgetAndUpdateConfig, reloadRevisionsOnSave, setDrillDownLevel, }, diff --git a/src/ui/utils/custom-export-menu-item.ts b/src/ui/utils/custom-export-menu-item.ts new file mode 100644 index 0000000000..90111b068e --- /dev/null +++ b/src/ui/utils/custom-export-menu-item.ts @@ -0,0 +1,34 @@ +import type {ExportActionArgs} from 'ui/libs/DatalensChartkit/components/ChartKitBase/components/Header/components/Menu/Items/Export/types'; +import type {MenuItemConfig} from 'ui/libs/DatalensChartkit/menu/Menu'; +import type {AlertsActionArgs} from 'ui/libs/DatalensChartkit/types/menu'; +import {getStore} from 'ui/store'; +import {openDialogSaveChartConfirm} from 'ui/store/actions/dialog'; + +export function getCustomExportActionWrapperWithSave( + { + message, + onApply, + canBeSaved, + }: { + message: string; + onApply: () => void; + canBeSaved: boolean; + }, + originalAction: MenuItemConfig['action'], +) { + return (originalActionArgs: ExportActionArgs | AlertsActionArgs) => { + return new Promise((resolve) => { + if (canBeSaved) { + openDialogSaveChartConfirm({ + onApply: async () => { + await onApply(); + resolve(originalAction(originalActionArgs)); + }, + message, + })(getStore().dispatch); + } else { + resolve(originalAction(originalActionArgs)); + } + }); + }; +}