feat(react): update to react 18
This commit is contained in:
@@ -5,9 +5,9 @@ import Footer from "./Footer/Footer";
|
||||
import Router from "./Router";
|
||||
import {
|
||||
BrowserRouter,
|
||||
Switch,
|
||||
Routes,
|
||||
Route,
|
||||
Redirect,
|
||||
Navigate,
|
||||
useParams,
|
||||
useLocation,
|
||||
} from "react-router-dom";
|
||||
@@ -15,10 +15,6 @@ import { Item } from "../model";
|
||||
import { Messages, MessagesProvider } from "../context/MessagesContext";
|
||||
import { ConfigData } from "../config";
|
||||
|
||||
interface Params {
|
||||
page: string;
|
||||
}
|
||||
|
||||
const useFetch = <D extends unknown>(url: string): D | undefined => {
|
||||
const [data, setData] = React.useState<D>();
|
||||
|
||||
@@ -38,6 +34,10 @@ const useFetch = <D extends unknown>(url: string): D | undefined => {
|
||||
|
||||
const useQuery = () => new URLSearchParams(useLocation().search);
|
||||
|
||||
const usePage = (params: Record<string, string | undefined>) => {
|
||||
return (params['*'] || '').replace(".html", "");
|
||||
};
|
||||
|
||||
const RouterWithPageParam = ({
|
||||
items,
|
||||
releases,
|
||||
@@ -47,12 +47,12 @@ const RouterWithPageParam = ({
|
||||
releases: string[];
|
||||
config: ConfigData;
|
||||
}) => {
|
||||
const { page } = useParams<Params>();
|
||||
const page = usePage(useParams());
|
||||
const query = useQuery();
|
||||
|
||||
return (
|
||||
<Router
|
||||
pageName={page}
|
||||
pageName={page || ""}
|
||||
search={query.get("search") || ""}
|
||||
items={items}
|
||||
releases={releases}
|
||||
@@ -62,15 +62,15 @@ const RouterWithPageParam = ({
|
||||
};
|
||||
|
||||
const HeaderWithPageParam = () => {
|
||||
const { page } = useParams<Params>();
|
||||
const page = usePage(useParams());
|
||||
|
||||
return <Header pageName={page} />;
|
||||
return <Header pageName={page || ""} />;
|
||||
};
|
||||
|
||||
const FooterWithPageParam = ({ items }: { items: Item[] }) => {
|
||||
const { page } = useParams<Params>();
|
||||
const page = usePage(useParams());
|
||||
|
||||
return <Footer pageName={page} items={items} />;
|
||||
return <Footer pageName={page || ""} items={items} />;
|
||||
};
|
||||
|
||||
interface Data {
|
||||
@@ -83,35 +83,41 @@ export default function App() {
|
||||
const messages = useFetch<Messages>(
|
||||
`${process.env.PUBLIC_URL}/messages.json`
|
||||
);
|
||||
const config = useFetch<ConfigData>(
|
||||
`${process.env.PUBLIC_URL}/config.json`
|
||||
);
|
||||
const config = useFetch<ConfigData>(`${process.env.PUBLIC_URL}/config.json`);
|
||||
|
||||
if (data && config) {
|
||||
const { items, releases } = data;
|
||||
return (
|
||||
<MessagesProvider messages={messages}>
|
||||
<BrowserRouter basename={`${process.env.PUBLIC_URL}`}>
|
||||
<Switch>
|
||||
<Route path={"/:page(.+).html"}>
|
||||
<div>
|
||||
<div className="page">
|
||||
<div className="page__header">
|
||||
<HeaderWithPageParam />
|
||||
</div>
|
||||
<div className={classNames("page__content")}>
|
||||
<RouterWithPageParam config={config} items={items} releases={releases} />
|
||||
</div>
|
||||
<div className="page__footer">
|
||||
<FooterWithPageParam items={items} />
|
||||
<Routes>
|
||||
<Route
|
||||
path={"/*"}
|
||||
element={
|
||||
<div>
|
||||
<div className="page">
|
||||
<div className="page__header">
|
||||
<HeaderWithPageParam />
|
||||
</div>
|
||||
<div className={classNames("page__content")}>
|
||||
<RouterWithPageParam
|
||||
config={config}
|
||||
items={items}
|
||||
releases={releases}
|
||||
/>
|
||||
</div>
|
||||
<div className="page__footer">
|
||||
<FooterWithPageParam items={items} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Route>
|
||||
<Route path={"/"}>
|
||||
<Redirect to={"/index.html"} />
|
||||
</Route>
|
||||
</Switch>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={"/"}
|
||||
element={<Navigate replace to={"/index.html"} />}
|
||||
/>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
</MessagesProvider>
|
||||
);
|
||||
|
||||
@@ -5,7 +5,7 @@ import Link from "../Link/Link";
|
||||
import LogoLink from "../LogoLink/LogoLink";
|
||||
import Search from "../Search/Search";
|
||||
import { radarNameShort } from "../../config";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import qs from "query-string";
|
||||
import { useMessages } from "../../context/MessagesContext";
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function Header({ pageName }: { pageName: string }) {
|
||||
const [searchOpen, setSearchOpen] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
const { searchLabel, pageHelp, pageOverview } = useMessages();
|
||||
const history = useHistory();
|
||||
const navigate = useNavigate();
|
||||
const searchRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const openSearch = () => {
|
||||
@@ -29,7 +29,7 @@ export default function Header({ pageName }: { pageName: string }) {
|
||||
};
|
||||
|
||||
const handleSearchSubmit = () => {
|
||||
history.push({
|
||||
navigate({
|
||||
pathname: "/overview.html",
|
||||
search: qs.stringify({ search: search }),
|
||||
});
|
||||
@@ -55,20 +55,20 @@ export default function Header({ pageName }: { pageName: string }) {
|
||||
<div className="nav__item">
|
||||
<Link pageName="help-and-about-tech-radar" className="icon-link">
|
||||
<span className="icon icon--question icon-link__icon" />
|
||||
{pageHelp.headlinePrefix || 'How to use' } {radarNameShort}?
|
||||
{pageHelp.headlinePrefix || "How to use"} {radarNameShort}?
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
<div className="nav__item">
|
||||
<Link pageName="overview" className="icon-link">
|
||||
<span className="icon icon--overview icon-link__icon" />
|
||||
{ pageOverview?.title || 'Technologies Overview' }
|
||||
{pageOverview?.title || "Technologies Overview"}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="nav__item">
|
||||
<button className="icon-link" onClick={handleOpenClick}>
|
||||
<span className="icon icon--search icon-link__icon" />
|
||||
{ searchLabel || 'Search' }
|
||||
{searchLabel || "Search"}
|
||||
</button>
|
||||
<div className={classNames("nav__search", { "is-open": searchOpen })}>
|
||||
<Search
|
||||
|
||||
@@ -6,7 +6,10 @@ interface Props {
|
||||
secondary?: boolean;
|
||||
}
|
||||
|
||||
const HeadlineGroup: React.FC<Props> = ({ children, secondary = false }) => (
|
||||
const HeadlineGroup: React.FC<React.PropsWithChildren<Props>> = ({
|
||||
children,
|
||||
secondary = false,
|
||||
}) => (
|
||||
<div
|
||||
className={classNames("headline-group", {
|
||||
"headline-group--secondary": secondary,
|
||||
|
||||
@@ -5,7 +5,10 @@ interface Props {
|
||||
alt?: string;
|
||||
}
|
||||
|
||||
const HeroHeadline: React.FC<Props> = ({ children, alt }) => (
|
||||
const HeroHeadline: React.FC<React.PropsWithChildren<Props>> = ({
|
||||
children,
|
||||
alt,
|
||||
}) => (
|
||||
<div className="hero-headline">
|
||||
{children} <span className="hero-headline__alt">{alt}</span>
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,6 @@ describe("Item", () => {
|
||||
it("Should render the item", () => {
|
||||
render(<Item item={testItem} />, { wrapper: MemoryRouter });
|
||||
|
||||
expect(screen.queryByText(testItem.title)).toBeInTheDocument();
|
||||
expect(screen.getByText(testItem.title)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ type ItemListProps = {
|
||||
itemStyle?: React.CSSProperties[];
|
||||
};
|
||||
|
||||
const ItemList: React.FC<ItemListProps> = ({
|
||||
const ItemList: React.FC<React.PropsWithChildren<ItemListProps>> = ({
|
||||
children,
|
||||
items,
|
||||
activeItem,
|
||||
|
||||
@@ -30,7 +30,12 @@ export default function PageQuadrant({
|
||||
<HeadlineGroup>
|
||||
<HeroHeadline>{translate(config, pageName)}</HeroHeadline>
|
||||
</HeadlineGroup>
|
||||
<QuadrantSection groups={groups} quadrantName={pageName} config={config} big />
|
||||
<QuadrantSection
|
||||
groups={groups}
|
||||
quadrantName={pageName}
|
||||
config={config}
|
||||
big
|
||||
/>
|
||||
</Fadeable>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import PageIndex from "./PageIndex/PageIndex";
|
||||
import PageOverview from "./PageOverview/PageOverview";
|
||||
import PageHelp from "./PageHelp/PageHelp";
|
||||
import PageQuadrant from "./PageQuadrant/PageQuadrant";
|
||||
import PageItem from "./PageItem/PageItem";
|
||||
import PageItemMobile from "./PageItemMobile/PageItemMobile";
|
||||
import {
|
||||
ConfigData,
|
||||
getItemPageNames,
|
||||
isMobileViewport,
|
||||
} from "../config";
|
||||
import { ConfigData, getItemPageNames, isMobileViewport } from "../config";
|
||||
import { Item } from "../model";
|
||||
|
||||
type RouterProps = {
|
||||
@@ -30,7 +27,11 @@ enum page {
|
||||
notFound,
|
||||
}
|
||||
|
||||
const getPageByName = (items: Item[], pageName: string, config: ConfigData): page => {
|
||||
const getPageByName = (
|
||||
items: Item[],
|
||||
pageName: string,
|
||||
config: ConfigData
|
||||
): page => {
|
||||
if (pageName === "index") {
|
||||
return page.index;
|
||||
}
|
||||
@@ -55,7 +56,7 @@ export default function Router({
|
||||
items,
|
||||
releases,
|
||||
search,
|
||||
config
|
||||
config,
|
||||
}: RouterProps) {
|
||||
const [statePageName, setStatePageName] = useState(pageName);
|
||||
const [leaving, setLeaving] = useState(false);
|
||||
@@ -63,7 +64,8 @@ export default function Router({
|
||||
|
||||
useEffect(() => {
|
||||
const nowLeaving =
|
||||
getPageByName(items, pageName, config) !== getPageByName(items, statePageName, config);
|
||||
getPageByName(items, pageName, config) !==
|
||||
getPageByName(items, statePageName, config);
|
||||
if (nowLeaving) {
|
||||
setLeaving(true);
|
||||
setNextPageName(pageName);
|
||||
|
||||
@@ -15,14 +15,14 @@ interface PageHelp {
|
||||
paragraphs: Paragraph[];
|
||||
quadrantsPreDescription?: string;
|
||||
quadrants: Quadrant[];
|
||||
rings: {name: string, description: string }[];
|
||||
rings: { name: string; description: string }[];
|
||||
ringsPreDescription?: string;
|
||||
sourcecodeLink?: {
|
||||
href: string;
|
||||
name: string;
|
||||
description: string;
|
||||
};
|
||||
headlinePrefix?: string
|
||||
headlinePrefix?: string;
|
||||
}
|
||||
|
||||
interface PageOverview {
|
||||
@@ -54,10 +54,9 @@ export interface Messages {
|
||||
|
||||
const MessagesContext = createContext<Messages | undefined>(undefined);
|
||||
|
||||
export const MessagesProvider: FC<{ messages?: Messages }> = ({
|
||||
messages,
|
||||
children,
|
||||
}) => (
|
||||
export const MessagesProvider: FC<
|
||||
React.PropsWithChildren<{ messages?: Messages }>
|
||||
> = ({ messages, children }) => (
|
||||
<MessagesContext.Provider value={messages}>
|
||||
{children}
|
||||
</MessagesContext.Provider>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import App from "./components/App";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import "./index.scss";
|
||||
|
||||
ReactDOM.render(
|
||||
const container = document.getElementById("root")!;
|
||||
const root = createRoot(container);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById("root")
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
@@ -16,5 +16,3 @@ describe("sanitize", () => {
|
||||
expect(res.__html).toEqual("<a href=\"https://example.org\">Example.org</a>");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user