Skip to content

Commit 54ab94a

Browse files
BTMPLbluetidepro
authored andcommitted
Feat: Added support for external links in sections (styleguidist#921)
* Added support for external links in sections * Removed the skip functionality, changed external prop to href prop * Removed leftover skip and external prop * Renamed test for target attribute * Reverted change to docs * Update example sections config and fialing tests * Use a unique string as Section key * Reverted change to Sections component; apparently there are cases where you dont get section slug * Reverted package-lock.json * Revert Cookbook.md * Properly unstage Cookbook * The cookbook is changed by pre-commit hook * Added test for the href prop * Changed jsx to markdown syntax
1 parent 56e2090 commit 54ab94a

File tree

11 files changed

+96
-12
lines changed

11 files changed

+96
-12
lines changed

docs/Components.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ Each section consists of (all fields are optional):
113113
- `exampleMode` — Initial state of the code example tab, uses [exampleMode](Configuration.md#examplemode).
114114
- `usageMode` — Initial state of the props and methods tab, uses [usageMode](Configuration.md#usagemode).
115115
- `ignore` — string/array of globs that should not be included in the section.
116+
- `href` - an URL to navigate to instead of navigating to the section content
117+
- `external` - if set, the link will open in a new window
116118

117119
Configuring a style guide with textual documentation section and a list of components would look like:
118120

@@ -134,6 +136,11 @@ module.exports = {
134136
{
135137
name: 'Configuration',
136138
content: 'docs/configuration.md'
139+
},
140+
{
141+
name: 'Live Demo',
142+
external: true,
143+
href: 'http://example.com'
137144
}
138145
]
139146
},

docs/Cookbook.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,9 @@ const icons = iconsContext.keys().reduce((icons, file) => {
117117
export default icons
118118
```
119119

120-
````jsx
120+
````markdown
121121
// IconGallery.md
122+
122123
```jsx noeditor
123124
const icons = require('./load-icons').default
124125

@@ -130,7 +131,7 @@ const iconElements = Object.keys(icons).map(iconName => {
130131
</span>
131132
)
132133
})
133-
;<div>{iconElements}</div>
134+
<div>{iconElements}</div>
134135
```
135136
````
136137

examples/sections/styleguide.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ module.exports = {
2525
},
2626
],
2727
},
28+
{
29+
name: 'Online documentation',
30+
href: 'https://github.com/styleguidist/react-styleguidist',
31+
external: true,
32+
},
2833
],
2934
sectionDepth: 2,
3035
},

loaders/utils/__tests__/__snapshots__/getSections.spec.js.snap

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ Array [
99
},
1010
"description": undefined,
1111
"exampleMode": "collapse",
12+
"external": undefined,
1213
"filepath": "components/Button/Readme.md",
14+
"href": undefined,
1315
"name": "Readme",
1416
"sectionDepth": 0,
1517
"sections": Array [],
@@ -89,7 +91,9 @@ Array [
8991
"content": undefined,
9092
"description": undefined,
9193
"exampleMode": "collapse",
94+
"external": undefined,
9295
"filepath": undefined,
96+
"href": undefined,
9397
"name": "Components",
9498
"sectionDepth": 0,
9599
"sections": Array [],
@@ -156,7 +160,9 @@ Array [
156160
"content": undefined,
157161
"description": undefined,
158162
"exampleMode": "collapse",
163+
"external": undefined,
159164
"filepath": undefined,
165+
"href": undefined,
160166
"name": "Ignore",
161167
"sectionDepth": 0,
162168
"sections": Array [],
@@ -240,7 +246,9 @@ Object {
240246
"content": undefined,
241247
"description": undefined,
242248
"exampleMode": "collapse",
249+
"external": undefined,
243250
"filepath": undefined,
251+
"href": undefined,
244252
"name": "Components",
245253
"sectionDepth": 0,
246254
"sections": Array [],
@@ -257,7 +265,9 @@ Object {
257265
},
258266
"description": undefined,
259267
"exampleMode": "collapse",
268+
"external": undefined,
260269
"filepath": "components/Button/Readme.md",
270+
"href": undefined,
261271
"name": "Readme",
262272
"sectionDepth": 0,
263273
"sections": Array [],
@@ -327,7 +337,9 @@ Object {
327337
"content": undefined,
328338
"description": undefined,
329339
"exampleMode": "collapse",
340+
"external": undefined,
330341
"filepath": undefined,
342+
"href": undefined,
331343
"name": "Ignore",
332344
"sectionDepth": 0,
333345
"sections": Array [],

loaders/utils/getSections.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@ function processSection(section, config, parentDepth) {
6868
slug: slugger.slug(section.name),
6969
sections: getSections(section.sections || [], config, sectionDepth),
7070
filepath: contentRelativePath,
71+
href: section.href,
7172
components: getSectionComponents(section, config),
7273
content,
74+
external: section.external,
7375
};
7476
}
7577

src/rsg-components/ComponentsList/ComponentsList.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import getUrl from '../../utils/getUrl';
66
function ComponentsList({ classes, items, useRouterLinks = false, useHashId, hashPath }) {
77
const mappedItems = items.map(item => ({
88
...item,
9-
href: getUrl({
10-
name: item.name,
11-
slug: item.slug,
12-
anchor: !useRouterLinks,
13-
hashPath: useRouterLinks ? hashPath : false,
14-
id: useRouterLinks ? useHashId : false,
15-
}),
9+
href: item.href
10+
? item.href
11+
: getUrl({
12+
name: item.name,
13+
slug: item.slug,
14+
anchor: !useRouterLinks,
15+
hashPath: useRouterLinks ? hashPath : false,
16+
id: useRouterLinks ? useHashId : false,
17+
}),
1618
}));
1719
return <ComponentsListRenderer classes={classes} items={mappedItems} />;
1820
}

src/rsg-components/ComponentsList/ComponentsList.spec.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@ it('should set the correct href for items', () => {
2020
expect(actual).toMatchSnapshot();
2121
});
2222

23+
it('if a custom href is provided, should use it instead of generating internal link', () => {
24+
const components = [
25+
{
26+
visibleName: 'External example',
27+
name: 'External example',
28+
href: 'http://example.com',
29+
},
30+
{
31+
visibleName: 'Input',
32+
name: 'Input',
33+
slug: 'input',
34+
},
35+
];
36+
37+
const actual = shallow(<ComponentsList items={components} classes={{}} />);
38+
expect(actual).toMatchSnapshot();
39+
});
40+
2341
it('should set a parameter on link when useHashId is activated', () => {
2442
const components = [
2543
{

src/rsg-components/ComponentsList/ComponentsListRenderer.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,16 @@ export function ComponentsListRenderer({ classes, items }) {
4242

4343
return (
4444
<ul className={classes.list}>
45-
{items.map(({ heading, visibleName, href, content }) => (
45+
{items.map(({ heading, visibleName, href, content, external }) => (
4646
<li
4747
className={cx(classes.item, (!content || !content.props.items.length) && classes.isChild)}
4848
key={href}
4949
>
50-
<Link className={cx(heading && classes.heading)} href={href}>
50+
<Link
51+
className={cx(heading && classes.heading)}
52+
href={href}
53+
target={external ? '_blank' : undefined}
54+
>
5155
{visibleName}
5256
</Link>
5357
{content}

src/rsg-components/ComponentsList/__snapshots__/ComponentsList.spec.js.snap

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`if a custom href is provided, should use it instead of generating internal link 1`] = `
4+
<Styled(ComponentsList)
5+
classes={Object {}}
6+
items={
7+
Array [
8+
Object {
9+
"href": "http://example.com",
10+
"name": "External example",
11+
"visibleName": "External example",
12+
},
13+
Object {
14+
"href": "blank#input",
15+
"name": "Input",
16+
"slug": "input",
17+
"visibleName": "Input",
18+
},
19+
]
20+
}
21+
/>
22+
`;
23+
324
exports[`should ignore items without visibleName 1`] = `
425
<ul>
526
<li

src/rsg-components/Link/Link.spec.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,13 @@ it('should compose passed class names', () => {
2323

2424
expect(actual.find('a').prop('className')).toBe('baseLinkClass customClass');
2525
});
26+
27+
it('should properly pass the target attribute', () => {
28+
const actual = shallow(
29+
<LinkRenderer href={href} target="_blank" classes={{}}>
30+
{children}
31+
</LinkRenderer>
32+
);
33+
34+
expect(actual.find('a').prop('target')).toBe('_blank');
35+
});

src/rsg-components/Sections/Sections.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import SectionsRenderer from 'rsg-components/Sections/SectionsRenderer';
66
export default function Sections({ sections, depth }) {
77
return (
88
<SectionsRenderer>
9-
{sections.map((section, idx) => <Section key={idx} section={section} depth={depth} />)}
9+
{sections
10+
.filter(section => !section.href)
11+
.map((section, idx) => <Section key={idx} section={section} depth={depth} />)}
1012
</SectionsRenderer>
1113
);
1214
}

0 commit comments

Comments
 (0)