Repository: styleguidist/react-styleguidist
Pull request #921 (2018-04-04 06:51:56)

Added support for external links in sections
This PR adds support for external links in the sidebar (for example to link to external documentation or more extensive demos).

The two added flags are:

`section.external` - for the link
`section.skip` - skips rendering of the section in the main content (for both external links and logical grouping in the sidebar only)

codecov-io: # [Codecov](https://codecov.io/gh/styleguidist/react-styleguidist/pull/921?src=pr&el=h1) Report
> Merging [#921](https://codecov.io/gh/styleguidist/react-styleguidist/pull/921?src=pr&el=desc) into [master](https://codecov.io/gh/styleguidist/react-styleguidist/commit/4396dc28f097e7374def088063e2fcf211cd1520?src=pr&el=desc) will **decrease** coverage by `0.05%`.
> The diff coverage is `71.42%`.

| [Impacted Files](https://codecov.io/gh/styleguidist/react-styleguidist/pull/921?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [loaders/utils/getSections.js](https://codecov.io/gh/styleguidist/react-styleguidist/pull/921/diff?src=pr&el=tree#diff-bG9hZGVycy91dGlscy9nZXRTZWN0aW9ucy5qcw==) | `100% <ø> (ø)` | :arrow_up: |
| [...rc/rsg-components/ComponentsList/ComponentsList.js](https://codecov.io/gh/styleguidist/react-styleguidist/pull/921/diff?src=pr&el=tree#diff-c3JjL3JzZy1jb21wb25lbnRzL0NvbXBvbmVudHNMaXN0L0NvbXBvbmVudHNMaXN0Lmpz) | `100% <ø> (ø)` | :arrow_up: |
| [src/rsg-components/Sections/Sections.js](https://codecov.io/gh/styleguidist/react-styleguidist/pull/921/diff?src=pr&el=tree#diff-c3JjL3JzZy1jb21wb25lbnRzL1NlY3Rpb25zL1NlY3Rpb25zLmpz) | `100% <100%> (ø)` | :arrow_up: |
| [...omponents/ComponentsList/ComponentsListRenderer.js](https://codecov.io/gh/styleguidist/react-styleguidist/pull/921/diff?src=pr&el=tree#diff-c3JjL3JzZy1jb21wb25lbnRzL0NvbXBvbmVudHNMaXN0L0NvbXBvbmVudHNMaXN0UmVuZGVyZXIuanM=) | `100% <100%> (ø)` | :arrow_up: |
| [src/utils/getUrl.js](https://codecov.io/gh/styleguidist/react-styleguidist/pull/921/diff?src=pr&el=tree#diff-c3JjL3V0aWxzL2dldFVybC5qcw==) | `88.23% <33.33%> (-5.1%)` | :arrow_down: |

BTMPL: Hello @sapegin - I've update as per your recommendation
BTMPL: I see that the coverage is lowered - please let me know if I should provide additional tests.
styleguidist-bot: 
<!--
  0 failure: 
  1 warning:  Changes were made...
  
  
  DangerID: danger-id-default;
-->


<table>
  <thead>
    <tr>
      <th width="50"></th>
      <th width="100%" data-danger-table="true">Warnings</th>
    </tr>
  </thead>
  <tbody><tr>
      <td>:warning:</td>
      <td>

  Changes were made to `package-lock.json`, but not to `package.json`.

Please remove `package-lock.json` changes from your pull request. Try to run `git checkout master -- package-lock.json` and commit changes.
  </td>
    </tr>
  </tbody>
</table>



<p align="right">
  Generated by :no_entry_sign: <a href="http://github.com/danger/danger-js/">dangerJS</a>
</p>


Patch:
diff --git a/docs/Components.md b/docs/Components.md
index ebceeb7d..a5ce3f70 100644
--- a/docs/Components.md
+++ b/docs/Components.md
@@ -113,6 +113,8 @@ Each section consists of (all fields are optional):
 - `exampleMode` — Initial state of the code example tab, uses [exampleMode](Configuration.md#examplemode).
 - `usageMode` — Initial state of the props and methods tab, uses [usageMode](Configuration.md#usagemode).
 - `ignore` — string/array of globs that should not be included in the section.
+- `href` - an URL to navigate to instead of navigating to the section content
+- `external` - if set, the link will open in a new window
 
 Configuring a style guide with textual documentation section and a list of components would look like:
 
@@ -134,6 +136,11 @@ module.exports = {
         {
           name: 'Configuration',
           content: 'docs/configuration.md'
+        },
+        {
+          name: 'Live Demo',
+          external: true,
+          href: 'http://example.com'
         }
       ]
     },
diff --git a/docs/Configuration.md b/docs/Configuration.md
index e93f058e..5529118c 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -21,7 +21,7 @@ By default, Styleguidist will look for `styleguide.config.js` file in your proje
 - [`handlers`](#handlers)
 - [`ignore`](#ignore)
 - [`logger`](#logger)
-- [`mountPointId`](#mountPointId)
+- [`mountPointId`](#mountpointid)
 - [`pagePerSection`](#pagepersection)
 - [`printBuildInstructions`](#printbuildinstructions)
 - [`printServerInstructions`](#printserverinstructions)
diff --git a/docs/Cookbook.md b/docs/Cookbook.md
index 10c4a115..2867eae0 100644
--- a/docs/Cookbook.md
+++ b/docs/Cookbook.md
@@ -117,8 +117,9 @@ const icons = iconsContext.keys().reduce((icons, file) => {
 export default icons
 ```
 
-````jsx
+````markdown
 // IconGallery.md
+
 ```jsx noeditor
 const icons = require('./load-icons').default
 
@@ -130,7 +131,7 @@ const iconElements = Object.keys(icons).map(iconName => {
     </span>
   )
 })
-;<div>{iconElements}</div>
+<div>{iconElements}</div>
 ```
 ````
 
diff --git a/docs/Documenting.md b/docs/Documenting.md
index 6d3fdc5d..2a3852f4 100644
--- a/docs/Documenting.md
+++ b/docs/Documenting.md
@@ -157,7 +157,7 @@ Use @visibleName JSDoc tag to define component names that are used in the Styleg
 class Button extends React.Component {
 ```
 
-The component will be displayed with a custom “The Bust Button Ever🐙” name and this will not change the name of the component used in code of your app or Styleguidist examples.
+The component will be displayed with a custom “The Best Button Ever 🐙” name and this will not change the name of the component used in code of your app or Styleguidist examples.
 
 ## Using JSDoc tags
 
diff --git a/examples/basic/src/components/CounterButton/CounterButton.js b/examples/basic/src/components/CounterButton/CounterButton.js
index 9dab6738..05c99717 100644
--- a/examples/basic/src/components/CounterButton/CounterButton.js
+++ b/examples/basic/src/components/CounterButton/CounterButton.js
@@ -16,10 +16,10 @@ export default class CounterButton extends Component {
 	 *
 	 * @public
 	 * @version 1.0.5
-	 * @param {Number} newValue New value for the counter
+	 * @param {Number} [newValue=0] New value for the counter
 	 * @returns {string} Test
 	 */
-	set(newValue) {
+	set(newValue = 0) {
 		this.setState({
 			value: parseInt(newValue, 10),
 		});
diff --git a/examples/customised/styleguide.config.js b/examples/customised/styleguide.config.js
index b0fe6c04..967f8ef3 100644
--- a/examples/customised/styleguide.config.js
+++ b/examples/customised/styleguide.config.js
@@ -47,7 +47,7 @@ module.exports = {
 	styleguideComponents: {
 		LogoRenderer: path.join(__dirname, 'styleguide/components/Logo'),
 		StyleGuideRenderer: path.join(__dirname, 'styleguide/components/StyleGuide'),
-		SectionsRenderer: path.join(__dirname, 'styleguide/components/Sections'),
+		SectionsRenderer: path.join(__dirname, 'styleguide/components/SectionsRenderer'),
 	},
 	webpackConfig: {
 		module: {
diff --git a/examples/customised/styleguide/components/Sections.js b/examples/customised/styleguide/components/SectionsRenderer.js
similarity index 63%
rename from examples/customised/styleguide/components/Sections.js
rename to examples/customised/styleguide/components/SectionsRenderer.js
index 847708e1..39e9ffea 100644
--- a/examples/customised/styleguide/components/Sections.js
+++ b/examples/customised/styleguide/components/SectionsRenderer.js
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import Styled from 'rsg-components/Styled';
 import Heading from 'rsg-components/Heading';
 // Import default implementation from react-styleguidist using the full path
-import DefaultSections from 'react-styleguidist/lib/rsg-components/Sections/SectionsRenderer';
+import DefaultSectionsRenderer from 'react-styleguidist/lib/rsg-components/Sections/SectionsRenderer';
 
 const styles = ({ fontFamily, color, space }) => ({
 	headingSpacer: {
@@ -18,11 +18,12 @@ const styles = ({ fontFamily, color, space }) => ({
 export function SectionsRenderer({ classes, children }) {
 	return (
 		<div>
-			<div className={classes.headingSpacer}>
-				<Heading level={1}>Example Components</Heading>
-				<p className={classes.descriptionText}>These are the greatest components</p>
-			</div>
-			<DefaultSections>{children}</DefaultSections>
+			{!!children.length &&
+				<div className={classes.headingSpacer}>
+					<Heading level={1}>Example Components</Heading>
+					<p className={classes.descriptionText}>These are the greatest components</p>
+				</div>}
+			<DefaultSectionsRenderer>{children}</DefaultSectionsRenderer>
 		</div>
 	);
 }
diff --git a/examples/sections/styleguide.config.js b/examples/sections/styleguide.config.js
index 0e776ad4..46d364b6 100644
--- a/examples/sections/styleguide.config.js
+++ b/examples/sections/styleguide.config.js
@@ -25,6 +25,11 @@ module.exports = {
 						},
 					],
 				},
+				{
+					name: 'Online documentation',
+					href: 'https://github.com/styleguidist/react-styleguidist',
+					external: true,
+				},
 			],
 			sectionDepth: 2,
 		},
diff --git a/loaders/utils/getProps.js b/loaders/utils/getProps.js
index b6a3dcaf..dff4c006 100644
--- a/loaders/utils/getProps.js
+++ b/loaders/utils/getProps.js
@@ -11,6 +11,13 @@ const logger = require('glogg')('rsg');
 
 const examplesLoader = path.resolve(__dirname, '../examples-loader.js');
 
+const JS_DOC_METHOD_PARAM_TAG_SYNONYMS = ['param', 'arg', 'argument'];
+const JS_DOC_METHOD_RETURN_TAG_SYNONYMS = ['return', 'returns'];
+const JS_DOC_ALL_SYNONYMS = [
+	...JS_DOC_METHOD_PARAM_TAG_SYNONYMS,
+	...JS_DOC_METHOD_RETURN_TAG_SYNONYMS,
+];
+
 // HACK: We have to make sure that doclets is a proper object with correct prototype to
 // work around an issue in react-docgen that breaks the build if a component has JSDoc tags
 // like @see in its description, see https://github.com/reactjs/react-docgen/issues/155
@@ -33,6 +40,10 @@ const doesExternalExampleFileExist = (componentPath, exampleFile) => {
 	return doesFileExist;
 };
 
+const getMergedTag = (tags, names) => {
+	return names.reduce((params, name) => [...params, ...(tags[name] || [])], []);
+};
+
 /**
  * 1. Remove non-public methods.
  * 2. Extract doclets.
@@ -50,11 +61,30 @@ module.exports = function getProps(doc, filepath) {
 		return doclets && doclets.public;
 	});
 
-	// Parse the docblock of the remaining methods with doctrine to retrieve the JsDoc tags
+	// Parse the docblock of the remaining methods with doctrine to retrieve
+	// the JSDoc tags
 	doc.methods = doc.methods.map(method => {
-		return Object.assign(method, {
-			tags: getDoctrineTags(doctrine.parse(method.docblock)),
-		});
+		const allTags = getDoctrineTags(
+			doctrine.parse(method.docblock, { sloppy: true, unwrap: true })
+		);
+
+		// Merge with react-docgen information about arguments and return value
+		// with information from JSDoc
+
+		const paramTags = getMergedTag(allTags, JS_DOC_METHOD_PARAM_TAG_SYNONYMS);
+		const params =
+			method.params &&
+			method.params.map(param =>
+				Object.assign(param, paramTags.find(tagParam => tagParam.name === param.name))
+			);
+
+		const returnTags = getMergedTag(allTags, JS_DOC_METHOD_RETURN_TAG_SYNONYMS);
+		const returns = method.returns || returnTags[0];
+
+		// Remove tag synonyms
+		const tags = _.omit(allTags, JS_DOC_ALL_SYNONYMS);
+
+		return Object.assign(method, returns && { returns }, params && { params }, { tags });
 	});
 
 	if (doc.description) {
@@ -68,6 +98,7 @@ module.exports = function getProps(doc, filepath) {
 
 		let exampleFileExists = false;
 		let exampleFile = doc.doclets.example;
+
 		// doc.doclets.example might be a boolean or undefined
 		if (typeof doc.doclets.example === 'string') {
 			exampleFile = doc.doclets.example.trim();
@@ -87,7 +118,8 @@ module.exports = function getProps(doc, filepath) {
 		Object.keys(doc.props).forEach(propName => {
 			const prop = doc.props[propName];
 			const doclets = getDocletsObject(prop.description);
-			// when a prop is listed in defaultProps but not in props the prop.description is undefined
+
+			// When a prop is listed in defaultProps but not in props the prop.description is undefined
 			const documentation = doctrine.parse(prop.description || '');
 
 			// documentation.description is the description without tags
@@ -109,8 +141,8 @@ module.exports = function getProps(doc, 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
+		// Custom tag is added both to doclets and tags
+		// Removing from both locations
 		delete doc.doclets.visibleName;
 		if (doc.tags) {
 			delete doc.tags.visibleName;
diff --git a/loaders/utils/getSections.js b/loaders/utils/getSections.js
index 22a4b772..05845ee0 100644
--- a/loaders/utils/getSections.js
+++ b/loaders/utils/getSections.js
@@ -68,8 +68,10 @@ function processSection(section, config, parentDepth) {
 		slug: slugger.slug(section.name),
 		sections: getSections(section.sections || [], config, sectionDepth),
 		filepath: contentRelativePath,
+		href: section.href,
 		components: getSectionComponents(section, config),
 		content,
+		external: section.external,
 	};
 }
 
diff --git a/package-lock.json b/package-lock.json
index d3610e52..516f7250 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8579,7 +8579,7 @@
         },
         "onetime": {
           "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
+          "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
           "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
           "dev": true
         },
@@ -8739,7 +8739,7 @@
         },
         "onetime": {
           "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
+          "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
           "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
           "dev": true
         },
@@ -8877,7 +8877,7 @@
         },
         "onetime": {
           "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
+          "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
           "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
           "dev": true
         },
@@ -13169,9 +13169,9 @@
       }
     },
     "url-parse": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.1.tgz",
-      "integrity": "sha512-x95Td74QcvICAA0+qERaVkRpTGKyBHHYdwL2LXZm5t/gBtCB9KQSO/0zQgSTYEV1p0WcvSg79TLNPSvd5IDJMQ==",
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz",
+      "integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==",
       "requires": {
         "querystringify": "^2.0.0",
         "requires-port": "^1.0.0"
diff --git a/src/rsg-components/Argument/Argument.spec.js b/src/rsg-components/Argument/Argument.spec.js
index e369938c..3d79cd8a 100644
--- a/src/rsg-components/Argument/Argument.spec.js
+++ b/src/rsg-components/Argument/Argument.spec.js
@@ -22,6 +22,44 @@ it('should render argument without type', () => {
 	expect(actual).toMatchSnapshot();
 });
 
+it('should render optional argument', () => {
+	const actual = shallow(
+		<ArgumentRenderer
+			{...props}
+			type={{ type: 'OptionalType', expression: { name: 'Array' } }}
+			description={description}
+		/>
+	);
+
+	expect(actual).toMatchSnapshot();
+});
+
+it('should render default value of argument', () => {
+	const actual = shallow(
+		<ArgumentRenderer
+			{...props}
+			type={{ name: 'String' }}
+			default="bar"
+			description={description}
+		/>
+	);
+
+	expect(actual).toMatchSnapshot();
+});
+
+it('should render default value of optional argument', () => {
+	const actual = shallow(
+		<ArgumentRenderer
+			{...props}
+			type={{ type: 'OptionalType', expression: { name: 'Boolean' } }}
+			default="true"
+			description={description}
+		/>
+	);
+
+	expect(actual).toMatchSnapshot();
+});
+
 it('should render argument without description', () => {
 	const actual = shallow(<ArgumentRenderer {...props} name={name} type={type} />);
 
diff --git a/src/rsg-components/Argument/ArgumentRenderer.js b/src/rsg-components/Argument/ArgumentRenderer.js
index 3d0c701c..5a45c5c0 100644
--- a/src/rsg-components/Argument/ArgumentRenderer.js
+++ b/src/rsg-components/Argument/ArgumentRenderer.js
@@ -13,6 +13,11 @@ export const styles = ({ space }) => ({
 });
 
 export function ArgumentRenderer({ classes, name, type, description, returns, block, ...props }) {
+	const isOptional = type && type.type === 'OptionalType';
+	const defaultValue = props.default;
+	if (isOptional) {
+		type = type.expression;
+	}
 	return (
 		<Group className={block && classes.block} {...props}>
 			{returns && 'Returns'}
@@ -22,7 +27,13 @@ export function ArgumentRenderer({ classes, name, type, description, returns, bl
 					{type && ':'}
 				</span>
 			)}
-			{type && <Type>{type.name}</Type>}
+			{type && (
+				<Type>
+					{type.name}
+					{isOptional && '?'}
+					{!!defaultValue && `=${defaultValue}`}
+				</Type>
+			)}
 			{type && description && ` — `}
 			{description && <Markdown text={`${description}`} inline />}
 		</Group>
@@ -33,6 +44,7 @@ ArgumentRenderer.propTypes = {
 	classes: PropTypes.object.isRequired,
 	name: PropTypes.string,
 	type: PropTypes.object,
+	default: PropTypes.string,
 	description: PropTypes.string,
 	returns: PropTypes.bool,
 	block: PropTypes.bool,
diff --git a/src/rsg-components/Argument/__snapshots__/Argument.spec.js.snap b/src/rsg-components/Argument/__snapshots__/Argument.spec.js.snap
index b7340b41..a0f34189 100644
--- a/src/rsg-components/Argument/__snapshots__/Argument.spec.js.snap
+++ b/src/rsg-components/Argument/__snapshots__/Argument.spec.js.snap
@@ -53,6 +53,57 @@ exports[`should render argument without type 1`] = `
 </Group>
 `;
 
+exports[`should render default value of argument 1`] = `
+<Group
+  default="bar"
+  separator=" "
+>
+  <Styled(Type)>
+    String
+    =bar
+  </Styled(Type)>
+   — 
+  <Markdown
+    inline={true}
+    text="Converts foo to bar"
+  />
+</Group>
+`;
+
+exports[`should render default value of optional argument 1`] = `
+<Group
+  default="true"
+  separator=" "
+>
+  <Styled(Type)>
+    Boolean
+    ?
+    =true
+  </Styled(Type)>
+   — 
+  <Markdown
+    inline={true}
+    text="Converts foo to bar"
+  />
+</Group>
+`;
+
+exports[`should render optional argument 1`] = `
+<Group
+  separator=" "
+>
+  <Styled(Type)>
+    Array
+    ?
+  </Styled(Type)>
+   — 
+  <Markdown
+    inline={true}
+    text="Converts foo to bar"
+  />
+</Group>
+`;
+
 exports[`should render return value 1`] = `
 <Group
   separator=" "
diff --git a/src/rsg-components/ComponentsList/ComponentsList.js b/src/rsg-components/ComponentsList/ComponentsList.js
index 48984d80..b5a65c41 100644
--- a/src/rsg-components/ComponentsList/ComponentsList.js
+++ b/src/rsg-components/ComponentsList/ComponentsList.js
@@ -6,13 +6,15 @@ import getUrl from '../../utils/getUrl';
 function ComponentsList({ classes, items, useRouterLinks = false, useHashId, hashPath }) {
 	const mappedItems = items.map(item => ({
 		...item,
-		href: getUrl({
-			name: item.name,
-			slug: item.slug,
-			anchor: !useRouterLinks,
-			hashPath: useRouterLinks ? hashPath : false,
-			id: useRouterLinks ? useHashId : false,
-		}),
+		href: item.href
+			? item.href
+			: getUrl({
+					name: item.name,
+					slug: item.slug,
+					anchor: !useRouterLinks,
+					hashPath: useRouterLinks ? hashPath : false,
+					id: useRouterLinks ? useHashId : false,
+			  }),
 	}));
 	return <ComponentsListRenderer classes={classes} items={mappedItems} />;
 }
diff --git a/src/rsg-components/ComponentsList/ComponentsList.spec.js b/src/rsg-components/ComponentsList/ComponentsList.spec.js
index aff72897..f49f6dd4 100644
--- a/src/rsg-components/ComponentsList/ComponentsList.spec.js
+++ b/src/rsg-components/ComponentsList/ComponentsList.spec.js
@@ -20,6 +20,24 @@ it('should set the correct href for items', () => {
 	expect(actual).toMatchSnapshot();
 });
 
+it('if a custom href is provided, should use it instead of generating internal link', () => {
+	const components = [
+		{
+			visibleName: 'External example',
+			name: 'External example',
+			href: 'http://example.com',
+		},
+		{
+			visibleName: 'Input',
+			name: 'Input',
+			slug: 'input',
+		},
+	];
+
+	const actual = shallow(<ComponentsList items={components} classes={{}} />);
+	expect(actual).toMatchSnapshot();
+});
+
 it('should set a parameter on link when useHashId is activated', () => {
 	const components = [
 		{
diff --git a/src/rsg-components/ComponentsList/ComponentsListRenderer.js b/src/rsg-components/ComponentsList/ComponentsListRenderer.js
index 4da47a06..3e29cc5d 100644
--- a/src/rsg-components/ComponentsList/ComponentsListRenderer.js
+++ b/src/rsg-components/ComponentsList/ComponentsListRenderer.js
@@ -42,12 +42,16 @@ export function ComponentsListRenderer({ classes, items }) {
 
 	return (
 		<ul className={classes.list}>
-			{items.map(({ heading, visibleName, href, content }) => (
+			{items.map(({ heading, visibleName, href, content, external }) => (
 				<li
 					className={cx(classes.item, (!content || !content.props.items.length) && classes.isChild)}
 					key={href}
 				>
-					<Link className={cx(heading && classes.heading)} href={href}>
+					<Link
+						className={cx(heading && classes.heading)}
+						href={href}
+						target={external ? '_blank' : undefined}
+					>
 						{visibleName}
 					</Link>
 					{content}
diff --git a/src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap b/src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap
index eeed28c4..5d7b273a 100644
--- a/src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap
+++ b/src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap
@@ -1,5 +1,26 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`if a custom href is provided, should use it instead of generating internal link 1`] = `
+<Styled(ComponentsList)
+  classes={Object {}}
+  items={
+    Array [
+      Object {
+        "href": "http://example.com",
+        "name": "External example",
+        "visibleName": "External example",
+      },
+      Object {
+        "href": "blank#input",
+        "name": "Input",
+        "slug": "input",
+        "visibleName": "Input",
+      },
+    ]
+  }
+/>
+`;
+
 exports[`should ignore items without visibleName 1`] = `
 <ul>
   <li
diff --git a/src/rsg-components/Link/Link.spec.js b/src/rsg-components/Link/Link.spec.js
index 7917cd9c..7f7a5c6d 100644
--- a/src/rsg-components/Link/Link.spec.js
+++ b/src/rsg-components/Link/Link.spec.js
@@ -23,3 +23,13 @@ it('should compose passed class names', () => {
 
 	expect(actual.find('a').prop('className')).toBe('baseLinkClass customClass');
 });
+
+it('should properly pass the target attribute', () => {
+	const actual = shallow(
+		<LinkRenderer href={href} target="_blank" classes={{}}>
+			{children}
+		</LinkRenderer>
+	);
+
+	expect(actual.find('a').prop('target')).toBe('_blank');
+});
diff --git a/src/rsg-components/Sections/Sections.js b/src/rsg-components/Sections/Sections.js
index aa2840a8..7c761c57 100644
--- a/src/rsg-components/Sections/Sections.js
+++ b/src/rsg-components/Sections/Sections.js
@@ -6,7 +6,9 @@ import SectionsRenderer from 'rsg-components/Sections/SectionsRenderer';
 export default function Sections({ sections, depth }) {
 	return (
 		<SectionsRenderer>
-			{sections.map((section, idx) => <Section key={idx} section={section} depth={depth} />)}
+			{sections
+				.filter(section => !section.href)
+				.map((section, idx) => <Section key={idx} section={section} depth={depth} />)}
 		</SectionsRenderer>
 	);
 }
diff --git a/src/rsg-components/slots/index.js b/src/rsg-components/slots/index.js
index 66f6046c..c58f258c 100644
--- a/src/rsg-components/slots/index.js
+++ b/src/rsg-components/slots/index.js
@@ -1,8 +1,8 @@
 import Editor from 'rsg-components/Editor';
 import Usage from 'rsg-components/Usage';
-import IsolateButton from './IsolateButton';
-import CodeTabButton from './CodeTabButton';
-import UsageTabButton from './UsageTabButton';
+import IsolateButton from 'rsg-components/slots/IsolateButton';
+import CodeTabButton from 'rsg-components/slots/CodeTabButton';
+import UsageTabButton from 'rsg-components/slots/UsageTabButton';
 
 export const EXAMPLE_TAB_CODE_EDITOR = 'rsg-code-editor';
 export const DOCS_TAB_USAGE = 'rsg-usage';
diff --git a/src/utils/getRouteData.js b/src/utils/getRouteData.js
index 06debc5f..abf46283 100644
--- a/src/utils/getRouteData.js
+++ b/src/utils/getRouteData.js
@@ -49,7 +49,7 @@ export default function getRouteData(sections, hash, pagePerSection) {
 			hashArray.forEach((hashName, index) => {
 				// Filter the requested component if required but only on the first depth
 				// so in the next time of iteration, it will be trying to filter only on the second depth and so on
-				filteredSections = filterComponentsInSectionsByExactName(sections, hashName, false);
+				filteredSections = filterComponentsInSectionsByExactName(sections, hashName, isolate);
 
 				// If filteredSections exists, its because is an array of an component
 				// else it is an array of sections and depending his sectionDepth


Test patch:
diff --git a/loaders/utils/__tests__/__snapshots__/getProps.spec.js.snap b/loaders/utils/__tests__/__snapshots__/getProps.spec.js.snap
index 78b7653e..ba516cec 100644
--- a/loaders/utils/__tests__/__snapshots__/getProps.spec.js.snap
+++ b/loaders/utils/__tests__/__snapshots__/getProps.spec.js.snap
@@ -1,5 +1,131 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`should accept @return as a synonym of @returns 1`] = `
+Array [
+  Object {
+    "docblock": "
+Baz method with foo param
+
+@public
+@return {string} test
+",
+    "returns": Object {
+      "description": "test",
+      "title": "return",
+      "type": Object {
+        "name": "string",
+        "type": "NameExpression",
+      },
+    },
+    "tags": Object {
+      "public": Array [
+        Object {
+          "description": null,
+          "title": "public",
+          "type": null,
+        },
+      ],
+    },
+  },
+]
+`;
+
+exports[`should get method info from docblock and merge it 1`] = `
+Array [
+  Object {
+    "docblock": "
+Baz method with foo param
+
+@public
+@returns {string} test
+",
+    "returns": Object {
+      "description": "test",
+      "title": "returns",
+      "type": Object {
+        "name": "string",
+        "type": "NameExpression",
+      },
+    },
+    "tags": Object {
+      "public": Array [
+        Object {
+          "description": null,
+          "title": "public",
+          "type": null,
+        },
+      ],
+    },
+  },
+]
+`;
+
+exports[`should get method params info from docblock and merge it with passed method info 1`] = `
+Array [
+  Object {
+    "docblock": "
+Foo method with baz param
+
+@public
+@param {string} [baz=bar]
+@arg {string} foo param described with @arg tag
+@argument {string} test param described with @argument tag
+@returns {string} test
+",
+    "params": Array [
+      Object {
+        "default": "bar",
+        "description": null,
+        "name": "baz",
+        "title": "param",
+        "type": Object {
+          "expression": Object {
+            "name": "string",
+            "type": "NameExpression",
+          },
+          "type": "OptionalType",
+        },
+      },
+      Object {
+        "description": "param described with @arg tag",
+        "name": "foo",
+        "title": "arg",
+        "type": Object {
+          "name": "string",
+          "type": "NameExpression",
+        },
+      },
+      Object {
+        "description": "param described with @argument tag",
+        "name": "test",
+        "title": "argument",
+        "type": Object {
+          "name": "string",
+          "type": "NameExpression",
+        },
+      },
+    ],
+    "returns": Object {
+      "description": "test",
+      "title": "returns",
+      "type": Object {
+        "name": "string",
+        "type": "NameExpression",
+      },
+    },
+    "tags": Object {
+      "public": Array [
+        Object {
+          "description": null,
+          "title": "public",
+          "type": null,
+        },
+      ],
+    },
+  },
+]
+`;
+
 exports[`should highlight code in description (fenced code block) 1`] = `
 Object {
   "description": "The only true button.
diff --git a/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap b/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap
index 0b27d102..e06be44c 100644
--- a/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap
+++ b/loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap
@@ -9,7 +9,9 @@ Array [
     },
     "description": undefined,
     "exampleMode": "collapse",
+    "external": undefined,
     "filepath": "components/Button/Readme.md",
+    "href": undefined,
     "name": "Readme",
     "sectionDepth": 0,
     "sections": Array [],
@@ -89,7 +91,9 @@ Array [
     "content": undefined,
     "description": undefined,
     "exampleMode": "collapse",
+    "external": undefined,
     "filepath": undefined,
+    "href": undefined,
     "name": "Components",
     "sectionDepth": 0,
     "sections": Array [],
@@ -156,7 +160,9 @@ Array [
     "content": undefined,
     "description": undefined,
     "exampleMode": "collapse",
+    "external": undefined,
     "filepath": undefined,
+    "href": undefined,
     "name": "Ignore",
     "sectionDepth": 0,
     "sections": Array [],
@@ -240,7 +246,9 @@ Object {
   "content": undefined,
   "description": undefined,
   "exampleMode": "collapse",
+  "external": undefined,
   "filepath": undefined,
+  "href": undefined,
   "name": "Components",
   "sectionDepth": 0,
   "sections": Array [],
@@ -257,7 +265,9 @@ Object {
   },
   "description": undefined,
   "exampleMode": "collapse",
+  "external": undefined,
   "filepath": "components/Button/Readme.md",
+  "href": undefined,
   "name": "Readme",
   "sectionDepth": 0,
   "sections": Array [],
@@ -327,7 +337,9 @@ Object {
   "content": undefined,
   "description": undefined,
   "exampleMode": "collapse",
+  "external": undefined,
   "filepath": undefined,
+  "href": undefined,
   "name": "Ignore",
   "sectionDepth": 0,
   "sections": Array [],
diff --git a/loaders/utils/__tests__/getProps.spec.js b/loaders/utils/__tests__/getProps.spec.js
index 7b33a94b..3a74da83 100644
--- a/loaders/utils/__tests__/getProps.spec.js
+++ b/loaders/utils/__tests__/getProps.spec.js
@@ -62,6 +62,83 @@ it('should remove non-public methods', () => {
 	expect(result).toMatchSnapshot();
 });
 
+it('should get method info from docblock and merge it', () => {
+	const result = getProps(
+		{
+			displayName: 'Button',
+			methods: [
+				{
+					docblock: `
+Baz method with foo param
+
+@public
+@returns {string} test
+`,
+				},
+			],
+		},
+		__filename
+	);
+
+	expect(result.methods).toMatchSnapshot();
+});
+
+it('should accept @return as a synonym of @returns', () => {
+	const result = getProps(
+		{
+			displayName: 'Button',
+			methods: [
+				{
+					docblock: `
+Baz method with foo param
+
+@public
+@return {string} test
+`,
+				},
+			],
+		},
+		__filename
+	);
+
+	expect(result.methods).toMatchSnapshot();
+});
+
+it('should get method params info from docblock and merge it with passed method info', () => {
+	const result = getProps(
+		{
+			displayName: 'Button',
+			methods: [
+				{
+					docblock: `
+Foo method with baz param
+
+@public
+@param {string} [baz=bar]
+@arg {string} foo param described with @arg tag
+@argument {string} test param described with @argument tag
+@returns {string} test
+`,
+					params: [
+						{
+							name: 'baz',
+						},
+						{
+							name: 'foo',
+						},
+						{
+							name: 'test',
+						},
+					],
+				},
+			],
+		},
+		__filename
+	);
+
+	expect(result.methods).toMatchSnapshot();
+});
+
 it('should return an object for props with doclets', () => {
 	const result = getProps(
 		{