Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion playwright/editing-dashboard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const navigateToDashboardHub = async (page: Page) => {

const navigateToGenericDashboard = async (page: Page, dashboardName: string) => {
await navigateToDashboardHub(page);
await page.getByRole('link', { name: dashboardName }).click();
await page.getByRole('link', { name: dashboardName, exact: true }).click();
await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: 30000 });
};

Expand Down Expand Up @@ -123,6 +123,7 @@ test.describe('Set Dashboard as Homepage from Generic Page', () => {
await page.getByText(`'${nonDefaultName}' has been set as homepage`).waitFor({ state: 'visible', timeout: 10000 });

await navigateToDashboardHub(page);
await page.getByRole('link', { name: nonDefaultName, exact: true }).waitFor({ state: 'visible', timeout: 10000 });

expect(await hasHomeIcon(page, nonDefaultName)).toBe(true);
expect(await hasHomeIcon(page, defaultName)).toBe(false);
Expand Down
46 changes: 46 additions & 0 deletions playwright/widget-layout.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,50 @@ test.describe('Widget Layout - Add Widget from Drawer', () => {
const resetButton = page.getByRole('button', { name: 'Reset to default' });
await expect(resetButton).toBeVisible();
});

test('should not show the widget drawer by default on page load', async ({ page }) => {
await page.locator('#widget-layout-container .pf-v6-widget-grid-tile__title')
.first()
.waitFor({ state: 'visible', timeout: 10000 });

const drawerText = page.getByText('Add new and previously removed widgets');
await expect(drawerText).not.toBeVisible();
});
});

test.describe('Widget Layout - Empty Dashboard', () => {
test('should auto-open the widget drawer when dashboard has no widgets', async ({ page }) => {
await disableCookiePrompt(page);
await page.addInitScript(() => {
const originalFetch = window.fetch;
window.fetch = async (...args) => {
const url = typeof args[0] === 'string' ? args[0] : args[0] instanceof Request ? args[0].url : '';
if (
(url.includes('/api/widget-layout/v1/') && url.includes('dashboardType=')) ||
(url.includes('/api/chrome-service/v1/dashboard-templates') && url.includes('dashboard='))
) {
return new Response(JSON.stringify({
data: [{
id: 1,
default: true,
templateBase: { name: 'landing-landingPage', displayName: 'Landing Page' },
templateConfig: { sm: [], md: [], lg: [], xl: [] },
dashboardName: 'Test Dashboard',
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
userId: 'test-user',
}],
}), { status: 200, headers: { 'Content-Type': 'application/json' } });
}
return originalFetch(...args);
};
});

await page.goto('/');
await page.getByRole('button', { name: 'Add widgets' }).waitFor({ state: 'visible', timeout: 10000 });

const drawerText = page.getByText('Add new and previously removed widgets');
await expect(drawerText).toBeVisible({ timeout: 10000 });
});
});
48 changes: 25 additions & 23 deletions src/Components/DnDLayout/GridLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,21 @@ const sidebarBreakpoints = { xl: 1250, lg: 1100, md: 800, sm: 500 };
const documentationLink =
'https://docs.redhat.com/en/documentation/red_hat_hybrid_cloud_console/1-latest/html-single/getting_started_with_the_red_hat_hybrid_cloud_console/index#customizing-main-page_navigating-the-console';

