diff --git a/superset-frontend/src/dashboard/components/SliceHeader/SliceHeader.test.tsx b/superset-frontend/src/dashboard/components/SliceHeader/SliceHeader.test.tsx
new file mode 100644
index 0000000000..b036ef696c
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/SliceHeader/SliceHeader.test.tsx
@@ -0,0 +1,399 @@
+/**
+ * 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 { Slice } from 'src/types/Chart';
+import React from 'react';
+import { render, screen } from 'spec/helpers/testing-library';
+import userEvent from '@testing-library/user-event';
+import SliceHeader from '.';
+
+jest.mock('src/dashboard/components/SliceHeaderControls', () => ({
+ __esModule: true,
+ default: (props: any) => (
+
+
+ toggleExpandSlice
+
+
+ forceRefresh
+
+
+
+ exploreChart
+
+
+
+ exportCSV
+
+
+
+ handleToggleFullSize
+
+
+
+ addSuccessToast
+
+
+
+ addDangerToast
+
+
+ ),
+}));
+
+jest.mock('src/dashboard/containers/FiltersBadge', () => ({
+ __esModule: true,
+ default: (props: any) => (
+
+ ),
+}));
+
+const createProps = () => ({
+ filters: {}, // is in typing but not being used
+ editMode: false,
+ annotationQuery: { param01: 'annotationQuery' } as any,
+ annotationError: { param01: 'annotationError' } as any,
+ cachedDttm: [] as string[],
+ updatedDttm: 1617207718004,
+ isCached: [false],
+ isExpanded: false,
+ sliceName: 'Vaccine Candidates per Phase',
+ supersetCanExplore: true,
+ supersetCanCSV: true,
+ sliceCanEdit: false,
+ slice: ({
+ slice_id: 312,
+ slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20312%7D',
+ slice_name: 'Vaccine Candidates per Phase',
+ form_data: {
+ adhoc_filters: [],
+ bottom_margin: 'auto',
+ color_scheme: 'SUPERSET_DEFAULT',
+ columns: [],
+ datasource: '58__table',
+ groupby: ['clinical_stage'],
+ label_colors: {},
+ metrics: ['count'],
+ row_limit: 10000,
+ show_legend: false,
+ time_range: 'No filter',
+ time_range_endpoints: ['inclusive', 'exclusive'],
+ url_params: {},
+ viz_type: 'dist_bar',
+ x_ticks_layout: 'auto',
+ y_axis_format: 'SMART_NUMBER',
+ slice_id: 312,
+ },
+ viz_type: 'dist_bar',
+ datasource: '58__table',
+ description: null,
+ description_markeddown: '',
+ owners: [],
+ modified: '20 hours ago ',
+ changed_on: 1617143411366,
+ } as unknown) as Slice,
+ componentId: 'CHART-aGfmWtliqA',
+ dashboardId: 26,
+ isFullSize: false,
+ chartStatus: 'rendered',
+ addSuccessToast: jest.fn(),
+ addDangerToast: jest.fn(),
+ handleToggleFullSize: jest.fn(),
+ updateSliceName: jest.fn(),
+ toggleExpandSlice: jest.fn(),
+ forceRefresh: jest.fn(),
+ exploreChart: jest.fn(),
+ exportCSV: jest.fn(),
+});
+
+test('Should render', () => {
+ const props = createProps();
+ render( , { useRedux: true });
+ expect(screen.getByTestId('slice-header')).toBeInTheDocument();
+});
+
+test('Should render - default props', () => {
+ const props = createProps();
+
+ // @ts-ignore
+ delete props.forceRefresh;
+ // @ts-ignore
+ delete props.updateSliceName;
+ // @ts-ignore
+ delete props.toggleExpandSlice;
+ // @ts-ignore
+ delete props.exploreChart;
+ // @ts-ignore
+ delete props.exportCSV;
+ // @ts-ignore
+ delete props.innerRef;
+ // @ts-ignore
+ delete props.editMode;
+ // @ts-ignore
+ delete props.annotationQuery;
+ // @ts-ignore
+ delete props.annotationError;
+ // @ts-ignore
+ delete props.cachedDttm;
+ // @ts-ignore
+ delete props.updatedDttm;
+ // @ts-ignore
+ delete props.isCached;
+ // @ts-ignore
+ delete props.isExpanded;
+ // @ts-ignore
+ delete props.sliceName;
+ // @ts-ignore
+ delete props.supersetCanExplore;
+ // @ts-ignore
+ delete props.supersetCanCSV;
+ // @ts-ignore
+ delete props.sliceCanEdit;
+
+ render( , { useRedux: true });
+ expect(screen.getByTestId('slice-header')).toBeInTheDocument();
+});
+
+test('Should render default props and "call" actions', () => {
+ const props = createProps();
+
+ // @ts-ignore
+ delete props.forceRefresh;
+ // @ts-ignore
+ delete props.updateSliceName;
+ // @ts-ignore
+ delete props.toggleExpandSlice;
+ // @ts-ignore
+ delete props.exploreChart;
+ // @ts-ignore
+ delete props.exportCSV;
+ // @ts-ignore
+ delete props.innerRef;
+ // @ts-ignore
+ delete props.editMode;
+ // @ts-ignore
+ delete props.annotationQuery;
+ // @ts-ignore
+ delete props.annotationError;
+ // @ts-ignore
+ delete props.cachedDttm;
+ // @ts-ignore
+ delete props.updatedDttm;
+ // @ts-ignore
+ delete props.isCached;
+ // @ts-ignore
+ delete props.isExpanded;
+ // @ts-ignore
+ delete props.sliceName;
+ // @ts-ignore
+ delete props.supersetCanExplore;
+ // @ts-ignore
+ delete props.supersetCanCSV;
+ // @ts-ignore
+ delete props.sliceCanEdit;
+
+ render( , { useRedux: true });
+ userEvent.click(screen.getByTestId('toggleExpandSlice'));
+ userEvent.click(screen.getByTestId('forceRefresh'));
+ userEvent.click(screen.getByTestId('exploreChart'));
+ userEvent.click(screen.getByTestId('exportCSV'));
+ userEvent.click(screen.getByTestId('addSuccessToast'));
+ userEvent.click(screen.getByTestId('addDangerToast'));
+ userEvent.click(screen.getByTestId('handleToggleFullSize'));
+ expect(screen.getByTestId('slice-header')).toBeInTheDocument();
+});
+
+test('Should render title', () => {
+ const props = createProps();
+ render( , { useRedux: true });
+ expect(screen.getByText('Vaccine Candidates per Phase')).toBeInTheDocument();
+});
+
+test('Should render "annotationsLoading"', () => {
+ const props = createProps();
+ render( , { useRedux: true });
+ expect(
+ screen.getByRole('img', {
+ name: 'Annotation layers are still loading.',
+ }),
+ ).toBeInTheDocument();
+});
+
+test('Should render "annotationsError"', () => {
+ const props = createProps();
+ render( , { useRedux: true });
+ expect(
+ screen.getByRole('img', {
+ name: 'One ore more annotation layers failed loading.',
+ }),
+ ).toBeInTheDocument();
+});
+
+test('Should not render "annotationsError" and "annotationsLoading"', () => {
+ const props = createProps();
+ props.annotationQuery = {};
+ props.annotationError = {};
+ render( , { useRedux: true });
+ expect(
+ screen.queryByRole('img', {
+ name: 'One ore more annotation layers failed loading.',
+ }),
+ ).not.toBeInTheDocument();
+ expect(
+ screen.queryByRole('img', {
+ name: 'Annotation layers are still loading.',
+ }),
+ ).not.toBeInTheDocument();
+});
+
+test('Correct props to "FiltersBadge"', () => {
+ const props = createProps();
+ render( , { useRedux: true });
+ expect(screen.getByTestId('FiltersBadge')).toHaveAttribute(
+ 'data-chart-id',
+ '312',
+ );
+});
+
+test('Correct props to "SliceHeaderControls"', () => {
+ const props = createProps();
+ render( , { useRedux: true });
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-cached-dttm',
+ '',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-chart-status',
+ 'rendered',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-component-id',
+ 'CHART-aGfmWtliqA',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-dashboard-id',
+ '26',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-is-cached',
+ 'false',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-is-expanded',
+ 'false',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-is-full-size',
+ 'false',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-slice-can-edit',
+ 'false',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-superset-can-csv',
+ 'true',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-superset-can-explore',
+ 'true',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-test',
+ 'SliceHeaderControls',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-updated-dttm',
+ '1617207718004',
+ );
+ expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute(
+ 'data-slice',
+ JSON.stringify(props.slice),
+ );
+});
+
+test('Correct actions to "SliceHeaderControls"', () => {
+ const props = createProps();
+ render( , { useRedux: true });
+
+ expect(props.toggleExpandSlice).toBeCalledTimes(0);
+ userEvent.click(screen.getByTestId('toggleExpandSlice'));
+ expect(props.toggleExpandSlice).toBeCalledTimes(1);
+
+ expect(props.forceRefresh).toBeCalledTimes(0);
+ userEvent.click(screen.getByTestId('forceRefresh'));
+ expect(props.forceRefresh).toBeCalledTimes(1);
+
+ expect(props.exploreChart).toBeCalledTimes(0);
+ userEvent.click(screen.getByTestId('exploreChart'));
+ expect(props.exploreChart).toBeCalledTimes(1);
+
+ expect(props.exportCSV).toBeCalledTimes(0);
+ userEvent.click(screen.getByTestId('exportCSV'));
+ expect(props.exportCSV).toBeCalledTimes(1);
+
+ expect(props.addSuccessToast).toBeCalledTimes(0);
+ userEvent.click(screen.getByTestId('addSuccessToast'));
+ expect(props.addSuccessToast).toBeCalledTimes(1);
+
+ expect(props.addDangerToast).toBeCalledTimes(0);
+ userEvent.click(screen.getByTestId('addDangerToast'));
+ expect(props.addDangerToast).toBeCalledTimes(1);
+
+ expect(props.handleToggleFullSize).toBeCalledTimes(0);
+ userEvent.click(screen.getByTestId('handleToggleFullSize'));
+ expect(props.handleToggleFullSize).toBeCalledTimes(1);
+});
diff --git a/superset-frontend/src/dashboard/components/SliceHeader.tsx b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx
similarity index 86%
rename from superset-frontend/src/dashboard/components/SliceHeader.tsx
rename to superset-frontend/src/dashboard/components/SliceHeader/index.tsx
index 85eaaa05da..875852c578 100644
--- a/superset-frontend/src/dashboard/components/SliceHeader.tsx
+++ b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx
@@ -20,13 +20,13 @@ import React, { FC } from 'react';
import { styled, t } from '@superset-ui/core';
import { Tooltip } from 'src/common/components/Tooltip';
import { useSelector } from 'react-redux';
-import EditableTitle from '../../components/EditableTitle';
-import SliceHeaderControls from './SliceHeaderControls';
-import FiltersBadge from '../containers/FiltersBadge';
-import Icon from '../../components/Icon';
-import { RootState } from '../types';
-import { Slice } from '../../types/Chart';
-import FilterIndicator from './FiltersBadge/FilterIndicator';
+import EditableTitle from 'src/components/EditableTitle';
+import SliceHeaderControls from 'src/dashboard/components/SliceHeaderControls';
+import FiltersBadge from 'src/dashboard/containers/FiltersBadge';
+import Icon from 'src/components/Icon';
+import { RootState } from 'src/dashboard/types';
+import { Slice } from 'src/types/Chart';
+import FilterIndicator from 'src/dashboard/components/FiltersBadge/FilterIndicator';
type SliceHeaderProps = {
innerRef?: string;
@@ -101,7 +101,7 @@ const SliceHeader: FC = ({
);
return (
-
+
= ({
placement="top"
title={annoationsLoading}
>
-
+
)}
{!!Object.values(annotationError).length && (
@@ -130,7 +134,11 @@ const SliceHeader: FC = ({
placement="top"
title={annoationsError}
>
-
+
)}