Skip to content

Commit 412c14d

Browse files
Merge pull request #100 from solo-io/charlesthebird/customPages
Adding Custom Pages
2 parents ff7fa7a + a348600 commit 412c14d

27 files changed

+732
-74
lines changed

Dockerfile

+3-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ ENV VITE_PORTAL_SERVER_URL=$VITE_PORTAL_SERVER_URL \
4545
VITE_HOME_IMAGE_URL=$VITE_HOME_IMAGE_URL \
4646
VITE_APIS_IMAGE_URL=$VITE_APIS_IMAGE_URL \
4747
VITE_LOGO_IMAGE_URL=$VITE_LOGO_IMAGE_URL \
48-
VITE_COMPANY_NAME=$VITE_COMPANY_NAME
48+
VITE_COMPANY_NAME=$VITE_COMPANY_NAME \
49+
VITE_CUSTOM_PAGES=$VITE_CUSTOM_PAGES
4950

5051
# Copy the server files, (this includes the UI build).
5152
WORKDIR /app
@@ -70,4 +71,5 @@ ENTRYPOINT VITE_PORTAL_SERVER_URL=$VITE_PORTAL_SERVER_URL \
7071
VITE_APIS_IMAGE_URL=$VITE_APIS_IMAGE_URL \
7172
VITE_LOGO_IMAGE_URL=$VITE_LOGO_IMAGE_URL \
7273
VITE_COMPANY_NAME=$VITE_COMPANY_NAME \
74+
VITE_CUSTOM_PAGES=$VITE_CUSTOM_PAGES \
7375
node ./bin/www

Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ ifneq ($(VITE_COMPANY_NAME),)
104104
else ifneq ($(COMPANY_NAME),)
105105
UI_ARGS += VITE_COMPANY_NAME=$(COMPANY_NAME)
106106
endif
107+
#
108+
# CUSTOM PAGES
109+
ifneq ($(VITE_CUSTOM PAGES),)
110+
UI_ARGS += VITE_CUSTOM PAGES=$(VITE_CUSTOM_PAGES)
111+
else ifneq ($(CUSTOM_PAGES),)
112+
UI_ARGS += VITE_CUSTOM_PAGES=$(CUSTOM_PAGES)
113+
endif
107114

108115

109116

