From 9125e958dc4184ad9fd61a1c8af98d534cbcf14d Mon Sep 17 00:00:00 2001
From: Ady Beraud <102751374+ady-beraud@users.noreply.github.com>
Date: Thu, 16 May 2024 17:24:48 +0300
Subject: [PATCH] Modified HTML for Algolia Crawler (#5441)
* Modified HTML for Algolia Crawler
* Added anchor tags within user-guide headers
To implement Algolia correctly, my changes would need to be deployed
first, as it cannot run on localhost. In the meantime, I simulated
Algolia crawling locally using Cheerio, and it worked successfully on my
end.
---
.../ui/layout/articles/ArticleContent.tsx | 7 +++-
.../user-guide/UserGuideContent.tsx | 6 ++-
.../user-guide/UserGuideSidebarSection.tsx | 3 +-
.../shared-utils/wrapHeadingsWithAnchor.tsx | 40 +++++++++++++++++++
4 files changed, 52 insertions(+), 4 deletions(-)
create mode 100644 packages/twenty-website/src/shared-utils/wrapHeadingsWithAnchor.tsx
diff --git a/packages/twenty-website/src/app/_components/ui/layout/articles/ArticleContent.tsx b/packages/twenty-website/src/app/_components/ui/layout/articles/ArticleContent.tsx
index b9492a8cd..9041e504a 100644
--- a/packages/twenty-website/src/app/_components/ui/layout/articles/ArticleContent.tsx
+++ b/packages/twenty-website/src/app/_components/ui/layout/articles/ArticleContent.tsx
@@ -4,6 +4,7 @@ import { ReactNode } from 'react';
import styled from '@emotion/styled';
import { Theme } from '@/app/_components/ui/theme/theme';
+import { wrapHeadingsWithAnchor } from '@/shared-utils/wrapHeadingsWithAnchor';
const StyledContent = styled.div`
flex: 1;
@@ -27,6 +28,10 @@ const StyledContent = styled.div`
font-family: var(--font-gabarito);
color: ${Theme.text.color.primary};
font-weight: 700;
+ a {
+ text-decoration: none;
+ color: ${Theme.text.color.primary};
+ }
}
h1 {
@@ -89,5 +94,5 @@ const StyledContent = styled.div`
`;
export const ArticleContent = ({ children }: { children: ReactNode }) => {
- return {children};
+ return {wrapHeadingsWithAnchor(children)};
};
diff --git a/packages/twenty-website/src/app/_components/user-guide/UserGuideContent.tsx b/packages/twenty-website/src/app/_components/user-guide/UserGuideContent.tsx
index 1b0166ec6..39f3239a8 100644
--- a/packages/twenty-website/src/app/_components/user-guide/UserGuideContent.tsx
+++ b/packages/twenty-website/src/app/_components/user-guide/UserGuideContent.tsx
@@ -54,10 +54,11 @@ const StyledHeader = styled.div`
}
`;
-const StyledHeading = styled.div`
+const StyledHeading = styled.h1`
font-size: 40px;
font-weight: 700;
font-family: var(--font-gabarito);
+ margin: 0px;
@media (max-width: 800px) {
font-size: 28px;
}
@@ -77,12 +78,13 @@ const StyledHeaderInfoSectionTitle = styled.div`
font-family: var(--font-gabarito);
`;
-const StyledHeaderInfoSectionSub = styled.div`
+const StyledHeaderInfoSectionSub = styled.p`
display: flex;
flex-direction: column;
gap: ${Theme.spacing(4)};
color: ${Theme.text.color.tertiary};
line-height: 1.8;
+ margin: 0px;
`;
const StyledRectangle = styled.div`
diff --git a/packages/twenty-website/src/app/_components/user-guide/UserGuideSidebarSection.tsx b/packages/twenty-website/src/app/_components/user-guide/UserGuideSidebarSection.tsx
index 79f429290..8a8e33ac4 100644
--- a/packages/twenty-website/src/app/_components/user-guide/UserGuideSidebarSection.tsx
+++ b/packages/twenty-website/src/app/_components/user-guide/UserGuideSidebarSection.tsx
@@ -31,7 +31,7 @@ const StyledTitle = styled.div`
font-weight: 600;
`;
-const StyledSubTopicItem = styled.div<{ isselected: boolean }>`
+const StyledSubTopicItem = styled.a<{ isselected: boolean }>`
cursor: pointer;
display: flex;
flex-direction: row;
@@ -135,6 +135,7 @@ const UserGuideSidebarSection = ({
router.push(`/user-guide/${card.fileName}`)}
onMouseEnter={() => setHoveredItem(card.title)}
onMouseLeave={() => setHoveredItem(null)}
diff --git a/packages/twenty-website/src/shared-utils/wrapHeadingsWithAnchor.tsx b/packages/twenty-website/src/shared-utils/wrapHeadingsWithAnchor.tsx
new file mode 100644
index 000000000..3b7c85582
--- /dev/null
+++ b/packages/twenty-website/src/shared-utils/wrapHeadingsWithAnchor.tsx
@@ -0,0 +1,40 @@
+import React, {
+ Children,
+ cloneElement,
+ isValidElement,
+ ReactElement,
+ ReactNode,
+} from 'react';
+
+export const wrapHeadingsWithAnchor = (children: ReactNode): ReactNode => {
+ const hasChildren = (
+ element: ReactElement,
+ ): element is ReactElement<{ children: ReactNode }> => {
+ return element.props.children !== undefined;
+ };
+
+ return Children.map(children, (child) => {
+ if (
+ isValidElement(child) &&
+ typeof child.type === 'string' &&
+ ['h1', 'h2', 'h3', 'h4'].includes(child.type)
+ ) {
+ const id = child.props.children
+ .toString()
+ .replace(/\s+/g, '-')
+ .toLowerCase();
+ return cloneElement(child as ReactElement, {
+ id: id,
+ children: {child.props.children},
+ });
+ }
+
+ if (isValidElement(child) && hasChildren(child)) {
+ return cloneElement(child, {
+ children: wrapHeadingsWithAnchor(child.props.children),
+ });
+ }
+
+ return child;
+ });
+};