From deec60a98d206ca9f46d0997e244ffb8e9570f26 Mon Sep 17 00:00:00 2001 From: majakomel Date: Mon, 4 Nov 2024 22:41:24 +0100 Subject: [PATCH] Chart refactor --- components/Chart.js | 22 ++--- ...pper.tsx => ChartIntersectionObserver.tsx} | 14 +-- components/DomainChart.js | 94 ------------------- components/MATChart.js | 37 +++----- components/ThematicPage.tsx | 6 +- components/aggregation/mat/ChartHeader.js | 3 +- components/aggregation/mat/GridChart.js | 79 ++++++++-------- components/aggregation/mat/NoCharts.js | 2 +- components/aggregation/mat/TableView.js | 10 +- components/country/Apps.js | 3 +- components/country/Websites.js | 3 +- pages/domain/[domain].js | 21 +++-- 12 files changed, 96 insertions(+), 198 deletions(-) rename components/{ChartWrapper.tsx => ChartIntersectionObserver.tsx} (80%) delete mode 100644 components/DomainChart.js diff --git a/components/Chart.js b/components/Chart.js index eadf024e..2d5a272f 100644 --- a/components/Chart.js +++ b/components/Chart.js @@ -87,19 +87,15 @@ const Chart = ({ queryParams = {}, setState = null, headerOptions = {} }) => { return (
- {!chartData && !error ? ( - - ) : ( - <> - - {!!chartData?.size && } - - )} + <> + + {!!chartData?.size && } + {error && ( { +}: ChartIntersectionObserverProps) => { const router = useRouter() const { @@ -44,11 +44,7 @@ const ChartWrapper = ({ return (
{inView ? ( - + ) : ( )} @@ -56,4 +52,4 @@ const ChartWrapper = ({ ) } -export default ChartWrapper +export default ChartIntersectionObserver diff --git a/components/DomainChart.js b/components/DomainChart.js deleted file mode 100644 index f721a4b8..00000000 --- a/components/DomainChart.js +++ /dev/null @@ -1,94 +0,0 @@ -import GridChart, { - prepareDataForGridChart, -} from 'components/aggregation/mat/GridChart' -import { MATContextProvider } from 'components/aggregation/mat/MATContext' -import { MATLink } from 'components/Chart' -import { DetailsBox } from 'components/measurement/DetailsBox' -import { useRouter } from 'next/router' -import { memo, useEffect, useMemo } from 'react' -import { FormattedMessage, useIntl } from 'react-intl' -import { MATFetcher } from 'services/fetchers' -import useSWR from 'swr' - -const swrOptions = { - revalidateOnFocus: false, - dedupingInterval: 10 * 60 * 1000, -} - -const Chart = memo(function Chart({ queryParams = {}, setState }) { - const { - query: { probe_cc }, - } = useRouter() - const { locale } = useIntl() - - const apiQuery = useMemo(() => { - const qs = new URLSearchParams(queryParams).toString() - return qs - }, [queryParams]) - - const { data, error } = useSWR(apiQuery, MATFetcher, swrOptions) - - const [chartData, rowKeys, rowLabels] = useMemo(() => { - if (!data) { - return [null, 0] - } - - let chartData = data.data.sort((a, b) => - new Intl.Collator(locale).compare(a.probe_cc, b.probe_cc), - ) - - if (probe_cc) chartData = chartData.filter((d) => probe_cc === d.probe_cc) - - const [reshapedData, rowKeys, rowLabels] = prepareDataForGridChart( - chartData, - queryParams, - locale, - ) - - return [reshapedData, rowKeys, rowLabels] - }, [data, queryParams, probe_cc, locale]) - - useEffect(() => { - if (setState && data?.data) setState(data.data) - }, [data, setState]) - - const linkParams = { ...queryParams, ...(probe_cc && { probe_cc }) } - - return ( - -
-
- {!chartData && !error ? ( - - ) : ( - <> - - {!!chartData?.size && } - - )} -
- {error && ( - -
- - Error: {error.message} - -
{JSON.stringify(error, null, 2)}
-
- - } - /> - )} -
-
- ) -}) - -export default Chart diff --git a/components/MATChart.js b/components/MATChart.js index 0c2c29f5..493f6a11 100644 --- a/components/MATChart.js +++ b/components/MATChart.js @@ -87,35 +87,26 @@ const MATChart = ({ query, showFilters = true }) => { swrOptions, ) - const showLoadingIndicator = useMemo(() => isValidating, [isValidating]) - return ( <> {error && } - {showLoadingIndicator ? ( - //

{intl.formatMessage({ id: 'General.Loading' })}

- + {isValidating ? ( + ) : ( <> - {data?.data?.result?.length > 0 ? ( - <> - {data && data.data.dimension_count === 0 && ( - - )} - {data && data.data.dimension_count === 1 && ( - - )} - {data && data.data.dimension_count > 1 && ( - - )} - - ) : ( - + {data && data.data.dimension_count === 0 && ( + + )} + {data && data.data.dimension_count === 1 && ( + + )} + {data && data.data.dimension_count > 1 && ( + )} )} diff --git a/components/ThematicPage.tsx b/components/ThematicPage.tsx index 37ddad54..99ae41d4 100644 --- a/components/ThematicPage.tsx +++ b/components/ThematicPage.tsx @@ -1,6 +1,6 @@ import { useRouter } from 'next/router' -import ChartWrapper from 'components/ChartWrapper' +import ChartIntersectionObserver from 'components/ChartIntersectionObserver' import FindingsSection from 'components/FindingsSection' import ReportsSection from 'components/ReportsSection' import { @@ -88,7 +88,7 @@ const ThematicPage = ({ {filteredApps?.map((testName: string) => (
- @@ -102,7 +102,7 @@ const ThematicPage = ({ {filteredDomains?.map((domain: string) => (
- { probe_cc: true, test_name: true, legend: true, + logo: true, ...opts, } @@ -129,7 +130,7 @@ export const ChartHeader = ({ options: opts }) => { />
)} - + {options.logo && }
) diff --git a/components/aggregation/mat/GridChart.js b/components/aggregation/mat/GridChart.js index 27067f13..868b1462 100644 --- a/components/aggregation/mat/GridChart.js +++ b/components/aggregation/mat/GridChart.js @@ -1,5 +1,6 @@ import { Container } from '@nivo/core' import { Tooltip, TooltipProvider } from '@nivo/tooltip' +import { ChartSpinLoader } from 'components/Chart' import PropTypes from 'prop-types' import { memo, useMemo, useRef } from 'react' import { ChartHeader } from './ChartHeader' @@ -118,15 +119,11 @@ const GridChart = ({ gridHeight = Math.min(XAXIS_HEIGHT + rowCount * ROW_HEIGHT, GRID_MAX_HEIGHT) } - if (!data || data.size < 1) { - return - } - // To correctly align with the rows, generate a data row with only x-axis values // e.g [ {measurement_start_day: '2022-01-01'}, {measurement_start_day: '2022-01-02'}... ] - const xAxisData = data - .get(rowKeys[0]) - .map((d) => ({ [query.axis_x]: d[query.axis_x] })) + const xAxisData = data?.size + ? data.get(rowKeys[0]).map((d) => ({ [query.axis_x]: d[query.axis_x] })) + : null const rowHeight = noLabels ? 500 : ROW_HEIGHT @@ -135,38 +132,46 @@ const GridChart = ({
- - {/* Fake axis on top of list. Possible alternative: dummy chart with axis and valid tickValues */} - {/* Use a virtual list only for higher count of rows */} - {rowsToRender.length < 10 ? ( -
- {!noLabels && } - {rowsToRender.map((rowKey, index) => ( - + {!data && } + {data?.size === 0 && } + {data?.size > 0 && ( + // Fake axis on top of list. Possible alternative: dummy chart with axis and valid tickValues + // Use a virtual list only for higher count of rows + <> + {rowsToRender.length < 10 ? ( +
+ {!noLabels && } + {rowsToRender.map((rowKey, index) => ( + + ))} +
+ ) : ( + } + data={data} + rows={rowsToRender} + rowLabels={rowLabels} + gridHeight={gridHeight} indexBy={indexBy} - height={rowHeight} - label={rowLabels[rowKey]} + tooltipIndex={tooltipIndex} /> - ))} -
- ) : ( - } - data={data} - rows={rowsToRender} - rowLabels={rowLabels} - gridHeight={gridHeight} - indexBy={indexBy} - tooltipIndex={tooltipIndex} - /> + )} + )}
diff --git a/components/aggregation/mat/NoCharts.js b/components/aggregation/mat/NoCharts.js index 995dfc61..3f926e35 100644 --- a/components/aggregation/mat/NoCharts.js +++ b/components/aggregation/mat/NoCharts.js @@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl' export const NoCharts = ({ message }) => { return (
-
+
diff --git a/components/aggregation/mat/TableView.js b/components/aggregation/mat/TableView.js index aecb0424..22245ecd 100644 --- a/components/aggregation/mat/TableView.js +++ b/components/aggregation/mat/TableView.js @@ -1,4 +1,4 @@ -import { useMemo, useRef, useState } from 'react' +import { useMemo, useState } from 'react' import { useIntl } from 'react-intl' import Filters from './Filters' @@ -29,11 +29,11 @@ const prepareDataforTable = (data, query, locale) => { measurement_count: 0, } - rowData.forEach((d) => { - countKeys.forEach((countKey) => { + for (const d of rowData) { + for (const countKey of countKeys) { row[countKey] = row[countKey] + d[countKey] - }) - }) + } + } table.push(row) } diff --git a/components/country/Apps.js b/components/country/Apps.js index a3acc273..ebb7c2e8 100644 --- a/components/country/Apps.js +++ b/components/country/Apps.js @@ -1,7 +1,8 @@ -import Chart from 'components/Chart' import { useRouter } from 'next/router' import { useMemo } from 'react' import { FormattedMessage, useIntl } from 'react-intl' + +import Chart from 'components/Chart' import FormattedMarkdown from '../FormattedMarkdown' import SectionHeader from './SectionHeader' import { SimpleBox } from './boxes' diff --git a/components/country/Websites.js b/components/country/Websites.js index e0677797..11434430 100644 --- a/components/country/Websites.js +++ b/components/country/Websites.js @@ -1,7 +1,8 @@ -import Chart from 'components/Chart' import { useRouter } from 'next/router' import { useMemo } from 'react' import { FormattedMessage } from 'react-intl' + +import Chart from 'components/Chart' import FormattedMarkdown from '../FormattedMarkdown' import ConfirmedBlockedCategory from './ConfirmedBlockedCategory' import SectionHeader from './SectionHeader' diff --git a/pages/domain/[domain].js b/pages/domain/[domain].js index 6333b90f..cf81ff08 100644 --- a/pages/domain/[domain].js +++ b/pages/domain/[domain].js @@ -1,20 +1,21 @@ import axios from 'axios' -import TestGroupBadge, { CategoryBadge } from 'components/Badge' -import Chart from 'components/DomainChart' -import Flag from 'components/Flag' -import { StyledSticky } from 'components/SharedStyledComponents' -import { GridBox } from 'components/VirtualizedGrid' -import Form from 'components/domain/Form' import Head from 'next/head' import Link from 'next/link' import { useRouter } from 'next/router' -import { RecentMeasurements } from 'pages/as/[probe_asn]' import { useMemo, useState } from 'react' import { useIntl } from 'react-intl' -import { simpleFetcher } from 'services/fetchers' import useSWR from 'swr' + +import TestGroupBadge, { CategoryBadge } from 'components/Badge' +import BlockText from 'components/BlockText' +import Chart from 'components/Chart' +import Form from 'components/domain/Form' +import Flag from 'components/Flag' +import { StyledSticky } from 'components/SharedStyledComponents' +import { GridBox } from 'components/VirtualizedGrid' +import { RecentMeasurements } from 'pages/as/[probe_asn]' +import { simpleFetcher } from 'services/fetchers' import { getLocalisedRegionName } from 'utils/i18nCountries' -import BlockText from '../../components/BlockText' import { sortByKey } from '../../utils' const CountryList = ({ countries }) => { @@ -73,7 +74,7 @@ const ChartContainer = ({ domain, ...props }) => {
- + ) }