From b354a4a6f754e64de6a3af9a143066908022eba8 Mon Sep 17 00:00:00 2001 From: Lily Kuang Date: Thu, 13 Aug 2020 16:46:01 -0700 Subject: [PATCH 1/3] add card view sort by --- .../components/ListView/ListView_spec.jsx | 53 +++++++++- .../views/CRUD/chart/ChartList_spec.jsx | 8 +- .../CRUD/dashboard/DashboardList_spec.jsx | 20 +++- .../components/ListView/CardSortSelect.tsx | 96 +++++++++++++++++++ .../src/components/ListView/Filters.tsx | 49 ++++------ .../src/components/ListView/ListView.tsx | 19 +++- .../src/components/ListView/types.ts | 7 ++ .../src/components/ListView/utils.ts | 19 +++- .../src/views/CRUD/chart/ChartList.tsx | 22 +++++ .../views/CRUD/dashboard/DashboardList.tsx | 22 +++++ 10 files changed, 275 insertions(+), 40 deletions(-) create mode 100644 superset-frontend/src/components/ListView/CardSortSelect.tsx diff --git a/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx b/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx index 8625f1d06f249..fee729e117d28 100644 --- a/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx +++ b/superset-frontend/spec/javascripts/components/ListView/ListView_spec.jsx @@ -22,14 +22,17 @@ import { act } from 'react-dom/test-utils'; import { QueryParamProvider } from 'use-query-params'; 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 ListViewFilters from 'src/components/ListView/Filters'; import ListViewPagination from 'src/components/ListView/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 IndeterminateCheckbox from 'src/components/IndeterminateCheckbox'; function makeMockLocation(query) { const queryStr = encodeURIComponent(query); @@ -100,6 +103,14 @@ const mockedProps = { onSelect: jest.fn(), }, ], + cardSortSelectOptions: [ + { + desc: false, + id: 'something', + label: 'Alphabetical', + value: 'alphabetical', + }, + ], }; 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', () => { expect.assertions(1); 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(); + }); }); diff --git a/superset-frontend/spec/javascripts/views/CRUD/chart/ChartList_spec.jsx b/superset-frontend/spec/javascripts/views/CRUD/chart/ChartList_spec.jsx index 2083b4a6bec14..213ac39a940e8 100644 --- a/superset-frontend/spec/javascripts/views/CRUD/chart/ChartList_spec.jsx +++ b/superset-frontend/spec/javascripts/views/CRUD/chart/ChartList_spec.jsx @@ -24,6 +24,7 @@ import fetchMock from 'fetch-mock'; import { supersetTheme, ThemeProvider } from '@superset-ui/style'; import ChartList from 'src/views/CRUD/chart/ChartList'; +import ConfirmStatusChange from 'src/components/ConfirmStatusChange'; import ListView from 'src/components/ListView'; import PropertiesModal from 'src/explore/components/PropertiesModal'; import ListViewCard from 'src/components/ListViewCard'; @@ -49,7 +50,7 @@ const mockCharts = [...new Array(3)].map((_, i) => ({ })); fetchMock.get(chartsInfoEndpoint, { - permissions: ['can_list', 'can_edit'], + permissions: ['can_list', 'can_edit', 'can_delete'], }); fetchMock.get(chartssOwnersEndpoint, { result: [], @@ -113,4 +114,9 @@ describe('ChartList', () => { wrapper.find('[data-test="pencil"]').first().simulate('click'); expect(wrapper.find(PropertiesModal)).toExist(); }); + + it('delete', () => { + wrapper.find('[data-test="trash"]').first().simulate('click'); + expect(wrapper.find(ConfirmStatusChange)).toExist(); + }); }); diff --git a/superset-frontend/spec/javascripts/views/CRUD/dashboard/DashboardList_spec.jsx b/superset-frontend/spec/javascripts/views/CRUD/dashboard/DashboardList_spec.jsx index eef4ca03eb45b..37d5ca267284e 100644 --- a/superset-frontend/spec/javascripts/views/CRUD/dashboard/DashboardList_spec.jsx +++ b/superset-frontend/spec/javascripts/views/CRUD/dashboard/DashboardList_spec.jsx @@ -23,10 +23,11 @@ import configureStore from 'redux-mock-store'; import fetchMock from 'fetch-mock'; import { supersetTheme, ThemeProvider } from '@superset-ui/style'; +import ConfirmStatusChange from 'src/components/ConfirmStatusChange'; import DashboardList from 'src/views/CRUD/dashboard/DashboardList'; import ListView from 'src/components/ListView'; -import PropertiesModal from 'src/dashboard/components/PropertiesModal'; import ListViewCard from 'src/components/ListViewCard'; +import PropertiesModal from 'src/dashboard/components/PropertiesModal'; // store needed for withToasts(DashboardTable) const mockStore = configureStore([thunk]); @@ -50,7 +51,7 @@ const mockDashboards = [...new Array(3)].map((_, i) => ({ })); fetchMock.get(dashboardsInfoEndpoint, { - permissions: ['can_list', 'can_edit'], + permissions: ['can_list', 'can_edit', 'can_delete'], }); fetchMock.get(dashboardOwnersEndpoint, { result: [], @@ -104,4 +105,19 @@ describe('DashboardList', () => { wrapper.find('[data-test="pencil"]').first().simulate('click'); 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(); + }); }); diff --git a/superset-frontend/src/components/ListView/CardSortSelect.tsx b/superset-frontend/src/components/ListView/CardSortSelect.tsx new file mode 100644 index 0000000000000..b4efe1e771b6d --- /dev/null +++ b/superset-frontend/src/components/ListView/CardSortSelect.tsx @@ -0,0 +1,96 @@ +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; + 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 ( +