README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ This is an example Solo.io Gloo Platform Dev Portal frontend app, built with [Vi
2323
2. Build your image.
2424

2525
```sh
26-
docker build -t "your-image-name"
26+
docker build -t "your-image-name" .
2727
```
2828

2929
3. Push your image:
@@ -137,6 +137,13 @@ You can add these environment variables to a `.env.local` file in the `projects/
137137
- `VITE_HOME_IMAGE_URL` - This is an optional parameter to set the image URL on the home page.
138138
- `VITE_APIS_IMAGE_URL` - This is an optional parameter to set the image URL on the apis page.
139139
- `VITE_LOGO_IMAGE_URL` - This is an optional parameter to set the image URL for the logo in the upper left.
140+
- `VITE_CUSTOM_PAGES` - This is an optional value that describes Markdown or HTML custom pages that have been added to the `projects/ui/src/public` folder. In order to test this feature out out with the provided examples, set your `VITE_CUSTOM_PAGES` value to:
141+
```
142+
'[{"title": "Markdown Example", "path": "/pages/markdown-example.md"}, {"title": "HTML Example", "path": "/pages/html-example.html"}]'
143+
```
144+
When the website is opened, there should be two new pages in the top navigation bar.
145+
![custom pages example](readme_assets/custom-pages-navbar.png)
146+
The custom page's `path` property must be publicly accessible and end with `.md` or `.html`.
140147
141148
#### Environment Variables for PKCE Authorization Flow
142149

changelog/v0.0.36/custom-pages.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
changelog:
2+
- type: FIX
3+
issueLink: https://github.com/solo-io/solo-projects/issues/6860
4+
description: >-
5+
Adds the ability for users to create custom pages that show up in the UI.

projects/ui/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"@mantine/hooks": "^6.0.6",
2626
"@types/color": "^3.0.6",
2727
"color": "^4.2.3",
28+
"highlight.js": "^11.10.0",
2829
"mobx": "^6.8.0",
2930
"react": "^18.2.0",
3031
"react-dom": "^18.2.0",

projects/ui/public/pages/gg-logo.png

2.48 KB
Loading
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<!doctype html>
2+
<html>
3+
<body>
4+
<style>
5+
body {
6+
padding: 20px 50px;
7+
/* background-color: bisque; */
8+
}
9+
</style>
10+
11+
<h1>Example HTML Page</h1>
12+
13+
<h2>Section 1</h2>
14+
15+
<ul>
16+
<li>This is an example custom page.</li>
17+
<li>Feel free to update this to your needs.</li>
18+
</ul>
19+
20+
<h2>Section 2</h2>
21+
22+
<p>Any HTML content can go here.</p>
23+
24+
Here is an image:
25+
<br />
26+
<img
27+
src="/pages/gg-logo.png"
28+
alt="Gloo Gateway Logo"
29+
style="
30+
width: 50px;
31+
padding: 5px;
32+
border-radius: 8px;
33+
margin: 20px 0px;
34+
background: white;
35+
border: 2px solid #d6d6d6;
36+
"
37+
/>
38+
39+
<br />
40+
<button onclick="alert('Anything in an HTML iframe is supported here!')">
41+
Click me!
42+
</button>
43+
</body>
44+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Example Markdown Page (#)
2+
3+
This is a custom Markdown page test.
4+
5+
## Section 1 (##)
6+
7+
- Supports bullet points
8+
- Supports bullet points
9+
10+
### 1.1 (###)
11+
12+
Testing that **Bold works** here.
13+
14+
#### 1.1.1 (####)
15+
16+
Testing that _Italics works_ here.
17+
18+
##### 1.1.1 (#####)
19+
20+
Links work: [www.solo.io](www.solo.io)
21+
22+
1. Numbered lists work
23+
2. test
24+
3. test
25+
26+
Images work:
27+
28+
![Gloo Gateway Logo](/pages/gg-logo.png)
29+
30+
And code does too:
31+
32+
```ts
33+
const x = 123;
34+
function y() {
35+
return x + 5;
36+
}
37+
```

projects/ui/src/Components/ApiDetails/gloo-gateway-components/DocsTab/DocsTabContent.tsx

+2-24
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,7 @@
1-
import { css } from "@emotion/react";
2-
import styled from "@emotion/styled";
31
import { Box } from "@mantine/core";
4-
import Markdown from "react-markdown";
5-
import remarkGfm from "remark-gfm";
62
import { ApiVersion } from "../../../../Apis/api-types";
73
import { CardStyles } from "../../../../Styles/shared/Card.style";
8-
9-
const MarkdownOuterContainer = styled.div(
10-
({ theme }) => css`
11-
padding: 30px;
12-
* {
13-
margin: revert;
14-
padding: revert;
15-
font-family: revert;
16-
font-weight: revert;
17-
}
18-
blockquote p {
19-
color: ${theme.augustGrey};
20-
}
21-
`
22-
);
4+
import MarkdownRenderer from "../../../Common/MarkdownRenderer";
235

246
const DocsTabContent = ({
257
selectedApiVersion,
@@ -29,11 +11,7 @@ const DocsTabContent = ({
2911
return (
3012
<Box pb={"60px"}>
3113
<CardStyles.Card>
32-
<MarkdownOuterContainer>
33-
<Markdown remarkPlugins={[remarkGfm]}>
34-
{selectedApiVersion.documentation}
35-
</Markdown>
36-
</MarkdownOuterContainer>
14+
<MarkdownRenderer markdown={selectedApiVersion.documentation} />
3715
</CardStyles.Card>
3816
</Box>
3917
);

projects/ui/src/Components/App.tsx

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Global, ThemeProvider } from "@emotion/react";
22
import { MantineProvider } from "@mantine/core";
33
import { AppContextProvider } from "../Context/AppContext";
4+
import { AppUtilsContextProvider } from "../Context/AppUtilsContext";
45
import { defaultTheme, globalStyles } from "../Styles";
56
import { mantineThemeOverride } from "../Styles/global-styles/mantine-theme";
67
import PortalServerTypeChecker from "../Utility/PortalServerTypeChecker";
@@ -15,17 +16,19 @@ export function App() {
1516
return (
1617
<ThemeProvider theme={defaultTheme}>
1718
<Global styles={globalStyles} />
18-
<AppContextProvider>
19-
<PortalServerTypeChecker />
19+
<AppUtilsContextProvider>
20+
<AppContextProvider>
21+
<PortalServerTypeChecker />
2022

21-
<MantineProvider
22-
withGlobalStyles
23-
withNormalizeCSS
24-
theme={mantineThemeOverride}
25-
>
26-
<AppContent />
27-
</MantineProvider>
28-
</AppContextProvider>
23+
<MantineProvider
24+
withGlobalStyles
25+
withNormalizeCSS
26+
theme={mantineThemeOverride}
27+
>
28+
<AppContent />
29+
</MantineProvider>
30+
</AppContextProvider>
31+
</AppUtilsContextProvider>
2932
</ThemeProvider>
3033
);
3134
}

projects/ui/src/Components/AppContentRoutes.tsx

+19
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { Navigate, Route, Routes } from "react-router-dom";
44
import { AppContext } from "../Context/AppContext";
55
import { AuthContext } from "../Context/AuthContext";
66
import {
7+
customPages,
78
oidcAuthCodeConfigCallbackPath,
89
oidcAuthCodeConfigLogoutPath,
910
} from "../user_variables.tmplr";
11+
import { getCustomPagePath } from "../Utility/utility";
1012
import AdminSubscriptionsPage from "./AdminSubscriptions/AdminSubscriptionsPage";
1113
import AdminTeamsPage from "./AdminTeams/AdminTeamsPage";
1214
import { ApiDetailsPage } from "./ApiDetails/ApiDetailsPage";
@@ -15,6 +17,7 @@ import { AppsPage } from "./Apps/AppsPage";
1517
import AppDetailsPage from "./Apps/Details/AppDetailsPage";
1618
import { ErrorBoundary } from "./Common/ErrorBoundary";
1719
import LoggedOut from "./Common/LoggedOut";
20+
import CustomPageLanding from "./CustomPage/CustomPageLanding";
1821
import { HomePage } from "./Home/HomePage";
1922
import { Footer } from "./Structure/Footer";
2023
import TeamDetailsPage from "./Teams/Details/TeamDetailsPage";
@@ -173,6 +176,22 @@ function AppContentRoutes() {
173176
)}
174177
</>
175178
)}
179+
{customPages.map((page) => (
180+
<>
181+
{getCustomPagePath(page)}
182+
<Route
183+
key={page.path}
184+
path={getCustomPagePath(page)}
185+
element={
186+
<ErrorBoundary
187+
fallback={`There was an issue displaying the custom ${page.title} page.`}
188+
>
189+
<CustomPageLanding />
190+
</ErrorBoundary>
191+
}
192+
/>
193+
</>
194+
))}
176195
</Routes>
177196

178197
<Footer />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { css } from "@emotion/react";
2+
import styled from "@emotion/styled";
3+
import hljs from "highlight.js";
4+
import { useEffect, useRef } from "react";
5+
import Markdown from "react-markdown";
6+
import remarkGfm from "remark-gfm";
7+
import { borderRadiusConstants } from "../../Styles/constants";
8+
9+
export const MarkdownOuterContainer = styled.div(
10+
({ theme }) => css`
11+
padding: 30px;
12+
* {
13+
margin: revert;
14+
padding: revert;
15+
font-family: revert;
16+
font-weight: revert;
17+
}
18+
blockquote p {
19+
color: ${theme.augustGrey};
20+
}
21+
pre:has(code) {
22+
padding: 1rem 2rem;
23+
border-radius: ${borderRadiusConstants.small};
24+
width: 100%;
25+
background-color: #1c1b1b;
26+
}
27+
em {
28+
font-style: italic;
29+
}
30+
a {
31+
text-decoration: underline;
32+
}
33+
h1 {
34+
font-size: 2rem;
35+
}
36+
h2 {
37+
font-size: 1.7rem;
38+
}
39+
h3 {
40+
font-size: 1.5rem;
41+
}
42+
h4 {
43+
font-size: 1.2rem;
44+
}
45+
h5 {
46+
font-size: 1rem;
47+
}
48+
`
49+
);
50+
51+
const MarkdownRenderer = ({ markdown }: { markdown: string }) => {
52+
const mdContainerRef = useRef<HTMLDivElement | null>(null);
53+
54+
// Highlight the content when it's rendered.
55+
useEffect(() => {
56+
if (!markdown || !mdContainerRef.current) {
57+
return;
58+
}
59+
// Highlight each code element.
60+
// This is faster than doing `hljs.highlightAll()`.
61+
const codeElements = mdContainerRef.current.querySelectorAll("code");
62+
for (let i = 0; i < codeElements.length; i++) {
63+
hljs.highlightElement(codeElements[i]);
64+
}
65+
return () => {
66+
// If this "data-highlighted" attribute isn't reset, it may not
67+
// highlight the code correctly when the page is revisited.
68+
for (let i = 0; i < codeElements.length; i++) {
69+
codeElements[i]?.removeAttribute("data-highlighted");
70+
}
71+
};
72+
}, [markdown, mdContainerRef.current]);
73+
74+
return (
75+
<MarkdownOuterContainer ref={mdContainerRef}>
76+
<Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>
77+
</MarkdownOuterContainer>
78+
);
79+
};
80+
81+
export default MarkdownRenderer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useContext } from "react";
2+
import { AppUtilsContext } from "../../../Context/AppUtilsContext";
3+
import { CustomPage } from "../../../user_variables.tmplr";
4+
import { footerHeightPx } from "../../Structure/Footer";
5+
import { headerHeightPx } from "../../Structure/Header.style";
6+
7+
const CustomHtmlPage = ({
8+
customPage,
9+
customPageUrl,
10+
}: {
11+
customPage: CustomPage;
12+
customPageUrl: string;
13+
}) => {
14+
const { windowInnerWidth, windowInnerHeight } = useContext(AppUtilsContext);
15+
16+
return (
17+
<iframe
18+
title={customPage.title}
19+
style={{
20+
width: `${windowInnerWidth}px`,
21+
height: `${windowInnerHeight - footerHeightPx - headerHeightPx}px`,
22+
position: "fixed",
23+
}}
24+
src={customPageUrl}
25+
/>
26+
);
27+
};
28+
29+
export default CustomHtmlPage;

0 commit comments

Comments
 (0)