diff --git a/package.json b/package.json index 1b22773c7..3ecdf4e2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ably/ui", - "version": "15.0.1", + "version": "15.0.2", "description": "Home of the Ably design system library ([design.ably.com](https://design.ably.com)). It provides a showcase, development/test environment and a publishing pipeline for different distributables.", "repository": { "type": "git", @@ -90,7 +90,8 @@ "react": "^18.2.0", "react-dom": "^18.3.1", "redux": "^4.0.5", - "scroll-lock": "^2.1.4" + "scroll-lock": "^2.1.4", + "swr": "^2.2.5" }, "bugs": { "url": "https://github.com/ably/ably-ui/issues" diff --git a/src/core/Footer.tsx b/src/core/Footer.tsx index a6b754f21..282cde8e8 100644 --- a/src/core/Footer.tsx +++ b/src/core/Footer.tsx @@ -2,7 +2,7 @@ import React from "react"; import Icon from "./Icon"; import _absUrl from "./url-base.js"; -import Status from "./Status.js"; +import { StatusIcon } from "./Status"; type FooterProps = { paths: { @@ -209,14 +209,16 @@ const Footer = ({ paths, urlBase, statusUrl }: FooterProps) => { SDKs -
  • +
  • System status + -
  • diff --git a/src/core/Footer/__snapshots__/Footer.stories.tsx.snap b/src/core/Footer/__snapshots__/Footer.stories.tsx.snap index 82fb910c2..f8289c9bd 100644 --- a/src/core/Footer/__snapshots__/Footer.stories.tsx.snap +++ b/src/core/Footer/__snapshots__/Footer.stories.tsx.snap @@ -175,20 +175,14 @@ exports[`Components/Footer Default smoke-test 1`] = ` SDKs -
  • - + - System status - - - - - + System status +
  • diff --git a/src/core/MeganavContentDevelopers.tsx b/src/core/MeganavContentDevelopers.tsx index 226e297e3..31ca9d96e 100644 --- a/src/core/MeganavContentDevelopers.tsx +++ b/src/core/MeganavContentDevelopers.tsx @@ -2,7 +2,7 @@ import React from "react"; import Icon from "./Icon"; import { AbsUrl } from "./Meganav"; -import Status from "./Status"; +import { StatusIcon } from "./Status"; const MeganavContentDevelopers = ({ absUrl, @@ -186,12 +186,9 @@ const MeganavContentDevelopers = ({ href="https://status.ably.com/" className="group ui-meganav-media py-12" > -

    +

    Status - +

    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(), }; diff --git a/yarn.lock b/yarn.lock index 47b9bfb2d..ee1b2ac8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3515,6 +3515,11 @@ cli-width@^4.1.0: resolved "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz" integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== +client-only@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" + integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== + cliui@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" @@ -8237,6 +8242,14 @@ svgo@^2.8.0: picocolors "^1.0.0" stable "^0.1.8" +swr@^2.2.5: + version "2.2.5" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.2.5.tgz#063eea0e9939f947227d5ca760cc53696f46446b" + integrity sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg== + dependencies: + client-only "^0.0.1" + use-sync-external-store "^1.2.0" + tailwindcss@^3.3.6: version "3.4.15" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.15.tgz#04808bf4bf1424b105047d19e7d4bfab368044a9" @@ -8630,6 +8643,11 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +use-sync-external-store@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" + integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"