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"
|
||||
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":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
|
||||
|
||||
Reference in New Issue
Block a user