feat(homescreen and cards): Toggle thumbnails off or on and feature flag (#13683)

This commit is contained in:
Phillip Kelley-Dotson 2021-03-25 10:21:04 -07:00 committed by GitHub
parent 1d5c58d005
commit 3e4c3bddb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 150 additions and 18 deletions

View File

@ -23,8 +23,10 @@ import thunk from 'redux-thunk';
import fetchMock from 'fetch-mock';
import { act } from 'react-dom/test-utils';
import configureStore from 'redux-mock-store';
import * as featureFlags from 'src/featureFlags';
import Welcome from 'src/views/CRUD/welcome/Welcome';
import { ReactWrapper } from 'enzyme';
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
const mockStore = configureStore([thunk]);
const store = mockStore({});
@ -92,19 +94,19 @@ fetchMock.get(savedQueryInfoEndpoint, {
permissions: [],
});
describe('Welcome', () => {
const mockedProps = {
user: {
username: 'alpha',
firstName: 'alpha',
lastName: 'alpha',
createdOn: '2016-11-11T12:34:17',
userId: 5,
email: 'alpha@alpha.com',
isActive: true,
},
};
const mockedProps = {
user: {
username: 'alpha',
firstName: 'alpha',
lastName: 'alpha',
createdOn: '2016-11-11T12:34:17',
userId: 5,
email: 'alpha@alpha.com',
isActive: true,
},
};
describe('Welcome', () => {
let wrapper: ReactWrapper;
beforeAll(async () => {
@ -136,3 +138,44 @@ describe('Welcome', () => {
expect(dashboardCall).toHaveLength(1);
});
});
async function mountAndWait(props = mockedProps) {
const wrapper = mount(
<Provider store={store}>
<Welcome {...props} />
</Provider>,
);
await waitForComponentToPaint(wrapper);
return wrapper;
}
describe('Welcome page with toggle switch', () => {
let wrapper: ReactWrapper;
let isFeatureEnabledMock: any;
beforeAll(async () => {
isFeatureEnabledMock = jest
.spyOn(featureFlags, 'isFeatureEnabled')
.mockReturnValue(true);
await act(async () => {
wrapper = await mountAndWait();
});
});
afterAll(() => {
isFeatureEnabledMock.restore();
});
it('shows a toggle button when feature flags is turned on', async () => {
await waitForComponentToPaint(wrapper);
expect(wrapper.find('Switch')).toExist();
});
it('does not show thumbnails when switch is off', async () => {
act(() => {
// @ts-ignore
wrapper.find('button[role="switch"]').props().onClick();
});
await waitForComponentToPaint(wrapper);
expect(wrapper.find('ImageLoader')).not.toExist();
});
});

View File

@ -44,6 +44,8 @@ interface ChartCardProps {
favoriteStatus: boolean;
chartFilter?: string;
userId?: number;
showThumbnails?: boolean;
featureFlag?: boolean;
}
export default function ChartCard({
@ -55,10 +57,12 @@ export default function ChartCard({
addSuccessToast,
refreshData,
loading,
showThumbnails,
saveFavoriteStatus,
favoriteStatus,
chartFilter,
userId,
featureFlag,
}: ChartCardProps) {
const canEdit = hasPerm('can_write');
const canDelete = hasPerm('can_write');
@ -138,6 +142,7 @@ export default function ChartCard({
<ListViewCard
loading={loading}
title={chart.slice_name}
cover={!featureFlag || !showThumbnails ? <></> : null}
url={bulkSelectEnabled ? undefined : chart.url}
imgURL={chart.thumbnail_url || ''}
imgFallbackURL="/static/assets/images/chart-card-fallback.svg"

View File

@ -46,6 +46,8 @@ interface DashboardCardProps {
favoriteStatus: boolean;
dashboardFilter?: string;
userId?: number;
showThumbnails?: boolean;
featureFlag?: boolean;
}
function DashboardCard({
@ -60,6 +62,8 @@ function DashboardCard({
openDashboardEditModal,
favoriteStatus,
saveFavoriteStatus,
showThumbnails,
featureFlag,
}: DashboardCardProps) {
const canEdit = hasPerm('can_write');
const canDelete = hasPerm('can_write');
@ -146,6 +150,7 @@ function DashboardCard({
titleRight={
<Label>{dashboard.published ? t('published') : t('draft')}</Label>
}
cover={!featureFlag || !showThumbnails ? <></> : null}
url={bulkSelectEnabled ? undefined : dashboard.url}
imgURL={dashboard.thumbnail_url}
imgFallbackURL="/static/assets/images/dashboard-card-fallback.svg"

View File

@ -35,6 +35,8 @@ export interface DashboardTableProps {
search: string;
user?: User;
mine: Array<Dashboard>;
showThumbnails?: boolean;
featureFlag?: boolean;
}
export interface Dashboard {

View File

@ -44,6 +44,8 @@ interface ChartTableProps {
chartFilter?: string;
user?: User;
mine: Array<any>;
showThumbnails: boolean;
featureFlag: boolean;
}
function ChartTable({
@ -51,6 +53,8 @@ function ChartTable({
addDangerToast,
addSuccessToast,
mine,
showThumbnails,
featureFlag,
}: ChartTableProps) {
const history = useHistory();
const {
@ -180,7 +184,9 @@ function ChartTable({
chart={e}
userId={user?.userId}
hasPerm={hasPerm}
showThumbnails={showThumbnails}
bulkSelectEnabled={bulkSelectEnabled}
featureFlag={featureFlag}
refreshData={refreshData}
addDangerToast={addDangerToast}
addSuccessToast={addSuccessToast}

View File

@ -42,6 +42,8 @@ function DashboardTable({
addDangerToast,
addSuccessToast,
mine,
showThumbnails,
featureFlag,
}: DashboardTableProps) {
const history = useHistory();
const {
@ -191,6 +193,8 @@ function DashboardTable({
dashboard={e}
hasPerm={hasPerm}
bulkSelectEnabled={false}
featureFlag={featureFlag}
showThumbnails={showThumbnails}
dashboardFilter={dashboardFilter}
refreshData={refreshData}
addDangerToast={addDangerToast}

View File

@ -58,6 +58,8 @@ interface SavedQueriesProps {
addDangerToast: (arg0: string) => void;
addSuccessToast: (arg0: string) => void;
mine: Array<Query>;
showThumbnails: boolean;
featureFlag: boolean;
}
export const CardStyles = styled.div`
@ -110,6 +112,8 @@ const SavedQueries = ({
addDangerToast,
addSuccessToast,
mine,
showThumbnails,
featureFlag,
}: SavedQueriesProps) => {
const {
state: { loading, resourceCollection: queries },
@ -307,7 +311,7 @@ const SavedQueries = ({
imgFallbackURL="/static/assets/images/empty-query.svg"
description={t('Last run %s', q.changed_on_delta_humanized)}
cover={
q?.sql?.length ? (
q?.sql?.length && showThumbnails && featureFlag ? (
<QueryContainer>
<SyntaxHighlighter
language="sql"
@ -328,8 +332,10 @@ const SavedQueries = ({
{shortenSQL(q.sql, 25)}
</SyntaxHighlighter>
</QueryContainer>
) : (
) : showThumbnails && !q?.sql?.length ? (
false
) : (
<></>
)
}
actions={

View File

@ -21,6 +21,10 @@ import { styled, t } from '@superset-ui/core';
import Collapse from 'src/common/components/Collapse';
import { User } from 'src/types/bootstrapTypes';
import { reject } from 'lodash';
import {
getFromLocalStorage,
setInLocalStorage,
} from 'src/utils/localStorageHelpers';
import withToasts from 'src/messageToasts/enhancers/withToasts';
import Loading from 'src/components/Loading';
import {
@ -29,6 +33,8 @@ import {
mq,
getUserOwnedObjects,
} from 'src/views/CRUD/utils';
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
import { Switch } from 'src/common/components';
import ActivityTable from './ActivityTable';
import ChartTable from './ChartTable';
@ -83,9 +89,31 @@ const WelcomeContainer = styled.div`
}
`;
const WelcomeNav = styled.div`
height: 50px;
background-color: white;
margin-top: ${({ theme }) => theme.gridUnit * -4 - 1}px;
.navbar-brand {
margin-left: ${({ theme }) => theme.gridUnit * 2}px;
font-weight: ${({ theme }) => theme.typography.weights.bold};
}
.switch {
float: right;
margin: ${({ theme }) => theme.gridUnit * 5}px;
display: flex;
flex-direction: row;
span {
display: block;
margin: ${({ theme }) => theme.gridUnit * 1}px;
line-height: 1;
}
}
`;
function Welcome({ user, addDangerToast }: WelcomeProps) {
const recent = `/superset/recent_activity/${user.userId}/?limit=6`;
const [activeChild, setActiveChild] = useState('Viewed');
const [checked, setChecked] = useState(true);
const [activityData, setActivityData] = useState<ActivityData | null>(null);
const [chartData, setChartData] = useState<Array<object> | null>(null);
const [queryData, setQueryData] = useState<Array<object> | null>(null);
@ -93,7 +121,12 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
null,
);
const userid = user.userId;
const id = userid.toString();
useEffect(() => {
const userKey = getFromLocalStorage(id, null);
if (userKey && !userKey.thumbnails) setChecked(false);
getRecentAcitivtyObjs(user.userId, recent, addDangerToast)
.then(res => {
const data: ActivityData | null = {};
@ -117,7 +150,6 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
);
// Sets other activity data in parallel with recents api call
const id = user.userId;
getUserOwnedObjects(id, 'dashboard')
.then(r => {
setDashboardData(r);
@ -148,6 +180,11 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
});
}, []);
const handleToggle = () => {
setChecked(!checked);
setInLocalStorage(id, { thumbnails: !checked });
};
useEffect(() => {
setActivityData(activityData => ({
...activityData,
@ -161,6 +198,15 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
return (
<WelcomeContainer>
<WelcomeNav>
<span className="navbar-brand">Home</span>
{isFeatureEnabled(FeatureFlag.THUMBNAILS) ? (
<div className="switch">
<Switch checked={checked} onChange={handleToggle} />
<span>Thumbnails</span>
</div>
) : null}
</WelcomeNav>
<Collapse defaultActiveKey={['1', '2', '3', '4']} ghost bigger>
<Collapse.Panel header={t('Recents')} key="1">
{activityData && (activityData.Viewed || activityData.Examples) ? (
@ -178,21 +224,36 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
{!dashboardData ? (
<Loading position="inline" />
) : (
<DashboardTable user={user} mine={dashboardData} />
<DashboardTable
user={user}
mine={dashboardData}
showThumbnails={checked}
featureFlag={isFeatureEnabled(FeatureFlag.THUMBNAILS)}
/>
)}
</Collapse.Panel>
<Collapse.Panel header={t('Saved queries')} key="3">
{!queryData ? (
<Loading position="inline" />
) : (
<SavedQueries user={user} mine={queryData} />
<SavedQueries
showThumbnails={checked}
user={user}
mine={queryData}
featureFlag={isFeatureEnabled(FeatureFlag.THUMBNAILS)}
/>
)}
</Collapse.Panel>
<Collapse.Panel header={t('Charts')} key="4">
{!chartData ? (
<Loading position="inline" />
) : (
<ChartTable user={user} mine={chartData} />
<ChartTable
showThumbnails={checked}
user={user}
mine={chartData}
featureFlag={isFeatureEnabled(FeatureFlag.THUMBNAILS)}
/>
)}
</Collapse.Panel>
</Collapse>