diff --git a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
index a58df6bcf..d71864e63 100644
--- a/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
+++ b/superset-frontend/spec/javascripts/views/CRUD/welcome/Welcome_spec.tsx
@@ -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(
+
+
+ ,
+ );
+ 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();
+ });
+});
diff --git a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
index 457f46b70..c8ce8523b 100644
--- a/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
+++ b/superset-frontend/src/views/CRUD/chart/ChartCard.tsx
@@ -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({
> : null}
url={bulkSelectEnabled ? undefined : chart.url}
imgURL={chart.thumbnail_url || ''}
imgFallbackURL="/static/assets/images/chart-card-fallback.svg"
diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
index 1431eb81b..ea7c070d7 100644
--- a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
+++ b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx
@@ -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={
}
+ cover={!featureFlag || !showThumbnails ? <>> : null}
url={bulkSelectEnabled ? undefined : dashboard.url}
imgURL={dashboard.thumbnail_url}
imgFallbackURL="/static/assets/images/dashboard-card-fallback.svg"
diff --git a/superset-frontend/src/views/CRUD/types.ts b/superset-frontend/src/views/CRUD/types.ts
index 4de3055c1..6d369d864 100644
--- a/superset-frontend/src/views/CRUD/types.ts
+++ b/superset-frontend/src/views/CRUD/types.ts
@@ -35,6 +35,8 @@ export interface DashboardTableProps {
search: string;
user?: User;
mine: Array;
+ showThumbnails?: boolean;
+ featureFlag?: boolean;
}
export interface Dashboard {
diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
index 5eb0b294d..addfaafec 100644
--- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx
@@ -44,6 +44,8 @@ interface ChartTableProps {
chartFilter?: string;
user?: User;
mine: Array;
+ 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}
diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
index 27a41d4dc..64d1f8019 100644
--- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx
@@ -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}
diff --git a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
index 47a55fa66..e8ec84acd 100644
--- a/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/SavedQueries.tsx
@@ -58,6 +58,8 @@ interface SavedQueriesProps {
addDangerToast: (arg0: string) => void;
addSuccessToast: (arg0: string) => void;
mine: Array;
+ 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 ? (
- ) : (
+ ) : showThumbnails && !q?.sql?.length ? (
false
+ ) : (
+ <>>
)
}
actions={
diff --git a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
index 0d1f3ba66..bb9da4a6a 100644
--- a/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
+++ b/superset-frontend/src/views/CRUD/welcome/Welcome.tsx
@@ -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(null);
const [chartData, setChartData] = useState | null>(null);
const [queryData, setQueryData] = useState | 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 (
+
+ Home
+ {isFeatureEnabled(FeatureFlag.THUMBNAILS) ? (
+
+
+ Thumbnails
+
+ ) : null}
+
{activityData && (activityData.Viewed || activityData.Examples) ? (
@@ -178,21 +224,36 @@ function Welcome({ user, addDangerToast }: WelcomeProps) {
{!dashboardData ? (
) : (
-
+
)}
{!queryData ? (
) : (
-
+
)}
{!chartData ? (
) : (
-
+
)}