This is an automated email from the ASF dual-hosted git repository.

diegopucci pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 31aad28a31 refactor: Migrate SliceAdder to typescript (#30697)
31aad28a31 is described below

commit 31aad28a31c117ad9c5c5a1957b6043a75e93020
Author: Enzo Martellucci <[email protected]>
AuthorDate: Thu Oct 31 15:57:49 2024 +0100

    refactor: Migrate SliceAdder to typescript (#30697)
---
 .../spec/fixtures/mockSliceEntities.js             | 133 +++++++++++++++++----
 .../{SliceAdder.test.jsx => SliceAdder.test.tsx}   | 100 ++++++++++------
 .../components/{SliceAdder.jsx => SliceAdder.tsx}  | 115 +++++++++++-------
 ...liceDragPreview.jsx => AddSliceDragPreview.tsx} |  53 ++++----
 4 files changed, 269 insertions(+), 132 deletions(-)

diff --git a/superset-frontend/spec/fixtures/mockSliceEntities.js 
b/superset-frontend/spec/fixtures/mockSliceEntities.js
index 2737d35d01..1a7bdad024 100644
--- a/superset-frontend/spec/fixtures/mockSliceEntities.js
+++ b/superset-frontend/spec/fixtures/mockSliceEntities.js
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { datasourceId } from 'spec/fixtures/mockDatasource';
+import { DatasourceType } from '@superset-ui/core';
 import { sliceId } from './mockChartQueries';
 
 export const filterId = 127;
@@ -47,8 +48,8 @@ export const sliceEntitiesForChart = {
       },
       viz_type: 'pie',
       datasource: datasourceId,
-      description: null,
-      description_markeddown: '',
+      description: '',
+      description_markdown: '',
       modified: '23 hours ago',
       changed_on: 1529453332615,
     },
@@ -79,10 +80,18 @@ export const sliceEntitiesForDashboard = {
       },
       viz_type: 'filter_box',
       datasource: '2__table',
-      description: null,
-      description_markeddown: '',
+      description: '',
+      description_markdown: '',
       modified: '23 hours ago',
       changed_on: 1529453332615,
