chore: Refactor dashboard header to func component (#31029)
This commit is contained in:
parent
832fed1db5
commit
f8adaf66c1
|
|
@ -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';
|
||||||
|
|
|
||||||
|
|
@ -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: [],
|
||||||
charts: {},
|
|
||||||
layout: {},
|
|
||||||
expandedSlices: {},
|
expandedSlices: {},
|
||||||
css: '',
|
|
||||||
customCss: '',
|
|
||||||
isStarred: false,
|
|
||||||
isLoading: false,
|
|
||||||
lastModifiedTime: 0,
|
|
||||||
refreshFrequency: 0,
|
refreshFrequency: 0,
|
||||||
shouldPersistRefreshFrequency: false,
|
shouldPersistRefreshFrequency: false,
|
||||||
onSave: jest.fn(),
|
css: '',
|
||||||
onChange: jest.fn(),
|
isStarred: false,
|
||||||
fetchFaveStar: jest.fn(),
|
|
||||||
fetchCharts: jest.fn(),
|
|
||||||
onRefresh: jest.fn(),
|
|
||||||
saveFaveStar: jest.fn(),
|
|
||||||
savePublished: jest.fn(),
|
|
||||||
isPublished: false,
|
isPublished: false,
|
||||||
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,
|
hasUnsavedChanges: false,
|
||||||
maxUndoHistoryExceeded: false,
|
maxUndoHistoryExceeded: false,
|
||||||
onUndo: jest.fn(),
|
editMode: false,
|
||||||
onRedo: jest.fn(),
|
lastModifiedTime: 0,
|
||||||
undoLength: 0,
|
},
|
||||||
redoLength: 0,
|
charts: {},
|
||||||
setMaxUndoHistoryExceeded: jest.fn(),
|
dashboardLayout: {
|
||||||
maxUndoHistoryToast: jest.fn(),
|
present: {
|
||||||
dashboardInfoChanged: jest.fn(),
|
[DASHBOARD_HEADER_ID]: {
|
||||||
dashboardTitleChanged: jest.fn(),
|
meta: {
|
||||||
showMenuDropdown: true,
|
text: 'Dashboard Title',
|
||||||
});
|
},
|
||||||
const props = createProps();
|
},
|
||||||
const editableProps = {
|
},
|
||||||
...props,
|
past: [],
|
||||||
|
future: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const editableState = {
|
||||||
|
dashboardState: {
|
||||||
|
...initialState.dashboardState,
|
||||||
editMode: true,
|
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,
|
||||||
|
dashboardState: {
|
||||||
|
...editableState.dashboardState,
|
||||||
hasUnsavedChanges: true,
|
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: {
|
||||||
|
...initialState.dashboardState,
|
||||||
isPublished: true,
|
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
|
|
@ -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);
|
|
||||||
Loading…
Reference in New Issue