mirror of
https://github.com/apache/superset.git
synced 2024-09-16 02:29:39 -04:00
feat: sort card view by Alphabetical, Recently Modified, and Least Recently Modified (#10601)
This commit is contained in:
parent
acb00f509c
commit
03a62f15d8
@ -22,14 +22,17 @@ import { act } from 'react-dom/test-utils';
|
|||||||
import { QueryParamProvider } from 'use-query-params';
|
import { QueryParamProvider } from 'use-query-params';
|
||||||
import { supersetTheme, ThemeProvider } from '@superset-ui/style';
|
import { supersetTheme, ThemeProvider } from '@superset-ui/style';
|
||||||
|
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import CardCollection from 'src/components/ListView/CardCollection';
|
||||||
|
import { CardSortSelect } from 'src/components/ListView/CardSortSelect';
|
||||||
|
import IndeterminateCheckbox from 'src/components/IndeterminateCheckbox';
|
||||||
import ListView from 'src/components/ListView/ListView';
|
import ListView from 'src/components/ListView/ListView';
|
||||||
import ListViewFilters from 'src/components/ListView/Filters';
|
import ListViewFilters from 'src/components/ListView/Filters';
|
||||||
import ListViewPagination from 'src/components/ListView/Pagination';
|
import ListViewPagination from 'src/components/ListView/Pagination';
|
||||||
import Pagination from 'src/components/Pagination';
|
import Pagination from 'src/components/Pagination';
|
||||||
import Button from 'src/components/Button';
|
import TableCollection from 'src/components/ListView/TableCollection';
|
||||||
|
|
||||||
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
|
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
|
||||||
import IndeterminateCheckbox from 'src/components/IndeterminateCheckbox';
|
|
||||||
|
|
||||||
function makeMockLocation(query) {
|
function makeMockLocation(query) {
|
||||||
const queryStr = encodeURIComponent(query);
|
const queryStr = encodeURIComponent(query);
|
||||||
@ -100,6 +103,14 @@ const mockedProps = {
|
|||||||
onSelect: jest.fn(),
|
onSelect: jest.fn(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
cardSortSelectOptions: [
|
||||||
|
{
|
||||||
|
desc: false,
|
||||||
|
id: 'something',
|
||||||
|
label: 'Alphabetical',
|
||||||
|
value: 'alphabetical',
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const factory = (props = mockedProps) =>
|
const factory = (props = mockedProps) =>
|
||||||
@ -281,6 +292,24 @@ describe('ListView', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('disable card view based on prop', async () => {
|
||||||
|
expect(wrapper.find(CardCollection).exists()).toBe(false);
|
||||||
|
expect(wrapper.find(CardSortSelect).exists()).toBe(false);
|
||||||
|
expect(wrapper.find(TableCollection).exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('enable card view based on prop', async () => {
|
||||||
|
const wrapper2 = factory({
|
||||||
|
...mockedProps,
|
||||||
|
renderCard: jest.fn(),
|
||||||
|
initialSort: [{ id: 'something' }],
|
||||||
|
});
|
||||||
|
await waitForComponentToPaint(wrapper2);
|
||||||
|
expect(wrapper2.find(CardCollection).exists()).toBe(true);
|
||||||
|
expect(wrapper2.find(CardSortSelect).exists()).toBe(true);
|
||||||
|
expect(wrapper2.find(TableCollection).exists()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('Throws an exception if filter missing in columns', () => {
|
it('Throws an exception if filter missing in columns', () => {
|
||||||
expect.assertions(1);
|
expect.assertions(1);
|
||||||
const props = {
|
const props = {
|
||||||
@ -377,4 +406,24 @@ describe('ListView', () => {
|
|||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('calls fetchData on card view sort', async () => {
|
||||||
|
const wrapper2 = factory({
|
||||||
|
...mockedProps,
|
||||||
|
renderCard: jest.fn(),
|
||||||
|
initialSort: [{ id: 'something' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
wrapper2.find('[data-test="card-sort-select"]').first().props().onChange({
|
||||||
|
desc: false,
|
||||||
|
id: 'something',
|
||||||
|
label: 'Alphabetical',
|
||||||
|
value: 'alphabetical',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper2.update();
|
||||||
|
expect(mockedProps.fetchData).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -24,6 +24,7 @@ import fetchMock from 'fetch-mock';
|
|||||||
import { supersetTheme, ThemeProvider } from '@superset-ui/style';
|
import { supersetTheme, ThemeProvider } from '@superset-ui/style';
|
||||||
|
|
||||||
import ChartList from 'src/views/CRUD/chart/ChartList';
|
import ChartList from 'src/views/CRUD/chart/ChartList';
|
||||||
|
import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
|
||||||
import ListView from 'src/components/ListView';
|
import ListView from 'src/components/ListView';
|
||||||
import PropertiesModal from 'src/explore/components/PropertiesModal';
|
import PropertiesModal from 'src/explore/components/PropertiesModal';
|
||||||
import ListViewCard from 'src/components/ListViewCard';
|
import ListViewCard from 'src/components/ListViewCard';
|
||||||
@ -49,7 +50,7 @@ const mockCharts = [...new Array(3)].map((_, i) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
fetchMock.get(chartsInfoEndpoint, {
|
fetchMock.get(chartsInfoEndpoint, {
|
||||||
permissions: ['can_list', 'can_edit'],
|
permissions: ['can_list', 'can_edit', 'can_delete'],
|
||||||
});
|
});
|
||||||
fetchMock.get(chartssOwnersEndpoint, {
|
fetchMock.get(chartssOwnersEndpoint, {
|
||||||
result: [],
|
result: [],
|
||||||
@ -113,4 +114,9 @@ describe('ChartList', () => {
|
|||||||
wrapper.find('[data-test="pencil"]').first().simulate('click');
|
wrapper.find('[data-test="pencil"]').first().simulate('click');
|
||||||
expect(wrapper.find(PropertiesModal)).toExist();
|
expect(wrapper.find(PropertiesModal)).toExist();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('delete', () => {
|
||||||
|
wrapper.find('[data-test="trash"]').first().simulate('click');
|
||||||
|
expect(wrapper.find(ConfirmStatusChange)).toExist();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -23,10 +23,11 @@ import configureStore from 'redux-mock-store';
|
|||||||
import fetchMock from 'fetch-mock';
|
import fetchMock from 'fetch-mock';
|
||||||
import { supersetTheme, ThemeProvider } from '@superset-ui/style';
|
import { supersetTheme, ThemeProvider } from '@superset-ui/style';
|
||||||
|
|
||||||
|
import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
|
||||||
import DashboardList from 'src/views/CRUD/dashboard/DashboardList';
|
import DashboardList from 'src/views/CRUD/dashboard/DashboardList';
|
||||||
import ListView from 'src/components/ListView';
|
import ListView from 'src/components/ListView';
|
||||||
import PropertiesModal from 'src/dashboard/components/PropertiesModal';
|
|
||||||
import ListViewCard from 'src/components/ListViewCard';
|
import ListViewCard from 'src/components/ListViewCard';
|
||||||
|
import PropertiesModal from 'src/dashboard/components/PropertiesModal';
|
||||||
|
|
||||||
// store needed for withToasts(DashboardTable)
|
// store needed for withToasts(DashboardTable)
|
||||||
const mockStore = configureStore([thunk]);
|
const mockStore = configureStore([thunk]);
|
||||||
@ -50,7 +51,7 @@ const mockDashboards = [...new Array(3)].map((_, i) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
fetchMock.get(dashboardsInfoEndpoint, {
|
fetchMock.get(dashboardsInfoEndpoint, {
|
||||||
permissions: ['can_list', 'can_edit'],
|
permissions: ['can_list', 'can_edit', 'can_delete'],
|
||||||
});
|
});
|
||||||
fetchMock.get(dashboardOwnersEndpoint, {
|
fetchMock.get(dashboardOwnersEndpoint, {
|
||||||
result: [],
|
result: [],
|
||||||
@ -104,4 +105,19 @@ describe('DashboardList', () => {
|
|||||||
wrapper.find('[data-test="pencil"]').first().simulate('click');
|
wrapper.find('[data-test="pencil"]').first().simulate('click');
|
||||||
expect(wrapper.find(PropertiesModal)).toExist();
|
expect(wrapper.find(PropertiesModal)).toExist();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('card view edits', () => {
|
||||||
|
wrapper.find('[data-test="pencil"]').last().simulate('click');
|
||||||
|
expect(wrapper.find(PropertiesModal)).toExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('delete', () => {
|
||||||
|
wrapper.find('[data-test="trash"]').first().simulate('click');
|
||||||
|
expect(wrapper.find(ConfirmStatusChange)).toExist();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('card view delete', () => {
|
||||||
|
wrapper.find('[data-test="trash"]').last().simulate('click');
|
||||||
|
expect(wrapper.find(ConfirmStatusChange)).toExist();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
114
superset-frontend/src/components/ListView/CardSortSelect.tsx
Normal file
114
superset-frontend/src/components/ListView/CardSortSelect.tsx
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* 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 React, { useState } from 'react';
|
||||||
|
import { styled, withTheme, SupersetThemeProps } from '@superset-ui/style';
|
||||||
|
import { PartialThemeConfig, Select } from 'src/components/Select';
|
||||||
|
import { CardSortSelectOption, FetchDataConfig, SortColumn } from './types';
|
||||||
|
import { filterSelectStyles } from './utils';
|
||||||
|
|
||||||
|
const SortTitle = styled.label`
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 27px;
|
||||||
|
margin: 0 0.4em 0 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SortContainer = styled.div`
|
||||||
|
display: inline-flex;
|
||||||
|
float: right;
|
||||||
|
font-size: ${({ theme }) => theme.typography.sizes.s}px;
|
||||||
|
padding: 24px 24px 0 0;
|
||||||
|
position: relative;
|
||||||
|
top: 8px;
|
||||||
|
`;
|
||||||
|
interface CardViewSelectSortProps {
|
||||||
|
onChange: (conf: FetchDataConfig) => any;
|
||||||
|
options: Array<CardSortSelectOption>;
|
||||||
|
initialSort?: SortColumn[];
|
||||||
|
pageIndex: number;
|
||||||
|
pageSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StyledSelectProps {
|
||||||
|
onChange: (value: CardSortSelectOption) => void;
|
||||||
|
options: CardSortSelectOption[];
|
||||||
|
selectStyles: any;
|
||||||
|
theme: SupersetThemeProps['theme'];
|
||||||
|
value: CardSortSelectOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
function StyledSelect({
|
||||||
|
onChange,
|
||||||
|
options,
|
||||||
|
selectStyles,
|
||||||
|
theme,
|
||||||
|
value,
|
||||||
|
}: StyledSelectProps) {
|
||||||
|
const filterSelectTheme: PartialThemeConfig = {
|
||||||
|
spacing: {
|
||||||
|
baseUnit: 1,
|
||||||
|
fontSize: theme.typography.sizes.s,
|
||||||
|
minWidth: '5em',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
data-test="card-sort-select"
|
||||||
|
clearable={false}
|
||||||
|
onChange={onChange}
|
||||||
|
options={options}
|
||||||
|
stylesConfig={selectStyles}
|
||||||
|
themeConfig={filterSelectTheme}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const StyledCardSortSelect = withTheme(StyledSelect);
|
||||||
|
|
||||||
|
export const CardSortSelect = ({
|
||||||
|
initialSort,
|
||||||
|
onChange,
|
||||||
|
options,
|
||||||
|
pageIndex,
|
||||||
|
pageSize,
|
||||||
|
}: CardViewSelectSortProps) => {
|
||||||
|
const defaultSort =
|
||||||
|
initialSort && options.find(({ id }) => id === initialSort[0].id);
|
||||||
|
const [selectedOption, setSelectedOption] = useState<CardSortSelectOption>(
|
||||||
|
defaultSort || options[0],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleOnChange = (selected: CardSortSelectOption) => {
|
||||||
|
setSelectedOption(selected);
|
||||||
|
const sortBy = [{ id: selected.id, desc: selected.desc }];
|
||||||
|
onChange({ pageIndex, pageSize, sortBy, filters: [] });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SortContainer>
|
||||||
|
<SortTitle>Sort:</SortTitle>
|
||||||
|
<StyledCardSortSelect
|
||||||
|
onChange={(value: CardSortSelectOption) => handleOnChange(value)}
|
||||||
|
options={options}
|
||||||
|
selectStyles={filterSelectStyles}
|
||||||
|
value={selectedOption}
|
||||||
|
/>
|
||||||
|
</SortContainer>
|
||||||
|
);
|
||||||
|
};
|
@ -23,29 +23,29 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
PaginatedSelect,
|
PaginatedSelect,
|
||||||
PartialThemeConfig,
|
PartialThemeConfig,
|
||||||
PartialStylesConfig,
|
|
||||||
} from 'src/components/Select';
|
} from 'src/components/Select';
|
||||||
|
|
||||||
import SearchInput from 'src/components/SearchInput';
|
import SearchInput from 'src/components/SearchInput';
|
||||||
import {
|
import {
|
||||||
Filter,
|
Filter,
|
||||||
Filters,
|
|
||||||
FilterValue,
|
FilterValue,
|
||||||
|
Filters,
|
||||||
InternalFilter,
|
InternalFilter,
|
||||||
SelectOption,
|
SelectOption,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
import { filterSelectStyles } from './utils';
|
||||||
|
|
||||||
interface BaseFilter {
|
interface BaseFilter {
|
||||||
Header: string;
|
Header: string;
|
||||||
initialValue: any;
|
initialValue: any;
|
||||||
}
|
}
|
||||||
interface SelectFilterProps extends BaseFilter {
|
interface SelectFilterProps extends BaseFilter {
|
||||||
name?: string;
|
|
||||||
onSelect: (selected: any) => any;
|
|
||||||
selects: Filter['selects'];
|
|
||||||
emptyLabel?: string;
|
emptyLabel?: string;
|
||||||
fetchSelects?: Filter['fetchSelects'];
|
fetchSelects?: Filter['fetchSelects'];
|
||||||
|
name?: string;
|
||||||
|
onSelect: (selected: any) => any;
|
||||||
paginate?: boolean;
|
paginate?: boolean;
|
||||||
|
selects: Filter['selects'];
|
||||||
theme: SupersetThemeProps['theme'];
|
theme: SupersetThemeProps['theme'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,40 +61,23 @@ const FilterTitle = styled.label`
|
|||||||
margin: 0 0.4em 0 0;
|
margin: 0 0.4em 0 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const filterSelectStyles: PartialStylesConfig = {
|
|
||||||
container: (provider, { getValue }) => ({
|
|
||||||
...provider,
|
|
||||||
// dynamic width based on label string length
|
|
||||||
minWidth: `${Math.min(
|
|
||||||
12,
|
|
||||||
Math.max(5, 3 + getValue()[0].label.length / 2),
|
|
||||||
)}em`,
|
|
||||||
}),
|
|
||||||
control: provider => ({
|
|
||||||
...provider,
|
|
||||||
borderWidth: 0,
|
|
||||||
boxShadow: 'none',
|
|
||||||
cursor: 'pointer',
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const CLEAR_SELECT_FILTER_VALUE = 'CLEAR_SELECT_FILTER_VALUE';
|
const CLEAR_SELECT_FILTER_VALUE = 'CLEAR_SELECT_FILTER_VALUE';
|
||||||
|
|
||||||
function SelectFilter({
|
function SelectFilter({
|
||||||
Header,
|
Header,
|
||||||
selects = [],
|
|
||||||
emptyLabel = 'None',
|
emptyLabel = 'None',
|
||||||
|
fetchSelects,
|
||||||
initialValue,
|
initialValue,
|
||||||
onSelect,
|
onSelect,
|
||||||
fetchSelects,
|
|
||||||
paginate = false,
|
paginate = false,
|
||||||
|
selects = [],
|
||||||
theme,
|
theme,
|
||||||
}: SelectFilterProps) {
|
}: SelectFilterProps) {
|
||||||
const filterSelectTheme: PartialThemeConfig = {
|
const filterSelectTheme: PartialThemeConfig = {
|
||||||
spacing: {
|
spacing: {
|
||||||
baseUnit: 2,
|
baseUnit: 2,
|
||||||
minWidth: '5em',
|
|
||||||
fontSize: theme.typography.sizes.s,
|
fontSize: theme.typography.sizes.s,
|
||||||
|
minWidth: '5em',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -235,12 +218,12 @@ function UIFilters({
|
|||||||
(
|
(
|
||||||
{
|
{
|
||||||
Header,
|
Header,
|
||||||
|
fetchSelects,
|
||||||
id,
|
id,
|
||||||
input,
|
input,
|
||||||
|
paginate,
|
||||||
selects,
|
selects,
|
||||||
unfilteredLabel,
|
unfilteredLabel,
|
||||||
fetchSelects,
|
|
||||||
paginate,
|
|
||||||
},
|
},
|
||||||
index,
|
index,
|
||||||
) => {
|
) => {
|
||||||
@ -249,24 +232,24 @@ function UIFilters({
|
|||||||
if (input === 'select') {
|
if (input === 'select') {
|
||||||
return (
|
return (
|
||||||
<StyledSelectFilter
|
<StyledSelectFilter
|
||||||
|
Header={Header}
|
||||||
|
emptyLabel={unfilteredLabel}
|
||||||
|
fetchSelects={fetchSelects}
|
||||||
|
initialValue={initialValue}
|
||||||
key={id}
|
key={id}
|
||||||
name={id}
|
name={id}
|
||||||
Header={Header}
|
|
||||||
selects={selects}
|
|
||||||
emptyLabel={unfilteredLabel}
|
|
||||||
initialValue={initialValue}
|
|
||||||
fetchSelects={fetchSelects}
|
|
||||||
paginate={paginate}
|
|
||||||
onSelect={(value: any) => updateFilterValue(index, value)}
|
onSelect={(value: any) => updateFilterValue(index, value)}
|
||||||
|
paginate={paginate}
|
||||||
|
selects={selects}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (input === 'search') {
|
if (input === 'search') {
|
||||||
return (
|
return (
|
||||||
<SearchFilter
|
<SearchFilter
|
||||||
key={id}
|
|
||||||
Header={Header}
|
Header={Header}
|
||||||
initialValue={initialValue}
|
initialValue={initialValue}
|
||||||
|
key={id}
|
||||||
onSubmit={(value: string) => updateFilterValue(index, value)}
|
onSubmit={(value: string) => updateFilterValue(index, value)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -28,7 +28,13 @@ import TableCollection from './TableCollection';
|
|||||||
import CardCollection from './CardCollection';
|
import CardCollection from './CardCollection';
|
||||||
import Pagination from './Pagination';
|
import Pagination from './Pagination';
|
||||||
import FilterControls from './Filters';
|
import FilterControls from './Filters';
|
||||||
import { FetchDataConfig, Filters, SortColumn } from './types';
|
import { CardSortSelect } from './CardSortSelect';
|
||||||
|
import {
|
||||||
|
FetchDataConfig,
|
||||||
|
Filters,
|
||||||
|
SortColumn,
|
||||||
|
CardSortSelectOption,
|
||||||
|
} from './types';
|
||||||
import { ListViewError, useListViewState } from './utils';
|
import { ListViewError, useListViewState } from './utils';
|
||||||
|
|
||||||
const ListViewStyles = styled.div`
|
const ListViewStyles = styled.div`
|
||||||
@ -188,6 +194,7 @@ export interface ListViewProps<T = any> {
|
|||||||
disableBulkSelect?: () => void;
|
disableBulkSelect?: () => void;
|
||||||
renderBulkSelectCopy?: (selects: any[]) => React.ReactNode;
|
renderBulkSelectCopy?: (selects: any[]) => React.ReactNode;
|
||||||
renderCard?: (row: T) => React.ReactNode;
|
renderCard?: (row: T) => React.ReactNode;
|
||||||
|
cardSortSelectOptions?: Array<CardSortSelectOption>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListView: FunctionComponent<ListViewProps> = ({
|
const ListView: FunctionComponent<ListViewProps> = ({
|
||||||
@ -205,6 +212,7 @@ const ListView: FunctionComponent<ListViewProps> = ({
|
|||||||
disableBulkSelect = () => {},
|
disableBulkSelect = () => {},
|
||||||
renderBulkSelectCopy = selected => t('%s Selected', selected.length),
|
renderBulkSelectCopy = selected => t('%s Selected', selected.length),
|
||||||
renderCard,
|
renderCard,
|
||||||
|
cardSortSelectOptions,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
getTableProps,
|
getTableProps,
|
||||||
@ -263,6 +271,15 @@ const ListView: FunctionComponent<ListViewProps> = ({
|
|||||||
updateFilterValue={applyFilterValue}
|
updateFilterValue={applyFilterValue}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{viewingMode === 'card' && cardSortSelectOptions && (
|
||||||
|
<CardSortSelect
|
||||||
|
initialSort={initialSort}
|
||||||
|
onChange={fetchData}
|
||||||
|
options={cardSortSelectOptions}
|
||||||
|
pageIndex={pageIndex}
|
||||||
|
pageSize={pageSize}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="body">
|
<div className="body">
|
||||||
{bulkSelectEnabled && (
|
{bulkSelectEnabled && (
|
||||||
|
@ -28,6 +28,13 @@ export interface SelectOption {
|
|||||||
value: any;
|
value: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CardSortSelectOption {
|
||||||
|
desc: boolean;
|
||||||
|
id: any;
|
||||||
|
label: string;
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Filter {
|
export interface Filter {
|
||||||
Header: string;
|
Header: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -34,7 +34,7 @@ import {
|
|||||||
} from 'use-query-params';
|
} from 'use-query-params';
|
||||||
|
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
|
import { PartialStylesConfig } from 'src/components/Select';
|
||||||
import {
|
import {
|
||||||
FetchDataConfig,
|
FetchDataConfig,
|
||||||
Filter,
|
Filter,
|
||||||
@ -255,3 +255,20 @@ export function useListViewState({
|
|||||||
applyFilterValue,
|
applyFilterValue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const filterSelectStyles: PartialStylesConfig = {
|
||||||
|
container: (provider, { getValue }) => ({
|
||||||
|
...provider,
|
||||||
|
// dynamic width based on label string length
|
||||||
|
minWidth: `${Math.min(
|
||||||
|
12,
|
||||||
|
Math.max(5, 3 + getValue()[0].label.length / 2),
|
||||||
|
)}em`,
|
||||||
|
}),
|
||||||
|
control: provider => ({
|
||||||
|
...provider,
|
||||||
|
borderWidth: 0,
|
||||||
|
boxShadow: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
@ -342,6 +342,27 @@ class ChartList extends React.PureComponent<Props, State> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
sortTypes = [
|
||||||
|
{
|
||||||
|
desc: false,
|
||||||
|
id: 'slice_name',
|
||||||
|
label: 'Alphabetical',
|
||||||
|
value: 'alphabetical',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: true,
|
||||||
|
id: 'changed_on_delta_humanized',
|
||||||
|
label: 'Recently Modified',
|
||||||
|
value: 'recently_modified',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: false,
|
||||||
|
id: 'changed_on_delta_humanized',
|
||||||
|
label: 'Least Recently Modified',
|
||||||
|
value: 'least_recently_modified',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
hasPerm = (perm: string) => {
|
hasPerm = (perm: string) => {
|
||||||
if (!this.state.permissions.length) {
|
if (!this.state.permissions.length) {
|
||||||
return false;
|
return false;
|
||||||
@ -592,6 +613,7 @@ class ChartList extends React.PureComponent<Props, State> {
|
|||||||
<ListView
|
<ListView
|
||||||
bulkActions={bulkActions}
|
bulkActions={bulkActions}
|
||||||
bulkSelectEnabled={bulkSelectEnabled}
|
bulkSelectEnabled={bulkSelectEnabled}
|
||||||
|
cardSortSelectOptions={this.sortTypes}
|
||||||
className="chart-list-view"
|
className="chart-list-view"
|
||||||
columns={this.columns}
|
columns={this.columns}
|
||||||
count={chartCount}
|
count={chartCount}
|
||||||
|
@ -266,6 +266,27 @@ class DashboardList extends React.PureComponent<Props, State> {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
sortTypes = [
|
||||||
|
{
|
||||||
|
desc: false,
|
||||||
|
id: 'dashboard_title',
|
||||||
|
label: 'Alphabetical',
|
||||||
|
value: 'alphabetical',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: true,
|
||||||
|
id: 'changed_on_delta_humanized',
|
||||||
|
label: 'Recently Modified',
|
||||||
|
value: 'recently_modified',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: false,
|
||||||
|
id: 'changed_on_delta_humanized',
|
||||||
|
label: 'Least Recently Modified',
|
||||||
|
value: 'least_recently_modified',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
hasPerm = (perm: string) => {
|
hasPerm = (perm: string) => {
|
||||||
if (!this.state.permissions.length) {
|
if (!this.state.permissions.length) {
|
||||||
return false;
|
return false;
|
||||||
@ -601,6 +622,7 @@ class DashboardList extends React.PureComponent<Props, State> {
|
|||||||
<ListView
|
<ListView
|
||||||
bulkActions={bulkActions}
|
bulkActions={bulkActions}
|
||||||
bulkSelectEnabled={bulkSelectEnabled}
|
bulkSelectEnabled={bulkSelectEnabled}
|
||||||
|
cardSortSelectOptions={this.sortTypes}
|
||||||
className="dashboard-list-view"
|
className="dashboard-list-view"
|
||||||
columns={this.columns}
|
columns={this.columns}
|
||||||
count={dashboardCount}
|
count={dashboardCount}
|
||||||
|
Loading…
Reference in New Issue
Block a user