Skip to content

Commit

Permalink
chore(website): Split form demos into separate pages
Browse files Browse the repository at this point in the history
  • Loading branch information
mlaursen committed Jul 18, 2021
1 parent 367cc0d commit 8594930
Show file tree
Hide file tree
Showing 94 changed files with 442 additions and 270 deletions.
10 changes: 7 additions & 3 deletions packages/dev-utils/src/indexer/generate.ts
Expand Up @@ -4,7 +4,7 @@ import { join, sep } from "path";

import { documentationRoot, src } from "../constants";
import { toId, toTitle } from "../utils";
import { parseDemoIndex } from "./parseDemoIndex";
import { parseDemoRenderer } from "./parseDemoIndex";
import { parseMarkdown } from "./parseMarkdown";
import { parseSassDocAnchors } from "./parseSassDocAnchors";
import {
Expand Down Expand Up @@ -62,6 +62,10 @@ function getTitleForRoute(route: string): string {
.replace("Api", "API")
.replace("Sassdoc", "SassDoc");

if (/Demos$/.test(title) && route.includes("form")) {
return title;
}

if (
/SassDoc|API|Demos|Changelog/.test(title) ||
(title === "Installation" && route.startsWith("/packages"))
Expand Down Expand Up @@ -104,9 +108,9 @@ export async function generate(
type = "theme";
summary =
"Create a custom theme for your app and this documentation site using the live preview. The theme builder supports changing the theme colors using the material design color palette as well as warning when there are contrast ratio problems.";
} else if (route.endsWith("/demos")) {
} else if (/(\/|-)demos$/.test(route)) {
type = "demos";
({ anchors, summary, demos } = parseDemoIndex(route));
({ anchors, summary, demos } = parseDemoRenderer(route));
} else if (/\/(api|sassdoc)$/.test(route)) {
const [routeType, pkgName] = route.split("/").reverse();
switch (routeType) {
Expand Down
6 changes: 4 additions & 2 deletions packages/dev-utils/src/indexer/getRoutes.ts
Expand Up @@ -21,11 +21,13 @@ export async function getRoutes({
const pagesFolder = join(documentationRoot, src, "pages");
const paths = await glob("**/*.+(ts|tsx)", {
cwd: pagesFolder,
ignore: ["api/**/*", "index.ts", "_*"],
ignore: ["api/**/*", "index.ts", "**/form/demos.tsx", "_*"],
});

const apiablePackages = getPackages("typescript");
const demoablePackages = apiablePackages.filter((name) => name !== "layout");
const demoablePackages = apiablePackages.filter(
(name) => name !== "layout" && name !== "form"
);
const sassdocablePackages = getPackages("scss");
const packages = getPackages();

Expand Down
20 changes: 15 additions & 5 deletions packages/dev-utils/src/indexer/parseDemoIndex.ts
Expand Up @@ -19,18 +19,28 @@ interface ReduceResult {
demos: DemoMetadata[];
}

export function parseDemoIndex(demoRoute: string): MarkdownResult {
const [, pkgName] = demoRoute.split("/").reverse();
const demoFolder = join(
export function parseDemoRenderer(demoRoute: string): MarkdownResult {
const [fileName, pkgName] = demoRoute.split("/").reverse();
let demoFolder = join(
documentationRoot,
src,
"components",
"Demos",
toTitle(pkgName, "")
);
const demoIndexPath = join(demoFolder, "index.tsx");

const contents = readFileSync(demoIndexPath, "utf8");
let demoRendererPath = join(demoFolder, "index.tsx");
if (fileName.endsWith("-demos")) {
// I really need to redo this...
const partName = toTitle(
fileName.replace("-demos", fileName.startsWith("validation") ? "" : "s"),
""
);
demoFolder = join(demoFolder, partName);
demoRendererPath = join(demoFolder, `${partName}.tsx`);
}

const contents = readFileSync(demoRendererPath, "utf8");
const lines = contents.split(/\r?\n/);

const { anchors, demos } = lines.reduce<ReduceResult>(
Expand Down
9 changes: 9 additions & 0 deletions packages/dev-utils/src/sandbox/constants.ts
Expand Up @@ -10,6 +10,15 @@ export const SANDBOXES_PATH = join(
"sandboxes"
);

export const FORM_PARTS = [
"FileInputs",
"SelectFields",
"SelectionControls",
"Sliders",
"TextFields",
"Validation",
];

export const DEPENDENCIES = [
"@react-md/layout",
"@react-md/states",
Expand Down
34 changes: 24 additions & 10 deletions packages/dev-utils/src/sandbox/createPackageJson.ts
@@ -1,6 +1,6 @@
import { JSONObject } from "../constants";
import { toId } from "../utils";
import { DEV_DEPENDENCIES } from "./constants";
import { DEV_DEPENDENCIES, FORM_PARTS } from "./constants";
import { ReferencedDependencies } from "./getAllDependencies";

function toDependencyJson(
Expand Down Expand Up @@ -48,19 +48,33 @@ function toDevDependencyJson(
return toDependencyJson(deps);
}

export function createPackageJson(
demoTitle: string,
fullDemoTitle: string,
packageName: string,
dependencies: ReferencedDependencies
): JSONObject {
interface CreatePackageJsonOptions {
demoTitle: string;
formPart: typeof FORM_PARTS[number] | undefined;
packageName: string;
dependencies: ReferencedDependencies;
fullDemoTitle: string;
}

export function createPackageJson({
demoTitle,
formPart,
packageName,
dependencies,
fullDemoTitle,
}: CreatePackageJsonOptions): JSONObject {
const allDeps = Array.from(dependencies);
let demoPrefix = `${toId(packageName)}/demos`;
if (formPart) {
demoPrefix = demoPrefix.replace("/demos", `/${toId(formPart)}-demos`);
}

const demoId = toId(demoTitle);
const packageDemoUrl = `https://react-md.dev/packages/${demoPrefix}#${demoId}`;

const packageJson: JSONObject = {
title: fullDemoTitle,
description: `Example from https://react-md.dev/packages/${toId(
packageName
)}/demos#${toId(demoTitle)}`,
description: `Example from ${packageDemoUrl}`,
main: "src/index.tsx",
dependencies: toDependencyJson(allDeps),
devDependencies: toDevDependencyJson(allDeps, DEV_DEPENDENCIES),
Expand Down
24 changes: 20 additions & 4 deletions packages/dev-utils/src/sandbox/createSandbox.ts
Expand Up @@ -10,6 +10,7 @@ import { getAliases } from "./aliases";
import {
DEMO_INDEX,
DEMO_INDEX_HTML,
FORM_PARTS,
SANDBOXES_PATH,
VARIABLES_SCSS_FILE,
} from "./constants";
Expand Down Expand Up @@ -53,6 +54,7 @@ function transformFileContents(
.replace(/<CodeBlock[^>]*>/g, "<pre><code>")
.replace(/<\/CodeBlock>/g, "</code></pre>")
.replace(/<(\/)?Code/g, "<$1code")
.replace(/\.(\.\/TextFieldThemeConfig)/, "$1")
.replace(aliasRegExp, `"${aliasReplacement}`);

if (demoName) {
Expand Down Expand Up @@ -119,11 +121,23 @@ function createJsxSandbox(
writeJsonSync(sandboxPath.replace(".json", "-js.json"), files, { spaces: 2 });
}

const FORM_PARTS_REGEXP = new RegExp(`\\/(${FORM_PARTS.join("|")})`);
const FORM_PART_REGEXP = new RegExp(
`(${FORM_PARTS.reduce<string>(
(s, part) => `${s ? `${s}|` : ""}${part.replace(/s$/, "")}`,
""
)})`
);

export function createSandbox(
demo: ImportDeclaration,
project: Project,
parentFolder: string
): void {
const formPart = demo
.getSourceFile()
.getFilePath()
.match(FORM_PART_REGEXP)?.[0];
const importName = demo.getModuleSpecifierValue();
const sourceFile = getDemoSourceFile(importName, parentFolder, project);
const [dependencies, resolvedFiles] = getAllDependencies(sourceFile, project);
Expand All @@ -148,12 +162,13 @@ export function createSandbox(
"src/_variables.scss": {
content: VARIABLES_SCSS_FILE,
},
"package.json": createPackageJson(
"package.json": createPackageJson({
demoTitle,
fullDemoTitle,
formPart,
packageName,
dependencies
),
dependencies,
fullDemoTitle,
}),
"src/Demo.tsx": {
content: transformFileContents(
sourceFile.getFullText(),
Expand All @@ -168,6 +183,7 @@ export function createSandbox(
const fileName = aliasedFileName
.replace(aliasRegExp, "")
.replace(`${demoName}${sep}`, "")
.replace(FORM_PARTS_REGEXP, "")
.replace(/\/?Demos\/[A-z]+\/?/, "");

const key = join("src", `${fileName}${getExtension(fileName)}`);
Expand Down
8 changes: 6 additions & 2 deletions packages/dev-utils/src/sandbox/createSandboxes.ts
Expand Up @@ -25,8 +25,8 @@ export function createSandboxes(

const sourceFile = project.getSourceFileOrThrow(demoFilePath);
const imports = sourceFile.getImportDeclarations();
const i =
imports.findIndex((imp) => imp.getText().endsWith('../DemoPage";')) + 1;
let i =
imports.findIndex((imp) => imp.getText().endsWith('/DemoPage";')) + 1;
if (i === 0) {
log.error(
"Unable to find a `DemoPage` token for the current source file:"
Expand All @@ -35,6 +35,10 @@ export function createSandboxes(
log.error(new Error().stack);
process.exit(1);
}
if (imports[i].getText().endsWith('Demos/types";')) {
i += 1;
}

const demos = imports
.slice(i)
.filter(
Expand Down
19 changes: 17 additions & 2 deletions packages/dev-utils/src/sandbox/index.ts
Expand Up @@ -3,7 +3,12 @@ import log from "loglevel";
import { join } from "path";

import { clean, format, glob } from "../utils";
import { DEMOS_FOLDER, SANDBOXES_FILE, SANDBOXES_PATH } from "./constants";
import {
FORM_PARTS,
DEMOS_FOLDER,
SANDBOXES_FILE,
SANDBOXES_PATH,
} from "./constants";
import { createSandboxes } from "./createSandboxes";

type Lookups = Record<string, Record<string, string>>;
Expand Down Expand Up @@ -43,7 +48,17 @@ export async function sandbox({

const fullPattern = `${DEMOS_FOLDER}/${pattern}/index.tsx`;
const demoFiles = await glob(fullPattern);
if (!demoFiles.length) {
if (
pattern === "*" ||
/form/.test(pattern) ||
new RegExp(FORM_PARTS.join("|"), "i").test(pattern)
) {
const FORM_FILES = FORM_PARTS.map(
(part) => `${DEMOS_FOLDER}/Form/${part}/${part}.tsx`
);
demoFiles.push(...FORM_FILES);
}
if (!demoFiles.length && pattern !== "*") {
log.error("No demos found with the following pattern:");
log.error(pattern);
log.error(new Error().stack);
Expand Down
@@ -0,0 +1,24 @@
import React, { ReactElement } from "react";
import { IconProvider } from "@react-md/icon";

import DemoPage from "components/Demos/DemoPage";
import { DemoConfig } from "components/Demos/types";

import FileInputExample from "./FileInputExample";
import fileInputExample from "./FileInputExample.md";

const demos: DemoConfig[] = [
{
name: "File Input Example",
description: fileInputExample,
children: <FileInputExample />,
},
];

export default function FileInputs(): ReactElement {
return (
<IconProvider>
<DemoPage demos={demos} packageName="form" fonts={["Material Icons"]} />
</IconProvider>
);
}
@@ -0,0 +1 @@
export { default } from "./FileInputs";
21 changes: 0 additions & 21 deletions packages/documentation/src/components/Demos/Form/README.md

This file was deleted.

Expand Up @@ -10,7 +10,7 @@ import {

import states from "constants/states";

import TextFieldThemeConfig from "./TextFieldThemeConfig";
import TextFieldThemeConfig from "../TextFieldThemeConfig";

interface State {
name: string;
Expand Down
Expand Up @@ -7,7 +7,7 @@ import {
useChecked,
} from "@react-md/form";

import TextFieldThemeConfig from "./TextFieldThemeConfig";
import TextFieldThemeConfig from "../TextFieldThemeConfig";

const options = Array.from({ length: 8 }, (_, i) => `Option ${i + 1}`);

Expand Down
@@ -0,0 +1,42 @@
import React, { ReactElement } from "react";
import { IconProvider } from "@react-md/icon";

import DemoPage from "components/Demos/DemoPage";
import { DemoConfig } from "components/Demos/types";

import NativeSelectExample from "./NativeSelectExample";
import nativeSelectExample from "./NativeSelectExample.md";

import SelectExample from "./SelectExample";
import selectExample from "./SelectExample.md";

import {
CustomizingSelectOptions,
customizingSelectOptions,
} from "./CustomizingSelectOptions";

const demos: DemoConfig[] = [
{
name: "Native Select Example",
description: nativeSelectExample,
children: <NativeSelectExample />,
},
{
name: "Select Example",
description: selectExample,
children: <SelectExample />,
},
{
name: "Customizing Select Options",
description: customizingSelectOptions,
children: <CustomizingSelectOptions />,
},
];

export default function Selects(): ReactElement | null {
return (
<IconProvider>
<DemoPage demos={demos} packageName="form" fonts={["Material Icons"]} />
</IconProvider>
);
}
@@ -0,0 +1 @@
export { default } from "./SelectFields";