Skip to content

Commit

Permalink
Add ui debug page frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
Etheryte committed Aug 27, 2024
1 parent 053d67c commit 3f09a46
Show file tree
Hide file tree
Showing 8 changed files with 595 additions and 0 deletions.
3 changes: 3 additions & 0 deletions web/html/src/manager/storybook/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
storybook: () => import("./storybook.renderer"),
};
29 changes: 29 additions & 0 deletions web/html/src/manager/storybook/layout.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
:global(.old-theme),
:global(.new-theme) {
.header {
padding: 8px 16px;
background: #eee;
}

.section {
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px;
border: 1px solid #eee;

&:not(:last-child) {
margin-bottom: 16px;
}
}

.striped {
background: repeating-linear-gradient(-45deg, #fff, #fff 10px, #eee 10px, #eee 12px);
}

.row {
display: flex;
flex-direction: row;
gap: 16px;
}
}
31 changes: 31 additions & 0 deletions web/html/src/manager/storybook/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react";

import styles from "./layout.module.less";

type Props = {
children?: React.ReactNode;
};

export const StorySection = (props: Props) => {
return (
<>
<div className={styles.section}>{props.children}</div>
</>
);
};

export const StripedStorySection = (props: Props) => {
return (
<>
<div className={`${styles.section} ${styles.striped}`}>{props.children}</div>
</>
);
};

type RowProps = {
children?: React.ReactNode;
};

export const StoryRow = (props: RowProps) => {
return <div className={styles.row}>{props.children}</div>;
};
370 changes: 370 additions & 0 deletions web/html/src/manager/storybook/stories.generated.ts

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions web/html/src/manager/storybook/stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as generatedStories from "./stories.generated";

const storyGroups = Object.groupBy(Object.values(generatedStories), (item) => item.groupName);

export default Object.entries(storyGroups).map(([title, stories]) => ({ title, stories }));
29 changes: 29 additions & 0 deletions web/html/src/manager/storybook/storybook.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
:global(.old-theme),
:global(.new-theme) {
.header {
// position: sticky;
top: 8px;
padding-bottom: 8px;
background: #fff;
z-index: 1;
}

.story {
display: flex;
gap: 16px;

> div {
flex: 1 0 auto;
}

> pre {
flex: 1 1 auto;
overflow: auto;
max-width: 50%;

code {
white-space: pre;
}
}
}
}
16 changes: 16 additions & 0 deletions web/html/src/manager/storybook/storybook.renderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as React from "react";

import SpaRenderer from "core/spa/spa-renderer";

import { MessagesContainer } from "components/toastr";

import { Storybook } from "./storybook";

export const renderer = (id: string) =>
SpaRenderer.renderNavigationReact(
<>
<MessagesContainer />
<Storybook />
</>,
document.getElementById(id)
);
112 changes: 112 additions & 0 deletions web/html/src/manager/storybook/storybook.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Fragment, useEffect, useState } from "react";

import debugUtils from "core/debugUtils";

import { Button } from "components/buttons";
import { IconTag } from "components/icontag";

import { StoryRow } from "./layout";
import stories from "./stories";
import styles from "./storybook.module.less";

const STORAGE_KEY = "storybook-show-code";

export const Storybook = () => {
const [_hash, setHash] = useState(window.location.hash);
const hash = _hash.replace(/^#/, "");
const normalize = (input: string = "") => input.replaceAll(" ", "-").toLowerCase();

const activeTabHash = normalize(hash) || normalize(stories[0]?.title);

const [, _invalidate] = useState(0);
const invalidate = () => _invalidate((ii) => ii + 1);

const [showCode, _setShowCode] = useState(!!localStorage.getItem(STORAGE_KEY));
const setShowCode = (value: boolean) => {
_setShowCode(value);
if (value) {
localStorage.setItem(STORAGE_KEY, "true");
} else {
localStorage.removeItem(STORAGE_KEY);
}
};

useEffect(() => {
const listener = () => setHash(window.location.hash);
window.addEventListener("hashchange", listener);
return () => {
window.removeEventListener("hashchange", listener);
};
}, []);

return (
<>
<div className={styles.header}>
<h1>
<IconTag type="experimental" />
{t("Development debugging page")}
</h1>
<p>{t("This is a hidden page used by developers, if you found it by accident, good job!")}</p>
<p>
<code>{document.body.className}</code>
</p>

<StoryRow>
<Button
text="toggle base theme"
className="btn-default"
handler={() => {
debugUtils.toggleTheme();
invalidate();
}}
/>
<Button
text="toggle theme update"
className="btn-default"
handler={() => {
debugUtils.toggleUpdatedTheme();
invalidate();
}}
/>
<Button text="toggle code" className="btn-default" handler={() => setShowCode(!showCode)} />
</StoryRow>
</div>

<div className="spacewalk-content-nav">
<ul className="nav nav-tabs">
{stories
.sort((a, b) => a.title.localeCompare(b.title))
.map((item) => {
const tabHash = normalize(item.title);
return (
<li key={tabHash} className={tabHash === activeTabHash ? "active" : ""}>
<a href={`#${tabHash}`}>{item.title}</a>
</li>
);
})}
</ul>
</div>

{stories.map((group) => (
<div key={`${group.title}`}>
{normalize(group.title) === activeTabHash && group.stories?.map((item) => (
<Fragment key={`${group.title}-${item.title}`}>
<p>
<code>{item.title}</code>
</p>
<div className={styles.story}>
<div>{item.component ? <item.component /> : null}</div>
{showCode ? (
<pre>
<code>{item.raw}</code>
</pre>
) : null}
</div>
<hr />
</Fragment>
))}
</div>
))}
</>
);
};

0 comments on commit 3f09a46

Please sign in to comment.