5 | <button>OK</button>
6 |
7 | `;
8 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/Hr/__snapshots__/Hr.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Markdown Hr should render a horizontal rule 1`] = `
4 | 5 | Hello *world*! 6 |7 | `; 8 | -------------------------------------------------------------------------------- /src/client/utils/__tests__/getAst.spec.ts: -------------------------------------------------------------------------------- 1 | import getAst from '../getAst'; 2 | 3 | describe('getAst', () => { 4 | test('return AST', () => { 5 | const result = getAst(`42`); 6 | expect(result).toMatchSnapshot(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/webpack/src/index.js: -------------------------------------------------------------------------------- 1 | import 'core-js'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import App from './App'; 5 | import './index.css'; 6 | 7 | ReactDOM.render(
8 | 1.2.3-a 9 |
10 | `; 11 | -------------------------------------------------------------------------------- /examples/preact/src/components/Button/Button.css: -------------------------------------------------------------------------------- 1 | .button { 2 | padding: 0.5em 1.5em; 3 | color: #666; 4 | background-color: #fff; 5 | border: 1px solid currentColor; 6 | border-radius: 0.3em; 7 | text-align: center; 8 | vertical-align: middle; 9 | cursor: pointer; 10 | } 11 | -------------------------------------------------------------------------------- /examples/styled-components/src/components/Box/Box.js: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Box = styled.div``; 5 | 6 | Box.propTypes = { 7 | big: PropTypes.bool, 8 | }; 9 | 10 | /* @component */ 11 | export default Box; 12 | -------------------------------------------------------------------------------- /src/scripts/__mocks__/build.ts: -------------------------------------------------------------------------------- 1 | import * as Rsg from '../../typings'; 2 | 3 | export default function build( 4 | config: Rsg.SanitizedStyleguidistConfig, 5 | callback: (err: Error | null, stats: any) => void 6 | ) { 7 | callback(null, { stats: true }); 8 | return {}; 9 | } 10 | -------------------------------------------------------------------------------- /examples/customised/src/components/Button/Button.css: -------------------------------------------------------------------------------- 1 | .root { 2 | padding: 0.5em 1.5em; 3 | color: #666; 4 | background-color: #fff; 5 | border: 1px solid currentColor; 6 | border-radius: 0.3em; 7 | text-align: center; 8 | vertical-align: middle; 9 | cursor: pointer; 10 | } 11 | -------------------------------------------------------------------------------- /examples/sections/src/components/Button/Button.css: -------------------------------------------------------------------------------- 1 | .button { 2 | padding: 0.5em 1.5em; 3 | color: #666; 4 | background-color: #fff; 5 | border: 1px solid currentColor; 6 | border-radius: 0.3em; 7 | text-align: center; 8 | vertical-align: middle; 9 | cursor: pointer; 10 | } 11 | -------------------------------------------------------------------------------- /examples/styled-components/src/components/Flex/Flex.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | interface FlexProps { 4 | bool: boolean 5 | } 6 | 7 | const Flex = styled('div')1`] = ` 12 |
15 | Pizza 16 |
17 | `; 18 | -------------------------------------------------------------------------------- /examples/styled-components/src/styles.ts: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components'; 2 | 3 | const GlobalStyle = createGlobalStyle` 4 | body { 5 | margin: 0; 6 | padding: 0; 7 | } 8 | html { 9 | box-sizing: border-box; 10 | } 11 | *, 12 | *:before, 13 | *:after { 14 | box-sizing: inherit; 15 | } 16 | `; 17 | 18 | export default GlobalStyle; 19 | -------------------------------------------------------------------------------- /src/client/rsg-components/Markdown/Hr/Hr.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import Hr from './index'; 5 | 6 | describe('Markdown Hr', () => { 7 | it('should render a horizontal rule', () => { 8 | const actual = renderer.create(
7 | Foo
8 |
9 | `;
10 |
11 | exports[`renderer should render deprecated argument name 1`] = `
12 |
15 | Foo
16 |
17 | `;
18 |
--------------------------------------------------------------------------------
/src/client/rsg-components/ReactExample/__snapshots__/ReactExample.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`should render code 1`] = `
4 | 7 | This is pre-formatted text. 8 |9 | `; 10 | 11 | exports[`Markdown Pre should render highlighted code 1`] = ` 12 |
OK",
17 | }
18 | }
19 | />
20 | `;
21 |
--------------------------------------------------------------------------------
/src/client/rsg-components/PlaygroundError/PlaygroundError.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRenderer } from 'react-test-renderer/shallow';
3 | import { PlaygroundErrorRenderer } from './PlaygroundErrorRenderer';
4 |
5 | it('renderer should render message', () => {
6 | const message = 'Hello *world*!';
7 | const renderer = createRenderer();
8 | renderer.render( );
9 |
10 | expect(renderer.getRenderOutput()).toMatchSnapshot();
11 | });
12 |
--------------------------------------------------------------------------------
/.github/workflows/danger-js.yml:
--------------------------------------------------------------------------------
1 | name: Danger JS
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | check:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 | - uses: actions/setup-node@v2
18 | with:
19 | node-version: '16'
20 | cache: npm
21 | - uses: danger/danger-js@9.1.8
22 | env:
23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
24 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/Details/Details.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 |
4 | import { Details, DetailsSummary } from './index';
5 |
6 | describe('Markdown Details', () => {
7 | it('should render a Details', () => {
8 | const actual = renderer.create(
9 |
10 | Solution
11 | This is a hidden text.
12 |
13 | );
14 |
15 | expect(actual.toJSON()).toMatchSnapshot();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/loaders/utils/getComponents.ts:
--------------------------------------------------------------------------------
1 | import processComponent from './processComponent';
2 | import * as Rsg from '../../typings';
3 |
4 | /**
5 | * Process each component in a list.
6 | *
7 | * @param {Array} components File names of components.
8 | * @param {object} config
9 | * @returns {object|null}
10 | */
11 | export default function getComponents(
12 | components: string[],
13 | config: Rsg.SanitizedStyleguidistConfig
14 | ) {
15 | return components.map(filepath => processComponent(filepath, config));
16 | }
17 |
--------------------------------------------------------------------------------
/src/loaders/utils/client/__tests__/requireInRuntime.spec.js:
--------------------------------------------------------------------------------
1 | import requireInRuntime from '../requireInRuntime';
2 |
3 | const map = {
4 | a: () => 'a',
5 | };
6 |
7 | test('return a module from the map', () => {
8 | const result = requireInRuntime(map, 'a');
9 | expect(result).toBeDefined();
10 | expect(result()).toBe('a');
11 | });
12 |
13 | test('throw if module is not in the map', () => {
14 | const fn = () => requireInRuntime(map, 'pizza');
15 | expect(fn).toThrowError('require() statements can be added');
16 | });
17 |
--------------------------------------------------------------------------------
/src/scripts/build.ts:
--------------------------------------------------------------------------------
1 | import webpack from 'webpack';
2 | import makeWebpackConfig from './make-webpack-config';
3 | import * as Rsg from '../typings';
4 |
5 | export default function build(
6 | config: Rsg.SanitizedStyleguidistConfig,
7 | callback: (err: Error, stats: webpack.Stats) => void
8 | ) {
9 | return webpack(makeWebpackConfig(config, 'production'), (err, stats) => {
10 | // require('fs').writeFileSync('stats.json', JSON.stringify(stats.toJson()));
11 | callback(err as Error, stats as webpack.Stats);
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Error/Error.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRenderer } from 'react-test-renderer/shallow';
3 | import { ErrorRenderer } from './ErrorRenderer';
4 |
5 | it('renderer should render error message', () => {
6 | const error = { toString: () => 'error' };
7 | const info = { componentStack: 'info' };
8 | const renderer = createRenderer();
9 | renderer.render( );
10 |
11 | expect(renderer.getRenderOutput()).toMatchSnapshot();
12 | });
13 |
--------------------------------------------------------------------------------
/site/src/components/Stack.module.css:
--------------------------------------------------------------------------------
1 | .stack.stack > * {
2 | margin-bottom: 0;
3 | }
4 | .stack--xxs.stack--xxs > * + * {
5 | margin-top: 0.125rem; /* 2px */
6 | }
7 | .stack--xs.stack--xs > * + * {
8 | margin-top: 0.25rem; /* 4px */
9 | }
10 | .stack--s.stack--s > * + * {
11 | margin-top: 0.5rem; /* 8px */
12 | }
13 | .stack--m.stack--m > * + * {
14 | margin-top: 1rem; /* 16px */
15 | }
16 | .stack--l.stack--l > * + * {
17 | margin-top: 2rem; /* 32px */
18 | }
19 | .stack--xl.stack--xl > * + * {
20 | margin-top: 4rem; /* 32px */
21 | }
22 |
--------------------------------------------------------------------------------
/src/loaders/utils/__tests__/resolveESModule.spec.ts:
--------------------------------------------------------------------------------
1 | import { generate } from 'escodegen';
2 | import { builders as b } from 'ast-types';
3 | import resolveESModule from '../resolveESModule';
4 |
5 | it('should return an array of AST', () => {
6 | const result = resolveESModule('path/to/module', 'NameOfVar');
7 |
8 | expect(generate(b.program(result))).toMatchInlineSnapshot(`
9 | "const NameOfVar$0 = require('path/to/module');
10 | const NameOfVar = NameOfVar$0.default || (NameOfVar$0['NameOfVar'] || NameOfVar$0);"
11 | `);
12 | });
13 |
--------------------------------------------------------------------------------
/site/src/components/ImageLink.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import clsx from 'clsx';
4 | import styles from './ImageLink.module.css';
5 |
6 | export const ImageLink = ({ children, className, ...rest }) => (
7 |
8 | {children}
9 |
10 | );
11 |
12 | ImageLink.propTypes = {
13 | href: PropTypes.string.isRequired,
14 | className: PropTypes.string,
15 | children: PropTypes.node.isRequired,
16 | };
17 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Semantic Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | release:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: actions/setup-node@v2
15 | with:
16 | node-version: '20'
17 | cache: npm
18 |
19 | - run: npm ci
20 |
21 | - run: npx semantic-release
22 | env:
23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
24 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
25 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/MarkdownHeading/MarkdownHeading.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import MarkdownHeading from './index';
4 |
5 | describe('Markdown Heading', () => {
6 | it('should render a heading with a wrapper that provides margin and an id', () => {
7 | const actual = renderer.create(
8 |
9 | The markdown heading
10 |
11 | );
12 |
13 | expect(actual).toMatchSnapshot();
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/src/scripts/server.ts:
--------------------------------------------------------------------------------
1 | import WebpackDevServer from 'webpack-dev-server';
2 | import webpack from 'webpack';
3 | import createServer from './create-server';
4 | import * as Rsg from '../typings';
5 |
6 | export default function server(
7 | config: Rsg.SanitizedStyleguidistConfig,
8 | callback: (error?: Error) => void
9 | ): { app: WebpackDevServer; compiler: webpack.Compiler } {
10 | const env = 'development';
11 | const serverInfo = createServer(config, env);
12 |
13 | serverInfo.app.startCallback(callback);
14 |
15 | return serverInfo;
16 | }
17 |
--------------------------------------------------------------------------------
/examples/styled-components/src/StyleGuideWrapper.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactNode } from 'react';
2 | import ThemeProvider from './ThemeProvider';
3 | import GlobalStyle from './styles';
4 |
5 | interface Props {
6 | children?: ReactNode
7 | }
8 |
9 | const StyleGuideWrapper = function({ children }: Props) {
10 | return (
11 |
12 | <>
13 |
14 | {children}
15 | >
16 |
17 | )
18 | };
19 |
20 | export default StyleGuideWrapper;
21 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/Blockquote/__snapshots__/Blockquote.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Markdown Blockquote should preserve custom css class 1`] = `
4 |
7 | To be, or not to be: that is the question
8 |
9 | `;
10 |
11 | exports[`Markdown Blockquote should render a blockquote 1`] = `
12 |
15 | To be, or not to be: that is the question
16 |
17 | `;
18 |
--------------------------------------------------------------------------------
/src/client/utils/filterComponentsByName.ts:
--------------------------------------------------------------------------------
1 | import getFilterRegExp from './getFilterRegExp';
2 | import * as Rsg from '../../typings';
3 |
4 | /**
5 | * Fuzzy filters components list by component name.
6 | *
7 | * @param {array} components
8 | * @param {string} query
9 | * @return {array}
10 | */
11 | export default function filterComponentsByName(
12 | components: Rsg.Component[],
13 | query: string
14 | ): Rsg.Component[] {
15 | const regExp = getFilterRegExp(query);
16 | return components.filter(({ name }) => regExp.test(name as string));
17 | }
18 |
--------------------------------------------------------------------------------
/src/client/utils/filterSectionExamples.ts:
--------------------------------------------------------------------------------
1 | import * as Rsg from '../../typings';
2 |
3 | /**
4 | * Return a copy of the given section with the examples array filtered
5 | * to contain only the specified index
6 | *
7 | * @param {object} section
8 | * @param {number} index
9 | * @returns {object}
10 | */
11 | export default function filterSectionExamples(section: Rsg.Section, index = -1): Rsg.Section {
12 | const content = Array.isArray(section.content) ? [section.content[index]] : [];
13 | return {
14 | ...section,
15 | content,
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/src/bin/__tests__/styleguidist.spec.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const vm = require('vm');
3 | const path = require('path');
4 | const stripShebang = require('strip-shebang');
5 |
6 | const BIN_PATH = path.resolve(__dirname, '../styleguidist.js');
7 |
8 | // Very simple test to be sure we’re not using any syntax that is not supported in versions of Node we support
9 | it('should not throw when parsing a script', () => {
10 | const code = stripShebang(fs.readFileSync(BIN_PATH, 'utf8'));
11 |
12 | expect(() => new vm.Script(code)).not.toThrow();
13 | });
14 |
--------------------------------------------------------------------------------
/src/loaders/utils/__tests__/__snapshots__/processComponent.spec.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`processComponent() should return an object for section with content 1`] = `
4 | Object {
5 | "filepath": "../../../../pizza.js",
6 | "hasExamples": true,
7 | "metadata": Object {},
8 | "module": Object {
9 | "require": "pizza.js",
10 | },
11 | "pathLine": "../../../../pizza.js",
12 | "props": Object {
13 | "require": "!!~/src/loaders/props-loader.js!pizza.js",
14 | },
15 | "slug": "pizza",
16 | }
17 | `;
18 |
--------------------------------------------------------------------------------
/examples/basic/Readme.md:
--------------------------------------------------------------------------------
1 | # React Styleguidist basic example style guide
2 |
3 | See [deployed version](https://react-styleguidist.js.org/examples/basic/).
4 |
5 | 
6 |
7 | How to start locally:
8 |
9 | ```
10 | git clone https://github.com/styleguidist/react-styleguidist.git
11 | cd react-styleguidist/examples/basic
12 | npm install
13 | npx styleguidist server
14 | ```
15 |
16 | Then open [http://localhost:6060](http://localhost:6060) in your browser.
17 |
--------------------------------------------------------------------------------
/src/client/utils/__tests__/filterComponentsByExactName.spec.ts:
--------------------------------------------------------------------------------
1 | import deepfreeze from 'deepfreeze';
2 | import filterComponentsByExactName from '../filterComponentsByExactName';
3 |
4 | const components = deepfreeze([
5 | {
6 | name: 'Button',
7 | },
8 | {
9 | name: 'Image',
10 | },
11 | ]);
12 |
13 | describe('filterComponentsByExactName', () => {
14 | it('should return components with exact name', () => {
15 | const result = filterComponentsByExactName(components, 'Image');
16 | expect(result.map(x => x.name)).toEqual(['Image']);
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Wrapper/Wrapper.ts:
--------------------------------------------------------------------------------
1 | import { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | interface Props {
5 | onError: (e: Error) => void;
6 | children?: React.ReactNode;
7 | }
8 | export default class Wrapper extends Component {
9 | public static propTypes = {
10 | children: PropTypes.node.isRequired,
11 | onError: PropTypes.func.isRequired,
12 | };
13 |
14 | public componentDidCatch(error: Error) {
15 | this.props.onError(error);
16 | }
17 |
18 | public render() {
19 | return this.props.children;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/client/utils/__tests__/filterSectionExamples.spec.ts:
--------------------------------------------------------------------------------
1 | import deepfreeze from 'deepfreeze';
2 | import filterSectionExamples from '../filterSectionExamples';
3 |
4 | const section = deepfreeze({
5 | content: ['a', 'b', 'c', 'd'],
6 | other: 'info',
7 | });
8 |
9 | describe('filterSectionExamples', () => {
10 | it('should return a shallow copy of a section with example filtered by given index', () => {
11 | const result = filterSectionExamples(section as any, 2);
12 | expect(result).toEqual({
13 | content: ['c'],
14 | other: 'info',
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/loaders/utils/expandDefaultComponent.ts:
--------------------------------------------------------------------------------
1 | const COMPONENT_PLACEHOLDER = '__COMPONENT__';
2 | const COMPONENT_PLACEHOLDER_REGEXP = new RegExp(COMPONENT_PLACEHOLDER, 'g');
3 |
4 | /**
5 | * Wrap a string with require() statement.
6 | *
7 | * @param {string} source Source code.
8 | * @param {string} componentName Name that will be used instead of a placeholder.
9 | * @returns {string}
10 | */
11 | export default function expandDefaultComponent(source: string, componentName: string): string {
12 | return source.replace(COMPONENT_PLACEHOLDER_REGEXP, componentName);
13 | }
14 |
--------------------------------------------------------------------------------
/src/client/utils/__tests__/__snapshots__/getAst.spec.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`getAst return AST 1`] = `
4 | Node {
5 | "body": Array [
6 | Node {
7 | "end": 2,
8 | "expression": Node {
9 | "end": 2,
10 | "raw": "42",
11 | "start": 0,
12 | "type": "Literal",
13 | "value": 42,
14 | },
15 | "start": 0,
16 | "type": "ExpressionStatement",
17 | },
18 | ],
19 | "end": 2,
20 | "sourceType": "module",
21 | "start": 0,
22 | "type": "Program",
23 | }
24 | `;
25 |
--------------------------------------------------------------------------------
/src/loaders/utils/__tests__/processComponent.spec.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import processComponent from '../processComponent';
3 |
4 | const config = {
5 | configDir: __dirname,
6 | getExampleFilename: (componentpath: string) =>
7 | path.join(path.dirname(componentpath), 'Readme.md'),
8 | getComponentPathLine: (componentpath: string) => componentpath,
9 | };
10 |
11 | it('processComponent() should return an object for section with content', () => {
12 | const result = processComponent('pizza.js', config as any);
13 |
14 | expect(result).toMatchSnapshot();
15 | });
16 |
--------------------------------------------------------------------------------
/.github/workflows/coverage.yml:
--------------------------------------------------------------------------------
1 | name: Codecov
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | report:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 | - uses: actions/setup-node@v2
18 | with:
19 | node-version: '16'
20 | cache: npm
21 |
22 | - run: npm ci
23 | - run: npm run test:coverage -- --runInBand
24 |
25 | - uses: codecov/codecov-action@v1
26 | with:
27 | verbose: true
28 |
--------------------------------------------------------------------------------
/src/scripts/consts.ts:
--------------------------------------------------------------------------------
1 | export const HOMEPAGE = 'https://react-styleguidist.js.org/';
2 | export const BUGS = 'https://github.com/styleguidist/react-styleguidist/issues';
3 | export const DOCS_CONFIG = 'https://react-styleguidist.js.org/docs/configuration';
4 | export const DOCS_COMPONENTS = 'https://react-styleguidist.js.org/docs/components';
5 | export const DOCS_WEBPACK = 'https://react-styleguidist.js.org/docs/webpack';
6 | export const DOCS_DOCUMENTING = 'https://react-styleguidist.js.org/docs/documenting';
7 | export const DOCS_THIRDPARTIES = 'https://react-styleguidist.js.org/docs/thirdparties';
8 |
--------------------------------------------------------------------------------
/examples/webpack/src/App.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | import React, { Component } from 'react';
4 | import logo from './logo.svg';
5 | import './App.css';
6 |
7 | class App extends Component {
8 | render() {
9 | return (
10 |
11 |
12 |
13 | Welcome to React
14 |
15 |
16 | To get started, edit src/App.js and save to reload.
17 |
18 |
19 | );
20 | }
21 | }
22 |
23 | export default App;
24 |
--------------------------------------------------------------------------------
/examples/cra/src/App.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | import React, { Component } from 'react';
4 | import logo from './logo.svg';
5 | import './App.css';
6 |
7 | class App extends Component {
8 | render() {
9 | return (
10 |
11 |
12 |
13 | Welcome to React
14 |
15 |
16 | To get started, edit src/App.js and save to reload.
17 |
18 |
19 | );
20 | }
21 | }
22 |
23 | export default App;
24 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "es2015",
5 | "baseUrl": ".",
6 | "moduleResolution": "node",
7 | "jsx": "react",
8 | "strict": true,
9 | "esModuleInterop": true,
10 | "noUnusedLocals": true,
11 | "allowSyntheticDefaultImports": true,
12 | "outDir": "./lib",
13 | "lib": ["dom"],
14 | "skipLibCheck": true,
15 | "paths": {
16 | "rsg-components/*": ["src/client/rsg-components/*"]
17 | }
18 | },
19 | "files": ["dangerfile.ts"],
20 | "include": ["src"],
21 | "exclude": ["node_modules"]
22 | }
23 |
--------------------------------------------------------------------------------
/site/src/components/Stack.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import clsx from 'clsx';
4 | import styles from './Stack.module.css';
5 |
6 | export const Stack = ({ children, gap, className, as: Component = 'div', ...rest }) => (
7 |
8 | {children}
9 |
10 | );
11 |
12 | Stack.propTypes = {
13 | children: PropTypes.node.isRequired,
14 | gap: PropTypes.oneOf(['xxs', 'xs', 's', 'm', 'l', 'xl']).isRequired,
15 | className: PropTypes.string,
16 | as: PropTypes.string,
17 | };
18 |
--------------------------------------------------------------------------------
/src/loaders/utils/client/__tests__/evalInContext.spec.js:
--------------------------------------------------------------------------------
1 | import evalInContext from '../evalInContext';
2 |
3 | describe('evalInContext', () => {
4 | test('return a function', () => {
5 | const result = evalInContext(`alert('header')`, (a) => a, `alert('code')`);
6 | expect(typeof result).toBe('function');
7 | });
8 |
9 | test('create a separate scope for the body', () => {
10 | const fn = () =>
11 | evalInContext(
12 | `const react = require('react')`,
13 | (a) => a,
14 | `const react = require('react')
15 | const x = 42
16 | `
17 | );
18 | expect(fn).not.toThrow();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 100,
3 | "singleQuote": true,
4 | "trailingComma": "es5",
5 | "useTabs": true,
6 | "proseWrap": "never",
7 | "overrides": [
8 | {
9 | "files": "*.md",
10 | "options": {
11 | "printWidth": 70,
12 | "useTabs": false,
13 | "semi": false,
14 | "trailingComma": "none",
15 | "arrowParens": "avoid",
16 | "proseWrap": "never"
17 | }
18 | },
19 | {
20 | "files": "*.{json,babelrc,eslintrc,remarkrc,prettierrc}",
21 | "options": {
22 | "useTabs": false
23 | }
24 | }
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/site/src/components/Box.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import clsx from 'clsx';
4 | import styles from './Box.module.css';
5 |
6 | export const Box = ({ children, textAlign, className, as: Component = 'div', ...rest }) => (
7 |
11 | {children}
12 |
13 | );
14 |
15 | Box.propTypes = {
16 | children: PropTypes.node,
17 | textAlign: PropTypes.oneOf(['center']),
18 | className: PropTypes.string,
19 | as: PropTypes.string,
20 | };
21 |
--------------------------------------------------------------------------------
/src/loaders/utils/requireIt.ts:
--------------------------------------------------------------------------------
1 | import { builders as b, ASTNode } from 'ast-types';
2 | import * as Rsg from '../../typings';
3 |
4 | /**
5 | * Return a require() statement AST.
6 | *
7 | * @param {string} filepath Module name.
8 | * @returns {object}
9 | */
10 | export default function requireIt(filepath: string): Rsg.RequireItResult {
11 | const obj = { require: filepath };
12 | Object.defineProperty(obj, 'toAST', {
13 | enumerable: false,
14 | value(): ASTNode {
15 | return b.callExpression(b.identifier('require'), [b.literal(filepath)]);
16 | },
17 | });
18 | return obj as Rsg.RequireItResult;
19 | }
20 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Heading/__snapshots__/Heading.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Heading should render a heading 1`] = `
4 |
7 | The heading
8 |
9 | `;
10 |
11 | exports[`Heading should render a heading according to the level 1`] = `
12 |
15 | The heading
16 |
17 | `;
18 |
19 | exports[`Heading should render a heading according to the level 2`] = `
20 |
23 | The heading
24 |
25 | `;
26 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/Pre/Pre.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 |
4 | import Pre from './index';
5 |
6 | describe('Markdown Pre', () => {
7 | it('should render a pre', () => {
8 | const actual = renderer.create(This is pre-formatted text.
);
9 |
10 | expect(actual.toJSON()).toMatchSnapshot();
11 | });
12 |
13 | it('should render highlighted code', () => {
14 | const code = '';
15 | const actual = renderer.create({code});
16 |
17 | expect(actual.toJSON()).toMatchSnapshot();
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/src/client/consts.ts:
--------------------------------------------------------------------------------
1 | export const DisplayModes = Object.freeze({
2 | // Show all sections and components (default)
3 | all: 'all',
4 | // Show one section
5 | section: 'section',
6 | // Show one component
7 | component: 'component',
8 | // Show one example inside component or section
9 | example: 'example',
10 | // Show error 404
11 | notFound: 'notFound',
12 | });
13 |
14 | export const ExampleModes = Object.freeze({
15 | hide: 'hide',
16 | collapse: 'collapse',
17 | expand: 'expand',
18 | });
19 |
20 | export const UsageModes = Object.freeze({
21 | hide: 'hide',
22 | collapse: 'collapse',
23 | expand: 'expand',
24 | });
25 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | examples/*/styleguide/*
3 | # this example contains typescript and has never been linted
4 | # linting it could take time.
5 | # TODO: remove this next line and fix all errors
6 | examples/styled-components
7 | lib/*
8 | src/typings/dependencies/*
9 | coverage/*
10 | travis_phantomjs/*
11 | # When ESlint looks at imports in .d.ts files,
12 | # it often duplicates automatically imports
13 | # @example: this `import { ASTNode } from 'ast-types';`
14 | # becomes this `import { ASTNode, ASTNode } from 'ast-types';`
15 | # TODO: fix tamia about this and remove this rule
16 | *.d.ts
17 | site/build/
18 | site/.docusaurus/
19 |
--------------------------------------------------------------------------------
/src/loaders/utils/highlightCodeInMarkdown.ts:
--------------------------------------------------------------------------------
1 | import remark from 'remark';
2 | import visit from 'unist-util-visit';
3 | import highlightCode from './highlightCode';
4 |
5 | function highlight() {
6 | return (ast: any) => {
7 | visit(ast, 'code', (node: any) => {
8 | node.value = highlightCode(node.value, node.lang);
9 | });
10 | };
11 | }
12 |
13 | /**
14 | * Highlight code in code snippets in Markdown.
15 | *
16 | * @param {string} markdown
17 | * @returns {string}
18 | */
19 | export default function highlightCodeInMarkdown(markdown: string): string {
20 | return remark().use(highlight).processSync(markdown).toString();
21 | }
22 |
--------------------------------------------------------------------------------
/src/client/rsg-components/ExamplePlaceholder/ExamplePlaceholder.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, fireEvent } from '@testing-library/react';
3 | import { ExamplePlaceholderRenderer } from './ExamplePlaceholderRenderer';
4 |
5 | test('should render an example placeholder after button click', () => {
6 | const { getByText, queryByText } = render(
7 |
8 | );
9 | fireEvent.click(getByText(/add examples to this component/i));
10 | expect(getByText('Pizza.md')).toBeInTheDocument();
11 | expect(queryByText(/add examples to this component/i)).not.toBeInTheDocument();
12 | });
13 |
--------------------------------------------------------------------------------
/src/loaders/utils/__tests__/expandDefaultComponent.spec.ts:
--------------------------------------------------------------------------------
1 | import expandDefaultComponent from '../expandDefaultComponent';
2 |
3 | it('expandDefaultComponent() replace placeholders with component name', () => {
4 | const exampleMarkdown = `
5 |
6 | <__COMPONENT__>
7 | text
8 | Name of component: __COMPONENT__
9 |
10 | <__COMPONENT__ />
11 |
12 | `;
13 | const result = expandDefaultComponent(exampleMarkdown, 'FooComponent');
14 | expect(result).not.toMatch(/__COMPONENT__/);
15 | expect(result).toMatch(/FooComponent/);
16 | expect((result.match(/FooComponent/g) || '').length).toBe(4);
17 | });
18 |
--------------------------------------------------------------------------------
/src/loaders/utils/getNameFromFilePath.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import startCase from 'lodash/startCase';
3 |
4 | /**
5 | * your-buttonTS -> YourButtonTS
6 | * your_button--TS -> YourButtonTS
7 | */
8 | function transformFileNameToDisplayName(displayName: string): string {
9 | return startCase(displayName).replace(/\s/g, '');
10 | }
11 |
12 | export default function getNameFromFilePath(filePath: string): string {
13 | let fileName = path.basename(filePath, path.extname(filePath));
14 | if (fileName === 'index') {
15 | fileName = path.basename(path.dirname(filePath));
16 | }
17 |
18 | return transformFileNameToDisplayName(fileName);
19 | }
20 |
--------------------------------------------------------------------------------
/src/typings/RecursivePartial.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * In a custom config file you might only want to override some parameters
3 | * This is the usage of the recursive Partial
4 | * `interface Test{param:string, paramObject:{p1:number, p2:boolean}}`
5 | * becomes
6 | * `interface TestPartial{param?:string, paramObject?:{p1?:number, p2?:boolean}}`
7 | * where everything is optional
8 | */
9 | export type RecursivePartial = {
10 | [P in keyof T]?: T[P] extends (infer U)[]
11 | ? RecursivePartial[]
12 | : T[P] extends (...args: unknown[]) => unknown
13 | ? T[P]
14 | : T[P] extends Record
15 | ? RecursivePartial
16 | : T[P];
17 | };
18 |
--------------------------------------------------------------------------------
/examples/sections/docs/Components.md:
--------------------------------------------------------------------------------
1 | Tempor qui ad ad sint nulla sint magna aliquip qui ex. Non commodo mollit et exercitation nostrud esse. Minim aliqua cillum est amet dolore ipsum qui tempor eu ea.
2 |
3 | Ut veniam sit pariatur deserunt non officia. Esse commodo proident quis culpa esse enim occaecat occaecat laborum nostrud non non sunt. Labore incididunt reprehenderit sunt elit reprehenderit nulla nulla Lorem aliquip incididunt. Qui cillum consectetur Lorem anim amet ex magna deserunt sunt.
4 |
5 | List of components:
6 |
7 | - [Buttons](#/Components?id=section-buttons)
8 | - [Fields](#/Components?id=section-fields)
9 | - [Others](#/Components?id=section-others)
10 |
--------------------------------------------------------------------------------
/src/loaders/utils/sortProps.ts:
--------------------------------------------------------------------------------
1 | import sortBy from 'lodash/sortBy';
2 | import { PropDescriptor } from 'react-docgen';
3 |
4 | /**
5 | * Sorts an array of properties by their 'required' property first and 'name'
6 | * property second.
7 | *
8 | * @param {array} props
9 | * @return {array} Sorted properties
10 | */
11 | function sortProps(props: PropDescriptor[]) {
12 | const requiredPropNames = sortBy(props.filter(prop => prop.required), 'name');
13 | const optionalPropNames = sortBy(props.filter(prop => !prop.required), 'name');
14 | const sortedProps = requiredPropNames.concat(optionalPropNames);
15 | return sortedProps;
16 | }
17 |
18 | export default sortProps;
19 |
--------------------------------------------------------------------------------
/site/scripts/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | # Deploy to Netlify
6 |
7 | EXAMPLE_DIR="../examples/basic"
8 | STATIC_DIR="static"
9 |
10 | echo "Node $(node -v)"
11 | echo "npm $(npm -v)"
12 |
13 | # Build a basic example
14 | echo
15 | echo "Building the basic example..."
16 | cd "$EXAMPLE_DIR"
17 | npm install
18 | npm run styleguide:build
19 | cd -
20 |
21 | # Copy to the public folder
22 | echo
23 | echo "Copying the basic example..."
24 | mkdir -p "$STATIC_DIR/examples/basic"
25 | cp -R $EXAMPLE_DIR/styleguide/* "$STATIC_DIR/examples/basic"
26 |
27 | # Build the site
28 | echo
29 | echo "Building the site..."
30 | npm run sync
31 | npm run build
32 |
--------------------------------------------------------------------------------
/src/loaders/utils/__tests__/highlightCodeInMarkdown.spec.ts:
--------------------------------------------------------------------------------
1 | import highlightCodeInMarkdown from '../highlightCodeInMarkdown';
2 |
3 | it('should highlight code with specified language', () => {
4 | const text = `
5 | The only true button.
6 |
7 | \`\`\`html
8 | Hello React
9 | \`\`\`
10 | `;
11 | const actual = highlightCodeInMarkdown(text);
12 | expect(actual).toMatchSnapshot();
13 | });
14 |
15 | it('should not highlight code without language', () => {
16 | const text = `
17 | The only \`true\` button.
18 |
19 | \`\`\`
20 | Hello React
21 | \`\`\`
22 | `;
23 | const actual = highlightCodeInMarkdown(text);
24 | expect(actual).toMatchSnapshot();
25 | });
26 |
--------------------------------------------------------------------------------
/src/loaders/utils/getAllContentPages.ts:
--------------------------------------------------------------------------------
1 | import * as Rsg from '../../typings';
2 |
3 | /**
4 | * Get all section content pages.
5 | *
6 | * @param {Array} sections
7 | * @returns {Array}
8 | */
9 | export default function getAllContentPages(
10 | sections: Rsg.LoaderSection[]
11 | ): (Rsg.MarkdownExample | Rsg.RequireItResult)[] {
12 | return sections.reduce((pages: (Rsg.MarkdownExample | Rsg.RequireItResult)[], section) => {
13 | if (section.content) {
14 | pages = pages.concat([section.content]);
15 | }
16 |
17 | if (section.sections) {
18 | pages = pages.concat(getAllContentPages(section.sections));
19 | }
20 |
21 | return pages;
22 | }, []);
23 | }
24 |
--------------------------------------------------------------------------------
/examples/preact/styleguide.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | components: 'src/components/**/[A-Z]*.js',
3 | defaultExample: true,
4 | webpackConfig: {
5 | resolve: {
6 | alias: {
7 | react: 'preact-compat',
8 | 'react-dom': 'preact-compat',
9 | },
10 | },
11 | module: {
12 | rules: [
13 | {
14 | test: /\.jsx?$/,
15 | exclude: /node_modules/,
16 | loader: 'babel-loader',
17 | },
18 | {
19 | test: /\.css$/,
20 | use: [
21 | 'style-loader',
22 | {
23 | loader: 'css-loader',
24 | options: {
25 | modules: true,
26 | },
27 | },
28 | ],
29 | },
30 | ],
31 | },
32 | },
33 | };
34 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Welcome/__snapshots__/Welcome.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renderer should render welcome screen 1`] = `
4 |
5 |
23 |
24 | `;
25 |
--------------------------------------------------------------------------------
/src/loaders/utils/filterComponentsWithExample.ts:
--------------------------------------------------------------------------------
1 | import * as Rsg from '../../typings';
2 |
3 | /**
4 | * Filter out components without an example file.
5 | *
6 | * @param {Array} sections
7 | * @returns {Array}
8 | */
9 | export default function filterComponentsWithExample(
10 | sections: Rsg.LoaderSection[]
11 | ): Rsg.LoaderSection[] {
12 | return sections
13 | .map(section => ({
14 | ...section,
15 | sections: filterComponentsWithExample(section.sections),
16 | components: section.components.filter(component => component.hasExamples),
17 | }))
18 | .filter(
19 | section => section.components.length > 0 || section.sections.length > 0 || section.content
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/examples/basic/styleguide.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { version } = require('./package');
3 |
4 | module.exports = {
5 | components: 'src/components/**/[A-Z]*.js',
6 | defaultExample: true,
7 | moduleAliases: {
8 | 'rsg-example': path.resolve(__dirname, 'src'),
9 | },
10 | ribbon: {
11 | url: 'https://github.com/styleguidist/react-styleguidist',
12 | },
13 | version,
14 | webpackConfig: {
15 | module: {
16 | rules: [
17 | {
18 | test: /\.jsx?$/,
19 | exclude: /node_modules/,
20 | loader: 'babel-loader',
21 | },
22 | {
23 | test: /\.css$/,
24 | use: ['style-loader', 'css-loader'],
25 | },
26 | ],
27 | },
28 | },
29 | };
30 |
--------------------------------------------------------------------------------
/examples/react-native/src/SimpleCard.md:
--------------------------------------------------------------------------------
1 | Basic use example:
2 |
3 | ```jsx
4 | const Text = require('react-native').Text
5 | ;
6 | Basic example 1
7 |
8 | ```
9 |
10 | Basic use example with color:
11 |
12 | ```jsx
13 | const Text = require('react-native').Text
14 | ;
15 | Basic example 2
16 |
17 | ```
18 |
19 | Basic use example with title color and color property:
20 |
21 | ```jsx
22 | const Text = require('react-native').Text
23 | ;
24 | Basic example 3
25 |
26 | ```
27 |
--------------------------------------------------------------------------------
/test/data/styleguide.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const dir = path.resolve(__dirname, 'lib');
4 |
5 | module.exports = {
6 | title: 'React Style Guide Example',
7 | defaultExample: true,
8 | components: './components/**/[A-Z]*.js',
9 | webpackConfig: {
10 | module: {
11 | rules: [
12 | {
13 | test: /\.jsx?$/,
14 | include: dir,
15 | loader: 'babel-loader',
16 | },
17 | {
18 | test: /\.css$/,
19 | include: dir,
20 | use: [
21 | 'style-loader',
22 | {
23 | loader: 'css-loader',
24 | options: {
25 | modules: true,
26 | },
27 | },
28 | ],
29 | },
30 | ],
31 | },
32 | },
33 | };
34 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/List/__snapshots__/List.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Markdown List should render an ordered list 1`] = `
4 |
7 | -
10 | First
11 |
12 | -
15 | Second
16 |
17 |
18 | `;
19 |
20 | exports[`Markdown List should render an unordered list 1`] = `
21 |
24 | -
27 | First
28 |
29 | -
32 | Second
33 |
34 |
35 | `;
36 |
--------------------------------------------------------------------------------
/src/typings/RsgPropsObject.ts:
--------------------------------------------------------------------------------
1 | import { DocumentationObject, MethodDescriptor, PropDescriptor } from 'react-docgen';
2 | import { RequireItResult } from './RsgRequireItResult';
3 |
4 | export interface MethodWithDocblock extends MethodDescriptor {
5 | docblock: string;
6 | }
7 |
8 | export interface TempPropsObject extends DocumentationObject {
9 | displayName: string;
10 | visibleName?: string;
11 | methods?: MethodWithDocblock[];
12 | doclets: Record;
13 | example?: RequireItResult | null;
14 | }
15 |
16 | export interface PropsObject extends Omit {
17 | props?: Record | PropDescriptor[];
18 | examples?: RequireItResult | null;
19 | }
20 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/Blockquote/Blockquote.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import Blockquote from './index';
4 |
5 | describe('Markdown Blockquote', () => {
6 | it('should render a blockquote', () => {
7 | const actual = renderer.create(
8 | To be, or not to be: that is the question
9 | );
10 |
11 | expect(actual).toMatchSnapshot();
12 | });
13 |
14 | it('should preserve custom css class', () => {
15 | const actual = renderer.create(
16 | To be, or not to be: that is the question
17 | );
18 |
19 | expect(actual).toMatchSnapshot();
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/List/List.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 |
4 | import List from './index';
5 |
6 | describe('Markdown List', () => {
7 | it('should render an unordered list', () => {
8 | const actual = renderer.create(
9 |
10 | First
11 | Second
12 |
13 | );
14 |
15 | expect(actual.toJSON()).toMatchSnapshot();
16 | });
17 |
18 | it('should render an ordered list', () => {
19 | const actual = renderer.create(
20 |
21 | First
22 | Second
23 |
24 | );
25 |
26 | expect(actual.toJSON()).toMatchSnapshot();
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/test/apps/basic/styleguide.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const dir = path.resolve(__dirname, 'lib');
4 |
5 | module.exports = {
6 | title: 'React Style Guide Example',
7 | defaultExample: true,
8 | components: './components/**/[A-Z]*.js',
9 | webpackConfig: {
10 | module: {
11 | rules: [
12 | {
13 | test: /\.jsx?$/,
14 | include: dir,
15 | loader: 'babel-loader',
16 | },
17 | {
18 | test: /\.css$/,
19 | include: dir,
20 | use: [
21 | 'style-loader',
22 | {
23 | loader: 'css-loader',
24 | options: {
25 | modules: true,
26 | },
27 | },
28 | ],
29 | },
30 | ],
31 | },
32 | },
33 | };
34 |
--------------------------------------------------------------------------------
/examples/express/styleguide.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | title: 'Style guide example',
3 | components: './src/components/**/[A-Z]*.js',
4 | webpackConfig: {
5 | module: {
6 | rules: [
7 | {
8 | test: /\.jsx?$/,
9 | exclude: /node_modules/,
10 | loader: 'babel-loader',
11 | },
12 | {
13 | test: /\.css$/,
14 | use: [
15 | 'style-loader',
16 | {
17 | loader: 'css-loader',
18 | options: {
19 | modules: true,
20 | },
21 | },
22 | ],
23 | },
24 | ],
25 | },
26 | },
27 | configureServer(app) {
28 | app.get('/custom', (req, res) => {
29 | res.status(200).send({ response: 'Server invoked' });
30 | });
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/examples/basic/src/components/Button/Button.css:
--------------------------------------------------------------------------------
1 | .button {
2 | padding: 0.5em 1.5em;
3 | color: #666;
4 | background-color: #fff;
5 | border: 1px solid currentColor;
6 | border-radius: 0.3em;
7 | text-align: center;
8 | vertical-align: middle;
9 | cursor: pointer;
10 | }
11 |
12 | .button[disabled] {
13 | opacity: 0.35;
14 | cursor: default;
15 | }
16 |
17 | .checks {
18 | background-image: linear-gradient(45deg, #f5f5f5 25%, transparent 25%),
19 | linear-gradient(-45deg, #f5f5f5 25%, transparent 25%),
20 | linear-gradient(45deg, transparent 75%, #f5f5f5 75%),
21 | linear-gradient(-45deg, transparent 75%, #f5f5f5 75%);
22 | background-size: 16px 16px;
23 | background-position: 0 0, 0 8px, 8px -8px, -8px 0px;
24 | }
25 |
--------------------------------------------------------------------------------
/examples/sections/src/components/ThemeButton/Readme.md:
--------------------------------------------------------------------------------
1 | An example of using React Context with a component:
2 |
3 | ```jsx
4 | import ThemeContext from '../../ThemeContext.js'
5 |
6 | const [theme, setTheme] = React.useState('light')
7 | const toggleTheme = () =>
8 | setTheme(theme === 'light' ? 'dark' : 'light')
9 | ;<>
10 |
17 |
18 |
19 |
20 |
21 | Themable button
22 |
23 |
24 | >
25 | ```
26 |
--------------------------------------------------------------------------------
/src/client/rsg-components/ComponentsList/ComponentsList.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import ComponentsListRenderer from 'rsg-components/ComponentsList/ComponentsListRenderer';
4 | import * as Rsg from '../../../typings';
5 |
6 | interface ComponentsListProps {
7 | items: Rsg.TOCItem[];
8 | }
9 |
10 | const ComponentsList: React.FunctionComponent = ({ items }) => {
11 | const visibleItems = items.filter(item => item.visibleName);
12 |
13 | return visibleItems.length > 0 ? : null;
14 | };
15 |
16 | ComponentsList.propTypes = {
17 | items: PropTypes.array.isRequired,
18 | };
19 |
20 | export default ComponentsList;
21 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Logo/LogoRenderer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Styled, { JssInjectedProps } from 'rsg-components/Styled';
3 | import * as Rsg from '../../../typings';
4 |
5 | const styles = ({ color, fontFamily, fontSize }: Rsg.Theme) => ({
6 | logo: {
7 | color: color.base,
8 | margin: 0,
9 | fontFamily: fontFamily.base,
10 | fontSize: fontSize.h4,
11 | fontWeight: 'normal',
12 | },
13 | });
14 |
15 | interface Props extends JssInjectedProps {
16 | children?: React.ReactNode;
17 | }
18 |
19 | export const LogoRenderer = ({ classes, children }: Props) => {
20 | return {children}
;
21 | };
22 |
23 | export default Styled(styles)(LogoRenderer);
24 |
--------------------------------------------------------------------------------
/src/loaders/utils/getComponentPatternsFromSections.ts:
--------------------------------------------------------------------------------
1 | import * as Rsg from '../../typings';
2 |
3 | /**
4 | * Return all glob patterns from all sections.
5 | *
6 | * NOTE: a section cannot have components & subsections
7 | * @param {Array} sections
8 | * @returns {Array}
9 | */
10 | export default function getComponentPatternsFromSections(sections: Rsg.ConfigSection[]): string[] {
11 | return sections.reduce((patterns: string[], section) => {
12 | if (Array.isArray(section.components)) {
13 | return patterns.concat(section.components);
14 | }
15 |
16 | if (section.sections) {
17 | return patterns.concat(getComponentPatternsFromSections(section.sections));
18 | }
19 |
20 | return patterns;
21 | }, []);
22 | }
23 |
--------------------------------------------------------------------------------
/examples/themed/src/Components/Button/Button.css:
--------------------------------------------------------------------------------
1 | .button {
2 | padding: 0.5em 1.5em;
3 | color: #666;
4 | background-color: #fff;
5 | border: 1px solid currentColor;
6 | border-radius: 0.3em;
7 | text-align: center;
8 | vertical-align: middle;
9 | cursor: pointer;
10 | }
11 |
12 | .button[disabled] {
13 | opacity: 0.35;
14 | cursor: default;
15 | }
16 |
17 | .checks {
18 | background-image: linear-gradient(45deg, #f5f5f5 25%, transparent 25%),
19 | linear-gradient(-45deg, #f5f5f5 25%, transparent 25%),
20 | linear-gradient(45deg, transparent 75%, #f5f5f5 75%),
21 | linear-gradient(-45deg, transparent 75%, #f5f5f5 75%);
22 | background-size: 16px 16px;
23 | background-position: 0 0, 0 8px, 8px -8px, -8px 0px;
24 | }
25 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Heading/Heading.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import Heading from './index';
4 |
5 | describe('Heading', () => {
6 | it('should render a heading according to the level', () => {
7 | const actualH3 = renderer.create(The heading );
8 | expect(actualH3.toJSON()).toMatchSnapshot();
9 |
10 | const actualH5 = renderer.create(The heading );
11 | expect(actualH5.toJSON()).toMatchSnapshot();
12 | });
13 |
14 | it('should render a heading', () => {
15 | const actual = renderer.create(The heading );
16 | expect(actual.toJSON()).toMatchSnapshot();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/scripts/utils/__tests__/findFileCaseInsensitive.spec.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import findFileCaseInsensitive, { clearCache } from '../findFileCaseInsensitive';
3 |
4 | it('should return a file path with the correct case if a file exists', () => {
5 | const result = findFileCaseInsensitive(path.join(__dirname, 'Findfilecaseinsensitive.Spec.TS'));
6 | expect(result).toMatch(__filename);
7 | });
8 |
9 | it('should return undefined if a file doesn’t exist', () => {
10 | const result = findFileCaseInsensitive(path.join(__dirname, 'pizza.js'));
11 | expect(result).toBeFalsy();
12 | });
13 |
14 | it('cache clean function shouldn’t throw', () => {
15 | const fn = () => clearCache();
16 | expect(fn).not.toThrowError();
17 | });
18 |
--------------------------------------------------------------------------------
/examples/styled-components/src/components/Button/Button.md:
--------------------------------------------------------------------------------
1 | ### React Styleguidist styled-components TypeScript
2 |
3 | Secondary and primary buttons:
4 |
5 | ```jsx harmony
6 |
7 | ```
8 |
9 | Disabled buttons:
10 |
11 | ```jsx harmony
12 |
13 | ```
14 |
15 | Full width button:
16 |
17 | ```js
18 |
19 | ```
20 |
21 | Button as a link:
22 |
23 | ```js
24 |
25 | ```
26 |
--------------------------------------------------------------------------------
/examples/styled-components/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "jsx": "react",
5 | "target": "esnext",
6 | "moduleResolution": "node",
7 | "allowJs": true,
8 | "checkJs": false,
9 | "noEmit": true,
10 | "pretty": true,
11 | "strict": true,
12 | "isolatedModules": false,
13 | "skipLibCheck": true,
14 | "esModuleInterop": true,
15 | "module": "commonjs",
16 | "lib": [
17 | "dom",
18 | "es6",
19 | "es7",
20 | "dom.iterable",
21 | "scripthost"
22 | ],
23 | "types": [
24 | "./node_modules/@types/"
25 | ]
26 | },
27 | "include": [
28 | "src"
29 | ],
30 | "exclude": [
31 | "node_modules"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/Checkbox/CheckboxRenderer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import Styled, { JssInjectedProps } from 'rsg-components/Styled';
5 |
6 | const styles = () => ({
7 | input: {
8 | isolate: false,
9 | display: 'inline-block',
10 | verticalAlign: 'middle',
11 | },
12 | });
13 |
14 | export const CheckboxRenderer: React.FunctionComponent = ({
15 | classes,
16 | ...rest
17 | }) => {
18 | return ;
19 | };
20 | CheckboxRenderer.propTypes = {
21 | classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
22 | };
23 |
24 | export default Styled(styles)(CheckboxRenderer);
25 |
--------------------------------------------------------------------------------
/src/client/utils/getAst.ts:
--------------------------------------------------------------------------------
1 | import { Parser, Node, Options } from 'acorn';
2 |
3 | export interface Program extends Node {
4 | body: Node[];
5 | }
6 |
7 | export const ACORN_OPTIONS: Options = {
8 | ecmaVersion: 2019,
9 | sourceType: 'module',
10 | };
11 |
12 | /**
13 | * Parse source code with Acorn and return AST, returns undefined in case of errors
14 | */
15 | export default function getAst(code: string): Program | undefined {
16 | try {
17 | return (Parser.parse(code, {
18 | ...ACORN_OPTIONS,
19 | // types of acorn are too simplistic and we have to use the body
20 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
21 | }) as any) as Program;
22 | } catch (err) {
23 | return undefined;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example plugins/index.js can be used to load plugins
3 | //
4 | // You can change the location of this file or turn off loading
5 | // the plugins file with the 'pluginsFile' configuration option.
6 | //
7 | // You can read more here:
8 | // https://on.cypress.io/plugins-guide
9 | // ***********************************************************
10 |
11 | // This function is called when a project is opened or re-opened (e.g. due to
12 | // the project's config changing)
13 |
14 | module.exports = (/* on, config */) => {
15 | // `on` is used to hook into various events Cypress emits
16 | // `config` is the resolved Cypress config
17 | };
18 |
--------------------------------------------------------------------------------
/src/loaders/utils/__tests__/__snapshots__/highlightCodeInMarkdown.spec.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`should highlight code with specified language 1`] = `
4 | "The only true button.
5 |
6 | \`\`\`html
7 | <p>Hello React</p>
8 | \`\`\`
9 | "
10 | `;
11 |
12 | exports[`should not highlight code without language 1`] = `
13 | "The only \`true\` button.
14 |
15 | Hello React
16 | "
17 | `;
18 |
--------------------------------------------------------------------------------
/test/components/Label/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | /**
5 | * The only true label.
6 | */
7 | export default function Label({ color, background, children }) {
8 | const styles = {
9 | color,
10 | background,
11 | padding: '.5em 1em',
12 | borderRadius: '0.3em',
13 | fontFamily: 'arial',
14 | };
15 |
16 | // eslint-disable-next-line jsx-a11y/label-has-for
17 | return ;
18 | }
19 | Label.propTypes = {
20 | /**
21 | * Label text.
22 | */
23 | children: PropTypes.string.isRequired,
24 | color: PropTypes.string,
25 | background: PropTypes.string,
26 | };
27 | Label.defaultProps = {
28 | color: '#333',
29 | background: 'white',
30 | };
31 |
--------------------------------------------------------------------------------
/examples/react-native/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "react-styleguidist-example-react-native",
4 | "slug": "react-styleguidist-example-react-native",
5 | "privacy": "public",
6 | "sdkVersion": "32.0.0",
7 | "platforms": [
8 | "ios",
9 | "android"
10 | ],
11 | "version": "1.0.0",
12 | "orientation": "portrait",
13 | "icon": "./assets/icon.png",
14 | "splash": {
15 | "image": "./assets/splash.png",
16 | "resizeMode": "contain",
17 | "backgroundColor": "#ffffff"
18 | },
19 | "updates": {
20 | "fallbackToCacheTimeout": 0
21 | },
22 | "assetBundlePatterns": [
23 | "**/*"
24 | ],
25 | "ios": {
26 | "supportsTablet": true
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/Hr/HrRenderer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Styled, { JssInjectedProps } from 'rsg-components/Styled';
4 | import * as Rsg from '../../../../typings';
5 |
6 | const styles = ({ space, color }: Rsg.Theme) => ({
7 | hr: {
8 | borderBottom: [[1, color.border, 'solid']],
9 | marginTop: 0,
10 | marginBottom: space[2],
11 | },
12 | });
13 |
14 | export const HrRenderer: React.FunctionComponent = ({ classes }) => {
15 | return
;
16 | };
17 | HrRenderer.propTypes = {
18 | classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
19 | };
20 |
21 | export default Styled(styles)(HrRenderer);
22 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Error/__snapshots__/Error.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renderer should render error message 1`] = `
4 |
5 |
6 | error
7 | info
8 |
9 |
10 |
11 | This may be due to an error in a component you are overriding, or a bug in React Styleguidist.
12 |
13 |
14 | If you believe this is a bug,
15 |
23 | please submit an issue
24 |
25 | .
26 |
27 |
28 |
29 | `;
30 |
--------------------------------------------------------------------------------
/src/client/utils/filterComponentExamples.ts:
--------------------------------------------------------------------------------
1 | import * as Rsg from '../../typings';
2 |
3 | /**
4 | * Return a copy of the given component with the examples array filtered
5 | * to contain only the specified index:
6 | * filterComponentExamples({ examples: [1,2,3], ...other }, 2) → { examples: [3], ...other }
7 | *
8 | * @param {object} component
9 | * @param {number} index
10 | * @returns {object}
11 | */
12 | export default function filterComponentExamples(
13 | component: Rsg.Component,
14 | index: number
15 | ): Rsg.Component {
16 | return {
17 | ...component,
18 | props: {
19 | ...component.props,
20 | examples:
21 | component.props && component.props.examples ? [component.props.examples[index]] : [],
22 | },
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/examples/basic/src/components/Label/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | /**
5 | * The only true label.
6 | */
7 | export default function Label({ color, background, children }) {
8 | const styles = {
9 | color,
10 | background,
11 | padding: '.5em 1em',
12 | borderRadius: '0.3em',
13 | fontFamily: 'arial',
14 | };
15 |
16 | // eslint-disable-next-line jsx-a11y/label-has-for
17 | return ;
18 | }
19 | Label.propTypes = {
20 | /**
21 | * Label text.
22 | */
23 | children: PropTypes.string.isRequired,
24 | color: PropTypes.string,
25 | background: PropTypes.string,
26 | };
27 | Label.defaultProps = {
28 | color: '#333',
29 | background: 'white',
30 | };
31 |
--------------------------------------------------------------------------------
/src/loaders/utils/client/requireInRuntime.ts:
--------------------------------------------------------------------------------
1 | type Module = { [name: string]: any } | (() => any);
2 | type RequireMap = { [filepath: string]: Module };
3 |
4 | /**
5 | * Return module from a given map (like {react: require('react')}) or throw.
6 | * We allow to require modules only from Markdown examples (won’t work dynamically because we need to know all required
7 | * modules in advance to be able to bundle them with the code).
8 | */
9 | export default function requireInRuntime(requireMap: RequireMap, filepath: string): Module {
10 | if (!(filepath in requireMap)) {
11 | throw new Error(
12 | `import or require() statements can be added only by editing a Markdown example file: ${filepath}`
13 | );
14 | }
15 |
16 | return requireMap[filepath];
17 | }
18 |
--------------------------------------------------------------------------------
/examples/webpack/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | entry: './src/index.js',
5 | output: {
6 | filename: 'bundle.js',
7 | path: path.resolve(__dirname, 'build'),
8 | },
9 | module: {
10 | rules: [
11 | {
12 | test: /\.jsx?$/,
13 | exclude: /node_modules/,
14 | loader: 'babel-loader',
15 | },
16 | {
17 | test: /\.css$/,
18 | use: [
19 | 'style-loader',
20 | {
21 | loader: 'css-loader',
22 | options: {
23 | importLoaders: 1,
24 | },
25 | },
26 | ],
27 | },
28 | {
29 | test: /\.svg$/,
30 | loader: 'file-loader',
31 | options: {
32 | name: 'static/media/[name].[hash:8].[ext]',
33 | },
34 | },
35 | ],
36 | },
37 | };
38 |
--------------------------------------------------------------------------------
/src/typings/dependencies/webpack-merge.ts:
--------------------------------------------------------------------------------
1 | declare module 'webpack-merge' {
2 | import { Configuration, WebpackPluginInstance } from 'webpack';
3 |
4 | type MetaConfig = Configuration | ((env?: string) => Configuration);
5 | type mergeFunction = (...configs: MetaConfig[]) => Configuration;
6 | type customizeArrayFuntion = () => any[];
7 | interface WebpackMergeOptions {
8 | customizeArray: customizeArrayFuntion;
9 | }
10 | const webpackMerge: {
11 | (options: WebpackMergeOptions): mergeFunction;
12 | (...configs: MetaConfig[]): Configuration;
13 | unique(
14 | key: string,
15 | uniques: string[],
16 | getter?: (plugin: WebpackPluginInstance) => string | undefined | false
17 | ): customizeArrayFuntion;
18 | };
19 | export = webpackMerge;
20 | }
21 |
--------------------------------------------------------------------------------
/examples/themed/styleguide.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { version } = require('./package');
3 |
4 | module.exports = {
5 | components: 'src/components/**/[A-Z]*.js',
6 | defaultExample: true,
7 | moduleAliases: {
8 | 'rsg-example': path.resolve(__dirname, 'src'),
9 | },
10 | ribbon: {
11 | url: 'https://github.com/styleguidist/react-styleguidist',
12 | },
13 | theme: 'styleguide.theme.js',
14 | styles: 'styleguide.styles.js',
15 | version,
16 | webpackConfig: {
17 | module: {
18 | rules: [
19 | {
20 | test: /\.jsx?$/,
21 | exclude: /node_modules/,
22 | loader: 'babel-loader',
23 | },
24 | {
25 | test: /\.css$/,
26 | use: ['style-loader', 'css-loader'],
27 | },
28 | ],
29 | },
30 | },
31 | };
32 |
--------------------------------------------------------------------------------
/src/client/rsg-components/slots/UsageTabButton.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRenderer } from 'react-test-renderer/shallow';
3 | import UsageTabButton from './UsageTabButton';
4 |
5 | const props = {
6 | name: 'Pizza',
7 | onClick: () => {},
8 | };
9 |
10 | it('should renderer a button', () => {
11 | const renderer = createRenderer();
12 | renderer.render( );
13 |
14 | expect(renderer.getRenderOutput()).toMatchSnapshot();
15 | });
16 |
17 | it('should renderer null if there are not props or methods', () => {
18 | const renderer = createRenderer();
19 | renderer.render( );
20 |
21 | expect(renderer.getRenderOutput()).toBe(null);
22 | });
23 |
--------------------------------------------------------------------------------
/test/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | // import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/examples/styled-components/styleguide.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { version } = require('./package');
3 |
4 | module.exports = {
5 | components: 'src/components/**/*.{js,tsx}',
6 | styleguideComponents: {
7 | Wrapper: path.join(__dirname, 'src/StyleGuideWrapper'),
8 | },
9 | defaultExample: true,
10 | moduleAliases: {
11 | 'rsg-example': path.resolve(__dirname, 'src'),
12 | },
13 | usageMode: 'expand',
14 | version,
15 | webpackConfig: {
16 | module: {
17 | rules: [
18 | {
19 | test: /\.(js|ts)x?$/,
20 | exclude: /node_modules/,
21 | loader: 'babel-loader',
22 | },
23 | ],
24 | noParse: /\.(css|scss)/,
25 | },
26 | resolve: {
27 | extensions: ['.js', 'jsx', '.ts', '.tsx', '.json'],
28 | },
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Para/Para.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRenderer } from 'react-test-renderer/shallow';
3 | import { ParaRenderer, styles } from './ParaRenderer';
4 |
5 | const props = {
6 | classes: classes(styles),
7 | };
8 |
9 | it('should render paragraph as a ', () => {
10 | const renderer = createRenderer();
11 | renderer.render(Pizza );
12 |
13 | expect(renderer.getRenderOutput()).toMatchSnapshot();
14 | });
15 |
16 | it('should render paragraph as a ', () => {
17 | const renderer = createRenderer();
18 | renderer.render(
19 |
20 | Pizza
21 |
22 | );
23 |
24 | expect(renderer.getRenderOutput()).toMatchSnapshot();
25 | });
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F914 Support question"
3 | about: I have a question or don’t know how to do something
4 | ---
5 |
6 | --------------^ Click “Preview”!
7 |
8 | If you have a support question, ask it on [Stack Overflow](https://stackoverflow.com/questions/tagged/react-styleguidist) using the `react-styleguidist` tag.
9 |
10 | Before submitting a new question, make sure you:
11 |
12 | - Searched existing [Stack Overflow questions](https://stackoverflow.com/questions/tagged/react-styleguidist).
13 | - Searched opened and closed [GitHub issues](https://github.com/styleguidist/react-styleguidist/issues?utf8=%E2%9C%93&q=is%3Aissue).
14 | - Read [the documentation](https://react-styleguidist.js.org/docs/getting-started).
15 | - Googled your question.
16 |
--------------------------------------------------------------------------------
/test/components/RandomButton/RandomButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import sample from 'lodash/sample';
4 |
5 | /**
6 | * Button that changes label on every click.
7 | */
8 | export default class RandomButton extends Component {
9 | static propTypes = {
10 | /**
11 | * List of possible labels.
12 | */
13 | variants: PropTypes.array.isRequired,
14 | };
15 |
16 | constructor(props) {
17 | super();
18 | this.state = {
19 | label: sample(props.variants),
20 | };
21 | }
22 |
23 | handleClick = () => {
24 | this.setState({
25 | label: sample(this.props.variants),
26 | });
27 | };
28 |
29 | render() {
30 | return ;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/sections/src/components/ThemeButton/ThemeButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import ThemeContext from '../../ThemeContext';
4 | import './ThemeButton.css';
5 |
6 | /**
7 | * Button that is themed by a context value
8 | */
9 | export default class ThemeButton extends Component {
10 | render() {
11 | const className =
12 | this.context === 'light' ? 'ThemeButton ThemeButton--light' : 'ThemeButton ThemeButton--dark';
13 | return ;
14 | }
15 | }
16 |
17 | /** ThemeContext {string} */
18 | ThemeButton.contextType = ThemeContext;
19 | ThemeButton.propTypes = {
20 | /**
21 | * ThemeButton label
22 | */
23 | children: PropTypes.string.isRequired,
24 | };
25 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Name/Name.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRenderer } from 'react-test-renderer/shallow';
3 | import { NameRenderer, styles } from './NameRenderer';
4 |
5 | const props = {
6 | classes: classes(styles),
7 | };
8 |
9 | it('renderer should render argument name', () => {
10 | const renderer = createRenderer();
11 | renderer.render(Foo );
12 |
13 | expect(renderer.getRenderOutput()).toMatchSnapshot();
14 | });
15 |
16 | it('renderer should render deprecated argument name', () => {
17 | const renderer = createRenderer();
18 | renderer.render(
19 |
20 | Foo
21 |
22 | );
23 |
24 | expect(renderer.getRenderOutput()).toMatchSnapshot();
25 | });
26 |
--------------------------------------------------------------------------------
/src/scripts/__tests__/server.spec.ts:
--------------------------------------------------------------------------------
1 | import server from '../server';
2 | import getConfig from '../config';
3 |
4 | jest.mock('../create-server', () => () => {
5 | return {
6 | app: {
7 | startCallback: (cb: () => void) => cb(),
8 | close: (cb: () => void) => cb(),
9 | },
10 | compiler: {},
11 | };
12 | });
13 |
14 | test('server should return an object containing a server instance', () => {
15 | const config = getConfig();
16 | const callback = jest.fn();
17 | const serverInfo = server(config, callback);
18 |
19 | expect(callback).toBeCalled();
20 | expect(serverInfo.app).toBeTruthy();
21 | expect(serverInfo.compiler).toBeTruthy();
22 | expect(typeof serverInfo.app.startCallback).toBe('function');
23 | expect(typeof serverInfo.app.close).toBe('function');
24 | });
25 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Message/Message.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRenderer } from 'react-test-renderer/shallow';
3 | import { MessageRenderer } from './MessageRenderer';
4 |
5 | it('renderer should render message', () => {
6 | const message = 'Hello *world*!';
7 | const renderer = createRenderer();
8 | renderer.render({message} );
9 |
10 | expect(renderer.getRenderOutput()).toMatchSnapshot();
11 | });
12 |
13 | it('renderer should render message for array', () => {
14 | const messages = ['Hello *world*!', 'Foo _bar_'];
15 | const renderer = createRenderer();
16 | renderer.render({messages} );
17 |
18 | expect(renderer.getRenderOutput()).toMatchSnapshot();
19 | });
20 |
--------------------------------------------------------------------------------
/src/client/utils/__tests__/filterComponentExamples.spec.ts:
--------------------------------------------------------------------------------
1 | import deepfreeze from 'deepfreeze';
2 | import filterComponentExamples from '../filterComponentExamples';
3 | import * as Rsg from '../../../typings';
4 |
5 | const examples: Rsg.Example[] = ['a', 'b', 'c', 'd'].map(x => ({ type: 'markdown', content: x }));
6 |
7 | const component = deepfreeze({
8 | props: {
9 | examples,
10 | },
11 | other: 'info',
12 | });
13 |
14 | describe('filterComponentExamples', () => {
15 | it('should return a shallow copy of a component with example filtered by given index', () => {
16 | const result = filterComponentExamples(component, 2);
17 | expect(result).toEqual({
18 | props: {
19 | examples: [{ type: 'markdown', content: 'c' }],
20 | },
21 | other: 'info',
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/Table/Table.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 |
4 | import { Table, TableHead, TableBody, TableRow, TableCell } from './index';
5 |
6 | describe('Markdown Table', () => {
7 | it('should render a table', () => {
8 | const actual = renderer.create(
9 |
10 |
11 |
12 | 1st header
13 | 2nd header
14 |
15 |
16 |
17 |
18 | 1st cell
19 | 2nd cell
20 |
21 |
22 |
23 | );
24 |
25 | expect(actual.toJSON()).toMatchSnapshot();
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F680 Feature request"
3 | about: I have a suggestion (and might want to implement myself)
4 | ---
5 |
6 |
7 |
8 | **The problem**
9 |
10 |
11 |
12 | **Proposed solution**
13 |
14 |
15 |
16 | **Alternative solutions**
17 |
18 |
19 |
20 | **Additional context**
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Arguments/__snapshots__/Arguments.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renderer should render arguments 1`] = `
4 |
7 |
16 |
19 |
20 | `;
21 |
22 | exports[`renderer should render heading 1`] = `
23 |
26 |
29 |
32 | Arguments
33 |
34 |
35 |
38 |
39 | `;
40 |
--------------------------------------------------------------------------------
/src/client/rsg-components/ComplexType/ComplexType.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, fireEvent } from '@testing-library/react';
3 | import ComplexType from './ComplexTypeRenderder';
4 |
5 | function renderComponent(name = 'color', raw = 'red | blue') {
6 | return render( );
7 | }
8 |
9 | describe('ComplexType', () => {
10 | test('should render name', () => {
11 | const { getByRole } = renderComponent();
12 | expect(getByRole('button')).toHaveTextContent('color');
13 | });
14 |
15 | test('should render raw text in the tooltip', () => {
16 | const { container, getByRole } = renderComponent();
17 | fireEvent.focus(getByRole('button'));
18 | expect(container.querySelector('[data-tippy-root]')).toHaveTextContent('red | blue');
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/examples/cra/src/components/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import './Button.css';
5 |
6 | /**
7 | * The only true button.
8 | */
9 | export default function Button({ color, size, children }) {
10 | const styles = {
11 | color,
12 | fontSize: Button.sizes[size],
13 | };
14 |
15 | return (
16 |
19 | );
20 | }
21 | Button.propTypes = {
22 | /**
23 | * Button label.
24 | */
25 | children: PropTypes.string.isRequired,
26 | color: PropTypes.string,
27 | size: PropTypes.oneOf(['small', 'normal', 'large']),
28 | };
29 | Button.defaultProps = {
30 | color: '#333',
31 | size: 'normal',
32 | };
33 | Button.sizes = {
34 | small: '10px',
35 | normal: '14px',
36 | large: '18px',
37 | };
38 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/Table/__snapshots__/Table.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Markdown Table should render a table 1`] = `
4 |
7 |
10 |
11 |
14 | 1st header
15 |
16 |
19 | 2nd header
20 |
21 |
22 |
23 |
24 |
25 |
28 | 1st cell
29 |
30 |
33 | 2nd cell
34 |
35 |
36 |
37 |
38 | `;
39 |
--------------------------------------------------------------------------------
/src/client/utils/__tests__/__snapshots__/filterSectionsByName.spec.ts.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`filterSectionsByName should recursively filter sections and components by name 1`] = `
4 | Array [
5 | Object {
6 | "components": Array [],
7 | "name": "General",
8 | "sections": Array [
9 | Object {
10 | "components": Array [
11 | Object {
12 | "name": "Button",
13 | },
14 | ],
15 | "name": "Particles",
16 | "sections": Array [],
17 | },
18 | ],
19 | },
20 | ]
21 | `;
22 |
23 | exports[`filterSectionsByName should skip sections without matches inside 1`] = `
24 | Array [
25 | Object {
26 | "components": Array [],
27 | "name": "General",
28 | "sections": Array [],
29 | },
30 | ]
31 | `;
32 |
--------------------------------------------------------------------------------
/examples/webpack/src/components/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import './Button.css';
5 |
6 | /**
7 | * The only true button.
8 | */
9 | export default function Button({ color, size, children }) {
10 | const styles = {
11 | color,
12 | fontSize: Button.sizes[size],
13 | };
14 |
15 | return (
16 |
19 | );
20 | }
21 | Button.propTypes = {
22 | /**
23 | * Button label.
24 | */
25 | children: PropTypes.string.isRequired,
26 | color: PropTypes.string,
27 | size: PropTypes.oneOf(['small', 'normal', 'large']),
28 | };
29 | Button.defaultProps = {
30 | color: '#333',
31 | size: 'normal',
32 | };
33 | Button.sizes = {
34 | small: '10px',
35 | normal: '14px',
36 | large: '18px',
37 | };
38 |
--------------------------------------------------------------------------------
/src/client/utils/__tests__/filterComponentsInSectionsByExactName.spec.ts:
--------------------------------------------------------------------------------
1 | import deepfreeze from 'deepfreeze';
2 | import filterComponentsInSectionsByExactName from '../filterComponentsInSectionsByExactName';
3 |
4 | const sections = deepfreeze([
5 | {
6 | name: 'General',
7 | sections: [
8 | {
9 | name: 'Particles',
10 | components: [
11 | {
12 | name: 'Button',
13 | },
14 | {
15 | name: 'Image',
16 | },
17 | ],
18 | },
19 | ],
20 | },
21 | ]);
22 |
23 | describe('filterComponentsInSectionsByExactName', () => {
24 | it('should return components at any level with exact name', () => {
25 | const result = filterComponentsInSectionsByExactName(sections, 'Image', true)[0];
26 | expect(result.components && result.components.map(x => x.name)).toEqual(['Image']);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/examples/preact/src/components/Button/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import './Button.css';
5 |
6 | /**
7 | * The only true button.
8 | */
9 | export default function Button({ color, size, children }) {
10 | const styles = {
11 | color,
12 | fontSize: Button.sizes[size],
13 | };
14 |
15 | return (
16 |
19 | );
20 | }
21 | Button.propTypes = {
22 | /**
23 | * Button label.
24 | */
25 | children: PropTypes.string.isRequired,
26 | color: PropTypes.string,
27 | size: PropTypes.oneOf(['small', 'normal', 'large']),
28 | };
29 | Button.defaultProps = {
30 | color: '#333',
31 | size: 'normal',
32 | };
33 | Button.sizes = {
34 | small: '10px',
35 | normal: '14px',
36 | large: '18px',
37 | };
38 |
--------------------------------------------------------------------------------
/examples/sections/src/components/Button/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import './Button.css';
5 |
6 | /**
7 | * The only true button.
8 | */
9 | export default function Button({ color, size, children }) {
10 | const styles = {
11 | color,
12 | fontSize: Button.sizes[size],
13 | };
14 |
15 | return (
16 |
19 | );
20 | }
21 | Button.propTypes = {
22 | /**
23 | * Button label.
24 | */
25 | children: PropTypes.string.isRequired,
26 | color: PropTypes.string,
27 | size: PropTypes.oneOf(['small', 'normal', 'large']),
28 | };
29 | Button.defaultProps = {
30 | color: '#333',
31 | size: 'normal',
32 | };
33 | Button.sizes = {
34 | small: '10px',
35 | normal: '14px',
36 | large: '18px',
37 | };
38 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Sections/SectionsRenderer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Styled, { JssInjectedProps } from 'rsg-components/Styled';
4 |
5 | const styles = () => ({
6 | // Just default jss-isolate rules
7 | root: {},
8 | });
9 |
10 | interface SectionsRendererProps extends JssInjectedProps {
11 | children: React.ReactNode;
12 | }
13 |
14 | export const SectionsRenderer: React.FunctionComponent = ({
15 | classes,
16 | children,
17 | }) => {
18 | return {children} ;
19 | };
20 |
21 | SectionsRenderer.propTypes = {
22 | classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
23 | children: PropTypes.any,
24 | };
25 |
26 | export default Styled(styles)(SectionsRenderer);
27 |
--------------------------------------------------------------------------------
/dangerfile.ts:
--------------------------------------------------------------------------------
1 | import { danger, warn } from 'danger';
2 |
3 | const packageChanged = danger.git.modified_files.includes('package.json');
4 | const lockfileChanged = danger.git.modified_files.includes('package-lock.json');
5 |
6 | if (packageChanged && !lockfileChanged) {
7 | warn(`Changes were made to \`package.json\`, but not to \`package-lock.json\`.
8 |
9 | If you’ve changed any dependencies (added, removed or updated any packages), please run \`npm install\` and commit changes in package-lock.json file. Make sure you’re using npm 5+.`);
10 | }
11 |
12 | if (!packageChanged && lockfileChanged) {
13 | warn(`Changes were made to \`package-lock.json\`, but not to \`package.json\`.
14 |
15 | Please remove \`package-lock.json\` changes from your pull request. Try to run \`git checkout master -- package-lock.json\` and commit changes.`);
16 | }
17 |
--------------------------------------------------------------------------------
/examples/customised/src/components/Button/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import s from './Button.css';
5 |
6 | /**
7 | * The only true button.
8 | */
9 | export default function Button({ color, size, children }) {
10 | const styles = {
11 | color,
12 | fontSize: Button.sizes[size],
13 | };
14 |
15 | return (
16 |
19 | );
20 | }
21 | Button.propTypes = {
22 | /**
23 | * Button label.
24 | */
25 | children: PropTypes.string.isRequired,
26 | color: PropTypes.string,
27 | size: PropTypes.oneOf(['small', 'normal', 'large']),
28 | };
29 | Button.defaultProps = {
30 | color: '#333',
31 | size: 'normal',
32 | };
33 | Button.sizes = {
34 | small: '10px',
35 | normal: '14px',
36 | large: '18px',
37 | };
38 |
--------------------------------------------------------------------------------
/src/scripts/utils/findFileCaseInsensitive.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import memoize from 'lodash/memoize';
4 |
5 | const readdirSync = memoize(fs.readdirSync);
6 |
7 | /**
8 | * Find a file in a directory, case-insensitive
9 | *
10 | * @param {string} filepath
11 | * @return {string|undefined} File path with correct case
12 | */
13 | export default function findFileCaseInsensitive(filepath: string): string | undefined {
14 | const dir = path.dirname(filepath);
15 | const fileNameLower = path.basename(filepath).toLowerCase();
16 | const files = readdirSync(dir);
17 | const found = files.find(file => file.toLowerCase() === fileNameLower);
18 | return found && path.join(dir, found);
19 | }
20 |
21 | /**
22 | * Clear cache.
23 | */
24 | export function clearCache() {
25 | (readdirSync.cache as any).clear();
26 | }
27 |
--------------------------------------------------------------------------------
/docs/Readme.md:
--------------------------------------------------------------------------------
1 | # React Styleguidist Documentation
2 |
3 | - **[Getting Started](https://react-styleguidist.js.org/docs/getting-started): install and run Styleguidist**
4 | - [Documenting components](https://react-styleguidist.js.org/docs/documenting): how to write documentation
5 | - [Locating components](https://react-styleguidist.js.org/docs/components): point Styleguidist to your React components
6 | - [Configuring webpack](https://react-styleguidist.js.org/docs/webpack): tell Styleguidist how to load your code
7 | - [Cookbook](https://react-styleguidist.js.org/docs/cookbook): how to solve common tasks with Styleguidist
8 |
9 | ---
10 |
11 | - [Configuration](https://react-styleguidist.js.org/docs/configuration)
12 | - [CLI commands and options](https://react-styleguidist.js.org/docs/cli)
13 | - [Node.js API](https://react-styleguidist.js.org/docs/api)
14 |
--------------------------------------------------------------------------------
/site/Readme.md:
--------------------------------------------------------------------------------
1 | # [React Styleguidist site](https://react-styleguidist.js.org)
2 |
3 | [](https://app.netlify.com/sites/styleguidist/deploys)
4 |
5 | This site is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static site generator.
6 |
7 | ### Installation
8 |
9 | ```
10 | npm install
11 | npm run sync
12 | ```
13 |
14 | ### Local Development
15 |
16 | ```
17 | npm start
18 | ```
19 |
20 | This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server.
21 |
22 | ### Build
23 |
24 | ```
25 | npm run build
26 | ```
27 |
28 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
29 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Usage/Usage.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { MethodDescriptor } from 'react-docgen';
3 | import { PropDescriptor } from 'rsg-components/Props/util';
4 | import Props from 'rsg-components/Props';
5 | import Methods from 'rsg-components/Methods';
6 | import isEmpty from 'lodash/isEmpty';
7 |
8 | const Usage: React.FunctionComponent<{
9 | props: { methods?: MethodDescriptor[]; props?: PropDescriptor[] };
10 | }> = ({ props: { props, methods } }) => {
11 | const propsNode = props && !isEmpty(props) && ;
12 | const methodsNode = methods && !isEmpty(methods) && ;
13 |
14 | if (!propsNode && !methodsNode) {
15 | return null;
16 | }
17 |
18 | return (
19 |
20 | {propsNode}
21 | {methodsNode}
22 |
23 | );
24 | };
25 |
26 | export default Usage;
27 |
--------------------------------------------------------------------------------
/src/loaders/utils/getAst.ts:
--------------------------------------------------------------------------------
1 | import { Parser, Node as AcornNode, Options } from 'acorn';
2 | import Logger from 'glogg';
3 |
4 | const logger = Logger('rsg');
5 |
6 | export const ACORN_OPTIONS: Options = {
7 | ecmaVersion: 2019,
8 | sourceType: 'module',
9 | };
10 |
11 | /**
12 | * Parse source code with Acorn and return AST, returns undefined in case of errors
13 | */
14 | export default function getAst(
15 | code: string,
16 | plugins: ((BaseParser: typeof Parser) => typeof Parser)[] = []
17 | ): AcornNode | undefined {
18 | const parser = Parser.extend(...plugins);
19 |
20 | try {
21 | return parser.parse(code, ACORN_OPTIONS);
22 | } catch (err) {
23 | if (err instanceof Error) {
24 | logger.debug(`Acorn cannot parse example code: ${err.message}\n\nCode:\n${code}`);
25 | return undefined;
26 | }
27 | return undefined;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/test/components/Button/Button.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | /**
5 | * The only true button.
6 | */
7 | export default function Button({ color, size, children }) {
8 | const styles = {
9 | color,
10 | fontSize: Button.sizes[size],
11 | };
12 |
13 | return ;
14 | }
15 | Button.propTypes = {
16 | /**
17 | * Button label.
18 | */
19 | children: PropTypes.string.isRequired,
20 | color: PropTypes.string,
21 | size: PropTypes.oneOf(['small', 'normal', 'large']),
22 | /**
23 | * A prop that should not be visible in the doc.
24 | * @ignore
25 | */
26 | ignoredProp: PropTypes.bool,
27 | };
28 | Button.defaultProps = {
29 | color: '#333',
30 | size: 'normal',
31 | };
32 | Button.sizes = {
33 | small: '10px',
34 | normal: '14px',
35 | large: '18px',
36 | };
37 |
--------------------------------------------------------------------------------
/examples/cra/src/components/RandomButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import sample from 'lodash/sample';
4 |
5 | import './RandomButton.css';
6 |
7 | /**
8 | * Button that changes label on every click.
9 | */
10 | export default class RandomButton extends Component {
11 | static propTypes = {
12 | /**
13 | * List of possible labels.
14 | */
15 | variants: PropTypes.array.isRequired,
16 | };
17 |
18 | constructor(props) {
19 | super();
20 | this.state = {
21 | label: sample(props.variants),
22 | };
23 | }
24 |
25 | handleClick = () => {
26 | this.setState({
27 | label: sample(this.props.variants),
28 | });
29 | };
30 |
31 | render() {
32 | return (
33 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/preact/src/components/PushButton/PushButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import './PushButton.css';
5 |
6 | /**
7 | * An example-less button.
8 | */
9 | export default function PushButton({ color, size, children }) {
10 | const styles = {
11 | color,
12 | fontSize: PushButton.sizes[size],
13 | };
14 |
15 | return (
16 |
19 | );
20 | }
21 | PushButton.propTypes = {
22 | /**
23 | * PushButton label.
24 | */
25 | children: PropTypes.string.isRequired,
26 | color: PropTypes.string,
27 | size: PropTypes.oneOf(['small', 'normal', 'large']),
28 | };
29 | PushButton.defaultProps = {
30 | color: '#333',
31 | size: 'normal',
32 | };
33 | PushButton.sizes = {
34 | small: '10px',
35 | normal: '14px',
36 | large: '18px',
37 | };
38 |
--------------------------------------------------------------------------------
/src/client/rsg-components/slots/__snapshots__/IsolateButton.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`should renderer a link home in isolated mode 1`] = `
4 |
9 |
10 |
11 | `;
12 |
13 | exports[`should renderer a link to example isolated mode 1`] = `
14 |
19 |
20 |
21 | `;
22 |
23 | exports[`should renderer a link to isolated mode 1`] = `
24 |
29 |
30 |
31 | `;
32 |
--------------------------------------------------------------------------------
/examples/webpack/src/components/RandomButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import sample from 'lodash/sample';
4 |
5 | import './RandomButton.css';
6 |
7 | /**
8 | * Button that changes label on every click.
9 | */
10 | export default class RandomButton extends Component {
11 | static propTypes = {
12 | /**
13 | * List of possible labels.
14 | */
15 | variants: PropTypes.array.isRequired,
16 | };
17 |
18 | constructor(props) {
19 | super();
20 | this.state = {
21 | label: sample(props.variants),
22 | };
23 | }
24 |
25 | handleClick = () => {
26 | this.setState({
27 | label: sample(this.props.variants),
28 | });
29 | };
30 |
31 | render() {
32 | return (
33 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Sections/Sections.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Section from 'rsg-components/Section';
4 | import SectionsRenderer from 'rsg-components/Sections/SectionsRenderer';
5 | import * as Rsg from '../../../typings';
6 |
7 | const Sections: React.FunctionComponent<{
8 | sections: Rsg.Section[];
9 | depth: number;
10 | root?: boolean;
11 | }> = ({ sections, depth }) => {
12 | return (
13 |
14 | {sections
15 | .filter(section => !section.externalLink)
16 | .map((section, idx) => (
17 |
18 | ))}
19 |
20 | );
21 | };
22 |
23 | Sections.propTypes = {
24 | sections: PropTypes.array.isRequired,
25 | depth: PropTypes.number.isRequired,
26 | root: PropTypes.bool,
27 | };
28 |
29 | export default Sections;
30 |
--------------------------------------------------------------------------------
/src/typings/index.ts:
--------------------------------------------------------------------------------
1 | import './dependencies/acorn-jsx';
2 | import './dependencies/findup';
3 | import './dependencies/listify';
4 | import './dependencies/react-docgen';
5 | import './dependencies/webpack-merge';
6 | import './dependencies/common-dir';
7 | import './dependencies/github-slugger';
8 | import './dependencies/strip-shebang';
9 | import './dependencies/deabsdeep';
10 | import './dependencies/glogg';
11 | import './dependencies/mini-html-webpack-template';
12 | import './dependencies/stripHtmlComments';
13 | import './dependencies/deepfreeze';
14 | import './dependencies/q-i';
15 | import './dependencies/to-ast';
16 |
17 | export * from './RsgComponent';
18 | export * from './RsgExample';
19 | export * from './RsgPropsObject';
20 | export * from './RsgRequireItResult';
21 | export * from './RsgSection';
22 | export * from './RsgStyleguidistConfig';
23 | export * from './RsgTheme';
24 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Context/Context.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as Rsg from '../../../typings';
3 |
4 | const StyleGuideContext = React.createContext({
5 | codeRevision: 0,
6 | cssRevision: '0',
7 | config: {} as Rsg.ProcessedStyleguidistConfig,
8 | slots: {},
9 | displayMode: 'collapse',
10 | });
11 |
12 | export default StyleGuideContext;
13 |
14 | export interface SlotObject {
15 | id: string;
16 | render: React.FunctionComponent;
17 | }
18 |
19 | export interface StyleGuideContextContents {
20 | codeRevision: number;
21 | cssRevision: string;
22 | config: Rsg.ProcessedStyleguidistConfig;
23 | slots: Record)[]>;
24 | displayMode: string;
25 | }
26 |
27 | export function useStyleGuideContext(): StyleGuideContextContents {
28 | return React.useContext(StyleGuideContext);
29 | }
30 |
--------------------------------------------------------------------------------
/test/components/Price/Price.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | const unitSymbols = {
5 | USD: '$',
6 | EUR: '€',
7 | };
8 |
9 | /**
10 | * Price component that renders a price and a unit.
11 | */
12 | export default function Price(props) {
13 | let Host = 'span';
14 | if (props.emphasize) {
15 | Host = 'em';
16 | }
17 | return (
18 |
19 | {props.value}
20 | {!props.symbol ? props.unit : unitSymbols[props.unit]}
21 |
22 | );
23 | }
24 |
25 | Price.propTypes = {
26 | /** Price value. */
27 | value: PropTypes.number.isRequired,
28 | /** Price unit */
29 | unit: PropTypes.oneOf(['EUR', 'USD']),
30 | /** Flag that determines if the price should be emphasized or not. */
31 | emphasize: PropTypes.bool,
32 | /** Defines if the unit should be shown as a symbol or not. */
33 | symbol: PropTypes.bool.isRequired,
34 | };
35 |
--------------------------------------------------------------------------------
/examples/customised/src/components/RandomButton/RandomButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import sample from 'lodash/sample';
4 |
5 | import s from './RandomButton.css';
6 |
7 | /**
8 | * Button that changes label on every click.
9 | */
10 | export default class RandomButton extends Component {
11 | static propTypes = {
12 | /**
13 | * List of possible labels.
14 | */
15 | variants: PropTypes.array.isRequired,
16 | };
17 |
18 | constructor(props) {
19 | super();
20 | this.state = {
21 | label: sample(props.variants),
22 | };
23 | }
24 |
25 | handleClick = () => {
26 | this.setState({
27 | label: sample(this.props.variants),
28 | });
29 | };
30 |
31 | render() {
32 | return (
33 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/preact/src/components/RandomButton/RandomButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import sample from 'lodash/sample';
4 |
5 | import './RandomButton.css';
6 |
7 | /**
8 | * Button that changes label on every click.
9 | */
10 | export default class RandomButton extends Component {
11 | static propTypes = {
12 | /**
13 | * List of possible labels.
14 | */
15 | variants: PropTypes.array.isRequired,
16 | };
17 |
18 | constructor(props) {
19 | super();
20 | this.state = {
21 | label: sample(props.variants),
22 | };
23 | }
24 |
25 | handleClick = () => {
26 | this.setState({
27 | label: sample(this.props.variants),
28 | });
29 | };
30 |
31 | render() {
32 | return (
33 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/sections/src/components/RandomButton/RandomButton.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import sample from 'lodash/sample';
4 |
5 | import './RandomButton.css';
6 |
7 | /**
8 | * Button that changes label on every click.
9 | */
10 | export default class RandomButton extends Component {
11 | static propTypes = {
12 | /**
13 | * List of possible labels.
14 | */
15 | variants: PropTypes.array.isRequired,
16 | };
17 |
18 | constructor(props) {
19 | super();
20 | this.state = {
21 | label: sample(props.variants),
22 | };
23 | }
24 |
25 | handleClick = () => {
26 | this.setState({
27 | label: sample(this.props.variants),
28 | });
29 | };
30 |
31 | render() {
32 | return (
33 |
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 90
3 |
4 | # Number of days of inactivity before a stale issue is closed
5 | daysUntilClose: 7
6 |
7 | # Issues with these labels will never be considered stale
8 | exemptLabels:
9 | - "help wanted"
10 |
11 | # Ignore issues with an assignee
12 | exemptAssignees: true
13 |
14 | # Label to use when marking an issue as stale
15 | staleLabel: wontfix
16 |
17 | # Comment to post when marking an issue as stale. Set to `false` to disable
18 | markComment: >
19 | 😴 This issue has been automatically marked as stale because it has not had
20 | recent activity. It will be closed in a week without any further activity.
21 | Consider opening a pull request if you still have this issue or want this feature.
22 |
23 | # Comment to post when closing a stale issue. Set to `false` to disable
24 | closeComment: false
25 |
--------------------------------------------------------------------------------
/examples/express/src/components/CustomEndpoint/CustomEndpoint.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | /* eslint-disable compat/compat */
4 |
5 | export default class CustomEndpoint extends Component {
6 | state = { response: 'No Server Response' };
7 |
8 | handleInvokeEndpoint = () => {
9 | fetch('http://localhost:6060/custom', { method: 'GET' })
10 | .then((responseObj) => responseObj.json())
11 | .then(({ response } = {}) => this.setState({ response, error: null }))
12 | .catch(() =>
13 | this.setState({
14 | response: null,
15 | error: 'Ouch, something went wrong!',
16 | })
17 | );
18 | };
19 |
20 | render() {
21 | return (
22 |
23 | {this.state.response}
24 | {this.state.error ? {this.state.error} : null}
25 |
26 |
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/Table/TableRenderer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Styled, { JssInjectedProps } from 'rsg-components/Styled';
4 | import * as Rsg from '../../../../typings';
5 |
6 | const styles = ({ space }: Rsg.Theme) => ({
7 | table: {
8 | marginTop: 0,
9 | marginBottom: space[2],
10 | borderCollapse: 'collapse',
11 | },
12 | });
13 |
14 | interface TableProps extends JssInjectedProps {
15 | children: React.ReactNode;
16 | }
17 |
18 | export const TableRenderer: React.FunctionComponent = ({ classes, children }) => {
19 | return {children}
;
20 | };
21 |
22 | TableRenderer.propTypes = {
23 | classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
24 | children: PropTypes.any.isRequired,
25 | };
26 |
27 | export default Styled(styles)(TableRenderer);
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F41B Bug report"
3 | about: Something isn’t working as expected
4 | ---
5 |
6 | **Current behavior**
7 |
8 |
9 |
10 | **To reproduce**
11 |
12 |
26 |
27 | **Expected behavior**
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/client/rsg-components/Markdown/Table/TableHeadRenderer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Styled, { JssInjectedProps } from 'rsg-components/Styled';
4 | import * as Rsg from '../../../../typings';
5 |
6 | const styles = ({ color }: Rsg.Theme) => ({
7 | thead: {
8 | borderBottom: [[1, color.border, 'solid']],
9 | },
10 | });
11 |
12 | interface TableHeadProps extends JssInjectedProps {
13 | children: React.ReactNode;
14 | }
15 |
16 | export const TableHeadRenderer: React.FunctionComponent = ({
17 | classes,
18 | children,
19 | }) => {
20 | return {children};
21 | };
22 |
23 | TableHeadRenderer.propTypes = {
24 | classes: PropTypes.objectOf(PropTypes.string.isRequired).isRequired,
25 | children: PropTypes.any.isRequired,
26 | };
27 |
28 | export default Styled(styles)(TableHeadRenderer);
29 |
--------------------------------------------------------------------------------