refactor: Migrate SliceAdder to typescript (#30697)
This commit is contained in:
parent
58edc79820
commit
31aad28a31
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
Loading…
Reference in New Issue