const LayoutEmptyState = () => {
const setDrawerExpanded = useSetAtom(drawerExpandedAtom);

useEffect(() => {
setDrawerExpanded(true);
}, []);

return (
<PageSection hasBodyWrapper={false} className="empty-layout pf-v6-u-p-0">
<EmptyState headingLevel="h2" icon={PlusCircleIcon} titleText="No dashboard content" variant={EmptyStateVariant.lg} className="pf-v6-u-p-sm">
<EmptyStateBody>
You don&apos;t have any widgets on your dashboard. To populate your dashboard, drag <GripVerticalIcon /> items from the blue widget bank to
this dashboard body here.
</EmptyStateBody>
<EmptyStateActions>
<Button variant="link" icon={<ExternalLinkAltIcon />} iconPosition="end" component="a" href={documentationLink}>
Learn about your widget dashboard
</Button>
</EmptyStateActions>
</EmptyState>
</PageSection>
);
};
const LayoutEmptyState = () => (
<PageSection hasBodyWrapper={false} className="empty-layout pf-v6-u-p-0">
<EmptyState headingLevel="h2" icon={PlusCircleIcon} titleText="No dashboard content" variant={EmptyStateVariant.lg} className="pf-v6-u-p-sm">
<EmptyStateBody>
You don&apos;t have any widgets on your dashboard. To populate your dashboard, drag <GripVerticalIcon /> items from the blue widget bank to
this dashboard body here.
</EmptyStateBody>
<EmptyStateActions>
<Button variant="link" icon={<ExternalLinkAltIcon />} iconPosition="end" component="a" href={documentationLink}>
Learn about your widget dashboard
</Button>
</EmptyStateActions>
</EmptyState>
</PageSection>
);

