diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls/SliceHeaderControls.test.tsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/SliceHeaderControls.test.tsx
new file mode 100644
index 00000000000..b89ce7bdd6c
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/SliceHeaderControls.test.tsx
@@ -0,0 +1,191 @@
+/**
+ * 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 userEvent from '@testing-library/user-event';
+import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import SliceHeaderControls from '.';
+
+jest.mock('src/common/components', () => {
+ const original = jest.requireActual('src/common/components');
+ return {
+ ...original,
+ NoAnimationDropdown: (props: any) => (
+
+ {props.overlay}
+ {props.children}
+
+ ),
+ };
+});
+
+const createProps = () => ({
+ addDangerToast: jest.fn(),
+ addSuccessToast: jest.fn(),
+ exploreChart: jest.fn(),
+ exportCSV: jest.fn(),
+ forceRefresh: jest.fn(),
+ handleToggleFullSize: jest.fn(),
+ toggleExpandSlice: jest.fn(),
+ slice: {
+ slice_id: 371,
+ slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20371%7D',
+ slice_name: 'Vaccine Candidates per Country & Stage',
+ form_data: {
+ adhoc_filters: [],
+ color_scheme: 'supersetColors',
+ datasource: '58__table',
+ groupby: ['product_category', 'clinical_stage'],
+ linear_color_scheme: 'schemeYlOrBr',
+ metric: 'count',
+ queryFields: {
+ groupby: 'groupby',
+ metric: 'metrics',
+ secondary_metric: 'metrics',
+ },
+ row_limit: 10000,
+ slice_id: 371,
+ time_range: 'No filter',
+ time_range_endpoints: ['inclusive', 'exclusive'],
+ url_params: {},
+ viz_type: 'sunburst',
+ },
+ viz_type: 'sunburst',
+ datasource: '58__table',
+ description: 'test-description',
+ description_markeddown: '',
+ owners: [],
+ modified: '22 hours ago',
+ changed_on: 1617143411523,
+ },
+ isCached: [false],
+ isExpanded: false,
+ cachedDttm: [null],
+ updatedDttm: 1617213803803,
+ supersetCanExplore: true,
+ supersetCanCSV: true,
+ sliceCanEdit: false,
+ componentId: 'CHART-fYo7IyvKZQ',
+ dashboardId: 26,
+ isFullSize: false,
+ chartStatus: 'rendered',
+});
+
+test('Should render', () => {
+ const props = createProps();
+ render(, { useRedux: true });
+ expect(
+ screen.getByRole('button', { name: 'More Options' }),
+ ).toBeInTheDocument();
+ expect(screen.getByTestId('NoAnimationDropdown')).toBeInTheDocument();
+});
+
+test('Should render default props', () => {
+ const props = createProps();
+
+ // @ts-ignore
+ delete props.forceRefresh;
+ // @ts-ignore
+ delete props.toggleExpandSlice;
+ // @ts-ignore
+ delete props.exploreChart;
+ // @ts-ignore
+ delete props.exportCSV;
+ // @ts-ignore
+ delete props.cachedDttm;
+ // @ts-ignore
+ delete props.updatedDttm;
+ // @ts-ignore
+ delete props.isCached;
+ // @ts-ignore
+ delete props.isExpanded;
+ // @ts-ignore
+ delete props.sliceCanEdit;
+
+ render(, { useRedux: true });
+
+ userEvent.click(screen.getByRole('menuitem', { name: 'Maximize chart' }));
+ userEvent.click(screen.getByRole('menuitem', { name: /Force refresh/ }));
+ userEvent.click(
+ screen.getByRole('menuitem', { name: 'Toggle chart description' }),
+ );
+ userEvent.click(
+ screen.getByRole('menuitem', { name: 'View chart in Explore' }),
+ );
+ userEvent.click(screen.getByRole('menuitem', { name: 'Export CSV' }));
+ userEvent.click(screen.getByRole('menuitem', { name: /Force refresh/ }));
+
+ expect(
+ screen.getByRole('button', { name: 'More Options' }),
+ ).toBeInTheDocument();
+ expect(screen.getByTestId('NoAnimationDropdown')).toBeInTheDocument();
+});
+
+test('Should "export to CSV"', () => {
+ const props = createProps();
+ render(, { useRedux: true });
+
+ expect(props.exportCSV).toBeCalledTimes(0);
+ userEvent.click(screen.getByRole('menuitem', { name: 'Export CSV' }));
+ expect(props.exportCSV).toBeCalledTimes(1);
+ expect(props.exportCSV).toBeCalledWith(371);
+});
+
+test('Should "View chart in Explore"', () => {
+ const props = createProps();
+ render(, { useRedux: true });
+
+ expect(props.exploreChart).toBeCalledTimes(0);
+ userEvent.click(
+ screen.getByRole('menuitem', { name: 'View chart in Explore' }),
+ );
+ expect(props.exploreChart).toBeCalledTimes(1);
+ expect(props.exploreChart).toBeCalledWith(371);
+});
+
+test('Should "Toggle chart description"', () => {
+ const props = createProps();
+ render(, { useRedux: true });
+
+ expect(props.toggleExpandSlice).toBeCalledTimes(0);
+ userEvent.click(
+ screen.getByRole('menuitem', { name: 'Toggle chart description' }),
+ );
+ expect(props.toggleExpandSlice).toBeCalledTimes(1);
+ expect(props.toggleExpandSlice).toBeCalledWith(371);
+});
+
+test('Should "Force refresh"', () => {
+ const props = createProps();
+ render(, { useRedux: true });
+
+ expect(props.forceRefresh).toBeCalledTimes(0);
+ userEvent.click(screen.getByRole('menuitem', { name: /Force refresh/ }));
+ expect(props.forceRefresh).toBeCalledTimes(1);
+ expect(props.forceRefresh).toBeCalledWith(371, 26);
+});
+
+test('Should "Maximize chart"', () => {
+ const props = createProps();
+ render(, { useRedux: true });
+
+ expect(props.handleToggleFullSize).toBeCalledTimes(0);
+ userEvent.click(screen.getByRole('menuitem', { name: 'Maximize chart' }));
+ expect(props.handleToggleFullSize).toBeCalledTimes(1);
+});
diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls.jsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx
similarity index 94%
rename from superset-frontend/src/dashboard/components/SliceHeaderControls.jsx
rename to superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx
index 8963b565e6b..edef0213771 100644
--- a/superset-frontend/src/dashboard/components/SliceHeaderControls.jsx
+++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx
@@ -27,11 +27,11 @@ import {
} from '@superset-ui/core';
import { Menu, NoAnimationDropdown } from 'src/common/components';
import ShareMenuItems from 'src/dashboard/components/menu/ShareMenuItems';
-import downloadAsImage from '../../utils/downloadAsImage';
-import getDashboardUrl from '../util/getDashboardUrl';
-import { getActiveFilters } from '../util/activeDashboardFilters';
-import { FeatureFlag, isFeatureEnabled } from '../../featureFlags';
-import CrossFilterScopingModal from './CrossFilterScopingModal/CrossFilterScopingModal';
+import downloadAsImage from 'src/utils/downloadAsImage';
+import getDashboardUrl from 'src/dashboard/util/getDashboardUrl';
+import { getActiveFilters } from 'src/dashboard/util/activeDashboardFilters';
+import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
+import CrossFilterScopingModal from 'src/dashboard/components/CrossFilterScopingModal/CrossFilterScopingModal';
const propTypes = {
slice: PropTypes.object.isRequired,
@@ -302,7 +302,11 @@ class SliceHeaderControls extends React.PureComponent {
triggerNode.closest(SCREENSHOT_NODE_SELECTOR)
}
>
-
+