From 6f8c142cce79713f5325064b0c05410811756ead Mon Sep 17 00:00:00 2001 From: seaerchin Date: Fri, 8 Nov 2024 14:15:16 +0800 Subject: [PATCH 01/10] refactor: rework not found layout 1. duplicate infobar to not found layout 2. export styles from infobar so it's kept in sync 3. add client component for search button --- .../components/complex/Infobar/Infobar.tsx | 2 +- .../next/components/complex/Infobar/index.ts | 2 +- .../next/layouts/NotFound/NotFound.tsx | 73 +++++++++++++++---- .../next/layouts/NotFound/SearchButton.tsx | 33 +++++++++ 4 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx diff --git a/packages/components/src/templates/next/components/complex/Infobar/Infobar.tsx b/packages/components/src/templates/next/components/complex/Infobar/Infobar.tsx index 07bec8eb3..21cd14577 100644 --- a/packages/components/src/templates/next/components/complex/Infobar/Infobar.tsx +++ b/packages/components/src/templates/next/components/complex/Infobar/Infobar.tsx @@ -38,7 +38,7 @@ const createInfobarStyles = tv({ }, }) -const compoundStyles = createInfobarStyles() +export const compoundStyles = createInfobarStyles() const Infobar = ({ title, diff --git a/packages/components/src/templates/next/components/complex/Infobar/index.ts b/packages/components/src/templates/next/components/complex/Infobar/index.ts index 90fc4abce..5129a0acd 100644 --- a/packages/components/src/templates/next/components/complex/Infobar/index.ts +++ b/packages/components/src/templates/next/components/complex/Infobar/index.ts @@ -1 +1 @@ -export { default } from "./Infobar" +export { default, compoundStyles } from "./Infobar" diff --git a/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx b/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx index 265658978..5885a000d 100644 --- a/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx +++ b/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx @@ -1,6 +1,9 @@ import { type NotFoundPageSchemaType } from "~/engine" -import { renderComponent } from "../../render" +import { getTailwindVariantLayout } from "~/utils" +import { compoundStyles } from "../../components/complex/Infobar/Infobar" +import { LinkButton } from "../../components/internal/LinkButton" import { Skeleton } from "../Skeleton" +import { NotFoundSearchButton } from "./SearchButton" const NotFoundLayout = ({ site, @@ -9,7 +12,13 @@ const NotFoundLayout = ({ LinkComponent, ScriptComponent, }: NotFoundPageSchemaType) => { + const simplifiedLayout = getTailwindVariantLayout(layout) + return ( + // NOTE: This is taken from Infobar in components. + // However, we duplicated it here so that we can set the + // search button as a client component and avoid streaming over a + // huge payload to our end user - {renderComponent({ - component: { - type: "infobar", - title: "404: Page not found", - description: "Sorry, the page you were looking for cannot be found", - buttonLabel: "Go to homepage", - buttonUrl: "/", - }, - layout, - site, - LinkComponent, - })} +
+
+
+
+

+ 404: Page not found +

+ +

+ Sorry, the page you were looking for cannot be found +