const getResizeHandle = (resizeHandleAxis: string, ref: React.Ref<HTMLDivElement>) => (
<div ref={ref} className={`react-resizable-handle react-resizable-handle-${resizeHandleAxis}`}>
Expand Down Expand Up @@ -95,6 +87,10 @@ const GridLayout = ({ template, saveTemplate, isLoaded, isLayoutLocked = false,
const activeLayout = newTemplate[layoutVariant] || [];
setCurrentlyUsedWidgets(activeLayout.map((item) => item.widgetType));

if (activeLayout.length === 0) {
setDrawerExpanded(true);
}

await saveTemplate(newTemplate as LocalExtendedTemplateConfig);
};

Expand All @@ -108,6 +104,12 @@ const GridLayout = ({ template, saveTemplate, isLoaded, isLayoutLocked = false,

const activeLayout = patternFlyTemplate[layoutVariant] || [];

useEffect(() => {
if (isLoaded && activeLayout.length === 0) {
setDrawerExpanded(true);
}
}, [isLoaded]);

return (
<div id="widget-layout-container" style={{ position: 'relative' }} ref={layoutRef}>
{activeLayout.length === 0 && isLoaded && <LayoutEmptyState />}
Expand Down
19 changes: 17 additions & 2 deletions src/Modules/GenericDashboardPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Breadcrumb, BreadcrumbItem, PageSection } from '@patternfly/react-core';
import React, { useEffect, useRef } from 'react';
import GridLayout from '../Components/DnDLayout/GridLayout';
import { useAtomValue, useSetAtom } from 'jotai';
import { Provider, useAtomValue, useSetAtom } from 'jotai';
import { lockedLayoutAtom } from '../state/lockedLayoutAtom';
import { Link, useParams } from 'react-router-dom';
import useDashboardTemplate from '../hooks/useDashboardTemplate';
Expand All @@ -11,10 +11,12 @@
import { resolvedWidgetMappingAtom } from '../state/widgetMappingAtom';
import { notificationsAtom, useRemoveNotification } from '../state/notificationsAtom';
import Portal from '@redhat-cloud-services/frontend-components-notifications/Portal';
import { backendFlagAtom, store } from '../state/store';
import { useFlag } from '@unleash/proxy-client-react';

const GenericDashboardPage = () => {
const GenericDashboardPageInner = () => {
const { id } = useParams<{ id: string }>();
const isLayoutLocked = useAtomValue(lockedLayoutAtom);

Check warning on line 19 in src/Modules/GenericDashboardPage.tsx

View workflow job for this annotation

GitHub Actions / lint

'isLayoutLocked' is assigned a value but never used
const { template, saveTemplate, renameDashboard, isLoaded, dashboard } = useDashboardTemplate(Number(id));
const resolveWidgetMapping = useSetAtom(resolvedWidgetMappingAtom);
const { visibilityFunctions } = useChrome();
Expand All @@ -23,6 +25,13 @@
const notifications = useAtomValue(notificationsAtom);
const removeNotification = useRemoveNotification();

const setBackendFlag = useSetAtom(backendFlagAtom);
const isNewBackend = useFlag('platform.widget-layout.new-backend');

useEffect(() => {
setBackendFlag(isNewBackend);
}, [isNewBackend]);

useEffect(() => {
if (visibilityFunctions) {
resolveWidgetMapping(visibilityFunctions);
Expand Down Expand Up @@ -53,4 +62,10 @@
);
};

const GenericDashboardPage = () => (
<Provider store={store}>
<GenericDashboardPageInner />
</Provider>
);

export default GenericDashboardPage;
2 changes: 1 addition & 1 deletion src/api/dashboard-templates-new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ export const renameDashboardTemplate = async (templateId: DashboardTemplate['id'
});
handleErrors(resp);
const json = await resp.json();
return json.data;
return json;
};

// POST /api/widget-layout/v1/{id}/copy
Expand Down
13 changes: 11 additions & 2 deletions src/hooks/useDashboardConfig.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useAtom } from 'jotai';
import { useAtom, useSetAtom } from 'jotai';
import DebouncePromise from 'awesome-debounce-promise';
import { templateAtom, templateIdAtom } from '../state/templateAtom';
import { layoutVariantAtom } from '../state/layoutAtom';
Expand All @@ -14,6 +14,7 @@ import {
import useCurrentUser from './useCurrentUser';
import { useAddNotification } from '../state/notificationsAtom';
import { useApi } from './useApi';
import { drawerExpandedAtom } from '../state/drawerExpandedAtom';
import { useFlag } from '@unleash/proxy-client-react';

const sidebarBreakpoints = { xl: 1250, lg: 1100, md: 800, sm: 500 };
Expand All @@ -30,12 +31,20 @@ const useDashboardConfig = (layoutType: LayoutTypes = 'landing-landingPage') =>
const layoutRef = useRef<HTMLDivElement>(null);
const api = useApi();
const debouncedPatchDashboardTemplate = useMemo(() => DebouncePromise(api.patchDashboardTemplate, 1500, { onlyResolvesLast: true }), [api]);
const setDrawerExpanded = useSetAtom(drawerExpandedAtom);

useEffect(() => {
if (!currentUser || templateId >= 0) {
if (!currentUser) {
return;
}

if (templateId >= 0) {
setIsLoaded(true);
return;
}

setDrawerExpanded(false);

api
.getDashboardTemplates(mappedLayoutType)
.then((templates) => {
Expand Down
3 changes: 3 additions & 0 deletions src/hooks/useDashboardTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useAtomValue, useSetAtom } from 'jotai';
import { renameDashboardAtom } from '../state/dashboardsAtom';
import { templateIdAtom } from '../state/templateAtom';
import { backendFlagAtom } from '../state/store';
import { drawerExpandedAtom } from '../state/drawerExpandedAtom';
import { widgetKeyMap } from '../consts';

const remapShortKeys = (config: ExtendedTemplateConfig): ExtendedTemplateConfig => {
Expand Down Expand Up @@ -68,8 +69,10 @@ const useDashboardTemplate = (id: number) => {
const debouncedPatchDashboardTemplate = useMemo(() => DebouncePromise(api.patchDashboardTemplateHub, 1500, { onlyResolvesLast: true }), [api]);
const renameDashboardInList = useSetAtom(renameDashboardAtom);
const invalidateStartPage = useSetAtom(templateIdAtom);
const setDrawerExpanded = useSetAtom(drawerExpandedAtom);

useEffect(() => {
setDrawerExpanded(false);
const fetchTemplate = async () => {
setIsLoaded(false);
setError(null);
Expand Down
Loading