+      changed_on_humanized: '',
+      datasource_id: 0,
+      datasource_type: DatasourceType.Query,
+      datasource_url: '',
+      datasource_name: '',
+      owners: [{ id: 0 }],
+      created_by: { id: 0 },
+      thumbnail_url: '',
     },
     128: {
       slice_id: 128,
@@ -91,10 +100,18 @@ export const sliceEntitiesForDashboard = {
       form_data: {},
       viz_type: 'big_number',
       datasource: '2__table',
-      description: null,
-      description_markeddown: '',
+      description: '',
+      description_markdown: '',
       modified: '23 hours ago',
       changed_on: 1529453332628,
+      changed_on_humanized: '',
+      datasource_id: 0,
+      datasource_type: DatasourceType.Query,
+      datasource_url: '',
+      datasource_name: '',
+      owners: [{ id: 0 }],
+      created_by: { id: 0 },
+      thumbnail_url: '',
     },
     129: {
       slice_id: 129,
@@ -103,10 +120,19 @@ export const sliceEntitiesForDashboard = {
       form_data: {},
       viz_type: 'table',
       datasource: '2__table',
-      description: null,
-      description_markeddown: '',
+      description: '',
+      description_markdown: 'dd',
       modified: '23 hours ago',
       changed_on: 1529453332637,
+      changed_on_humanized: '',
+
+      datasource_id: 0,
+      datasource_type: DatasourceType.Query,
+      datasource_url: '',
+      datasource_name: '',
+      owners: [{ id: 0 }],
+      created_by: { id: 0 },
+      thumbnail_url: '',
     },
     130: {
       slice_id: 130,
@@ -115,10 +141,19 @@ export const sliceEntitiesForDashboard = {
       form_data: {},
       viz_type: 'line',
       datasource: '2__table',
-      description: null,
-      description_markeddown: '',
+      description: '',
+      description_markdown: '',
       modified: '23 hours ago',
       changed_on: 1529453332645,
+      changed_on_humanized: '',
+
+      datasource_id: 0,
+      datasource_type: DatasourceType.SlTable,
+      datasource_url: '',
+      datasource_name: '',
+      owners: [{ id: 0 }],
+      created_by: { id: 0 },
+      thumbnail_url: '',
     },
     131: {
       slice_id: 131,
@@ -127,10 +162,19 @@ export const sliceEntitiesForDashboard = {
       form_data: {},
       viz_type: 'world_map',
       datasource: '2__table',
-      description: null,
-      description_markeddown: '',
+      description: '',
+      description_markdown: '',
       modified: '23 hours ago',
       changed_on: 1529453332654,
+      changed_on_humanized: '',
+
+      datasource_id: 0,
+      datasource_type: DatasourceType.Table,
+      datasource_url: '',
+      datasource_name: '',
+      owners: [{ id: 0 }],
+      created_by: { id: 0 },
+      thumbnail_url: '',
     },
     132: {
       slice_id: 132,
@@ -139,10 +183,19 @@ export const sliceEntitiesForDashboard = {
       form_data: {},
       viz_type: 'bubble',
       datasource: '2__table',
-      description: null,
-      description_markeddown: '',
+      description: '',
+      description_markdown: '',
       modified: '23 hours ago',
       changed_on: 1529453332663,
+      changed_on_humanized: '',
+
+      datasource_id: 0,
+      datasource_type: DatasourceType.Query,
+      datasource_url: '',
+      datasource_name: '',
+      owners: [{ id: 0 }],
+      created_by: { id: 0 },
+      thumbnail_url: '',
     },
     133: {
       slice_id: 133,
@@ -151,10 +204,19 @@ export const sliceEntitiesForDashboard = {
       form_data: {},
       viz_type: 'sunburst_v2',
       datasource: '2__table',
-      description: null,
-      description_markeddown: '',
+      description: '',
+      description_markdown: '',
       modified: '23 hours ago',
       changed_on: 1529453332673,
+      changed_on_humanized: '',
+
+      datasource_id: 0,
+      datasource_type: DatasourceType.Query,
+      datasource_url: '',
+      datasource_name: '',
+      owners: [{ id: 0 }],
+      created_by: { id: 0 },
+      thumbnail_url: '',
     },
     134: {
       slice_id: 134,
@@ -163,10 +225,19 @@ export const sliceEntitiesForDashboard = {
       form_data: {},
       viz_type: 'area',
       datasource: '2__table',
-      description: null,
-      description_markeddown: '',
+      description: '',
+      description_markdown: '',
       modified: '23 hours ago',
       changed_on: 1529453332680,
+      changed_on_humanized: '',
+
+      datasource_id: 0,
+      datasource_type: DatasourceType.Dataset,
+      datasource_url: '',
+      datasource_name: '',
+      owners: [{ id: 0 }],
+      created_by: { id: 0 },
+      thumbnail_url: '',
     },
     135: {
       slice_id: 135,
@@ -175,10 +246,19 @@ export const sliceEntitiesForDashboard = {
       form_data: {},
       viz_type: 'box_plot',
       datasource: '2__table',
-      description: null,
-      description_markeddown: '',
+      description: '',
+      description_markdown: '',
       modified: '23 hours ago',
       changed_on: 1529453332688,
+      changed_on_humanized: '',
+
+      datasource_id: 0,
+      datasource_type: DatasourceType.Table,
+      datasource_url: '',
+      datasource_name: '',
+      owners: [{ id: 0 }],
+      created_by: { id: 0 },
+      thumbnail_url: '',
     },
     136: {
       slice_id: 136,
@@ -187,10 +267,19 @@ export const sliceEntitiesForDashboard = {
       form_data: {},
       viz_type: 'treemap_v2',
       datasource: '2__table',
-      description: null,
-      description_markeddown: '',
+      description: '',
+      description_markdown: '',
       modified: '23 hours ago',
       changed_on: 1529453332700,
+      changed_on_humanized: '',
+
+      datasource_id: 0,
+      datasource_type: DatasourceType.Table,
+      datasource_url: '',
+      datasource_name: '',
+      owners: [{ id: 0 }],
+      created_by: { id: 0 },
+      thumbnail_url: '',
     },
   },
   isLoading: false,
diff --git a/superset-frontend/src/dashboard/components/SliceAdder.test.jsx 
b/superset-frontend/src/dashboard/components/SliceAdder.test.tsx
similarity index 67%
rename from superset-frontend/src/dashboard/components/SliceAdder.test.jsx
rename to superset-frontend/src/dashboard/components/SliceAdder.test.tsx
index 6ae0346edb..a0a6acb568 100644
--- a/superset-frontend/src/dashboard/components/SliceAdder.test.jsx
+++ b/superset-frontend/src/dashboard/components/SliceAdder.test.tsx
@@ -16,35 +16,45 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { shallow } from 'enzyme';
+import { shallow, ShallowWrapper } from 'enzyme';
 import sinon from 'sinon';
 
 import SliceAdder, {
   ChartList,
   DEFAULT_SORT_KEY,
+  SliceAdderProps,
 } from 'src/dashboard/components/SliceAdder';
 import { sliceEntitiesForDashboard as mockSliceEntities } from 
'spec/fixtures/mockSliceEntities';
 import { styledShallow } from 'spec/helpers/theming';
 
-jest.mock('lodash/debounce', () => fn => {
-  // eslint-disable-next-line no-param-reassign
-  fn.throttle = jest.fn();
-  return fn;
-});
+jest.mock(
+  'lodash/debounce',
+  () => (fn: { throttle: jest.Mock<any, any, any> }) => {
+    // eslint-disable-next-line no-param-reassign
+    fn.throttle = jest.fn();
+    return fn;
+  },
+);
 
 describe('SliceAdder', () => {
-  const props = {
-    ...mockSliceEntities,
+  const props: SliceAdderProps = {
+    slices: {
+      ...mockSliceEntities.slices,
+    },
     fetchSlices: jest.fn(),
     updateSlices: jest.fn(),
     selectedSliceIds: [127, 128],
     userId: 1,
+    dashboardId: 0,
+    editMode: false,
+    errorMessage: '',
+    isLoading: false,
+    lastUpdated: 0,
   };
   const errorProps = {
     ...props,
     errorMessage: 'this is error',
   };
-
   describe('SliceAdder.sortByComparator', () => {
     it('should sort by timestamp descending', () => {
       const sortedTimestamps = Object.values(props.slices)
@@ -84,72 +94,88 @@ describe('SliceAdder', () => {
   });
 
   it('componentDidMount', () => {
-    sinon.spy(SliceAdder.prototype, 'componentDidMount');
-    sinon.spy(props, 'fetchSlices');
-
+    const componentDidMountSpy = sinon.spy(
+      SliceAdder.prototype,
+      'componentDidMount',
+    );
+    const fetchSlicesSpy = sinon.spy(props, 'fetchSlices');
     shallow(<SliceAdder {...props} />, {
       lifecycleExperimental: true,
     });
-    expect(SliceAdder.prototype.componentDidMount.calledOnce).toBe(true);
-    expect(props.fetchSlices.calledOnce).toBe(true);
 
-    SliceAdder.prototype.componentDidMount.restore();
-    props.fetchSlices.restore();
+    expect(componentDidMountSpy.calledOnce).toBe(true);
+
+    expect(fetchSlicesSpy.calledOnce).toBe(true);
+
+    componentDidMountSpy.restore();
+    fetchSlicesSpy.restore();
   });
 
   describe('UNSAFE_componentWillReceiveProps', () => {
-    let wrapper;
+    let wrapper: ShallowWrapper;
+    let setStateSpy: sinon.SinonSpy;
+
     beforeEach(() => {
       wrapper = shallow(<SliceAdder {...props} />);
       wrapper.setState({ filteredSlices: Object.values(props.slices) });
-      sinon.spy(wrapper.instance(), 'setState');
+      setStateSpy = sinon.spy(wrapper.instance() as SliceAdder, 'setState');
     });
     afterEach(() => {
-      wrapper.instance().setState.restore();
+      setStateSpy.restore();
     });
 
     it('fetch slices should update state', () => {
-      wrapper.instance().UNSAFE_componentWillReceiveProps({
+      const instance = wrapper.instance() as SliceAdder;
+      instance.UNSAFE_componentWillReceiveProps({
         ...props,
         lastUpdated: new Date().getTime(),
       });
-      expect(wrapper.instance().setState.calledOnce).toBe(true);
+      expect(setStateSpy.calledOnce).toBe(true);
 
-      const stateKeys = Object.keys(
-        wrapper.instance().setState.lastCall.args[0],
-      );
+      const stateKeys = Object.keys(setStateSpy.lastCall.args[0]);
       expect(stateKeys).toContain('filteredSlices');
     });
 
     it('select slices should update state', () => {
-      wrapper.instance().UNSAFE_componentWillReceiveProps({
+      const instance = wrapper.instance() as SliceAdder;
+
+      instance.UNSAFE_componentWillReceiveProps({
         ...props,
         selectedSliceIds: [127],
       });
-      expect(wrapper.instance().setState.calledOnce).toBe(true);
 
-      const stateKeys = Object.keys(
-        wrapper.instance().setState.lastCall.args[0],
-      );
+      expect(setStateSpy.calledOnce).toBe(true);
+
+      const stateKeys = Object.keys(setStateSpy.lastCall.args[0]);
       expect(stateKeys).toContain('selectedSliceIdsSet');
     });
   });
 
   describe('should rerun filter and sort', () => {
-    let wrapper;
-    let spy;
+    let wrapper: ShallowWrapper<SliceAdder>;
+    let spy: jest.Mock;
+
     beforeEach(() => {
-      spy = props.fetchSlices;
-      wrapper = shallow(<SliceAdder {...props} fetchSlices={spy} />);
-      wrapper.setState({ filteredSlices: Object.values(props.slices) });
+      spy = jest.fn();
+      const fetchSlicesProps: SliceAdderProps = {
+        ...props,
+        fetchSlices: spy,
+      };
+      wrapper = shallow(<SliceAdder {...fetchSlicesProps} />);
+      wrapper.setState({
+        filteredSlices: Object.values(fetchSlicesProps.slices),
+      });
     });
+
     afterEach(() => {
       spy.mockReset();
     });
 
     it('searchUpdated', () => {
       const newSearchTerm = 'new search term';
-      wrapper.instance().handleChange(newSearchTerm);
+
+      (wrapper.instance() as SliceAdder).handleChange(newSearchTerm);
+
       expect(spy).toHaveBeenCalled();
       expect(spy).toHaveBeenCalledWith(
         props.userId,
@@ -160,7 +186,9 @@ describe('SliceAdder', () => {
 
     it('handleSelect', () => {
       const newSortBy = 'viz_type';
-      wrapper.instance().handleSelect(newSortBy);
+
+      (wrapper.instance() as SliceAdder).handleSelect(newSortBy);
+
       expect(spy).toHaveBeenCalled();
       expect(spy).toHaveBeenCalledWith(props.userId, '', newSortBy);
     });
diff --git a/superset-frontend/src/dashboard/components/SliceAdder.jsx 
b/superset-frontend/src/dashboard/components/SliceAdder.tsx
similarity index 82%
rename from superset-frontend/src/dashboard/components/SliceAdder.jsx
rename to superset-frontend/src/dashboard/components/SliceAdder.tsx
index b610fe152c..09f6270f83 100644
--- a/superset-frontend/src/dashboard/components/SliceAdder.jsx
+++ b/superset-frontend/src/dashboard/components/SliceAdder.tsx
@@ -18,9 +18,9 @@
  */
 /* eslint-env browser */
 import { Component } from 'react';
-import PropTypes from 'prop-types';
 import AutoSizer from 'react-virtualized-auto-sizer';
 import { FixedSizeList as List } from 'react-window';
+// @ts-ignore
 import { createFilter } from 'react-search-input';
 import { t, styled, css } from '@superset-ui/core';
 import { Input } from 'src/components/Input';
@@ -41,31 +41,40 @@ import {
   NEW_CHART_ID,
   NEW_COMPONENTS_SOURCE_ID,
 } from 'src/dashboard/util/constants';
-import { slicePropShape } from 'src/dashboard/util/propShapes';
 import { debounce, pickBy } from 'lodash';
 import Checkbox from 'src/components/Checkbox';
 import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
+import { Dispatch } from 'redux';
+import { Slice } from 'src/dashboard/types';
 import AddSliceCard from './AddSliceCard';
 import AddSliceDragPreview from './dnd/AddSliceDragPreview';
 import DragDroppable from './dnd/DragDroppable';
 
-const propTypes = {
-  fetchSlices: PropTypes.func.isRequired,
-  updateSlices: PropTypes.func.isRequired,
-  isLoading: PropTypes.bool.isRequired,
-  slices: PropTypes.objectOf(slicePropShape).isRequired,
-  lastUpdated: PropTypes.number.isRequired,
-  errorMessage: PropTypes.string,
-  userId: PropTypes.number.isRequired,
-  selectedSliceIds: PropTypes.arrayOf(PropTypes.number),
-  editMode: PropTypes.bool,
-  dashboardId: PropTypes.number,
+export type SliceAdderProps = {
+  fetchSlices: (
+    userId?: number,
+    filter_value?: string,
+    sortColumn?: string,
+  ) => Promise<void>;
+  updateSlices: (slices: {
+    [id: number]: Slice;
+  }) => (dispatch: Dispatch) => void;
+  isLoading: boolean;
+  slices: Record<number, Slice>;
+  lastUpdated: number;
+  errorMessage?: string;
+  userId: number;
+  selectedSliceIds?: number[];
+  editMode?: boolean;
+  dashboardId: number;
 };
 
-const defaultProps = {
-  selectedSliceIds: [],
-  editMode: false,
-  errorMessage: '',
+type SliceAdderState = {
+  filteredSlices: Slice[];
+  searchTerm: string;
+  sortBy: keyof Slice;
+  selectedSliceIdsSet: Set<number>;
+  showOnlyMyCharts: boolean;
 };
 
 const KEYS_TO_FILTERS = ['slice_name', 'viz_type', 'datasource_name'];
@@ -92,7 +101,7 @@ const Controls = styled.div`
   `}
 `;
 
-const StyledSelect = styled(Select)`
+const StyledSelect = styled(Select)<{ id?: string }>`
   margin-left: ${({ theme }) => theme.gridUnit * 2}px;
   min-width: 150px;
 `;
@@ -124,22 +133,33 @@ export const ChartList = styled.div`
   min-height: 0;
 `;
 
-class SliceAdder extends Component {
-  static sortByComparator(attr) {
+class SliceAdder extends Component<SliceAdderProps, SliceAdderState> {
+  private slicesRequest?: AbortController | Promise<void>;
+
+  static sortByComparator(attr: keyof Slice) {
     const desc = attr === 'changed_on' ? -1 : 1;
 
-    return (a, b) => {
-      if (a[attr] < b[attr]) {
+    return (a: Slice, b: Slice) => {
+      const aValue = a[attr] ?? Number.MIN_SAFE_INTEGER;
+      const bValue = b[attr] ?? Number.MIN_SAFE_INTEGER;
+
+      if (aValue < bValue) {
         return -1 * desc;
       }
-      if (a[attr] > b[attr]) {
+      if (aValue > bValue) {
         return 1 * desc;
       }
       return 0;
     };
   }
 
-  constructor(props) {
+  static defaultProps = {
+    selectedSliceIds: [],
+    editMode: false,
+    errorMessage: '',
+  };
+
+  constructor(props: SliceAdderProps) {
     super(props);
     this.state = {
       filteredSlices: [],
@@ -163,11 +183,15 @@ class SliceAdder extends Component {
   }
 
   componentDidMount() {
-    this.slicesRequest = this.props.fetchSlices(this.userIdForFetch());
+    this.slicesRequest = this.props.fetchSlices(
+      this.userIdForFetch(),
+      '',
+      this.state.sortBy,
+    );
   }
 
-  UNSAFE_componentWillReceiveProps(nextProps) {
-    const nextState = {};
+  UNSAFE_componentWillReceiveProps(nextProps: SliceAdderProps) {
+    const nextState: SliceAdderState = {} as SliceAdderState;
     if (nextProps.lastUpdated !== this.props.lastUpdated) {
       nextState.filteredSlices = this.getFilteredSortedSlices(
         nextProps.slices,
@@ -188,22 +212,27 @@ class SliceAdder extends Component {
 
   componentWillUnmount() {
     // Clears the redux store keeping only selected items
-    const selectedSlices = pickBy(this.props.slices, value =>
+    const selectedSlices = pickBy(this.props.slices, (value: Slice) =>
       this.state.selectedSliceIdsSet.has(value.slice_id),
     );
+
     this.props.updateSlices(selectedSlices);
-    if (this.slicesRequest && this.slicesRequest.abort) {
+    if (this.slicesRequest instanceof AbortController) {
       this.slicesRequest.abort();
     }
   }
 
-  getFilteredSortedSlices(slices, searchTerm, sortBy, showOnlyMyCharts) {
+  getFilteredSortedSlices(
+    slices: SliceAdderProps['slices'],
+    searchTerm: string,
+    sortBy: keyof Slice,
+    showOnlyMyCharts: boolean,
+  ) {
     return Object.values(slices)
       .filter(slice =>
         showOnlyMyCharts
-          ? (slice.owners &&
-              slice.owners.find(owner => owner.id === this.props.userId)) ||
-            (slice.created_by && slice.created_by.id === this.props.userId)
+          ? slice?.owners?.find(owner => owner.id === this.props.userId) ||
+            slice?.created_by?.id === this.props.userId
           : true,
       )
       .filter(createFilter(searchTerm, KEYS_TO_FILTERS))
@@ -219,7 +248,7 @@ class SliceAdder extends Component {
     );
   }, 300);
 
-  searchUpdated(searchTerm) {
+  searchUpdated(searchTerm: string) {
     this.setState(prevState => ({
       searchTerm,
       filteredSlices: this.getFilteredSortedSlices(
@@ -231,7 +260,7 @@ class SliceAdder extends Component {
     }));
   }
 
-  handleSelect(sortBy) {
+  handleSelect(sortBy: keyof Slice) {
     this.setState(prevState => ({
       sortBy,
       filteredSlices: this.getFilteredSortedSlices(
@@ -248,9 +277,10 @@ class SliceAdder extends Component {
     );
   }
 
-  rowRenderer({ key, index, style }) {
+  rowRenderer({ index, style }: { index: number; style: React.CSSProperties }) 
{
     const { filteredSlices, selectedSliceIdsSet } = this.state;
     const cellData = filteredSlices[index];
+
     const isSelected = selectedSliceIdsSet.has(cellData.slice_id);
     const type = CHART_TYPE;
     const id = NEW_CHART_ID;
@@ -261,7 +291,7 @@ class SliceAdder extends Component {
     };
     return (
       <DragDroppable
-        key={key}
+        key={cellData.slice_id}
         component={{ type, id, meta }}
         parentComponent={{
           id: NEW_COMPONENTS_SOURCE_ID,
@@ -295,7 +325,7 @@ class SliceAdder extends Component {
     );
   }
 
-  onShowOnlyMyCharts(showOnlyMyCharts) {
+  onShowOnlyMyCharts(showOnlyMyCharts: boolean) {
     if (!showOnlyMyCharts) {
       this.slicesRequest = this.props.fetchSlices(
         undefined,
@@ -390,15 +420,13 @@ class SliceAdder extends Component {
         {!this.props.isLoading && this.state.filteredSlices.length > 0 && (
           <ChartList>
             <AutoSizer>
-              {({ height, width }) => (
+              {({ height, width }: { height: number; width: number }) => (
                 <List
                   width={width}
                   height={height}
                   itemCount={this.state.filteredSlices.length}
                   itemSize={DEFAULT_CELL_HEIGHT}
-                  searchTerm={this.state.searchTerm}
-                  sortBy={this.state.sortBy}
-                  selectedSliceIds={this.props.selectedSliceIds}
+                  itemKey={index => this.state.filteredSlices[index].slice_id}
                 >
                   {this.rowRenderer}
                 </List>
@@ -422,7 +450,4 @@ class SliceAdder extends Component {
   }
 }
 
-SliceAdder.propTypes = propTypes;
-SliceAdder.defaultProps = defaultProps;
-
 export default SliceAdder;
diff --git 
a/superset-frontend/src/dashboard/components/dnd/AddSliceDragPreview.jsx 
b/superset-frontend/src/dashboard/components/dnd/AddSliceDragPreview.tsx
similarity index 72%
rename from 
superset-frontend/src/dashboard/components/dnd/AddSliceDragPreview.jsx
rename to superset-frontend/src/dashboard/components/dnd/AddSliceDragPreview.tsx
index 375e2cdc36..dfd2d03688 100644
--- a/superset-frontend/src/dashboard/components/dnd/AddSliceDragPreview.jsx
+++ b/superset-frontend/src/dashboard/components/dnd/AddSliceDragPreview.tsx
@@ -16,17 +16,28 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import PropTypes from 'prop-types';
-import { DragLayer } from 'react-dnd';
-
+import { DragLayer, XYCoord } from 'react-dnd';
+import { Slice } from 'src/dashboard/types';
 import AddSliceCard from '../AddSliceCard';
-import { slicePropShape } from '../../util/propShapes';
 import {
   NEW_COMPONENT_SOURCE_TYPE,
   CHART_TYPE,
 } from '../../util/componentTypes';
 
-const staticCardStyles = {
+interface DragItem {
+  index: number;
+  parentType: string;
+  type: string;
+}
+
+interface AddSliceDragPreviewProps {
+  dragItem: DragItem | null;
+  slices: Slice[] | null;
+  isDragging: boolean;
+  currentOffset: XYCoord | null;
+}
+
+const staticCardStyles: React.CSSProperties = {
   position: 'fixed',
   pointerEvents: 'none',
   top: 0,
@@ -35,25 +46,12 @@ const staticCardStyles = {
   width: 376 - 2 * 16,
 };
 
-const propTypes = {
-  dragItem: PropTypes.shape({
-    index: PropTypes.number.isRequired,
-  }),
-  slices: PropTypes.arrayOf(slicePropShape),
-  isDragging: PropTypes.bool.isRequired,
-  currentOffset: PropTypes.shape({
-    x: PropTypes.number.isRequired,
-    y: PropTypes.number.isRequired,
-  }),
-};
-
-const defaultProps = {
-  currentOffset: null,
-  dragItem: null,
-  slices: null,
-};
-
-function AddSliceDragPreview({ dragItem, slices, isDragging, currentOffset }) {
+const AddSliceDragPreview: React.FC<AddSliceDragPreviewProps> = ({
+  dragItem,
+  slices,
+  isDragging,
+  currentOffset,
+}) => {
   if (!isDragging || !currentOffset || !dragItem || !slices) return null;
 
   const slice = slices[dragItem.index];
@@ -77,14 +75,11 @@ function AddSliceDragPreview({ dragItem, slices, 
isDragging, currentOffset }) {
       datasourceName={slice.datasource_name}
     />
   );
-}
-
-AddSliceDragPreview.propTypes = propTypes;
-AddSliceDragPreview.defaultProps = defaultProps;
+};
 
 // This injects these props into the component
 export default DragLayer(monitor => ({
-  dragItem: monitor.getItem(),
+  dragItem: monitor.getItem() as DragItem | null,
   currentOffset: monitor.getSourceClientOffset(),
   isDragging: monitor.isDragging(),
 }))(AddSliceDragPreview);

Reply via email to