Skip to content

Commit 0eee528

Browse files
authored
Apps/roadmap/debounce (#182)
* fix type assertion for roadmap * [email protected] * instantiate correct types * simplify loading state * update deps * bind this to methods
1 parent 8bd31a9 commit 0eee528

File tree

11 files changed

+162
-167
lines changed

11 files changed

+162
-167
lines changed

apps/roadmap/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"dependencies": {
33
"@instructure/ui": "^11.2.0",
4-
"@instructure/ui-color-utils": "^10.2.0",
4+
"@instructure/ui-color-utils": "^11.2.0",
55
"react": "^19.2.0",
66
"react-dom": "^19.2.0"
77
},
@@ -24,5 +24,5 @@
2424
"typecheck": "tsgo --noEmit"
2525
},
2626
"type": "module",
27-
"version": "1.0.3"
27+
"version": "1.0.4"
2828
}

apps/roadmap/public/themeEditor.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*
1414
* Only runs on the "/pages/instructure-roadmap" path.
1515
*
16-
* @version 2025.10.29.00
16+
* @version 2025.12.02.00
1717
*
1818
*/
1919

@@ -178,14 +178,14 @@ if (matchesRoadmap || matchesCourse || matchesCourseWiki) {
178178
}
179179
};
180180

181-
const _pushState = history.pushState;
181+
const _pushState = history.pushState.bind(history);
182182
history.pushState = function pushState(...args) {
183183
const ret = _pushState.apply(this, args);
184184
window.dispatchEvent(new Event("locationchange"));
185185
return ret;
186186
};
187187

