I got it working and hopefully other people might find this useful too, as there are few and only non-typescript example configs to be found so far.
Things to note for my setup:
- react mui app is in
src/
, components are in src/components/
, using a modules:
webpack configuration to look into src/
for absolute imports.
- app has been created using the react app creator.
tsconfig.json
autocreated (overriding noEmit
later specifically for the "ts-loader" only, see below); there's one important exception/addition shown below.
- no separate webpack and babel configurations, included in
styleguide.config.js
.
tsconfig.json
is boilerplate, except for this gem paths:'rsg-components/*'
here, which needs to be added manually -- it is hidden in the React Styleguidist's Cookbook. Without it, we need to resort to alias definitions in the webpack configuration, including for replacing wrapper! With the correct paths
definition in tsconfig.json
things finally start to correctly fall into place.
{
"compilerOptions": {
"paths": {
"rsg-components/*": [
"node_modules/react-styleguidist/lib/client/rsg-components/*"
]
}
}
}
styleguide.config.js
lives in the project's top-level directory:
const path = require('path')
module.exports = {
components: [
'src/components/**/*.{ts,tsx}',
'src/models/**/*.ts',
],
ignore: [
'src/**/index.{ts,tsx}',
],
// We need to override how to decide on what an example file is, in order
// to remove default which tries to document undocumented components. Ugh.
// So, only document components for which we also have an explicit
// documentation file named the same as the component file, but ending in
// ".md" instead.
getExampleFilename: (cpath) => {
return cpath.replace(/\.(tsx?)$/, '.md')
},
// Show import commands without the component filename extension and only
// for the module; also remove the first "src/" path component.
getComponentPathLine: (cpath) => {
const cname = ['.tsx', '.ts'].reduce((name, ext) => path.basename(name, ext), cpath)
const cdir = path.dirname(cpath).replace(/^src\//, '')
return `import { ${cname} } from ${cdir}`
},
// How uncivilized: do not list components lacking an example.
skipComponentsWithoutExample: true,
// Always expand the props and methods of components.
usageMode: 'expand',
// Support rendering prop types of typescript components.
propsParser: require('react-docgen-typescript').withCustomConfig(
'./tsconfig.json',
{
"compilerOptions": { "noEmit": false },
}
).parse,
// Replace the standard wrapper for example component usage code with our
// own wrapper which brings in the Material UI theme.
styleguideComponents: {
Wrapper: path.join(__dirname, 'styleguidist/MuiThemeWrapper.tsx')
},
// Tell webpack what to look for and where and how to load it.
webpackConfig: {
resolve: {
extensions: ['.tsx', '.ts', '.js'],
// https://webpack.js.org/configuration/resolve/#resolvemodules;
// we're allowing absolute imports to be satisfied from the src/
// directory.
modules: [
path.resolve(__dirname, 'src/'),
'node_modules'
],
alias: {
// Could also be covered by a modules clause, but we are
// sticking with an alias instead to cover only exactly
// absolute "styleguidist/..." imports.
'styleguidist': path.join(__dirname, 'styleguidist'),
}
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
"@babel/preset-env",
"@babel/react",
]
},
},
{
loader: 'ts-loader',
options: {
// Important! Avoids "Error: TypeScript emitted no output for..." errors
compilerOptions: {
noEmit: false,
},
},
},
],
},
{
test: /\.css$/,
loader: 'style-loader!css-loader?modules',
},
{
test: /\.svg$/,
loader: 'url-loader',
},
{
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/',
}
},
]
}
]
},
}
}
- covering
styleguidist/
absolute imports could be covered with the modules:
clause, but in this case I opted for an alias instead. You might judge this differently :)
- I'm setting a rather restrictive regime of documenting only components with examples; get rid of it if you don't want that.
- The difficulties are in configuring webpack with the correct loaders; I've enabled typescript, TSX and JSX, as well as pulling in font resources.
- Important: wrapper is in
styleguidist/MuiThemeWrapper.tsx
.
And that's my wrapper styleguidist/MuiThemeWrapper.tsx
:
(for MUI v5, please see the answer to MUI v5 + React styleguidist + ScopedCSSBaseline + createTheme styleOverrides: body fontSize change not working)
import React from 'react'
import "fontsource-roboto/400.css"
import "fontsource-roboto/500.css"
import "fontsource-roboto/700.css"
import "fontsource-roboto-mono/400.css"
import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles'
import CssBaseline from '@material-ui/core/CssBaseline'
const muiTheme = createMuiTheme({})
const MuiThemeWrapper = ({children}) => (
<ThemeProvider theme={muiTheme}>
<CssBaseline />
{children}
</ThemeProvider>
)
export default MuiThemeWrapper
- explicitly pulling in the Roboto fonts I need in my react+Material UI project.
- applying the CSS baseline definition, as otherwise the
font-family
s won't be correctly defined.