Skip to content

Add custom component display names #933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
20 changes: 20 additions & 0 deletions docs/Cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- [How to add fonts from Google Fonts?](#how-to-add-fonts-from-google-fonts)
- [How to reuse project’s webpack config?](#how-to-reuse-projects-webpack-config)
- [How to use React Styleguidist with Redux, Relay or Styled Components?](#how-to-use-react-styleguidist-with-redux-relay-or-styled-components)
- [How to change the names of components displayed in Styleguidist UI?](#how-to-change-the-names-of-components-displayed-in-styleguidist-ui)
- [What’s the difference between Styleguidist and Storybook?](#whats-the-difference-between-styleguidist-and-storybook)
- [Are there any other projects like this?](#are-there-any-other-projects-like-this)

Expand Down Expand Up @@ -527,6 +528,25 @@ See in [configuring webpack](Webpack.md#reusing-your-projects-webpack-config).

See [working with third-party libraries](Thirdparties.md).

## How to change the names of components displayed in Styleguidist UI?

You might want to change your components' names to be displayed differently, for example, for stylistic purposes or to give them a more descriptive names in your styleguide.

This can be done by adding [@visibleName](Documenting.md#defining-custom-component-names) tag to your component documentation.

In case you want to change components' names in bulk, for example, based on their current name, you can use [updateDocs](Configuration.md#updatedocs) config option:

```javascript
module.exports = {
updateDocs(docs) {
if (docs && docs.displayName) {
docs.visibleName = docs.displayName.toLowerCase()
}
return docs
}
}
```

## What’s the difference between Styleguidist and Storybook?

Both tools are good and mature, they have many similarities but also some distinctions that may make you choose one or the other. For me the biggest distinction is how you describe component variations.
Expand Down
16 changes: 16 additions & 0 deletions docs/Documenting.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Styleguidist generates documentation for your components based on the comments i
- [External examples using doclet tags](#external-examples-using-doclet-tags)
- [Public methods](#public-methods)
- [Ignoring props](#ignoring-props)
- [Defining custom component names](#defining-custom-component-names)
- [Using JSDoc tags](#using-jsdoc-tags)
- [Writing code examples](#writing-code-examples)
- [Limitations](#limitations)
Expand Down Expand Up @@ -143,6 +144,21 @@ MyComponent.propTypes = {
}
```

## Defining custom component names

Use @visibleName JSDoc tag to define component names that are used in the Styleguidist UI:

```javascript
/**
* The only true button.
*
* @visibleName The Best Button Ever 🐙
*/
class Button extends React.Component {
```

Now the component will be displayed with a custom 'The Bust Button Ever🐙' name and this will not change the name of the component that is used in the JSX.

## Using JSDoc tags

You can use the following [JSDoc](http://usejsdoc.org/) tags when documenting components, props and methods:
Expand Down
3 changes: 2 additions & 1 deletion examples/basic/src/components/PushButton/PushButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import PropTypes from 'prop-types';
import './PushButton.css';

/**
* An example-less button.
* An example-less button with custom display name.
* @visibleName Push Button 🎉
*/
export default function PushButton({ color, size, children }) {
const styles = {
Expand Down
17 changes: 17 additions & 0 deletions loaders/utils/__tests__/getProps.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,20 @@ it('should guess a displayName for components that react-docgen was not able to
);
expect(result).toHaveProperty('displayName', 'YourComponent');
});

describe('with @visibleName tag present in the description', () => {
const result = getProps({
description: 'bar\n@visibleName foo',
});
it('should set visibleName property on the docs object', () => {
expect(result).toHaveProperty('visibleName', 'foo');
});

it('should delete visibleName from doclets on the docs object', () => {
expect(result.doclets).not.toHaveProperty('visibleName');
});

it('should delete visibleName from tags on the docs object', () => {
expect(result.tags).not.toHaveProperty('visibleName');
});
});
11 changes: 11 additions & 0 deletions loaders/utils/getProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,16 @@ module.exports = function getProps(doc, filepath) {
doc.displayName = getNameFromFilePath(filepath);
}

if (doc.doclets && doc.doclets.visibleName) {
doc.visibleName = doc.doclets.visibleName;

// custom tag is added both to doclets and tags
// removing from both locations
delete doc.doclets.visibleName;
if (doc.tags) {
delete doc.tags.visibleName;
}
}

return doc;
};
12 changes: 8 additions & 4 deletions src/rsg-components/ComponentsList/ComponentsList.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import { ComponentsListRenderer } from './ComponentsListRenderer';
it('should set the correct href for items', () => {
const components = [
{
visibleName: 'Button',
name: 'Button',
slug: 'button',
},
{
visibleName: 'Input',
name: 'Input',
slug: 'input',
},
Expand All @@ -21,10 +23,12 @@ it('should set the correct href for items', () => {
it('should set a parameter on link when useHashId is activated', () => {
const components = [
{
visibleName: 'Button',
name: 'Button',
slug: 'button',
},
{
visibleName: 'Input',
name: 'Input',
slug: 'input',
},
Expand Down Expand Up @@ -69,12 +73,12 @@ it('should set a sub route on link when useHashId is deactivated', () => {
it('should render sections with nested components', () => {
const components = [
{
name: 'Button',
visibleName: 'Button',
slug: 'button',
href: '#button',
},
{
name: 'Input',
visibleName: 'Input',
slug: 'input',
href: '#input',
},
Expand All @@ -90,10 +94,10 @@ it('should return null when the list is empty', () => {
expect(actual.getElement()).toBe(null);
});

it('should ignore items without name', () => {
it('should ignore items without visibleName', () => {
const components = [
{
name: 'Button',
visibleName: 'Button',
slug: 'button',
href: '#button',
},
Expand Down
6 changes: 3 additions & 3 deletions src/rsg-components/ComponentsList/ComponentsListRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ const styles = ({ color, fontFamily, fontSize, space, mq }) => ({
});

export function ComponentsListRenderer({ classes, items }) {
items = items.filter(item => item.name);
items = items.filter(item => item.visibleName);

if (!items.length) {
return null;
}

return (
<ul className={classes.list}>
{items.map(({ heading, name, href, content }) => (
{items.map(({ heading, visibleName, href, content }) => (
<li
className={cx(classes.item, (!content || !content.props.items.length) && classes.isChild)}
key={href}
>
<Link className={cx(heading && classes.heading)} href={href}>
{name}
{visibleName}
</Link>
{content}
</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should ignore items without name 1`] = `
exports[`should ignore items without visibleName 1`] = `
<ul>
<li
className=""
Expand Down Expand Up @@ -52,11 +52,13 @@ exports[`should set a parameter on link when useHashId is activated 1`] = `
"href": "blank#/Components?id=button",
"name": "Button",
"slug": "button",
"visibleName": "Button",
},
Object {
"href": "blank#/Components?id=input",
"name": "Input",
"slug": "input",
"visibleName": "Input",
},
]
}
Expand Down Expand Up @@ -92,11 +94,13 @@ exports[`should set the correct href for items 1`] = `
"href": "blank#button",
"name": "Button",
"slug": "button",
"visibleName": "Button",
},
Object {
"href": "blank#input",
"name": "Input",
"slug": "input",
"visibleName": "Input",
},
]
}
Expand Down
4 changes: 2 additions & 2 deletions src/rsg-components/ReactComponent/ReactComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default class ReactComponent extends Component {
config: { pagePerSection },
} = this.context;
const { component, depth, usageMode, exampleMode } = this.props;
const { name, slug, filepath, pathLine } = component;
const { name, visibleName, slug, filepath, pathLine } = component;
const { description, examples = [], tags = {} } = component.props;
if (!name) {
return null;
Expand All @@ -77,7 +77,7 @@ export default class ReactComponent extends Component {
}}
depth={depth}
>
{name}
{visibleName}
</SectionHeading>
}
examples={
Expand Down
2 changes: 2 additions & 0 deletions src/rsg-components/ReactComponent/ReactComponent.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const options = {

const component = {
name: 'Foo',
visibleName: 'Foo',
slug: 'foo',
pathLine: 'foo/bar.js',
props: {
Expand All @@ -32,6 +33,7 @@ const component = {
};
const componentWithEverything = {
name: 'Foo',
visibleName: 'Foo',
slug: 'foo',
pathLine: 'foo/bar.js',
props: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ exports[`ReactComponent should pass rendered description, usage, examples, etc.
],
},
"slug": "foo",
"visibleName": "Foo",
}
}
>
Expand Down Expand Up @@ -142,6 +143,7 @@ exports[`ReactComponent should pass rendered description, usage, examples, etc.
],
},
"slug": "foo",
"visibleName": "Foo",
}
}
/>
Expand Down Expand Up @@ -195,6 +197,7 @@ exports[`ReactComponent should pass rendered description, usage, examples, etc.
],
},
"slug": "foo",
"visibleName": "Foo",
}
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ exports[`renderStyleguide should render StyleGuide component 1`] = `
"displayName": "Button",
"examples": Array [],
},
"visibleName": "Button",
},
Object {
"module": "ImageModule",
Expand All @@ -25,6 +26,7 @@ exports[`renderStyleguide should render StyleGuide component 1`] = `
"displayName": "Image",
"examples": Array [],
},
"visibleName": "Image",
},
],
"sections": Array [],
Expand All @@ -48,6 +50,7 @@ exports[`renderStyleguide should render StyleGuide component 1`] = `
"displayName": "Button",
"examples": Array [],
},
"visibleName": "Button",
},
Object {
"module": "ImageModule",
Expand All @@ -56,6 +59,7 @@ exports[`renderStyleguide should render StyleGuide component 1`] = `
"displayName": "Image",
"examples": Array [],
},
"visibleName": "Image",
},
],
"sections": Array [],
Expand Down
29 changes: 29 additions & 0 deletions src/utils/__tests__/processComponents.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,35 @@ describe('processComponents', () => {
expect(result[0].name).toBe('Foo');
});

describe('should set visibleName property on the component', () => {
it('from an visibleName component prop if available', () => {
const components = deepfreeze([
{
props: {
displayName: 'Foo',
visibleName: 'Foo Bar',
},
module: 13,
},
]);
const result = processComponents(components);
expect(result[0].visibleName).toBe('Foo Bar');
});

it('from an displayName component prop if visibleName prop is not available', () => {
const components = deepfreeze([
{
props: {
displayName: 'Foo',
},
module: 13,
},
]);
const result = processComponents(components);
expect(result[0].visibleName).toBe('Foo');
});
});

it('should append @example doclet to all examples', () => {
const components = deepfreeze([
{
Expand Down
1 change: 1 addition & 0 deletions src/utils/processComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default function processComponents(components) {

// Add .name shortcuts for names instead of .props.displayName.
name: component.props.displayName,
visibleName: component.props.visibleName || component.props.displayName,

props: {
...component.props,
Expand Down