From b5809096f85f75c6f124e94f60bb393be51427db Mon Sep 17 00:00:00 2001 From: nitin <142569587+ehconitin@users.noreply.github.com> Date: Sat, 13 Sep 2025 18:52:43 +0530 Subject: [PATCH] Lazy load Nivo (#14454) oops :p --- .../PageLayoutRenderer.stories.tsx | 23 +-- .../graph/components/ChartSkeletonLoader.tsx | 17 +++ .../graph/components/GraphWidgetRenderer.tsx | 138 +++++++++++------- .../iframe/components/IframeWidget.tsx | 16 +- .../components/WorkerQueueMetricsSection.tsx | 22 ++- 5 files changed, 135 insertions(+), 81 deletions(-) create mode 100644 packages/twenty-front/src/modules/page-layout/widgets/graph/components/ChartSkeletonLoader.tsx diff --git a/packages/twenty-front/src/modules/page-layout/components/__stories__/PageLayoutRenderer.stories.tsx b/packages/twenty-front/src/modules/page-layout/components/__stories__/PageLayoutRenderer.stories.tsx index 4a657a7275..e96cb776a0 100644 --- a/packages/twenty-front/src/modules/page-layout/components/__stories__/PageLayoutRenderer.stories.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/__stories__/PageLayoutRenderer.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { expect, within } from '@storybook/test'; +import { expect, waitFor, within } from '@storybook/test'; import { MemoryRouter } from 'react-router-dom'; import { PageLayoutRenderer } from '@/page-layout/components/PageLayoutRenderer'; @@ -13,18 +13,21 @@ import { type PageLayoutWidgetWithData } from '../../types/pageLayoutTypes'; const validatePageLayoutContent = async (canvasElement: HTMLElement) => { const canvas = within(canvasElement); - const revenueElements = await canvas.findAllByText('Revenue'); - await expect(revenueElements).toHaveLength(2); - const goalProgressElements = await canvas.findAllByText('Goal Progress'); - await expect(goalProgressElements).toHaveLength(2); + await waitFor(async () => { + const revenueElements = canvas.getAllByText('Revenue'); + expect(revenueElements).toHaveLength(2); + + const goalProgressElements = canvas.getAllByText('Goal Progress'); + expect(goalProgressElements).toHaveLength(2); + + expect(canvas.getByText('Product Sales')).toBeInTheDocument(); + expect(canvas.getByText('Services')).toBeInTheDocument(); + expect(canvas.getByText('Support')).toBeInTheDocument(); + }); + await expect(await canvas.findByText('Revenue Sources')).toBeVisible(); await expect(await canvas.findByText('Quarterly Comparison')).toBeVisible(); - await expect(await canvas.findByText('$125,000')).toBeVisible(); - - await expect(await canvas.findByText('Product Sales')).toBeVisible(); - await expect(await canvas.findByText('Services')).toBeVisible(); - await expect(await canvas.findByText('Support')).toBeVisible(); }; const mixedGraphsPageLayout = { diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/ChartSkeletonLoader.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/ChartSkeletonLoader.tsx new file mode 100644 index 0000000000..034df965f0 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/ChartSkeletonLoader.tsx @@ -0,0 +1,17 @@ +import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader'; +import { useTheme } from '@emotion/react'; +import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'; + +export const ChartSkeletonLoader = () => { + const theme = useTheme(); + + return ( + + + + ); +}; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx index d5c0352ed9..1b3bd37e2d 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx @@ -1,11 +1,41 @@ import { GraphType } from '@/page-layout/mocks/mockWidgets'; import { getDefaultWidgetData } from '@/page-layout/utils/getDefaultWidgetData'; -import { GraphWidgetBarChart } from '@/page-layout/widgets/graph/components/GraphWidgetBarChart'; -import { GraphWidgetGaugeChart } from '@/page-layout/widgets/graph/components/GraphWidgetGaugeChart'; -import { GraphWidgetLineChart } from '@/page-layout/widgets/graph/components/GraphWidgetLineChart'; +import { ChartSkeletonLoader } from '@/page-layout/widgets/graph/components/ChartSkeletonLoader'; import { GraphWidgetNumberChart } from '@/page-layout/widgets/graph/components/GraphWidgetNumberChart'; -import { GraphWidgetPieChart } from '@/page-layout/widgets/graph/components/GraphWidgetPieChart'; import { type GraphWidget } from '@/page-layout/widgets/graph/types/GraphWidget'; +import { lazy, Suspense } from 'react'; + +const GraphWidgetBarChart = lazy(() => + import('@/page-layout/widgets/graph/components/GraphWidgetBarChart').then( + (module) => ({ + default: module.GraphWidgetBarChart, + }), + ), +); + +const GraphWidgetLineChart = lazy(() => + import('@/page-layout/widgets/graph/components/GraphWidgetLineChart').then( + (module) => ({ + default: module.GraphWidgetLineChart, + }), + ), +); + +const GraphWidgetPieChart = lazy(() => + import('@/page-layout/widgets/graph/components/GraphWidgetPieChart').then( + (module) => ({ + default: module.GraphWidgetPieChart, + }), + ), +); + +const GraphWidgetGaugeChart = lazy(() => + import('@/page-layout/widgets/graph/components/GraphWidgetGaugeChart').then( + (module) => ({ + default: module.GraphWidgetGaugeChart, + }), + ), +); type GraphWidgetRendererProps = { widget: GraphWidget; @@ -35,64 +65,72 @@ export const GraphWidgetRenderer = ({ widget }: GraphWidgetRendererProps) => { case GraphType.GAUGE: return ( - + }> + + ); case GraphType.PIE: return ( - + }> + + ); case GraphType.BAR: return ( - + }> + + ); case GraphType.LINE: return ( - + }> + + ); default: diff --git a/packages/twenty-front/src/modules/page-layout/widgets/iframe/components/IframeWidget.tsx b/packages/twenty-front/src/modules/page-layout/widgets/iframe/components/IframeWidget.tsx index 6eb30b458c..90d8c40e9c 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/iframe/components/IframeWidget.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/iframe/components/IframeWidget.tsx @@ -1,8 +1,6 @@ -import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader'; -import { useTheme } from '@emotion/react'; +import { ChartSkeletonLoader } from '@/page-layout/widgets/graph/components/ChartSkeletonLoader'; import styled from '@emotion/styled'; import { useState } from 'react'; -import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'; const StyledContainer = styled.div` background: ${({ theme }) => theme.background.transparent.lighter}; @@ -67,7 +65,6 @@ export const IframeWidget = ({ url, title = 'Embedded Content', }: IframeWidgetProps) => { - const theme = useTheme(); const [isLoading, setIsLoading] = useState(true); const [hasError, setHasError] = useState(false); @@ -95,16 +92,7 @@ export const IframeWidget = ({ {isLoading && ( - - - + )} + import('./WorkerMetricsGraph').then((module) => ({ + default: module.WorkerMetricsGraph, + })), +); type WorkerQueueMetricsSectionProps = { queue: AdminPanelWorkerQueueHealth; @@ -50,11 +56,13 @@ export const WorkerQueueMetricsSection = ({ /> - + }> + + ); };