chore: Refactor dashboard header to func component (#31029)

This commit is contained in:
Kamil Gabryjelski 2024-11-22 14:10:46 +01:00 committed by GitHub
parent 832fed1db5
commit f8adaf66c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 837 additions and 839 deletions

View File

@ -34,7 +34,7 @@ import { Global } from '@emotion/react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import ErrorBoundary from 'src/components/ErrorBoundary'; import ErrorBoundary from 'src/components/ErrorBoundary';
import BuilderComponentPane from 'src/dashboard/components/BuilderComponentPane'; import BuilderComponentPane from 'src/dashboard/components/BuilderComponentPane';
import DashboardHeader from 'src/dashboard/containers/DashboardHeader'; import DashboardHeader from 'src/dashboard/components/Header';
import Icons from 'src/components/Icons'; import Icons from 'src/components/Icons';
import IconButton from 'src/dashboard/components/IconButton'; import IconButton from 'src/dashboard/components/IconButton';
import { Droppable } from 'src/dashboard/components/dnd/DragDroppable'; import { Droppable } from 'src/dashboard/components/dnd/DragDroppable';

View File

@ -16,19 +16,17 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import * as redux from 'redux';
import { render, screen, fireEvent } from 'spec/helpers/testing-library'; import { render, screen, fireEvent } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import fetchMock from 'fetch-mock'; import fetchMock from 'fetch-mock';
import { getExtensionsRegistry } from '@superset-ui/core'; import { getExtensionsRegistry, JsonObject } from '@superset-ui/core';
import setupExtensions from 'src/setup/setupExtensions'; import setupExtensions from 'src/setup/setupExtensions';
import getOwnerName from 'src/utils/getOwnerName'; import getOwnerName from 'src/utils/getOwnerName';
import { HeaderProps } from './types';
import Header from '.'; import Header from '.';
import { DASHBOARD_HEADER_ID } from '../../util/constants';
const createProps = () => ({ const initialState = {
addSuccessToast: jest.fn(),
addDangerToast: jest.fn(),
addWarningToast: jest.fn(),
dashboardInfo: { dashboardInfo: {
id: 1, id: 1,
dash_edit_perm: false, dash_edit_perm: false,
@ -69,74 +67,69 @@ const createProps = () => ({
userId: 1, userId: 1,
username: 'admin', username: 'admin',
}, },
reports: {}, dashboardState: {
dashboardTitle: 'Dashboard Title', sliceIds: [],
expandedSlices: {},
refreshFrequency: 0,
shouldPersistRefreshFrequency: false,
css: '',
isStarred: false,
isPublished: false,
hasUnsavedChanges: false,
maxUndoHistoryExceeded: false,
editMode: false,
lastModifiedTime: 0,
},
charts: {}, charts: {},
layout: {}, dashboardLayout: {
expandedSlices: {}, present: {
css: '', [DASHBOARD_HEADER_ID]: {
customCss: '', meta: {
isStarred: false, text: 'Dashboard Title',
isLoading: false, },
lastModifiedTime: 0, },
refreshFrequency: 0, },
shouldPersistRefreshFrequency: false, past: [],
onSave: jest.fn(), future: [],
onChange: jest.fn(), },
fetchFaveStar: jest.fn(), };
fetchCharts: jest.fn(),
onRefresh: jest.fn(), const editableState = {
saveFaveStar: jest.fn(), dashboardState: {
savePublished: jest.fn(), ...initialState.dashboardState,
isPublished: false, editMode: true,
updateDashboardTitle: jest.fn(), },
editMode: false,
setEditMode: jest.fn(),
showBuilderPane: jest.fn(),
updateCss: jest.fn(),
setColorScheme: jest.fn(),
setUnsavedChanges: jest.fn(),
logEvent: jest.fn(),
setRefreshFrequency: jest.fn(),
hasUnsavedChanges: false,
maxUndoHistoryExceeded: false,
onUndo: jest.fn(),
onRedo: jest.fn(),
undoLength: 0,
redoLength: 0,
setMaxUndoHistoryExceeded: jest.fn(),
maxUndoHistoryToast: jest.fn(),
dashboardInfoChanged: jest.fn(),
dashboardTitleChanged: jest.fn(),
showMenuDropdown: true,
});
const props = createProps();
const editableProps = {
...props,
editMode: true,
dashboardInfo: { dashboardInfo: {
...props.dashboardInfo, ...initialState.dashboardInfo,
dash_edit_perm: true, dash_edit_perm: true,
dash_save_perm: true, dash_save_perm: true,
}, },
}; };
const undoProps = {
...editableProps, const undoState = {
undoLength: 1, ...editableState,
dashboardLayout: {
...initialState.dashboardLayout,
past: [{}],
},
}; };
const redoProps = {
...editableProps, const redoState = {
redoLength: 1, ...editableState,
dashboardLayout: {
...initialState.dashboardLayout,
future: [{}],
},
}; };
fetchMock.get('glob:*/csstemplateasyncmodelview/api/read', {}); fetchMock.get('glob:*/csstemplateasyncmodelview/api/read', {});
function setup(props: HeaderProps, initialState = {}) { function setup(overrideState: JsonObject = {}) {
return render( return render(
<div className="dashboard"> <div className="dashboard">
<Header {...props} /> <Header />
</div>, </div>,
{ useRedux: true, initialState }, { useRedux: true, initialState: { ...initialState, ...overrideState } },
); );
} }
@ -146,173 +139,223 @@ async function openActionsDropdown() {
expect(await screen.findByTestId('header-actions-menu')).toBeInTheDocument(); expect(await screen.findByTestId('header-actions-menu')).toBeInTheDocument();
} }
const addSuccessToast = jest.fn();
const addDangerToast = jest.fn();
const addWarningToast = jest.fn();
const onUndo = jest.fn();
const onRedo = jest.fn();
const setEditMode = jest.fn();
const setUnsavedChanges = jest.fn();
const fetchFaveStar = jest.fn();
const saveFaveStar = jest.fn();
const savePublished = jest.fn();
const fetchCharts = jest.fn();
const updateDashboardTitle = jest.fn();
const updateCss = jest.fn();
const onChange = jest.fn();
const onSave = jest.fn();
const setMaxUndoHistoryExceeded = jest.fn();
const maxUndoHistoryToast = jest.fn();
const logEvent = jest.fn();
const setRefreshFrequency = jest.fn();
const onRefresh = jest.fn();
const dashboardInfoChanged = jest.fn();
const dashboardTitleChanged = jest.fn();
beforeAll(() => {
jest.spyOn(redux, 'bindActionCreators').mockImplementation(() => ({
addSuccessToast,
addDangerToast,
addWarningToast,
onUndo,
onRedo,
setEditMode,
setUnsavedChanges,
fetchFaveStar,
saveFaveStar,
savePublished,
fetchCharts,
updateDashboardTitle,
updateCss,
onChange,
onSave,
setMaxUndoHistoryExceeded,
maxUndoHistoryToast,
logEvent,
setRefreshFrequency,
onRefresh,
dashboardInfoChanged,
dashboardTitleChanged,
}));
});
beforeEach(() => {
jest.clearAllMocks();
});
test('should render', () => { test('should render', () => {
const mockedProps = createProps(); const { container } = setup();
const { container } = setup(mockedProps);
expect(container).toBeInTheDocument(); expect(container).toBeInTheDocument();
}); });
test('should render the title', () => { test('should render the title', () => {
const mockedProps = createProps(); setup();
setup(mockedProps);
expect(screen.getByTestId('editable-title')).toHaveTextContent( expect(screen.getByTestId('editable-title')).toHaveTextContent(
'Dashboard Title', 'Dashboard Title',
); );
}); });
test('should render the editable title', () => { test('should render the editable title', () => {
setup(editableProps); setup(editableState);
expect(screen.getByDisplayValue('Dashboard Title')).toBeInTheDocument(); expect(screen.getByDisplayValue('Dashboard Title')).toBeInTheDocument();
}); });
test('should edit the title', () => { test('should edit the title', () => {
setup(editableProps); setup(editableState);
const editableTitle = screen.getByDisplayValue('Dashboard Title'); const editableTitle = screen.getByDisplayValue('Dashboard Title');
expect(editableProps.onChange).not.toHaveBeenCalled(); expect(onChange).not.toHaveBeenCalled();
userEvent.click(editableTitle); userEvent.click(editableTitle);
userEvent.clear(editableTitle); userEvent.clear(editableTitle);
userEvent.type(editableTitle, 'New Title'); userEvent.type(editableTitle, 'New Title');
userEvent.click(document.body); userEvent.click(document.body);
expect(editableProps.onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalled();
expect(screen.getByDisplayValue('New Title')).toBeInTheDocument(); expect(screen.getByDisplayValue('New Title')).toBeInTheDocument();
}); });
test('should render the "Draft" status', () => { test('should render the "Draft" status', () => {
const mockedProps = createProps(); setup();
setup(mockedProps);
expect(screen.getByText('Draft')).toBeInTheDocument(); expect(screen.getByText('Draft')).toBeInTheDocument();
}); });
test('should publish', () => { test('should publish', () => {
const mockedProps = createProps(); const canEditState = {
const canEditProps = {
...mockedProps,
dashboardInfo: { dashboardInfo: {
...mockedProps.dashboardInfo, ...initialState.dashboardInfo,
dash_edit_perm: true, dash_edit_perm: true,
dash_save_perm: true, dash_save_perm: true,
}, },
}; };
setup(canEditProps); setup(canEditState);
const draft = screen.getByText('Draft'); const draft = screen.getByText('Draft');
expect(mockedProps.savePublished).toHaveBeenCalledTimes(0); expect(savePublished).toHaveBeenCalledTimes(0);
userEvent.click(draft); userEvent.click(draft);
expect(mockedProps.savePublished).toHaveBeenCalledTimes(1); expect(savePublished).toHaveBeenCalledTimes(1);
}); });
test('should render metadata', () => { test('should render metadata', () => {
const mockedProps = createProps(); setup();
setup(mockedProps);
expect( expect(
screen.getByText(getOwnerName(mockedProps.dashboardInfo.created_by)), screen.getByText(getOwnerName(initialState.dashboardInfo.created_by)),
).toBeInTheDocument(); ).toBeInTheDocument();
expect( expect(
screen.getByText(mockedProps.dashboardInfo.changed_on_delta_humanized), screen.getByText(initialState.dashboardInfo.changed_on_delta_humanized),
).toBeInTheDocument(); ).toBeInTheDocument();
}); });
test('should render the "Undo" action as disabled', () => { test('should render the "Undo" action as disabled', () => {
setup(editableProps); setup(editableState);
expect(screen.getByTestId('undo-action').parentElement).toBeDisabled(); expect(screen.getByTestId('undo-action').parentElement).toBeDisabled();
}); });
test('should undo', () => { test('should undo', () => {
setup(undoProps); setup(undoState);
const undo = screen.getByTestId('undo-action'); const undo = screen.getByTestId('undo-action');
expect(undoProps.onUndo).not.toHaveBeenCalled(); expect(onUndo).not.toHaveBeenCalled();
userEvent.click(undo); userEvent.click(undo);
expect(undoProps.onUndo).toHaveBeenCalledTimes(1); expect(onUndo).toHaveBeenCalledTimes(1);
}); });
test('should undo with key listener', () => { test('should undo with key listener', () => {
undoProps.onUndo.mockReset(); onUndo.mockReset();
setup(undoProps); setup(undoState);
expect(undoProps.onUndo).not.toHaveBeenCalled(); expect(onUndo).not.toHaveBeenCalled();
fireEvent.keyDown(document.body, { key: 'z', code: 'KeyZ', ctrlKey: true }); fireEvent.keyDown(document.body, { key: 'z', code: 'KeyZ', ctrlKey: true });
expect(undoProps.onUndo).toHaveBeenCalledTimes(1); expect(onUndo).toHaveBeenCalledTimes(1);
}); });
test('should render the "Redo" action as disabled', () => { test('should render the "Redo" action as disabled', () => {
setup(editableProps); setup(editableState);
expect(screen.getByTestId('redo-action').parentElement).toBeDisabled(); expect(screen.getByTestId('redo-action').parentElement).toBeDisabled();
}); });
test('should redo', () => { test('should redo', () => {
setup(redoProps); setup(redoState);
const redo = screen.getByTestId('redo-action'); const redo = screen.getByTestId('redo-action');
expect(redoProps.onRedo).not.toHaveBeenCalled(); expect(onRedo).not.toHaveBeenCalled();
userEvent.click(redo); userEvent.click(redo);
expect(redoProps.onRedo).toHaveBeenCalledTimes(1); expect(onRedo).toHaveBeenCalledTimes(1);
}); });
test('should redo with key listener', () => { test('should redo with key listener', () => {
redoProps.onRedo.mockReset(); setup(redoState);
setup(redoProps); expect(onRedo).not.toHaveBeenCalled();
expect(redoProps.onRedo).not.toHaveBeenCalled();
fireEvent.keyDown(document.body, { key: 'y', code: 'KeyY', ctrlKey: true }); fireEvent.keyDown(document.body, { key: 'y', code: 'KeyY', ctrlKey: true });
expect(redoProps.onRedo).toHaveBeenCalledTimes(1); expect(onRedo).toHaveBeenCalledTimes(1);
}); });
test('should render the "Discard changes" button', () => { test('should render the "Discard changes" button', () => {
setup(editableProps); setup(editableState);
expect(screen.getByText('Discard')).toBeInTheDocument(); expect(screen.getByText('Discard')).toBeInTheDocument();
}); });
test('should render the "Save" button as disabled', () => { test('should render the "Save" button as disabled', () => {
setup(editableProps); setup(editableState);
expect(screen.getByText('Save').parentElement).toBeDisabled(); expect(screen.getByText('Save').parentElement).toBeDisabled();
}); });
test('should save', () => { test('should save', () => {
const unsavedProps = { const unsavedState = {
...editableProps, ...editableState,
hasUnsavedChanges: true, dashboardState: {
...editableState.dashboardState,
hasUnsavedChanges: true,
},
}; };
setup(unsavedProps); setup(unsavedState);
const save = screen.getByText('Save'); const save = screen.getByText('Save');
expect(unsavedProps.onSave).not.toHaveBeenCalled(); expect(onSave).not.toHaveBeenCalled();
userEvent.click(save); userEvent.click(save);
expect(unsavedProps.onSave).toHaveBeenCalledTimes(1); expect(onSave).toHaveBeenCalledTimes(1);
}); });
test('should NOT render the "Draft" status', () => { test('should NOT render the "Draft" status', () => {
const mockedProps = createProps(); const publishedState = {
const publishedProps = { ...initialState,
...mockedProps, dashboardState: {
isPublished: true, ...initialState.dashboardState,
isPublished: true,
},
}; };
setup(publishedProps); setup(publishedState);
expect(screen.queryByText('Draft')).not.toBeInTheDocument(); expect(screen.queryByText('Draft')).not.toBeInTheDocument();
}); });
test('should render the unselected fave icon', () => { test('should render the unselected fave icon', () => {
const mockedProps = createProps(); setup();
setup(mockedProps); expect(fetchFaveStar).toHaveBeenCalled();
expect(mockedProps.fetchFaveStar).toHaveBeenCalled();
expect( expect(
screen.getByRole('img', { name: 'favorite-unselected' }), screen.getByRole('img', { name: 'favorite-unselected' }),
).toBeInTheDocument(); ).toBeInTheDocument();
}); });
test('should render the selected fave icon', () => { test('should render the selected fave icon', () => {
const mockedProps = createProps(); const favedState = {
const favedProps = { dashboardState: {
...mockedProps, ...initialState.dashboardState,
isStarred: true, isStarred: true,
},
}; };
setup(favedProps); setup(favedState);
expect( expect(
screen.getByRole('img', { name: 'favorite-selected' }), screen.getByRole('img', { name: 'favorite-selected' }),
).toBeInTheDocument(); ).toBeInTheDocument();
}); });
test('should NOT render the fave icon on anonymous user', () => { test('should NOT render the fave icon on anonymous user', () => {
const mockedProps = createProps(); const anonymousUserState = {
const anonymousUserProps = {
...mockedProps,
user: undefined, user: undefined,
}; };
setup(anonymousUserProps); setup(anonymousUserState);
expect(() => expect(() =>
screen.getByRole('img', { name: 'favorite-unselected' }), screen.getByRole('img', { name: 'favorite-unselected' }),
).toThrow('Unable to find'); ).toThrow('Unable to find');
@ -322,42 +365,37 @@ test('should NOT render the fave icon on anonymous user', () => {
}); });
test('should fave', async () => { test('should fave', async () => {
const mockedProps = createProps(); setup();
setup(mockedProps);
const fave = screen.getByRole('img', { name: 'favorite-unselected' }); const fave = screen.getByRole('img', { name: 'favorite-unselected' });
expect(mockedProps.saveFaveStar).not.toHaveBeenCalled(); expect(saveFaveStar).not.toHaveBeenCalled();
userEvent.click(fave); userEvent.click(fave);
expect(mockedProps.saveFaveStar).toHaveBeenCalledTimes(1); expect(saveFaveStar).toHaveBeenCalledTimes(1);
}); });
test('should toggle the edit mode', () => { test('should toggle the edit mode', () => {
const mockedProps = createProps(); const canEditState = {
const canEditProps = {
...mockedProps,
dashboardInfo: { dashboardInfo: {
...mockedProps.dashboardInfo, ...initialState.dashboardInfo,
dash_edit_perm: true, dash_edit_perm: true,
}, },
}; };
setup(canEditProps); setup(canEditState);
const editDashboard = screen.getByText('Edit dashboard'); const editDashboard = screen.getByText('Edit dashboard');
expect(screen.queryByText('Edit dashboard')).toBeInTheDocument(); expect(screen.queryByText('Edit dashboard')).toBeInTheDocument();
userEvent.click(editDashboard); userEvent.click(editDashboard);
expect(mockedProps.logEvent).toHaveBeenCalled(); expect(logEvent).toHaveBeenCalled();
}); });
test('should render the dropdown icon', () => { test('should render the dropdown icon', () => {
const mockedProps = createProps(); setup();
setup(mockedProps);
expect(screen.getByRole('img', { name: 'more-horiz' })).toBeInTheDocument(); expect(screen.getByRole('img', { name: 'more-horiz' })).toBeInTheDocument();
}); });
test('should refresh the charts', async () => { test('should refresh the charts', async () => {
const mockedProps = createProps(); setup();
setup(mockedProps);
await openActionsDropdown(); await openActionsDropdown();
userEvent.click(screen.getByText('Refresh dashboard')); userEvent.click(screen.getByText('Refresh dashboard'));
expect(mockedProps.onRefresh).toHaveBeenCalledTimes(1); expect(onRefresh).toHaveBeenCalledTimes(1);
}); });
test('should render an extension component if one is supplied', () => { test('should render an extension component if one is supplied', () => {
@ -367,54 +405,48 @@ test('should render an extension component if one is supplied', () => {
)); ));
setupExtensions(); setupExtensions();
const mockedProps = createProps(); setup();
setup(mockedProps);
expect( expect(
screen.getByText('dashboard.nav.right extension component'), screen.getByText('dashboard.nav.right extension component'),
).toBeInTheDocument(); ).toBeInTheDocument();
}); });
test('should NOT render MetadataBar when in edit mode', () => { test('should NOT render MetadataBar when in edit mode', () => {
const mockedProps = { const state = {
...createProps(), ...editableState,
editMode: true,
dashboardInfo: { dashboardInfo: {
...createProps().dashboardInfo, ...initialState.dashboardInfo,
userId: '123', userId: '123',
}, },
}; };
setup(mockedProps); setup(state);
expect( expect(
screen.queryByText(mockedProps.dashboardInfo.changed_on_delta_humanized), screen.queryByText(state.dashboardInfo.changed_on_delta_humanized),
).not.toBeInTheDocument(); ).not.toBeInTheDocument();
}); });
test('should NOT render MetadataBar when embedded', () => { test('should NOT render MetadataBar when embedded', () => {
const mockedProps = { const state = {
...createProps(),
editMode: false,
dashboardInfo: { dashboardInfo: {
...createProps().dashboardInfo, ...initialState.dashboardInfo,
userId: undefined, userId: undefined,
}, },
}; };
setup(mockedProps); setup(state);
expect( expect(
screen.queryByText(mockedProps.dashboardInfo.changed_on_delta_humanized), screen.queryByText(state.dashboardInfo.changed_on_delta_humanized),
).not.toBeInTheDocument(); ).not.toBeInTheDocument();
}); });
test('should render MetadataBar when not in edit mode and not embedded', () => { test('should render MetadataBar when not in edit mode and not embedded', () => {
const mockedProps = { const state = {
...createProps(),
editMode: false,
dashboardInfo: { dashboardInfo: {
...createProps().dashboardInfo, ...initialState.dashboardInfo,
userId: '123', userId: '123',
}, },
}; };
setup(mockedProps); setup(state);
expect( expect(
screen.getByText(mockedProps.dashboardInfo.changed_on_delta_humanized), screen.getByText(state.dashboardInfo.changed_on_delta_humanized),
).toBeInTheDocument(); ).toBeInTheDocument();
}); });

File diff suppressed because it is too large Load Diff

View File

@ -1,137 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { updateDataMask } from 'src/dataMask/actions';
import DashboardHeader from 'src/dashboard/components/Header';
import isDashboardLoading from 'src/dashboard/util/isDashboardLoading';
import { dashboardInfoChanged } from 'src/dashboard/actions/dashboardInfo';
import {
setEditMode,
showBuilderPane,
fetchFaveStar,
saveFaveStar,
savePublished,
setColorScheme,
setUnsavedChanges,
fetchCharts,
updateCss,
onChange,
saveDashboardRequest,
setMaxUndoHistoryExceeded,
maxUndoHistoryToast,
setRefreshFrequency,
onRefresh,
} from 'src/dashboard/actions/dashboardState';
import {
undoLayoutAction,
redoLayoutAction,
updateDashboardTitle,
dashboardTitleChanged,
} from 'src/dashboard/actions/dashboardLayout';
import {
addSuccessToast,
addDangerToast,
addWarningToast,
} from 'src/components/MessageToasts/actions';
import { logEvent } from 'src/logger/actions';
import { DASHBOARD_HEADER_ID } from 'src/dashboard/util/constants';
function mapStateToProps({
dashboardLayout: undoableLayout,
dashboardState,
reports,
dashboardInfo,
charts,
dataMask,
user,
}) {
return {
dashboardInfo,
undoLength: undoableLayout.past.length,
redoLength: undoableLayout.future.length,
layout: undoableLayout.present,
dashboardTitle: (
(undoableLayout.present[DASHBOARD_HEADER_ID] || {}).meta || {}
).text,
expandedSlices: dashboardState.expandedSlices,
refreshFrequency: dashboardState.refreshFrequency,
shouldPersistRefreshFrequency:
!!dashboardState.shouldPersistRefreshFrequency,
customCss: dashboardState.css,
colorNamespace: dashboardState.colorNamespace,
colorScheme: dashboardState.colorScheme,
charts,
dataMask,
user,
isStarred: !!dashboardState.isStarred,
isPublished: !!dashboardState.isPublished,
isLoading: isDashboardLoading(charts),
hasUnsavedChanges: !!dashboardState.hasUnsavedChanges,
maxUndoHistoryExceeded: !!dashboardState.maxUndoHistoryExceeded,
lastModifiedTime: Math.max(
dashboardState.lastModifiedTime,
dashboardInfo.last_modified_time,
),
editMode: !!dashboardState.editMode,
slug: dashboardInfo.slug,
metadata: dashboardInfo.metadata,
reports,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
addSuccessToast,
addDangerToast,
addWarningToast,
onUndo: undoLayoutAction,
onRedo: redoLayoutAction,
setEditMode,
showBuilderPane,
setColorScheme,
setUnsavedChanges,
fetchFaveStar,
saveFaveStar,
savePublished,
fetchCharts,
updateDashboardTitle,
updateCss,
onChange,
onSave: saveDashboardRequest,
setMaxUndoHistoryExceeded,
maxUndoHistoryToast,
logEvent,
setRefreshFrequency,
onRefresh,
dashboardInfoChanged,
dashboardTitleChanged,
updateDataMask,
},
dispatch,
);
}
export default connect(mapStateToProps, mapDispatchToProps)(DashboardHeader);