+
+ +
+ + Go to homepage + + + +
+
+
+
) diff --git a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx new file mode 100644 index 000000000..9f0d59e6f --- /dev/null +++ b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx @@ -0,0 +1,33 @@ +"use client" + +import { NotFoundPageSchemaType } from "~/engine" +import { LinkButton } from "../../components/internal/LinkButton" + +type NotFoundSearchButtonProps = Pick +export const NotFoundSearchButton = ({ + LinkComponent, +}: NotFoundSearchButtonProps) => { + const permalink = window.location.pathname + const lastUrlSegment = permalink.split("/").at(-1) ?? "" + // NOTE: Replace all non-alphanumeric characters with spaces + // then remove all spaces and join by `+`. + // This is because we might have run-on spaces from sequences of symbols + // like: `+=`, which would lead to 2 spaces + const missingPath = lastUrlSegment + .replaceAll(/\W_/gi, " ") + .split(" ") + .filter((v) => !!v) + .join("+") + + return ( + + Search for this item + + ) +} From f105800a5774bea46a0b7432f4b90752336f891c Mon Sep 17 00:00:00 2001 From: seaerchin Date: Fri, 8 Nov 2024 15:05:10 +0800 Subject: [PATCH 02/10] fix: usestate and effect for pathname --- .../next/layouts/NotFound/SearchButton.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx index 9f0d59e6f..fc0575ebf 100644 --- a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx +++ b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx @@ -1,5 +1,7 @@ "use client" +import { useLayoutEffect, useState } from "react" + import { NotFoundPageSchemaType } from "~/engine" import { LinkButton } from "../../components/internal/LinkButton" @@ -7,7 +9,20 @@ type NotFoundSearchButtonProps = Pick export const NotFoundSearchButton = ({ LinkComponent, }: NotFoundSearchButtonProps) => { - const permalink = window.location.pathname + const [permalink, setPermalink] = useState("") + + useLayoutEffect(() => { + // If no userAgent prop is provided, and we're on the client-side, set navigatorUserAgent from navigator + // The check for typeof window and navigator ensures this only runs in browser environments, not during server-side rendering + // We use setNavigatorUserAgent to update the state, which will trigger a re-render with the correct user agent + if ( + typeof window !== "undefined" && + typeof window.location !== "undefined" + ) { + setPermalink(window.location.pathname) + } + }, []) + const lastUrlSegment = permalink.split("/").at(-1) ?? "" // NOTE: Replace all non-alphanumeric characters with spaces // then remove all spaces and join by `+`. From 486b81a951abd514d6577c9bbe061242d964f1d6 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Mon, 11 Nov 2024 18:21:03 +0800 Subject: [PATCH 03/10] chore: fix regex --- .../src/templates/next/layouts/NotFound/SearchButton.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx index fc0575ebf..d7fa61f23 100644 --- a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx +++ b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx @@ -12,9 +12,7 @@ export const NotFoundSearchButton = ({ const [permalink, setPermalink] = useState("") useLayoutEffect(() => { - // If no userAgent prop is provided, and we're on the client-side, set navigatorUserAgent from navigator // The check for typeof window and navigator ensures this only runs in browser environments, not during server-side rendering - // We use setNavigatorUserAgent to update the state, which will trigger a re-render with the correct user agent if ( typeof window !== "undefined" && typeof window.location !== "undefined" @@ -29,7 +27,7 @@ export const NotFoundSearchButton = ({ // This is because we might have run-on spaces from sequences of symbols // like: `+=`, which would lead to 2 spaces const missingPath = lastUrlSegment - .replaceAll(/\W_/gi, " ") + .replaceAll(/[\W_]/gi, " ") .split(" ") .filter((v) => !!v) .join("+") From 0ddaab108ff4801d54a15f8ae588ef89739cdb94 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Tue, 12 Nov 2024 16:32:12 +0800 Subject: [PATCH 04/10] chore: rework styling --- .../src/templates/next/layouts/NotFound/NotFound.tsx | 9 +++++---- .../src/templates/next/layouts/NotFound/SearchButton.tsx | 3 +-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx b/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx index 5885a000d..debc1815e 100644 --- a/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx +++ b/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx @@ -50,7 +50,7 @@ const NotFoundLayout = ({

- 404: Page not found + Page not found

- Sorry, the page you were looking for cannot be found + This page might have been moved or deleted. Try searching for + this page instead.

@@ -67,16 +68,16 @@ const NotFoundLayout = ({ layout: simplifiedLayout, })} > + Go to homepage - - diff --git a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx index d7fa61f23..107321989 100644 --- a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx +++ b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx @@ -36,11 +36,10 @@ export const NotFoundSearchButton = ({ - Search for this item + Search for this page ) } From cdef7d40843c8574657441949f449a7062067eb6 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Tue, 12 Nov 2024 16:36:46 +0800 Subject: [PATCH 05/10] refactor: shift method into util --- .../templates/next/layouts/NotFound/SearchButton.tsx | 12 ++---------- .../components/src/utils/getWordsFromPermalink.ts | 12 ++++++++++++ packages/components/src/utils/index.ts | 1 + 3 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 packages/components/src/utils/getWordsFromPermalink.ts diff --git a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx index 107321989..e94e4eddb 100644 --- a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx +++ b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx @@ -3,6 +3,7 @@ import { useLayoutEffect, useState } from "react" import { NotFoundPageSchemaType } from "~/engine" +import { getWordsFromPermalink } from "~/utils" import { LinkButton } from "../../components/internal/LinkButton" type NotFoundSearchButtonProps = Pick @@ -21,16 +22,7 @@ export const NotFoundSearchButton = ({ } }, []) - const lastUrlSegment = permalink.split("/").at(-1) ?? "" - // NOTE: Replace all non-alphanumeric characters with spaces - // then remove all spaces and join by `+`. - // This is because we might have run-on spaces from sequences of symbols - // like: `+=`, which would lead to 2 spaces - const missingPath = lastUrlSegment - .replaceAll(/[\W_]/gi, " ") - .split(" ") - .filter((v) => !!v) - .join("+") + const missingPath = getWordsFromPermalink(permalink) return ( { + const lastUrlSegment = permalink.split("/").at(-1) ?? "" + // NOTE: Replace all non-alphanumeric characters with spaces + // then remove all spaces and join by `+`. + // This is because we might have run-on spaces from sequences of symbols + // like: `+=`, which would lead to 2 spaces + return lastUrlSegment + .replaceAll(/[\W_]/gi, " ") + .split(" ") + .filter((v) => !!v) + .join("+") +} diff --git a/packages/components/src/utils/index.ts b/packages/components/src/utils/index.ts index 8796cccb2..a2a4aadc2 100644 --- a/packages/components/src/utils/index.ts +++ b/packages/components/src/utils/index.ts @@ -15,3 +15,4 @@ export { isExternalUrl } from "./isExternalUrl" export { orderedListSchemaBuilder } from "./orderedListSchemaBuilder" export * from "./tailwind" export { unorderedListSchemaBuilder } from "./unorderedListSchemaBuilder" +export { getWordsFromPermalink } from "./getWordsFromPermalink" From fefb91f03f934dfc7d940815d730167c087b06d0 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Tue, 12 Nov 2024 18:42:35 +0800 Subject: [PATCH 06/10] chore: add tests --- .../__tests__/getWordsFromPermalink.test.ts | 66 +++++++++++++++++++ .../src/utils/getWordsFromPermalink.ts | 7 +- 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 packages/components/src/utils/__tests__/getWordsFromPermalink.test.ts diff --git a/packages/components/src/utils/__tests__/getWordsFromPermalink.test.ts b/packages/components/src/utils/__tests__/getWordsFromPermalink.test.ts new file mode 100644 index 000000000..c07ddad37 --- /dev/null +++ b/packages/components/src/utils/__tests__/getWordsFromPermalink.test.ts @@ -0,0 +1,66 @@ +import { describe, expect, it } from "vitest" + +import { getWordsFromPermalink } from "~/utils" + +describe("getWordsFromPermalink", () => { + it("should trim out all symbols and return the permalink as a space separated sentence for single level permalinks", () => { + // Arrange + const singleLevelPermalink = "/this-._single=level|" + const expected = "this+single+level" + + // Act + const actual = getWordsFromPermalink(singleLevelPermalink) + + // Assert + expect(actual).toBe(expected) + }) + + it("should trim out all symbols and return the last section as a space separated sentence for nested permalinks", () => { + // Arrange + const nestedPermalink = "/nested/deeply/this-._nest'fff=level|" + const expected = "this+nest+fff+level" + + // Act + const actual = getWordsFromPermalink(nestedPermalink) + + // Assert + expect(actual).toBe(expected) + }) + + it("should preserve `=` in the original permalink", () => { + // Arrange + const singleLevelPermalink = "/this-._single=level|" + const expected = "this+single+level" + + // Act + const actual = getWordsFromPermalink(singleLevelPermalink) + + // Assert + expect(actual).toBe(expected) + }) + + it("should handle uri-encoded strings correctly", () => { + // Arrange + const singleLevelPermalink = "/this-._single=level|" + const encodedPermalink = encodeURIComponent(singleLevelPermalink) + const expected = "this+single+level" + + // Act + const actual = getWordsFromPermalink(encodedPermalink) + + // Assert + expect(actual).toBe(expected) + }) + + it("should work with a trailing /", () => { + // Arrange + const singleLevelPermalink = "/this-._single=level|/" + const expected = "this+single+level" + + // Act + const actual = getWordsFromPermalink(singleLevelPermalink) + + // Assert + expect(actual).toBe(expected) + }) +}) diff --git a/packages/components/src/utils/getWordsFromPermalink.ts b/packages/components/src/utils/getWordsFromPermalink.ts index de7a13247..19b476c61 100644 --- a/packages/components/src/utils/getWordsFromPermalink.ts +++ b/packages/components/src/utils/getWordsFromPermalink.ts @@ -1,10 +1,13 @@ export const getWordsFromPermalink = (permalink: string): string => { - const lastUrlSegment = permalink.split("/").at(-1) ?? "" + const trimmedPermalink = permalink.endsWith("/") + ? permalink.slice(0, -1) + : permalink + const lastUrlSegment = trimmedPermalink.split("/").at(-1) ?? "" // NOTE: Replace all non-alphanumeric characters with spaces // then remove all spaces and join by `+`. // This is because we might have run-on spaces from sequences of symbols // like: `+=`, which would lead to 2 spaces - return lastUrlSegment + return decodeURIComponent(lastUrlSegment) .replaceAll(/[\W_]/gi, " ") .split(" ") .filter((v) => !!v) From 29b40bc77b38c92476de8b0c29c74eda69ba039a Mon Sep 17 00:00:00 2001 From: seaerchin Date: Fri, 15 Nov 2024 15:20:45 +0800 Subject: [PATCH 07/10] chore: remove misleading test --- .../utils/__tests__/getWordsFromPermalink.test.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/components/src/utils/__tests__/getWordsFromPermalink.test.ts b/packages/components/src/utils/__tests__/getWordsFromPermalink.test.ts index c07ddad37..143f5b886 100644 --- a/packages/components/src/utils/__tests__/getWordsFromPermalink.test.ts +++ b/packages/components/src/utils/__tests__/getWordsFromPermalink.test.ts @@ -27,18 +27,6 @@ describe("getWordsFromPermalink", () => { expect(actual).toBe(expected) }) - it("should preserve `=` in the original permalink", () => { - // Arrange - const singleLevelPermalink = "/this-._single=level|" - const expected = "this+single+level" - - // Act - const actual = getWordsFromPermalink(singleLevelPermalink) - - // Assert - expect(actual).toBe(expected) - }) - it("should handle uri-encoded strings correctly", () => { // Arrange const singleLevelPermalink = "/this-._single=level|" From b1f1ff9da7906a00fb113e77be8826b08d0945c2 Mon Sep 17 00:00:00 2001 From: seaerchin Date: Fri, 15 Nov 2024 15:21:58 +0800 Subject: [PATCH 08/10] fix: useEffect --- .../src/templates/next/layouts/NotFound/SearchButton.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx index e94e4eddb..d8d0db2e2 100644 --- a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx +++ b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx @@ -1,6 +1,6 @@ "use client" -import { useLayoutEffect, useState } from "react" +import { useEffect, useState } from "react" import { NotFoundPageSchemaType } from "~/engine" import { getWordsFromPermalink } from "~/utils" @@ -12,7 +12,7 @@ export const NotFoundSearchButton = ({ }: NotFoundSearchButtonProps) => { const [permalink, setPermalink] = useState("") - useLayoutEffect(() => { + useEffect(() => { // The check for typeof window and navigator ensures this only runs in browser environments, not during server-side rendering if ( typeof window !== "undefined" && From 1a020954fe8583244992233291a6c8391792eb34 Mon Sep 17 00:00:00 2001 From: adriangohjw Date: Sat, 16 Nov 2024 12:54:58 +0800 Subject: [PATCH 09/10] update to import as type instead --- .../src/templates/next/layouts/NotFound/SearchButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx index d8d0db2e2..80e8d897d 100644 --- a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx +++ b/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react" -import { NotFoundPageSchemaType } from "~/engine" +import type { NotFoundPageSchemaType } from "~/engine" import { getWordsFromPermalink } from "~/utils" import { LinkButton } from "../../components/internal/LinkButton" From a5dc7421beaf6b8056f23bd8e69a4103c9a464cf Mon Sep 17 00:00:00 2001 From: adriangohjw Date: Sat, 16 Nov 2024 13:28:43 +0800 Subject: [PATCH 10/10] update filename to NotFoundSearchButton for consistency --- .../components/src/templates/next/layouts/NotFound/NotFound.tsx | 2 +- .../NotFound/{SearchButton.tsx => NotFoundSearchButton.tsx} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/components/src/templates/next/layouts/NotFound/{SearchButton.tsx => NotFoundSearchButton.tsx} (100%) diff --git a/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx b/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx index debc1815e..c272e2d29 100644 --- a/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx +++ b/packages/components/src/templates/next/layouts/NotFound/NotFound.tsx @@ -3,7 +3,7 @@ import { getTailwindVariantLayout } from "~/utils" import { compoundStyles } from "../../components/complex/Infobar/Infobar" import { LinkButton } from "../../components/internal/LinkButton" import { Skeleton } from "../Skeleton" -import { NotFoundSearchButton } from "./SearchButton" +import { NotFoundSearchButton } from "./NotFoundSearchButton" const NotFoundLayout = ({ site, diff --git a/packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx b/packages/components/src/templates/next/layouts/NotFound/NotFoundSearchButton.tsx similarity index 100% rename from packages/components/src/templates/next/layouts/NotFound/SearchButton.tsx rename to packages/components/src/templates/next/layouts/NotFound/NotFoundSearchButton.tsx