Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c8c2cd098a | |||
| 0f2a6ac87b |
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"presets": ["@babel/env", "@babel/react"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"parserOptions": {
|
||||||
|
"project": "./tsconfig.eslint.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||||
|
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
|
||||||
|
framework: '@storybook/react',
|
||||||
|
core: {
|
||||||
|
builder: 'webpack5',
|
||||||
|
},
|
||||||
|
// webpackFinal: async (config, { configType }) => {
|
||||||
|
// // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
|
||||||
|
// // You can change the configuration based on that.
|
||||||
|
// // 'PRODUCTION' is used when building the static version of storybook.
|
||||||
|
webpackFinal: async (config) => {
|
||||||
|
config.module.rules.forEach((rule) => {
|
||||||
|
// look for SVG import rule and replace
|
||||||
|
// NOTE: the rule before modification is /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/
|
||||||
|
if (rule.test?.toString().includes('svg')) {
|
||||||
|
rule.test = /\.(ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// handle asset loading with this
|
||||||
|
config.module.rules.unshift({
|
||||||
|
test: /\.svg(\?.*)?$/i,
|
||||||
|
issuer: /\.[jt]sx?$/,
|
||||||
|
use: ['@svgr/webpack'],
|
||||||
|
});
|
||||||
|
|
||||||
|
config.resolve.extensions = ['.tsx', '.ts', '.js'];
|
||||||
|
config.resolve.plugins = [new TsconfigPathsPlugin()];
|
||||||
|
|
||||||
|
// Return the altered config
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
features: {
|
||||||
|
emotionAlias: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/* eslint-disable react/react-in-jsx-scope */
|
||||||
|
import { NymThemeProvider } from '@nymproject/mui-theme';
|
||||||
|
import { Box } from '@mui/material';
|
||||||
|
|
||||||
|
export const parameters = {
|
||||||
|
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||||
|
controls: {
|
||||||
|
matchers: {
|
||||||
|
color: /(background|color)$/i,
|
||||||
|
date: /Date$/,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const withThemeProvider = (Story, context) => (
|
||||||
|
<div style={{ display: 'grid', height: '100%', gridTemplateColumns: '50% 50%' }}>
|
||||||
|
<div>
|
||||||
|
<NymThemeProvider mode="light">
|
||||||
|
<Box
|
||||||
|
p={4}
|
||||||
|
sx={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateRows: '80vh 2rem',
|
||||||
|
background: (theme) => theme.palette.background.default,
|
||||||
|
color: (theme) => theme.palette.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ overflowY: 'auto' }}>
|
||||||
|
<Story {...context} />
|
||||||
|
</Box>
|
||||||
|
<h4 style={{ textAlign: 'center' }}>Light mode</h4>
|
||||||
|
</Box>
|
||||||
|
</NymThemeProvider>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<NymThemeProvider mode="dark">
|
||||||
|
<Box
|
||||||
|
p={4}
|
||||||
|
sx={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateRows: '80vh 2rem',
|
||||||
|
background: (theme) => theme.palette.background.default,
|
||||||
|
color: (theme) => theme.palette.text.primary,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ overflowY: 'auto' }}>
|
||||||
|
<Story {...context} />
|
||||||
|
</Box>
|
||||||
|
<h4 style={{ textAlign: 'center' }}>Dark mode</h4>
|
||||||
|
</Box>
|
||||||
|
</NymThemeProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const decorators = [withThemeProvider];
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
# Example with React + Typescript + Webpack 5 + MUI
|
||||||
|
|
||||||
|
An example of using default Webpack and Typescript settings with React and MUI, including theming.
|
||||||
|
|
||||||
|
You can use this example as a seed for a new project.
|
||||||
|
|
||||||
|
Remember to build the dependency packages from the root of this repo by running:
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn
|
||||||
|
yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to make changes to the dependency packages, you can run `yarn watch` in that package to watch for chagnes and build them. This project will pick up the changes in the built package and hot-reload / recompile.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### Yarn workspaces
|
||||||
|
|
||||||
|
Packages from `ts-packages` are shared using Yarn workspaces. Make sure you add you new project to [package.json](../../package.json) to use the shared packages.
|
||||||
|
|
||||||
|
> ⚠️ **Warning**: Yarn workspaces will share all dependencies between projects and works by falling back to parent directories until a `node_modules` directory is found. So be careful when messing around with `node_modules` and resolution, because unexpected things could happen - for example, if you do not run `yarn` from the root and you have a `node_modules` in a directory that is a parent of the directory where you checkout out this repository, that `node_modules` will be used for resolving packages 🙀.
|
||||||
|
|
||||||
|
### Typescript
|
||||||
|
|
||||||
|
Shared Typescript config is in [tsconfig.json](./tsconfig.json), with specific production settings in [tsconfig.prod.json](./tsconfig.prod.json) that:
|
||||||
|
|
||||||
|
- exclude Storybook stories and Jest tests
|
||||||
|
- do not output typing `*.d.ts` files
|
||||||
|
|
||||||
|
### Webpack
|
||||||
|
|
||||||
|
Inherit config for Webpack 5 with additional tweaks including:
|
||||||
|
|
||||||
|
- favicon generation from [favicon asset files](../../assets/favicon/favicon.png)
|
||||||
|
- asset handling (svg, png, fonts, css, etc)
|
||||||
|
- minification
|
||||||
|
|
||||||
|
The development settings include:
|
||||||
|
|
||||||
|
- `ts-loader` for quick transpilation
|
||||||
|
- threaded type checking using `tsc`
|
||||||
|
- hot reloading using `react-refresh`
|
||||||
|
|
||||||
|
### Storybook
|
||||||
|
|
||||||
|
Storybook is available in [@nymproject/react](../react-components/src/stories/Introduction.stories.mdx) and can be run using `yarn storybook`.
|
||||||
|
|
||||||
|
### MUI and theming
|
||||||
|
|
||||||
|
The [Nym theme](../mui-theme/src/theme/theme.ts) provides a theme provider that you can add as follows:
|
||||||
|
|
||||||
|
```typescript jsx
|
||||||
|
export const App: React.FC = () => (
|
||||||
|
<AppContextProvider>
|
||||||
|
<AppTheme>
|
||||||
|
<Content />
|
||||||
|
</AppTheme>
|
||||||
|
</AppContextProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const AppTheme: React.FC = ({ children }) => {
|
||||||
|
const { mode } = useAppContext();
|
||||||
|
|
||||||
|
return <NymThemeProvider mode={mode}>{children}</NymThemeProvider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Content: React.FC = () => {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And augment typings for the Theme by adding [mui-theme.d.ts](./src/theme/mui-theme.d.ts):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Theme, ThemeOptions, Palette, PaletteOptions } from '@mui/material/styles';
|
||||||
|
import { NymTheme, NymPaletteWithExtensions, NymPaletteWithExtensionsOptions } from '@nymproject/mui-theme';
|
||||||
|
|
||||||
|
declare module '@mui/material/styles' {
|
||||||
|
interface Theme extends NymTheme {}
|
||||||
|
interface ThemeOptions extends Partial<NymTheme> {}
|
||||||
|
interface Palette extends NymPaletteWithExtensions {}
|
||||||
|
interface PaletteOptions extends NymPaletteWithExtensionsOptions {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Adding the above, means that any component now has the correct typings, for example, below the Nym palette interface is available for all MUI `Theme` instances with code completion for VSCode and IntelliJ:
|
||||||
|
|
||||||
|
```typescript jsx
|
||||||
|
import { Typography } from '@mui/material';
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
<Typography sx={{ color: (theme) => theme.palette.nym.networkExplorer.mixnodes.status.active }}>
|
||||||
|
The quick brown fox jumps over the white fence
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
```
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
};
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
{
|
||||||
|
"name": "@nymproject/cli-fig-docs",
|
||||||
|
"description": "A library to render user docs from a Fig spec",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@mui/icons-material": "^5.5.0",
|
||||||
|
"@mui/lab": "^5.0.0-alpha.72",
|
||||||
|
"@mui/material": "^5.0.1",
|
||||||
|
"@mui/styles": "^5.0.1",
|
||||||
|
"@nymproject/mui-theme": "^1.0.0",
|
||||||
|
"@nymproject/react": "^1.0.0",
|
||||||
|
"react": "^17.0.2",
|
||||||
|
"react-dom": "^17.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@storybook/addon-actions": "^6.5.8",
|
||||||
|
"@storybook/addon-essentials": "^6.5.8",
|
||||||
|
"@storybook/addon-interactions": "^6.5.8",
|
||||||
|
"@storybook/addon-links": "^6.5.8",
|
||||||
|
"@storybook/builder-webpack5": "^6.5.8",
|
||||||
|
"@storybook/manager-webpack5": "^6.5.8",
|
||||||
|
"@storybook/react": "^6.5.8",
|
||||||
|
"@storybook/testing-library": "^0.0.9",
|
||||||
|
"@babel/core": "^7.15.0",
|
||||||
|
"@babel/plugin-transform-async-to-generator": "^7.14.5",
|
||||||
|
"@babel/preset-env": "^7.15.0",
|
||||||
|
"@babel/preset-react": "^7.14.5",
|
||||||
|
"@babel/preset-typescript": "^7.15.0",
|
||||||
|
"@nymproject/eslint-config-react-typescript": "^1.0.0",
|
||||||
|
"@nymproject/webpack": "^1.0.0",
|
||||||
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
|
||||||
|
"@svgr/webpack": "^6.1.1",
|
||||||
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
|
"@testing-library/react": "^12.0.0",
|
||||||
|
"@types/jest": "^27.0.1",
|
||||||
|
"@types/node": "^16.7.13",
|
||||||
|
"@types/react": "^17.0.34",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||||
|
"@typescript-eslint/parser": "^5.13.0",
|
||||||
|
"@withfig/autocomplete-types": "^1.23.0",
|
||||||
|
"babel-loader": "^8.2.2",
|
||||||
|
"babel-plugin-root-import": "^5.1.0",
|
||||||
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
|
"css-loader": "^6.2.0",
|
||||||
|
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||||
|
"dotenv-webpack": "^7.0.3",
|
||||||
|
"eslint": "^8.10.0",
|
||||||
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
|
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"eslint-import-resolver-root-import": "^1.0.4",
|
||||||
|
"eslint-plugin-import": "^2.25.4",
|
||||||
|
"eslint-plugin-jest": "^26.1.1",
|
||||||
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
"eslint-plugin-react": "^7.29.2",
|
||||||
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
|
"eslint-plugin-storybook": "^0.5.12",
|
||||||
|
"favicons": "^6.2.2",
|
||||||
|
"favicons-webpack-plugin": "^5.0.2",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
|
"fork-ts-checker-webpack-plugin": "^7.2.1",
|
||||||
|
"html-webpack-plugin": "^5.3.2",
|
||||||
|
"jest": "^27.1.0",
|
||||||
|
"mini-css-extract-plugin": "^2.2.2",
|
||||||
|
"prettier": "^2.5.1",
|
||||||
|
"react-refresh-typescript": "^2.0.3",
|
||||||
|
"style-loader": "^3.2.1",
|
||||||
|
"thread-loader": "^3.0.4",
|
||||||
|
"ts-jest": "^27.0.5",
|
||||||
|
"ts-loader": "^9.2.5",
|
||||||
|
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||||
|
"typescript": "^4.6.2",
|
||||||
|
"url-loader": "^4.1.1",
|
||||||
|
"webpack": "^5.64.3",
|
||||||
|
"webpack-cli": "^4.8.0",
|
||||||
|
"webpack-dev-server": "^4.5.0",
|
||||||
|
"webpack-favicons": "^1.3.8",
|
||||||
|
"webpack-merge": "^5.8.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "webpack serve --progress --port 3000",
|
||||||
|
"build": "webpack build --progress --config webpack.prod.js",
|
||||||
|
"build:dev": "webpack build --progress",
|
||||||
|
"build:serve": "npx serve dist",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"tsc": "tsc",
|
||||||
|
"tsc:watch": "tsc --watch",
|
||||||
|
"lint": "eslint src",
|
||||||
|
"lint:fix": "eslint src --fix",
|
||||||
|
"storybook": "start-storybook -p 6006",
|
||||||
|
"storybook:build": "build-storybook"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { Box, Container, Grid, Typography } from '@mui/material';
|
||||||
|
import { NymLogo } from '@nymproject/react/logo/NymLogo';
|
||||||
|
import { useIsMounted } from '@nymproject/react/hooks/useIsMounted';
|
||||||
|
import { NymThemeProvider } from '@nymproject/mui-theme';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
import { ThemeToggle } from './ThemeToggle';
|
||||||
|
import { AppContextProvider, useAppContext } from './context';
|
||||||
|
import { FigDocs } from './components/FigDocs';
|
||||||
|
import NymClientSpec from '../../../tools/nym-cli/user-docs/fig-spec';
|
||||||
|
|
||||||
|
export const AppTheme: React.FC = ({ children }) => {
|
||||||
|
const { mode } = useAppContext();
|
||||||
|
|
||||||
|
return <NymThemeProvider mode={mode}>{children}</NymThemeProvider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Content: React.FC = () => {
|
||||||
|
const { mode } = useAppContext();
|
||||||
|
const theme = useTheme();
|
||||||
|
const isMounted = useIsMounted();
|
||||||
|
|
||||||
|
if (isMounted()) {
|
||||||
|
console.log('Content is mounted');
|
||||||
|
}
|
||||||
|
|
||||||
|
const swatches: Record<string, string> = {
|
||||||
|
'palette.primary.main': theme.palette.primary.main,
|
||||||
|
'palette.secondary.main': theme.palette.secondary.main,
|
||||||
|
'palette.info.main': theme.palette.info.main,
|
||||||
|
'palette.success.main': theme.palette.success.main,
|
||||||
|
'palette.text.primary': theme.palette.text.primary,
|
||||||
|
'theme.palette.nym.networkExplorer.mixnodes.status.active':
|
||||||
|
theme.palette.nym.networkExplorer.mixnodes.status.active,
|
||||||
|
'theme.palette.nym.networkExplorer.mixnodes.status.standby':
|
||||||
|
theme.palette.nym.networkExplorer.mixnodes.status.standby,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container sx={{ py: 4 }}>
|
||||||
|
<Box display="flex" flexDirection="row-reverse" pb={2}>
|
||||||
|
<ThemeToggle />
|
||||||
|
</Box>
|
||||||
|
<NymLogo height={50} />
|
||||||
|
<h1>Example App</h1>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Typography sx={{ color: ({ palette }) => palette.nym.networkExplorer.mixnodes.status.active }}>
|
||||||
|
This is an example app that uses React, Typescript, Webpack and the Nym theme + components.
|
||||||
|
</Typography>
|
||||||
|
<h4>Some colours from the theme (mode = {mode}) are:</h4>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
{Object.keys(swatches).map((key) => (
|
||||||
|
<Grid item key={key}>
|
||||||
|
<Box display="flex" alignItems="center">
|
||||||
|
<svg height="50px" width="50px">
|
||||||
|
<rect width="100%" height="100%" fill={swatches[key]} />
|
||||||
|
</svg>
|
||||||
|
<Typography mx={2}>
|
||||||
|
<code>{swatches[key]}</code>
|
||||||
|
<br />
|
||||||
|
<code>{key}</code>
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
<h1>Docs</h1>
|
||||||
|
<FigDocs figSpec={NymClientSpec} />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const App: React.FC = () => (
|
||||||
|
<AppContextProvider>
|
||||||
|
<AppTheme>
|
||||||
|
<Content />
|
||||||
|
</AppTheme>
|
||||||
|
</AppContextProvider>
|
||||||
|
);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { Button, Typography } from '@mui/material';
|
||||||
|
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||||
|
import LightModeIcon from '@mui/icons-material/LightMode';
|
||||||
|
import { useAppContext } from './context';
|
||||||
|
|
||||||
|
export const ThemeToggle: React.FC = () => {
|
||||||
|
const { mode, toggleMode } = useAppContext();
|
||||||
|
return (
|
||||||
|
<Button variant="outlined" color="secondary" onClick={toggleMode} sx={{ display: 'flex', alignItems: 'centre' }}>
|
||||||
|
{mode === 'dark' ? (
|
||||||
|
<DarkModeIcon sx={{ color: (theme) => theme.palette.text.secondary }} />
|
||||||
|
) : (
|
||||||
|
<LightModeIcon sx={{ color: (theme) => theme.palette.text.secondary }} />
|
||||||
|
)}
|
||||||
|
<Typography ml={1} color={(theme) => theme.palette.primary.light}>
|
||||||
|
Switch to {mode === 'dark' ? 'light mode' : 'dark mode'}
|
||||||
|
</Typography>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { ComponentMeta } from '@storybook/react';
|
||||||
|
import { FigDocs } from './index';
|
||||||
|
import NymClientSpec from '../../../../../tools/nym-cli/user-docs/fig-spec';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Docs/Fig',
|
||||||
|
component: FigDocs,
|
||||||
|
} as ComponentMeta<typeof FigDocs>;
|
||||||
|
|
||||||
|
export const Default = () => <FigDocs />;
|
||||||
|
|
||||||
|
export const NymCli = () => <FigDocs figSpec={NymClientSpec} />;
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
import { Card, CardContent, Typography, CardActions, Button, Stack, Divider, Box } from '@mui/material';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export const FigOption = (option: Fig.Option): JSX.Element | null => {
|
||||||
|
return (
|
||||||
|
<Box marginBottom={2} marginTop={2}>
|
||||||
|
<Typography variant="h6" fontWeight={700}>
|
||||||
|
<pre style={{ margin: 0 }}>
|
||||||
|
<code>{option.name}</code>
|
||||||
|
</pre>
|
||||||
|
</Typography>
|
||||||
|
<Typography color="text.secondary" marginBottom={2}>
|
||||||
|
{option.description}
|
||||||
|
</Typography>
|
||||||
|
{option.args && Array.isArray(option.args) ? (
|
||||||
|
<>
|
||||||
|
<Typography component="span" marginBottom={2}>
|
||||||
|
Args:
|
||||||
|
</Typography>
|
||||||
|
{option.args.map((arg, i) => (
|
||||||
|
<>
|
||||||
|
<Typography variant="h6" component="span" key={i}>
|
||||||
|
{arg.name} {arg.isOptional}
|
||||||
|
</Typography>
|
||||||
|
{Array.isArray(option.args) && i < option.args.length - 1 && <Divider />}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
option.args && (
|
||||||
|
<Typography component="span">
|
||||||
|
Args: {option.args.name} {option.args.isOptional === true && '--is optional'}
|
||||||
|
</Typography>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FigSubcommand = (subcommand: Fig.Subcommand): JSX.Element | null => {
|
||||||
|
return (
|
||||||
|
<Box marginLeft={2} marginTop={2}>
|
||||||
|
<Typography variant="h6" fontWeight={700}>
|
||||||
|
<pre style={{ margin: 0 }}>
|
||||||
|
<code>{subcommand.name}</code>
|
||||||
|
</pre>
|
||||||
|
</Typography>
|
||||||
|
<Typography color="text.secondary">{subcommand.description}</Typography>
|
||||||
|
{subcommand.subcommands && (
|
||||||
|
<>
|
||||||
|
<Typography component="div" margin={2}>
|
||||||
|
Subcommands:
|
||||||
|
</Typography>
|
||||||
|
<Divider />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{subcommand.subcommands
|
||||||
|
? subcommand.subcommands.map((command, i) => {
|
||||||
|
return (
|
||||||
|
<Box key={i}>
|
||||||
|
<FigSubcommand {...command} />
|
||||||
|
{i < subcommand.subcommands!.length - 1 && <Divider />}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: null}
|
||||||
|
{subcommand.options && (
|
||||||
|
<>
|
||||||
|
<Typography component="div" margin={2}>
|
||||||
|
Options:
|
||||||
|
</Typography>
|
||||||
|
<Divider />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{subcommand.options
|
||||||
|
? subcommand.options.map((option, i) => {
|
||||||
|
return (
|
||||||
|
<Box marginLeft={2} key={i}>
|
||||||
|
<FigOption {...option} />
|
||||||
|
{subcommand!.options && i < subcommand!.options.length - 1 && <Divider />}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: null}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FigDocs: React.FC<{
|
||||||
|
figSpec?: Fig.Spec;
|
||||||
|
}> = ({ figSpec }) => {
|
||||||
|
let subcommand: Fig.Subcommand | undefined;
|
||||||
|
if (figSpec) {
|
||||||
|
if (typeof figSpec !== 'function') {
|
||||||
|
subcommand = figSpec as Fig.Subcommand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div>Render some docs here</div>
|
||||||
|
{subcommand && (
|
||||||
|
<div>
|
||||||
|
<div>Name: {subcommand.name}</div>
|
||||||
|
{subcommand.subcommands && (
|
||||||
|
<Typography variant="h6" component="div" marginBottom={4}>
|
||||||
|
Subcommands:
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
{subcommand.subcommands?.map((command, i) => {
|
||||||
|
return (
|
||||||
|
<Card sx={{ minWidth: 275, marginBottom: 2 }} key={i}>
|
||||||
|
<CardContent>
|
||||||
|
<FigSubcommand {...command} />
|
||||||
|
{subcommand!.subcommands && i < subcommand!.subcommands.length - 1 && <Divider />}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{subcommand.options && (
|
||||||
|
<Typography variant="h6" component="div" marginBottom={4}>
|
||||||
|
Options:
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
{subcommand.options &&
|
||||||
|
subcommand.options.map((option, i) => {
|
||||||
|
return (
|
||||||
|
<Card sx={{ minWidth: 275, marginBottom: 2 }} key={i}>
|
||||||
|
<CardContent>
|
||||||
|
<FigOption {...option} />
|
||||||
|
{subcommand!.options && i < subcommand!.options.length - 1 && <Divider />}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { PaletteMode } from '@mui/material';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
mode: PaletteMode;
|
||||||
|
toggleMode: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppContext = React.createContext<State | undefined>(undefined);
|
||||||
|
|
||||||
|
export const useAppContext = (): State => {
|
||||||
|
const context = React.useContext<State | undefined>(AppContext);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('Please include a `import { AppContextProvider } from "./context"` before using this hook');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AppContextProvider: React.FC = ({ children }) => {
|
||||||
|
// light/dark mode
|
||||||
|
const [mode, setMode] = React.useState<PaletteMode>('dark');
|
||||||
|
|
||||||
|
const value = React.useMemo<State>(
|
||||||
|
() => ({
|
||||||
|
mode,
|
||||||
|
toggleMode: () => setMode((prevMode) => (prevMode !== 'light' ? 'light' : 'dark')),
|
||||||
|
}),
|
||||||
|
[mode],
|
||||||
|
);
|
||||||
|
|
||||||
|
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
|
||||||
|
};
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Nym Example with React, Typescript, Webpack</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { App } from './App';
|
||||||
|
|
||||||
|
ReactDOM.render(<App />, document.getElementById('app'));
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { Meta } from '@storybook/addon-docs';
|
||||||
|
|
||||||
|
<Meta title="Introduction" />
|
||||||
|
|
||||||
|
# Nym docs components
|
||||||
|
|
||||||
|
Renders user docs from a Fig spec
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import React, { render } from '@testing-library/react';
|
||||||
|
import '@testing-library/jest-dom/extend-expect';
|
||||||
|
import { App } from '../App';
|
||||||
|
|
||||||
|
describe('App', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
render(<App />);
|
||||||
|
});
|
||||||
|
it('should render without exploding', () => {
|
||||||
|
const { container } = render(<App />);
|
||||||
|
expect(container.firstChild).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/* eslint-disable no-shadow,@typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-interface,import/no-extraneous-dependencies */
|
||||||
|
import { Theme, ThemeOptions, Palette, PaletteOptions } from '@mui/material/styles';
|
||||||
|
import { NymTheme, NymPaletteWithExtensions, NymPaletteWithExtensionsOptions } from '@nymproject/mui-theme';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you are unfamiliar with Material UI theming, please read the following first:
|
||||||
|
* - https://mui.com/customization/theming/
|
||||||
|
* - https://mui.com/customization/palette/
|
||||||
|
* - https://mui.com/customization/dark-mode/#dark-mode-with-custom-palette
|
||||||
|
*
|
||||||
|
* This file adds typings to the theme using Typescript's module augmentation.
|
||||||
|
*
|
||||||
|
* Read the following if you are unfamiliar with module augmentation and declaration merging. Then
|
||||||
|
* look at the recommendations from Material UI docs for implementation:
|
||||||
|
* - https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
|
||||||
|
* - https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces
|
||||||
|
* - https://mui.com/customization/palette/#adding-new-colors
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* IMPORTANT:
|
||||||
|
*
|
||||||
|
* The type augmentation must match MUI's definitions. So, notice the use of `interface` rather than
|
||||||
|
* `type Foo = { ... }` - this is necessary to merge the definitions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module '@mui/material/styles' {
|
||||||
|
/**
|
||||||
|
* This augments the definitions of the MUI Theme with the Nym theme, as well as
|
||||||
|
* a partial `ThemeOptions` type used by `createTheme`
|
||||||
|
*
|
||||||
|
* IMPORTANT: only add extensions to the interfaces above, do not modify the lines below
|
||||||
|
*/
|
||||||
|
interface Theme extends NymTheme {}
|
||||||
|
interface ThemeOptions extends Partial<NymTheme> {}
|
||||||
|
interface Palette extends NymPaletteWithExtensions {}
|
||||||
|
interface PaletteOptions extends NymPaletteWithExtensionsOptions {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"noEmit": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
".storybook/*.js",
|
||||||
|
"src/**/*.js",
|
||||||
|
"src/**/*.jsx",
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.stories.*",
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"dist"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"types": ["@withfig/autocomplete-types"]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"build",
|
||||||
|
"dist",
|
||||||
|
"src/**/*.test.tsx"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"declaration": false
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"build",
|
||||||
|
"dist",
|
||||||
|
"**/*.stories.*",
|
||||||
|
"**/*.test.*",
|
||||||
|
"**/*.spec.*"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
const path = require('path');
|
||||||
|
const { mergeWithRules } = require('webpack-merge');
|
||||||
|
const { webpackCommon } = require('@nymproject/webpack');
|
||||||
|
|
||||||
|
module.exports = mergeWithRules({
|
||||||
|
module: {
|
||||||
|
rules: {
|
||||||
|
test: 'match',
|
||||||
|
use: 'replace',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})(webpackCommon(__dirname), {
|
||||||
|
entry: path.resolve(__dirname, 'src/index.tsx'),
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, 'dist'),
|
||||||
|
publicPath: '/',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
const { mergeWithRules } = require('webpack-merge');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||||
|
const ReactRefreshTypeScript = require('react-refresh-typescript');
|
||||||
|
const commonConfig = require('./webpack.common');
|
||||||
|
|
||||||
|
module.exports = mergeWithRules({
|
||||||
|
module: {
|
||||||
|
rules: {
|
||||||
|
test: 'match',
|
||||||
|
use: 'replace',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})(commonConfig, {
|
||||||
|
mode: 'development',
|
||||||
|
devtool: 'inline-source-map',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
options: {
|
||||||
|
getCustomTransformers: () => ({
|
||||||
|
before: [ReactRefreshTypeScript()],
|
||||||
|
}),
|
||||||
|
// `ts-loader` does not work with HMR unless `transpileOnly` is used.
|
||||||
|
// If you need type checking, `ForkTsCheckerWebpackPlugin` is an alternative.
|
||||||
|
transpileOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new ReactRefreshWebpackPlugin(),
|
||||||
|
|
||||||
|
// this can be included automatically by the dev server, however build mode fails if missing
|
||||||
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
|
],
|
||||||
|
|
||||||
|
target: 'web',
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
headers: {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
|
||||||
|
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||||
|
},
|
||||||
|
historyApiFallback: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// recommended for faster rebuild
|
||||||
|
optimization: {
|
||||||
|
runtimeChunk: true,
|
||||||
|
removeAvailableModules: false,
|
||||||
|
removeEmptyChunks: false,
|
||||||
|
splitChunks: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
cache: {
|
||||||
|
type: 'filesystem',
|
||||||
|
buildDependencies: {
|
||||||
|
// restart on config change
|
||||||
|
config: ['./webpack.config.js'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
const { mergeWithRules } = require('webpack-merge');
|
||||||
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||||
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
|
const commonConfig = require('./webpack.common');
|
||||||
|
|
||||||
|
module.exports = mergeWithRules({
|
||||||
|
module: {
|
||||||
|
rules: {
|
||||||
|
test: 'match',
|
||||||
|
use: 'replace',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})(commonConfig, {
|
||||||
|
mode: 'production',
|
||||||
|
|
||||||
|
// TODO: no source maps, add back
|
||||||
|
devtool: false,
|
||||||
|
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [MiniCssExtractPlugin.loader, 'css-loader'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimizer: ['...', new CssMinimizerPlugin()],
|
||||||
|
splitChunks: {
|
||||||
|
chunks: 'all',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: '[name].[contenthash].css',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
pathinfo: false,
|
||||||
|
filename: '[name].[contenthash].js',
|
||||||
|
},
|
||||||
|
});
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -6124,6 +6124,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.1.tgz#0de2875ac31b46b6c5bb1ae0a7d7f0ba5678dffe"
|
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.1.tgz#0de2875ac31b46b6c5bb1ae0a7d7f0ba5678dffe"
|
||||||
integrity sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==
|
integrity sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==
|
||||||
|
|
||||||
|
"@withfig/autocomplete-types@^1.23.0":
|
||||||
|
version "1.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@withfig/autocomplete-types/-/autocomplete-types-1.23.0.tgz#50d9fb3ae0c0660b5863ba7a3e827ca7fcf1cda4"
|
||||||
|
integrity sha512-dB9gccfRmkARPadT0hPZwzl4seAjeX6ILj9X3ClWeZ/FExJicRGYGTT8B+iF/gW+7EoPbmdY+C5Akl+NQSCEQQ==
|
||||||
|
|
||||||
"@xtuc/ieee754@^1.2.0":
|
"@xtuc/ieee754@^1.2.0":
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
|
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
|
||||||
|
|||||||
Reference in New Issue
Block a user