188-
const _replaceState = history.replaceState;
188+
const _replaceState = history.replaceState.bind(history);
189189
history.replaceState = function replaceState(...args) {
190190
const ret = _replaceState.apply(this, args);
191191
window.dispatchEvent(new Event("locationchange"));

apps/roadmap/src/App.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Flex, InstUISettingsProvider, canvas } from "@instructure/ui";
22
import type { FC } from "react";
33
import { useEffect, useMemo, useState } from "react";
44
import { Card, CardOverlay, Loading } from "./components";
5+
import type { Theme } from "@instructure/ui";
56
import {
67
getBrandConfig,
78
getColor,
@@ -11,14 +12,19 @@ import {
1112
} from "./utils";
1213
import "./App.css";
1314

15+
type ThemeOrOverride = Theme | Record<string, unknown>;
16+
1417
const App: FC = () => {
1518
const [overlayOpen, setOverlayOpen] = useState(false);
16-
const [selectedEntry, setSelectedEntry] = useState<PendoAPIFeature | null>(
19+
const [selectedEntry, setSelectedEntry] = useState<
20+
PendoAPIFeature | undefined
21+
>(undefined);
22+
const [brandConfig, setBrandConfig] = useState<ThemeOrOverride>();
23+
const [roadmap, setRoadmap] = useState<RoadmapFeatures | undefined>(
1724
undefined,
1825
);
19-
const [brandConfig, setBrandConfig] = useState<unknown>({});
20-
const [roadmap, setRoadmap] = useState<RoadmapFeatures | null>(undefined);
2126
const [isDark, setIsDark] = useState(false);
27+
const [showLoading, setShowLoading] = useState(false);
2228

2329
useEffect(() => {
2430
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
@@ -31,13 +37,13 @@ const App: FC = () => {
3137
}, []);
3238

3339
useEffect(() => {
34-
getRoadmap().then((data) => {
35-
setRoadmap(data);
40+
void getRoadmap().then((data) => {
41+
setRoadmap(data ?? undefined);
3642
});
3743
}, []);
3844

3945
useEffect(() => {
40-
getBrandConfig().then((config) => {
46+
void getBrandConfig().then((config) => {
4147
setBrandConfig(config);
4248
});
4349
}, []);
@@ -72,10 +78,9 @@ const App: FC = () => {
7278
}));
7379
}, [roadmap]);
7480

75-
// Debounce loading state
76-
const [showLoading, setShowLoading] = useState(false);
7781
useEffect(() => {
7882
if (!roadmap) {
83+
setShowLoading(false);
7984
const timeout = setTimeout(() => setShowLoading(true), 1000);
8085
return () => clearTimeout(timeout);
8186
} else {
@@ -87,7 +92,7 @@ const App: FC = () => {
8792
<InstUISettingsProvider
8893
theme={{
8994
...canvas,
90-
...(brandConfig as object),
95+
...brandConfig,
9196
typography: {
9297
...canvas.typography,
9398
fontFamily: "Atkinson Hyperlegible Next, sans-serif",
@@ -121,9 +126,9 @@ const App: FC = () => {
121126
/>
122127
)}
123128
</>
124-
) : showLoading ? (
129+
) : (showLoading ? (
125130
<Loading isDark={isDark} />
126-
) : null}
131+
) : null)}
127132
</InstUISettingsProvider>
128133
);
129134
};

apps/roadmap/src/components/card.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import Logos, { Colors } from "./logos";
1616
const Card: FC<{
1717
entry: PendoAPIFeature;
1818
setSelectedEntry: React.Dispatch<
19-
React.SetStateAction<PendoAPIFeature | null>
19+
React.SetStateAction<PendoAPIFeature | undefined>
2020
>;
2121
setOverlayOpen: React.Dispatch<React.SetStateAction<boolean>>;
2222
isDark: boolean;

apps/roadmap/src/components/logos.tsx

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,6 @@
11
import { InlineSVG } from "@instructure/ui";
22
import { darken, lighten } from "@instructure/ui-color-utils";
3-
import type { FC, ReactNode } from "react";
4-
5-
interface SVGInfo {
6-
color: string;
7-
SVG: ReactNode;
8-
title: string;
9-
viewBox: string;
10-
}
11-
12-
interface SVGProps {
13-
height?: string;
14-
width?: string;
15-
inline?: boolean;
16-
color?: string;
17-
valign?: "top" | "middle" | "bottom" | "unset";
18-
}
19-
20-
type ColorSVGProps = SVGProps & {
21-
children: ReactNode;
22-
title: string;
23-
viewBox: string;
24-
};
3+
import type { FC } from "react";
254

265
const ColorSVG: FC<ColorSVGProps> = ({
276
color = "currentColor",

apps/roadmap/src/index.d.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
11
/// <reference types="@instructure/ui" />
22

3+
type FC<P = {}> = import("react").FC<P>;
4+
5+
interface SVGInfo {
6+
color: string;
7+
SVG: ReactNode;
8+
title: string;
9+
viewBox: string;
10+
}
11+
12+
interface SVGProps {
13+
height?: string;
14+
width?: string;
15+
inline?: boolean;
16+
color?: string;
17+
valign?: "top" | "middle" | "bottom" | "unset";
18+
}
19+
20+
type ColorSVGProps = SVGProps & {
21+
children: ReactNode;
22+
title: string;
23+
viewBox: string;
24+
};
25+
326
interface PendoAPI {
427
results: PendoAPIFeature[];
528
}
@@ -20,7 +43,7 @@ interface PendoAPIFeature {
2043
product: {
2144
area: string | null;
2245
name: string;
23-
logo: SVGIconProps | undefined;
46+
logo: FC<SVGProps<SVGSVGElement>> | undefined;
2447
color: string | undefined;
2548
};
2649
}

apps/roadmap/src/utils/getBrandConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const getBrandConfig = (): Promise<BrandConfig> => {
4949
};
5050
window.addEventListener("message", handler);
5151
});
52-
brandConfigPromise.finally(() => {
52+
void brandConfigPromise.finally(() => {
5353
brandConfigPromise = undefined;
5454
});
5555
return brandConfigPromise;

apps/roadmap/src/utils/getRoadmap.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import paramsToPendo from "./paramsToPendo";
22

3-
let roadmapPromise: Promise<RoadmapFeatures | null> | null;
4-
let cachedRoadmap: RoadmapFeatures | null;
3+
let roadmapPromise: Promise<RoadmapFeatures | null> | undefined;
4+
let cachedRoadmap: RoadmapFeatures | null | undefined;
55

66
type RoadmapRequestEvent = MessageEvent<{ value?: string }>;
77

88
const getRoadmap = (): Promise<RoadmapFeatures | null> => {
99
console.debug("getRoadmap called");
10-
if (cachedRoadmap !== null) {
10+
if (cachedRoadmap !== undefined && cachedRoadmap !== null) {
1111
console.debug("Returning cached roadmap");
1212
return Promise.resolve(cachedRoadmap);
1313
}
14-
if (roadmapPromise) {
14+
if (roadmapPromise !== undefined) {
1515
console.debug("Returning existing roadmap promise");
16-
console.log(roadmapPromise);
1716
return roadmapPromise;
1817
}
1918
window.parent.postMessage({ type: "getRoadmap" }, "*");
@@ -32,7 +31,6 @@ const getRoadmap = (): Promise<RoadmapFeatures | null> => {
3231
roadmapPromise = undefined;
3332
resolve(result);
3433
}
35-
// Ignore unrelated events
3634
};
3735
window.addEventListener("message", handler);
3836
});

apps/roadmap/src/utils/paramsToPendo.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ const isPendoAPI = (obj: unknown): obj is PendoAPI => {
3333
);
3434
};
3535

36-
const paramsToPendo = (params: string | null): RoadmapFeatures | null => {
36+
const paramsToPendo = (params: string | null): RoadmapFeatures | undefined => {
3737
if (!params) {
38-
return null;
38+
return;
3939
}
4040

4141
let p: unknown;
@@ -50,20 +50,18 @@ const paramsToPendo = (params: string | null): RoadmapFeatures | null => {
5050
}
5151

5252
const stages = [
53-
...new Set((p as PendoAPI).results.map((result) => result.feature.stage)),
53+
...new Set(p.results.map((result) => result.feature.stage)),
5454
].filter(Boolean) as string[];
5555
const products = [
56-
...new Set((p as PendoAPI).results.map((result) => result.product.name)),
56+
...new Set(p.results.map((result) => result.product.name)),
5757
].filter(Boolean) as string[];
5858
const productAreas = [
59-
...new Set((p as PendoAPI).results.map((result) => result.product.area)),
59+
...new Set(p.results.map((result) => result.product.area)),
6060
].filter(Boolean) as string[];
6161
const labels = [
62-
...new Set(
63-
(p as PendoAPI).results.flatMap((result) => result.feature.labels),
64-
),
62+
...new Set(p.results.flatMap((result) => result.feature.labels)),
6563
].filter(Boolean) as string[];
66-
const features = (p as PendoAPI).results;
64+
const features = p.results;
6765

6866
const roadmap: RoadmapFeatures = {
6967
features,

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
},
2727
"version": "3.0.0",
2828
"name": "@instructure.ai/shared-configs",
29-
"packageManager": "pnpm@10.23.0",
29+
"packageManager": "pnpm@10.24.0",
3030
"private": true,
3131
"type": "module",
3232
"scripts": {
@@ -57,4 +57,4 @@
5757
"type": "git",
5858
"url": "https://github.com/instructure/instructure.ai"
5959
}
60-
}
60+
}

0 commit comments

Comments
 (0)