diff --git a/src/client/rsg-components/ComponentsList/ComponentsList.spec.tsx b/src/client/rsg-components/ComponentsList/ComponentsList.spec.tsx
index 3844718b0..450a44c73 100644
--- a/src/client/rsg-components/ComponentsList/ComponentsList.spec.tsx
+++ b/src/client/rsg-components/ComponentsList/ComponentsList.spec.tsx
@@ -13,115 +13,6 @@ const context = {
const Provider = (props: any) => ;
-it('should set the correct href for items', () => {
- const components = [
- {
- visibleName: 'Button',
- name: 'Button',
- slug: 'button',
- },
- {
- visibleName: 'Input',
- name: 'Input',
- slug: 'input',
- },
- ];
-
- const { getAllByRole } = render(
-
-
-
- );
-
- expect(Array.from(getAllByRole('link')).map(node => (node as HTMLAnchorElement).href)).toEqual([
- 'http://localhost/#button',
- 'http://localhost/#input',
- ]);
-});
-
-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 { getAllByRole } = render(
-
-
-
- );
-
- expect(Array.from(getAllByRole('link')).map(node => (node as HTMLAnchorElement).href)).toEqual([
- 'http://example.com/',
- 'http://localhost/#input',
- ]);
-});
-
-it('should set an id parameter on link when useHashId is activated', () => {
- const components = [
- {
- visibleName: 'Button',
- name: 'Button',
- slug: 'button',
- },
- {
- visibleName: 'Input',
- name: 'Input',
- slug: 'input',
- },
- ];
-
- const { getAllByRole } = render(
-
-
-
- );
-
- expect(Array.from(getAllByRole('link')).map(node => (node as HTMLAnchorElement).href)).toEqual([
- 'http://localhost/#/Components?id=button',
- 'http://localhost/#/Components?id=input',
- ]);
-});
-
-it('should set a sub route on link when useHashId is deactivated', () => {
- const components = [
- {
- visibleName: 'Button',
- name: 'Button',
- slug: 'button',
- },
- {
- visibleName: 'Input',
- name: 'Input',
- slug: 'input',
- },
- ];
-
- const { getAllByRole } = render(
-
-
-
- );
-
- expect(Array.from(getAllByRole('link')).map(node => (node as HTMLAnchorElement).href)).toEqual([
- 'http://localhost/#/Components/Button',
- 'http://localhost/#/Components/Input',
- ]);
-});
-
it('should not render any links when the list is empty', () => {
const { queryAllByRole } = render(
@@ -147,12 +38,7 @@ it('should ignore items without visibleName', () => {
const { getAllByRole } = render(
-
+
);
@@ -167,24 +53,21 @@ it('should show content of items that are open and not what is closed', () => {
visibleName: 'Button',
name: 'Button',
slug: 'button',
+ href: '#buttton',
content: Content for Button
,
},
{
visibleName: 'Input',
name: 'Input',
slug: 'input',
+ href: '#input',
content: Content for Input
,
},
];
const { getAllByTestId, getByText } = render(
-
+
);
@@ -201,12 +84,14 @@ it('should show content of initialOpen items even if they are not active', () =>
visibleName: 'Button',
name: 'Button',
slug: 'button',
+ href: '#button',
content: Content for Button
,
},
{
visibleName: 'Input',
name: 'Input',
slug: 'input',
+ href: '#input',
content: Content for Input
,
initialOpen: true,
},
@@ -214,12 +99,7 @@ it('should show content of initialOpen items even if they are not active', () =>
const { getAllByTestId, getByText } = render(
-
+
);
@@ -236,6 +116,7 @@ it('should show content of forcedOpen items even if they are initially collapsed
visibleName: 'Button',
name: 'Button',
slug: 'button',
+ href: '#button',
content: Content for Button
,
initialOpen: true,
},
@@ -243,6 +124,7 @@ it('should show content of forcedOpen items even if they are initially collapsed
visibleName: 'Input',
name: 'Input',
slug: 'input',
+ href: '#input',
content: Content for Input
,
initialOpen: true,
forcedOpen: true,
@@ -251,12 +133,7 @@ it('should show content of forcedOpen items even if they are initially collapsed
const { getAllByTestId, getByText } = render(
-
+
);
diff --git a/src/client/rsg-components/ComponentsList/ComponentsList.tsx b/src/client/rsg-components/ComponentsList/ComponentsList.tsx
index 83b338066..b6bf5fe67 100644
--- a/src/client/rsg-components/ComponentsList/ComponentsList.tsx
+++ b/src/client/rsg-components/ComponentsList/ComponentsList.tsx
@@ -1,49 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
import ComponentsListRenderer from 'rsg-components/ComponentsList/ComponentsListRenderer';
-import getUrl from '../../utils/getUrl';
import * as Rsg from '../../../typings';
interface ComponentsListProps {
items: Rsg.TOCItem[];
- hashPath?: string[];
- useRouterLinks?: boolean;
- useHashId?: boolean;
}
-const ComponentsList: React.FunctionComponent = ({
- items,
- useRouterLinks = false,
- useHashId,
- hashPath,
-}) => {
- const mappedItems = items
- .map(item => {
- const href = item.href
- ? item.href
- : getUrl({
- name: item.name,
- slug: item.slug,
- anchor: !useRouterLinks,
- hashPath: useRouterLinks ? hashPath : false,
- id: useRouterLinks ? useHashId : false,
- });
+const ComponentsList: React.FunctionComponent = ({ items }) => {
+ const visibleItems = items.filter(item => item.visibleName);
- return {
- ...item,
- href,
- };
- })
- .filter(item => item.visibleName);
-
- return mappedItems.length > 0 ? : null;
+ return visibleItems.length > 0 ? : null;
};
ComponentsList.propTypes = {
items: PropTypes.array.isRequired,
- hashPath: PropTypes.array,
- useRouterLinks: PropTypes.bool,
- useHashId: PropTypes.bool,
};
export default ComponentsList;
diff --git a/src/client/rsg-components/ComponentsList/ComponentsListRenderer.tsx b/src/client/rsg-components/ComponentsList/ComponentsListRenderer.tsx
index da772749d..3c7f3b0a8 100644
--- a/src/client/rsg-components/ComponentsList/ComponentsListRenderer.tsx
+++ b/src/client/rsg-components/ComponentsList/ComponentsListRenderer.tsx
@@ -84,6 +84,7 @@ const ComponentsListSectionRenderer: React.FunctionComponent setOpen(!open)}
target={shouldOpenInNewTab ? '_blank' : undefined}
+ data-testid="rsg-toc-link"
>
{visibleName}
diff --git a/src/client/rsg-components/ReactComponent/ReactComponent.spec.tsx b/src/client/rsg-components/ReactComponent/ReactComponent.spec.tsx
index 80d63ca42..e262fc712 100644
--- a/src/client/rsg-components/ReactComponent/ReactComponent.spec.tsx
+++ b/src/client/rsg-components/ReactComponent/ReactComponent.spec.tsx
@@ -24,6 +24,7 @@ const component = {
name: 'Foo',
visibleName: 'Foo',
slug: 'foo',
+ href: '#foo',
pathLine: 'foo/bar.js',
props: {
description: 'Bar',
diff --git a/src/client/rsg-components/ReactComponent/ReactComponent.tsx b/src/client/rsg-components/ReactComponent/ReactComponent.tsx
index 5e909c246..5c57893d2 100644
--- a/src/client/rsg-components/ReactComponent/ReactComponent.tsx
+++ b/src/client/rsg-components/ReactComponent/ReactComponent.tsx
@@ -53,7 +53,7 @@ export default class ReactComponent extends Component
{visibleName}
diff --git a/src/client/rsg-components/Section/Section.spec.tsx b/src/client/rsg-components/Section/Section.spec.tsx
index 8f1816af7..9255f4c71 100644
--- a/src/client/rsg-components/Section/Section.spec.tsx
+++ b/src/client/rsg-components/Section/Section.spec.tsx
@@ -100,7 +100,7 @@ test('should not render section in isolation mode by default', () => {
});
test('should render section in isolation mode', () => {
- const { getByLabelText } = render(
+ const { queryByLabelText } = render(
{
/>
);
- expect(getByLabelText(/show all components/i)).toBeInTheDocument();
+ expect(queryByLabelText(/open isolated/i)).toBeNull();
});
diff --git a/src/client/rsg-components/SectionHeading/SectionHeading.spec.tsx b/src/client/rsg-components/SectionHeading/SectionHeading.spec.tsx
index 6cbf4c62a..7a6a01b76 100644
--- a/src/client/rsg-components/SectionHeading/SectionHeading.spec.tsx
+++ b/src/client/rsg-components/SectionHeading/SectionHeading.spec.tsx
@@ -8,7 +8,13 @@ describe('SectionHeading', () => {
test('should forward slot properties to the toolbar', () => {
const actual = shallow(
-
+
A Section
);
@@ -51,20 +57,4 @@ describe('SectionHeading', () => {
expect(actual.find('h6')).toHaveLength(1);
});
-
- test('the href have id=section query parameter ', () => {
- const actual = shallow(
-
- A Section
-
- );
-
- expect(actual.prop('href')).toEqual('/?id=section');
- });
});
diff --git a/src/client/rsg-components/SectionHeading/SectionHeading.tsx b/src/client/rsg-components/SectionHeading/SectionHeading.tsx
index fe61cb63d..804414c89 100644
--- a/src/client/rsg-components/SectionHeading/SectionHeading.tsx
+++ b/src/client/rsg-components/SectionHeading/SectionHeading.tsx
@@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import Slot from 'rsg-components/Slot';
import SectionHeadingRenderer from 'rsg-components/SectionHeading/SectionHeadingRenderer';
-import getUrl from '../../utils/getUrl';
interface SectionHeadingProps {
children?: React.ReactNode;
@@ -10,6 +9,7 @@ interface SectionHeadingProps {
slotName: string;
slotProps: object;
depth: number;
+ href?: string;
deprecated?: boolean;
pagePerSection?: boolean;
}
@@ -19,13 +19,9 @@ const SectionHeading: React.FunctionComponent = ({
slotProps,
children,
id,
- pagePerSection,
+ href,
...rest
}) => {
- const href = pagePerSection
- ? getUrl({ slug: id, id: rest.depth !== 1, takeHash: true })
- : getUrl({ slug: id, anchor: true });
-
return (
}
diff --git a/src/client/rsg-components/SectionHeading/SectionHeadingRenderer.tsx b/src/client/rsg-components/SectionHeading/SectionHeadingRenderer.tsx
index 59208ffd2..46bcd8911 100644
--- a/src/client/rsg-components/SectionHeading/SectionHeadingRenderer.tsx
+++ b/src/client/rsg-components/SectionHeading/SectionHeadingRenderer.tsx
@@ -34,7 +34,7 @@ interface SectionHeadingRendererProps extends JssInjectedProps {
children?: React.ReactNode;
toolbar?: React.ReactNode;
id: string;
- href: string;
+ href?: string;
depth: number;
deprecated?: boolean;
}
@@ -70,7 +70,7 @@ SectionHeadingRenderer.propTypes = {
children: PropTypes.node,
toolbar: PropTypes.node,
id: PropTypes.string.isRequired,
- href: PropTypes.string.isRequired,
+ href: PropTypes.string,
depth: PropTypes.number.isRequired,
deprecated: PropTypes.bool,
};
diff --git a/src/client/rsg-components/Sections/Sections.tsx b/src/client/rsg-components/Sections/Sections.tsx
index 0a424b872..8ae27c947 100644
--- a/src/client/rsg-components/Sections/Sections.tsx
+++ b/src/client/rsg-components/Sections/Sections.tsx
@@ -12,7 +12,7 @@ const Sections: React.FunctionComponent<{
return (
{sections
- .filter(section => !section.href)
+ .filter(section => !section.externalLink)
.map((section, idx) => (
))}
diff --git a/src/client/rsg-components/StyleGuide/StyleGuide.spec.tsx b/src/client/rsg-components/StyleGuide/StyleGuide.spec.tsx
index 9b2659d5b..adbedc731 100644
--- a/src/client/rsg-components/StyleGuide/StyleGuide.spec.tsx
+++ b/src/client/rsg-components/StyleGuide/StyleGuide.spec.tsx
@@ -14,7 +14,7 @@ const sections: Rsg.Section[] = [
{
name: 'Foo',
visibleName: 'Foo',
- slug: 'foo',
+ href: '#foo',
pathLine: 'components/foo.js',
filepath: 'components/foo.js',
props: {
@@ -24,7 +24,7 @@ const sections: Rsg.Section[] = [
{
name: 'Bar',
visibleName: 'Bar',
- slug: 'bar',
+ href: '#bar',
pathLine: 'components/bar.js',
filepath: 'components/bar.js',
props: {
diff --git a/src/client/rsg-components/TableOfContents/TableOfContents.spec.tsx b/src/client/rsg-components/TableOfContents/TableOfContents.spec.tsx
index d26f5ca6e..c2c27a6a8 100644
--- a/src/client/rsg-components/TableOfContents/TableOfContents.spec.tsx
+++ b/src/client/rsg-components/TableOfContents/TableOfContents.spec.tsx
@@ -1,121 +1,95 @@
import React from 'react';
-import { render } from '@testing-library/react';
+import { render, fireEvent } from '@testing-library/react';
import { shallow } from 'enzyme';
-import noop from 'lodash/noop';
import TableOfContents from './TableOfContents';
import { TableOfContentsRenderer } from './TableOfContentsRenderer';
import Context from '../Context';
const components = [
{
+ visibleName: 'Button',
name: 'Button',
- slug: 'button',
+ href: '#button',
},
{
+ visibleName: 'Input',
name: 'Input',
- slug: 'input',
+ href: '#input',
},
{
+ visibleName: 'Textarea',
name: 'Textarea',
- slug: 'textarea',
+ href: '#textarea',
},
];
const sections = [
{
+ visibleName: 'Introduction',
name: 'Introduction',
- slug: 'introduction',
+ href: '#introduction',
content: 'intro.md',
},
{
+ visibleName: 'Buttons',
name: 'Buttons',
- slug: 'buttons',
+ href: '#buttons',
components: [
{
+ visibleName: 'Button',
name: 'Button',
- slug: 'button',
+ href: '#button',
},
],
},
{
+ visibleName: 'Forms',
name: 'Forms',
- slug: 'forms',
+ href: '#forms',
components: [
{
+ visibleName: 'Input',
name: 'Input',
- slug: 'input',
+ href: '#input',
},
{
+ visibleName: 'Textarea',
name: 'Textarea',
- slug: 'textarea',
+ href: '#textarea',
},
],
},
];
-it('should render a renderer', () => {
- const actual = shallow(
-
- );
-
- expect(actual).toMatchSnapshot();
-});
-
-it('should render renderer with sections with nested components', () => {
- const actual = shallow();
-
- expect(actual).toMatchSnapshot();
-});
-
it('should filter list when search field contains a query', () => {
- const searchTerm = 'but';
- const actual = shallow(
+ const searchTerm = 'put';
+ const { getByPlaceholderText, getAllByTestId, getByTestId } = render(
);
-
- expect(actual).toMatchSnapshot();
-
- actual.setState({ searchTerm });
-
- expect(actual).toMatchSnapshot();
+ expect(getAllByTestId('rsg-toc-link').length).toBe(3);
+ fireEvent.change(getByPlaceholderText('Filter by name'), { target: { value: searchTerm } });
+ expect(getAllByTestId('rsg-toc-link')).toHaveLength(1);
+ expect(getByTestId('rsg-toc-link')).toHaveTextContent('Input');
});
it('should filter section names', () => {
const searchTerm = 'frm';
- const actual = shallow();
-
- expect(actual).toMatchSnapshot();
-
- actual.setState({ searchTerm });
-
- expect(actual).toMatchSnapshot();
-});
-
-it('renderer should render table of contents', () => {
- const searchTerm = 'foo';
- const actual = shallow(
-
- foo
-
+ const { getByPlaceholderText, getAllByTestId, getByTestId } = render(
+
);
-
- expect(actual).toMatchSnapshot();
+ expect(getAllByTestId('rsg-toc-link').length).toBe(6);
+ fireEvent.change(getByPlaceholderText('Filter by name'), { target: { value: searchTerm } });
+ expect(getAllByTestId('rsg-toc-link')).toHaveLength(1);
+ expect(getByTestId('rsg-toc-link')).toHaveTextContent('Forms');
});
it('should call a callback when input value changed', () => {
@@ -184,36 +158,39 @@ it('should render components of a single top section as root', () => {
"content": undefined,
"forcedOpen": false,
"heading": false,
+ "href": "#button",
"initialOpen": true,
"name": "Button",
"sections": Array [],
"selected": false,
"shouldOpenInNewTab": false,
- "slug": "button",
+ "visibleName": "Button",
},
Object {
"components": Array [],
"content": undefined,
"forcedOpen": false,
"heading": false,
+ "href": "#input",
"initialOpen": true,
"name": "Input",
"sections": Array [],
"selected": false,
"shouldOpenInNewTab": false,
- "slug": "input",
+ "visibleName": "Input",
},
Object {
"components": Array [],
"content": undefined,
"forcedOpen": false,
"heading": false,
+ "href": "#textarea",
"initialOpen": true,
"name": "Textarea",
"sections": Array [],
"selected": false,
"shouldOpenInNewTab": false,
- "slug": "textarea",
+ "visibleName": "Textarea",
},
]
`);
@@ -256,7 +233,7 @@ it('should render as the link will open in a new window only if external present
"initialOpen": true,
"sections": Array [],
"selected": false,
- "shouldOpenInNewTab": true,
+ "shouldOpenInNewTab": false,
},
]
`);
@@ -272,8 +249,8 @@ it('should render components with useRouterLinks', () => {
sections={[
{
sections: [
- { visibleName: '1', name: 'Components', slug: 'Components', content: 'intro.md' },
- { visibleName: '2', content: 'chapter.md', slug: 'chap' },
+ { visibleName: '1', name: 'Components', href: '#/Components', content: 'intro.md' },
+ { visibleName: '2', content: 'chapter.md', href: '#/Chap' },
],
},
]}
@@ -305,20 +282,20 @@ it('should detect sections containing current selection when tocMode is collapse
sections: [
{
visibleName: '1',
- slug: 'Components',
- sections: [{ visibleName: '1.1', slug: 'Button' }],
+ href: '#/components',
+ sections: [{ visibleName: '1.1', href: '#/button' }],
},
{
visibleName: '2',
- slug: 'chap',
+ href: '#/chap',
content: 'chapter.md',
- sections: [{ visibleName: '2.1', slug: 'Chapter #1' }],
+ sections: [{ visibleName: '2.1', href: '#/chapter-1' }],
},
{ visibleName: '3', href: 'http://react-styleguidist.com' },
],
},
]}
- loc={{ pathname: '', hash: '/#Button' }}
+ loc={{ pathname: '', hash: 'button' }}
/>
);
diff --git a/src/client/rsg-components/TableOfContents/TableOfContents.tsx b/src/client/rsg-components/TableOfContents/TableOfContents.tsx
index 5b11bf74f..3ee7f2a85 100644
--- a/src/client/rsg-components/TableOfContents/TableOfContents.tsx
+++ b/src/client/rsg-components/TableOfContents/TableOfContents.tsx
@@ -4,7 +4,6 @@ import ComponentsList from 'rsg-components/ComponentsList';
import TableOfContentsRenderer from 'rsg-components/TableOfContents/TableOfContentsRenderer';
import filterSectionsByName from '../../utils/filterSectionsByName';
import { getHash } from '../../utils/handleHash';
-import getUrl from '../../utils/getUrl';
import * as Rsg from '../../../typings';
interface TableOfContentsProps {
@@ -54,18 +53,8 @@ export default class TableOfContents extends Component {
? this.renderLevel(children, useRouterLinks, childHashPath, sectionDepth === 0)
: { content: undefined, containsSelected: false };
- // get href
- const href = section.href
- ? section.href
- : getUrl({
- name: section.name,
- slug: section.slug,
- anchor: !useRouterLinks,
- hashPath: useRouterLinks ? hashPath : false,
- id: useRouterLinks ? useHashId : false,
- });
-
- const selected = href === windowHash;
+ const selected =
+ (!useRouterLinks && section.href ? getHash(section.href) : section.href) === windowHash;
if (containsSelected || selected) {
childrenContainSelected = true;
@@ -76,20 +65,13 @@ export default class TableOfContents extends Component {
heading: !!section.name && children.length > 0,
content,
selected,
- shouldOpenInNewTab: !!section.external && !!section.href,
+ shouldOpenInNewTab: !!section.external && !!section.externalLink,
initialOpen: this.props.tocMode !== 'collapse' || containsSelected,
forcedOpen: !!this.state.searchTerm.length,
};
});
return {
- content: (
-
- ),
+ content: ,
containsSelected: childrenContainSelected,
};
}
@@ -109,9 +91,9 @@ export default class TableOfContents extends Component {
? sections[0].sections
: sections[0].components
: sections;
- const filtered = firstLevel ? filterSectionsByName(firstLevel, searchTerm) : firstLevel;
+ const filtered = firstLevel ? filterSectionsByName(firstLevel, searchTerm) : firstLevel || [];
- return filtered ? this.renderLevel(filtered, useRouterLinks).content : null;
+ return this.renderLevel(filtered, useRouterLinks).content;
}
public render() {
diff --git a/src/client/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.tsx.snap b/src/client/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.tsx.snap
deleted file mode 100644
index 8997a3541..000000000
--- a/src/client/rsg-components/TableOfContents/__snapshots__/TableOfContents.spec.tsx.snap
+++ /dev/null
@@ -1,427 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renderer should render table of contents 1`] = `
-
-`;
-
-exports[`should filter list when search field contains a query 1`] = `
-
-
-
-`;
-
-exports[`should filter list when search field contains a query 2`] = `
-
-
-
-`;
-
-exports[`should filter section names 1`] = `
-
- ,
- "forcedOpen": false,
- "heading": true,
- "initialOpen": true,
- "name": "Buttons",
- "sections": Array [],
- "selected": false,
- "shouldOpenInNewTab": false,
- "slug": "buttons",
- },
- Object {
- "components": Array [
- Object {
- "name": "Input",
- "slug": "input",
- },
- Object {
- "name": "Textarea",
- "slug": "textarea",
- },
- ],
- "content": ,
- "forcedOpen": false,
- "heading": true,
- "initialOpen": true,
- "name": "Forms",
- "sections": Array [],
- "selected": false,
- "shouldOpenInNewTab": false,
- "slug": "forms",
- },
- ]
- }
- useHashId={false}
- useRouterLinks={false}
- />
-
-`;
-
-exports[`should filter section names 2`] = `
-
-
-
-`;
-
-exports[`should render a renderer 1`] = `
-
-
-
-`;
-
-exports[`should render renderer with sections with nested components 1`] = `
-
- ,
- "forcedOpen": false,
- "heading": true,
- "initialOpen": true,
- "name": "Buttons",
- "sections": Array [],
- "selected": false,
- "shouldOpenInNewTab": false,
- "slug": "buttons",
- },
- Object {
- "components": Array [
- Object {
- "name": "Input",
- "slug": "input",
- },
- Object {
- "name": "Textarea",
- "slug": "textarea",
- },
- ],
- "content": ,
- "forcedOpen": false,
- "heading": true,
- "initialOpen": true,
- "name": "Forms",
- "sections": Array [],
- "selected": false,
- "shouldOpenInNewTab": false,
- "slug": "forms",
- },
- ]
- }
- useHashId={false}
- useRouterLinks={false}
- />
-
-`;
diff --git a/src/client/rsg-components/slots/IsolateButton.spec.tsx b/src/client/rsg-components/slots/IsolateButton.spec.tsx
index 02b6b9140..28862a654 100644
--- a/src/client/rsg-components/slots/IsolateButton.spec.tsx
+++ b/src/client/rsg-components/slots/IsolateButton.spec.tsx
@@ -3,19 +3,19 @@ import { shallow } from 'enzyme';
import IsolateButton from './IsolateButton';
it('should renderer a link to isolated mode', () => {
- const actual = shallow();
+ const actual = shallow();
expect(actual).toMatchSnapshot();
});
it('should renderer a link to example isolated mode', () => {
- const actual = shallow();
+ const actual = shallow();
expect(actual).toMatchSnapshot();
});
it('should renderer a link home in isolated mode', () => {
- const actual = shallow();
+ const actual = shallow();
expect(actual).toMatchSnapshot();
});
diff --git a/src/client/rsg-components/slots/IsolateButton.tsx b/src/client/rsg-components/slots/IsolateButton.tsx
index 8d85bc069..38f9c071d 100644
--- a/src/client/rsg-components/slots/IsolateButton.tsx
+++ b/src/client/rsg-components/slots/IsolateButton.tsx
@@ -8,17 +8,18 @@ export interface IsolateButtonProps {
name: string;
example?: number;
isolated?: boolean;
+ href: string;
}
-const IsolateButton = ({ name, example, isolated }: IsolateButtonProps) => {
+const IsolateButton = ({ name, example, isolated, href }: IsolateButtonProps) => {
+ if (isolated && !href) {
+ return null;
+ }
+
const testID = example ? `${name}-${example}-isolate-button` : `${name}-isolate-button`;
return isolated ? (
-
+
) : (
diff --git a/src/client/utils/__tests__/getUrl.spec.ts b/src/client/utils/__tests__/getUrl.spec.ts
index 867c142d0..f55e5aa0d 100644
--- a/src/client/utils/__tests__/getUrl.spec.ts
+++ b/src/client/utils/__tests__/getUrl.spec.ts
@@ -104,18 +104,18 @@ describe('getUrl', () => {
});
it('should return a route path with a param id=foobar', () => {
- const result = getUrl({ name, slug, hashPath: ['Documentation'], id: true }, loc);
+ const result = getUrl({ name, slug, hashPath: ['Documentation'], useSlugAsIdParam: true }, loc);
expect(result).toBe('/styleguide/#/Documentation?id=foobar');
});
it('should return a param id=foobar', () => {
- const result = getUrl({ name, slug, takeHash: true, id: true }, loc);
+ const result = getUrl({ name, slug, takeHash: true, useSlugAsIdParam: true }, loc);
expect(result).toBe('/styleguide/#/Components?id=foobar');
});
it('should return to param id = foobar even if the hash has parameters', () => {
const result = getUrl(
- { name, slug, takeHash: true, id: true },
+ { name, slug, takeHash: true, useSlugAsIdParam: true },
{
...loc,
hash: '#/Components?foo=foobar',
diff --git a/src/client/utils/__tests__/processComponents.spec.ts b/src/client/utils/__tests__/processComponents.spec.ts
index 8885c3190..2c573c141 100644
--- a/src/client/utils/__tests__/processComponents.spec.ts
+++ b/src/client/utils/__tests__/processComponents.spec.ts
@@ -1,6 +1,8 @@
import deepfreeze from 'deepfreeze';
import processComponents from '../processComponents';
+const options = { useRouterLinks: false };
+
describe('processComponents', () => {
it('should set components’ displayName to a name property', () => {
const components = deepfreeze([
@@ -8,13 +10,25 @@ describe('processComponents', () => {
props: {
displayName: 'Foo',
},
- module: 13,
},
]);
- const result = processComponents(components);
+ const result = processComponents(components, options);
expect(result[0].name).toBe('Foo');
});
+ it('should calculate href', () => {
+ const components = deepfreeze([
+ {
+ slug: 'foo',
+ props: {
+ displayName: 'Foo',
+ },
+ },
+ ]);
+ const result = processComponents(components, options);
+ expect(result[0].href).toBe('/#foo');
+ });
+
describe('should set visibleName property on the component', () => {
it('from an visibleName component prop if available', () => {
const components = deepfreeze([
@@ -23,10 +37,9 @@ describe('processComponents', () => {
displayName: 'Foo',
visibleName: 'Foo Bar',
},
- module: 13,
},
]);
- const result = processComponents(components);
+ const result = processComponents(components, options);
expect(result[0].visibleName).toBe('Foo Bar');
});
@@ -36,10 +49,9 @@ describe('processComponents', () => {
props: {
displayName: 'Foo',
},
- module: 13,
},
]);
- const result = processComponents(components);
+ const result = processComponents(components, options);
expect(result[0].visibleName).toBe('Foo');
});
});
@@ -52,10 +64,9 @@ describe('processComponents', () => {
examples: [1, 2] as any[],
example: [3, 4] as any[],
},
- module: 11,
},
]);
- const result = processComponents(components);
+ const result = processComponents(components, options);
expect(result[0].props && result[0].props.examples).toEqual([1, 2, 3, 4]);
});
});
diff --git a/src/client/utils/__tests__/processSections.spec.ts b/src/client/utils/__tests__/processSections.spec.ts
index a29ab19af..8e791925e 100644
--- a/src/client/utils/__tests__/processSections.spec.ts
+++ b/src/client/utils/__tests__/processSections.spec.ts
@@ -6,12 +6,13 @@ const sections = deepfreeze([
sections: [
{
name: 'Components',
+ slug: 'components',
components: [
{
+ slug: 'button',
props: {
displayName: 'Button',
},
- module: 1,
},
],
},
@@ -19,19 +20,21 @@ const sections = deepfreeze([
},
]);
+const options = { useRouterLinks: false, hashPath: [] };
+
describe('processSections', () => {
it('should recursively process all sections and components', () => {
- const result = processSections(sections);
+ const result = processSections(sections, options);
const sectionsExpected = result[0].sections || [];
- expect(
- sectionsExpected.length &&
- sectionsExpected[0].components &&
- sectionsExpected[0].components[0].name
- ).toBe('Button');
+ const comp = sectionsExpected.length
+ ? sectionsExpected[0].components && sectionsExpected[0].components[0]
+ : undefined;
+ expect(comp?.name).toBe('Button');
+ expect(comp?.href).toBe('/#button');
});
it('should set visibleName property on each section', () => {
- const result = processSections(sections);
+ const result = processSections(sections, options);
const sectionsExpected = result[0].sections || [];
expect(sectionsExpected[0].visibleName).toBe('Components');
});
diff --git a/src/client/utils/getUrl.ts b/src/client/utils/getUrl.ts
index e84962f75..664ed4a22 100644
--- a/src/client/utils/getUrl.ts
+++ b/src/client/utils/getUrl.ts
@@ -65,7 +65,7 @@ interface GetUrlOptions {
*/
absolute: boolean;
hashPath: string[] | false;
- id: boolean;
+ useSlugAsIdParam: boolean;
takeHash: boolean;
}
@@ -86,7 +86,7 @@ export default function getUrl(
nochrome,
absolute,
hashPath,
- id,
+ useSlugAsIdParam,
takeHash,
}: Partial = {},
{
@@ -121,13 +121,13 @@ export default function getUrl(
if (hashPath) {
let encodedHashPath = hashPath.map(encodeURIComponent);
- if (!id) {
+ if (!useSlugAsIdParam) {
encodedHashPath = [...encodedHashPath, encodedName];
}
url += `#/${encodedHashPath.join('/')}`;
}
- if (id) {
+ if (useSlugAsIdParam) {
url += `?id=${slug}`;
}
diff --git a/src/client/utils/processComponents.ts b/src/client/utils/processComponents.ts
index d537bc0cc..4abc188ba 100644
--- a/src/client/utils/processComponents.ts
+++ b/src/client/utils/processComponents.ts
@@ -1,4 +1,11 @@
import * as Rsg from '../../typings';
+import getUrl from './getUrl';
+
+export interface HrefOptions {
+ hashPath?: string[];
+ useRouterLinks: boolean;
+ useHashId?: boolean;
+}
/**
* Do things that are hard or impossible to do in a loader: we don’t have access to component name
@@ -7,7 +14,10 @@ import * as Rsg from '../../typings';
* @param {Array} components
* @return {Array}
*/
-export default function processComponents(components: Rsg.Component[]): Rsg.Component[] {
+export default function processComponents(
+ components: Rsg.Component[],
+ { useRouterLinks, useHashId, hashPath }: HrefOptions
+): Rsg.Component[] {
return components.map(component => {
const newComponent: Rsg.Component = component.props
? {
@@ -22,6 +32,15 @@ export default function processComponents(components: Rsg.Component[]): Rsg.Comp
// Append @example doclet to all examples
examples: [...(component.props.examples || []), ...(component.props.example || [])],
},
+ href:
+ component.href ||
+ getUrl({
+ name: component.props.displayName,
+ slug: component.slug,
+ anchor: !useRouterLinks,
+ hashPath: useRouterLinks ? hashPath : false,
+ useSlugAsIdParam: useRouterLinks ? useHashId : false,
+ }),
}
: {};
diff --git a/src/client/utils/processSections.ts b/src/client/utils/processSections.ts
index 6c6574f84..63096d44f 100644
--- a/src/client/utils/processSections.ts
+++ b/src/client/utils/processSections.ts
@@ -1,5 +1,6 @@
-import processComponents from './processComponents';
import * as Rsg from '../../typings';
+import processComponents, { HrefOptions } from './processComponents';
+import getUrl from './getUrl';
/**
* Recursively process each component in all sections.
@@ -7,11 +8,34 @@ import * as Rsg from '../../typings';
* @param {Array} sections
* @return {Array}
*/
-export default function processSections(sections: Rsg.Section[]): Rsg.Section[] {
- return sections.map(section => ({
- ...section,
- visibleName: section.name,
- components: processComponents(section.components || []),
- sections: processSections(section.sections || []),
- }));
+export default function processSections(
+ sections: Rsg.Section[],
+ { useRouterLinks, useHashId = false, hashPath = [] }: HrefOptions
+): Rsg.Section[] {
+ return sections.map(section => {
+ const options = {
+ useRouterLinks,
+ useHashId: section.sectionDepth === 0,
+ hashPath: [...hashPath, section.name ? section.name : '-'],
+ };
+ const href =
+ section.href ||
+ getUrl({
+ name: section.name,
+ slug: section.slug,
+ anchor: !useRouterLinks,
+ hashPath: useRouterLinks ? hashPath : false,
+ useSlugAsIdParam: useRouterLinks ? useHashId : false,
+ });
+
+ return {
+ ...section,
+ // flag the section as an external link to avoid rendering it later
+ externalLink: !!section.href,
+ href,
+ visibleName: section.name,
+ components: processComponents(section.components || [], options),
+ sections: processSections(section.sections || [], options),
+ };
+ });
}
diff --git a/src/client/utils/renderStyleguide.tsx b/src/client/utils/renderStyleguide.tsx
index 17c8091ea..2f2ae67cc 100644
--- a/src/client/utils/renderStyleguide.tsx
+++ b/src/client/utils/renderStyleguide.tsx
@@ -29,7 +29,9 @@ export default function renderStyleguide(
doc: { title: string } = document,
hist: { replaceState: (name: string, title: string, url: string) => void } = window.history
): React.ReactElement {
- const allSections = processSections(styleguide.sections);
+ const allSections = processSections(styleguide.sections, {
+ useRouterLinks: styleguide.config.pagePerSection,
+ });
const { title, pagePerSection, theme, styles } = styleguide.config;
const { sections, displayMode } = getRouteData(allSections, loc.hash, pagePerSection);
diff --git a/src/typings/RsgSection.ts b/src/typings/RsgSection.ts
index 1fe24c638..a80dcad28 100644
--- a/src/typings/RsgSection.ts
+++ b/src/typings/RsgSection.ts
@@ -17,6 +17,8 @@ export interface BaseSection {
export interface ProcessedSection extends BaseSection {
visibleName?: string;
filepath?: string;
+ externalLink?: boolean;
+ href?: string;
}
/**
diff --git a/test/cypress/integration/component_spec.js b/test/cypress/integration/component_spec.js
index 738607728..0b56c52f7 100644
--- a/test/cypress/integration/component_spec.js
+++ b/test/cypress/integration/component_spec.js
@@ -97,9 +97,7 @@ describe('Single component', () => {
.should('have.length', 1);
// Toggle out of isolated example mode
- cy.get('@componentExamples')
- .find('[data-testid$="-isolate-button"]')
- .click();
+ cy.get('[data-testid$="-isolate-button"]').click();
// Assert the other examples are showing again
cy.get('@componentExamples')