From 2a1bbc2da16ec29317dbf1a99496c0cbdbf22ffa Mon Sep 17 00:00:00 2001 From: Kenneth Kalmer Date: Mon, 25 Nov 2024 15:11:24 +0000 Subject: [PATCH] refactor: Status component rebuilt with SWR Main reason is to have only a single ajax request for status data on the page, no matter how many status icons are embedded. Also extract the icon portion into its own StatusIcon component that can be better used in other components, especially inside other anchor tags --- src/core/Status.tsx | 68 +++++++++++++++--------------- src/core/Status/Status.stories.tsx | 42 ++++++++++++++---- 2 files changed, 68 insertions(+), 42 deletions(-) diff --git a/src/core/Status.tsx b/src/core/Status.tsx index c3f9c9ee9..4c800d8cf 100644 --- a/src/core/Status.tsx +++ b/src/core/Status.tsx @@ -1,4 +1,9 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; +import useSWR from "swr"; +import clsx from "clsx"; + +// Our SWR fetcher function +const fetcher = (url: string) => fetch(url).then((res) => res.json()); const indicatorClass = (indicator?: string) => { switch (indicator) { @@ -17,41 +22,37 @@ const indicatorClass = (indicator?: string) => { } }; +export const StatusIcon = ({ + statusUrl, + refreshInterval = 1000 * 60, +}: { + statusUrl: string; + refreshInterval?: number; +}) => { + const { data, error, isLoading } = useSWR(statusUrl, fetcher, { + refreshInterval, + }); + + return ( + + ); +}; + const Status = ({ statusUrl, additionalCSS, + refreshInterval = 1000 * 60, }: { statusUrl: string; additionalCSS?: string; + refreshInterval?: number; }) => { - const [data, setData] = useState<{ status: { indicator: string } } | null>( - null, - ); - - useEffect(() => { - let interval: NodeJS.Timeout; - - if (statusUrl !== "") { - const fetchData = async () => { - try { - const response = await fetch(statusUrl); - const jsonData = await response.json(); - setData(jsonData); - } catch (error) { - console.error("Error fetching status data:", error); - } - }; - - fetchData(); - - interval = setInterval(fetchData, 60000); // Fetch data every minute - } - - return () => { - clearInterval(interval); - }; - }, [statusUrl]); - return ( - - - + ); }; diff --git a/src/core/Status/Status.stories.tsx b/src/core/Status/Status.stories.tsx index 91c7b2423..0880ea9c1 100644 --- a/src/core/Status/Status.stories.tsx +++ b/src/core/Status/Status.stories.tsx @@ -1,5 +1,6 @@ import React from "react"; import { delay, http, HttpResponse } from "msw"; +import { SWRConfig } from "swr"; import Status from "../Status"; const statusUrl = "https://ntqy1wz94gjv.statuspage.io/api/v2/status.json"; @@ -13,6 +14,10 @@ export default { tags: ["!autodocs"], }; +const withEmptySWRCache = (component: JSX.Element) => ( + new Map() }}>{component} +); + export const Loading = { parameters: { msw: { @@ -29,10 +34,25 @@ export const Loading = { }, }, }, - render: () => , + render: () => + withEmptySWRCache(), +}; + +export const Error = { + parameters: { + msw: { + handlers: { + statusError: http.get(statusUrl, () => { + return HttpResponse.error(); + }), + }, + }, + }, + render: () => + withEmptySWRCache(), }; -const mockParametersWithStatus = (indicator) => { +const mockParametersWithStatus = (indicator: string) => { return { msw: { handlers: { @@ -52,30 +72,36 @@ const mockParametersWithStatus = (indicator) => { export const None = { parameters: mockParametersWithStatus("none"), - render: () => , + render: () => + withEmptySWRCache(), }; export const Operational = { parameters: mockParametersWithStatus("operational"), - render: () => , + render: () => + withEmptySWRCache(), }; export const Minor = { parameters: mockParametersWithStatus("minor"), - render: () => , + render: () => + withEmptySWRCache(), }; export const Major = { parameters: mockParametersWithStatus("major"), - render: () => , + render: () => + withEmptySWRCache(), }; export const Critical = { parameters: mockParametersWithStatus("critical"), - render: () => , + render: () => + withEmptySWRCache(), }; export const Unknown = { parameters: mockParametersWithStatus("unknown"), - render: () => , + render: () => + withEmptySWRCache(), };