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
-
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"