Skip to content

Commit

Permalink
fix: Correct behavior of the link closing isolated view (#1627)
Browse files Browse the repository at this point in the history
Fixes #1479
  • Loading branch information
Barthélémy Ledoux committed Jul 22, 2020
1 parent 967ba9e commit 9b6b6f6
Show file tree
Hide file tree
Showing 25 changed files with 192 additions and 763 deletions.
143 changes: 10 additions & 133 deletions src/client/rsg-components/ComponentsList/ComponentsList.spec.tsx
Expand Up @@ -13,115 +13,6 @@ const context = {

const Provider = (props: any) => <Context.Provider value={context} {...props} />;

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(
<Provider>
<ComponentsList items={components} />
</Provider>
);

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(
<Provider>
<ComponentsList items={components} />
</Provider>
);

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(
<Provider>
<ComponentsList items={components} useRouterLinks hashPath={['Components']} useHashId />
</Provider>
);

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(
<Provider>
<ComponentsList
items={components}
useRouterLinks
hashPath={['Components']}
useHashId={false}
/>
</Provider>
);

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(
<Provider>
Expand All @@ -147,12 +38,7 @@ it('should ignore items without visibleName', () => {

const { getAllByRole } = render(
<Provider>
<ComponentsList
items={components}
useRouterLinks
hashPath={['Components']}
useHashId={false}
/>
<ComponentsList items={components} />
</Provider>
);

Expand All @@ -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: <div data-testid="content">Content for Button</div>,
},
{
visibleName: 'Input',
name: 'Input',
slug: 'input',
href: '#input',
content: <div data-testid="content">Content for Input</div>,
},
];

const { getAllByTestId, getByText } = render(
<Provider>
<ComponentsList
items={components}
useRouterLinks
hashPath={['Components']}
useHashId={false}
/>
<ComponentsList items={components} />
</Provider>
);

Expand All @@ -201,25 +84,22 @@ it('should show content of initialOpen items even if they are not active', () =>
visibleName: 'Button',
name: 'Button',
slug: 'button',
href: '#button',
content: <div data-testid="content">Content for Button</div>,
},
{
visibleName: 'Input',
name: 'Input',
slug: 'input',
href: '#input',
content: <div data-testid="content">Content for Input</div>,
initialOpen: true,
},
];

const { getAllByTestId, getByText } = render(
<Provider>
<ComponentsList
items={components}
useRouterLinks
hashPath={['Components']}
useHashId={false}
/>
<ComponentsList items={components} />
</Provider>
);

Expand All @@ -236,13 +116,15 @@ it('should show content of forcedOpen items even if they are initially collapsed
visibleName: 'Button',
name: 'Button',
slug: 'button',
href: '#button',
content: <div data-testid="content">Content for Button</div>,
initialOpen: true,
},
{
visibleName: 'Input',
name: 'Input',
slug: 'input',
href: '#input',
content: <div data-testid="content">Content for Input</div>,
initialOpen: true,
forcedOpen: true,
Expand All @@ -251,12 +133,7 @@ it('should show content of forcedOpen items even if they are initially collapsed

const { getAllByTestId, getByText } = render(
<Provider>
<ComponentsList
items={components}
useRouterLinks
hashPath={['Components']}
useHashId={false}
/>
<ComponentsList items={components} />
</Provider>
);

Expand Down
35 changes: 3 additions & 32 deletions 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<ComponentsListProps> = ({
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<ComponentsListProps> = ({ items }) => {
const visibleItems = items.filter(item => item.visibleName);

return {
...item,
href,
};
})
.filter(item => item.visibleName);

return mappedItems.length > 0 ? <ComponentsListRenderer items={mappedItems} /> : null;
return visibleItems.length > 0 ? <ComponentsListRenderer items={visibleItems} /> : null;
};

ComponentsList.propTypes = {
items: PropTypes.array.isRequired,
hashPath: PropTypes.array,
useRouterLinks: PropTypes.bool,
useHashId: PropTypes.bool,
};

export default ComponentsList;
Expand Up @@ -84,6 +84,7 @@ const ComponentsListSectionRenderer: React.FunctionComponent<Rsg.TOCItem & JssIn
href={href}
onClick={() => setOpen(!open)}
target={shouldOpenInNewTab ? '_blank' : undefined}
data-testid="rsg-toc-link"
>
{visibleName}
</Link>
Expand Down
Expand Up @@ -24,6 +24,7 @@ const component = {
name: 'Foo',
visibleName: 'Foo',
slug: 'foo',
href: '#foo',
pathLine: 'foo/bar.js',
props: {
description: 'Bar',
Expand Down
3 changes: 2 additions & 1 deletion src/client/rsg-components/ReactComponent/ReactComponent.tsx
Expand Up @@ -53,7 +53,7 @@ export default class ReactComponent extends Component<ReactComponentProps, React
config: { pagePerSection },
} = this.context;
const { component, depth, usageMode, exampleMode } = this.props;
const { name, visibleName, slug = '-', filepath, pathLine } = component;
const { name, visibleName, slug = '-', filepath, pathLine, href } = component;
const { description = '', examples = [], tags = {} } = component.props || {};
if (!name) {
return null;
Expand All @@ -78,6 +78,7 @@ export default class ReactComponent extends Component<ReactComponentProps, React
...component,
isolated: displayMode !== DisplayModes.all,
}}
href={href}
depth={depth}
>
{visibleName}
Expand Down
4 changes: 2 additions & 2 deletions src/client/rsg-components/Section/Section.spec.tsx
Expand Up @@ -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(
<Provider
value={{
...context,
Expand All @@ -118,5 +118,5 @@ test('should render section in isolation mode', () => {
/>
</Provider>
);
expect(getByLabelText(/show all components/i)).toBeInTheDocument();
expect(queryByLabelText(/open isolated/i)).toBeNull();
});
24 changes: 7 additions & 17 deletions src/client/rsg-components/SectionHeading/SectionHeading.spec.tsx
Expand Up @@ -8,7 +8,13 @@ describe('SectionHeading', () => {

test('should forward slot properties to the toolbar', () => {
const actual = shallow(
<SectionHeading id="section" slotName="slot" slotProps={{ foo: 1, bar: 'baz' }} depth={2}>
<SectionHeading
id="section"
slotName="slot"
href="/#section"
slotProps={{ foo: 1, bar: 'baz' }}
depth={2}
>
A Section
</SectionHeading>
);
Expand Down Expand Up @@ -51,20 +57,4 @@ describe('SectionHeading', () => {

expect(actual.find('h6')).toHaveLength(1);
});

test('the href have id=section query parameter ', () => {
const actual = shallow(
<SectionHeading
id="section"
pagePerSection
slotName="slot"
slotProps={{ foo: 1, bar: 'baz' }}
depth={2}
>
A Section
</SectionHeading>
);

expect(actual.prop('href')).toEqual('/?id=section');
});
});
8 changes: 2 additions & 6 deletions src/client/rsg-components/SectionHeading/SectionHeading.tsx
Expand Up @@ -2,14 +2,14 @@ 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;
id: string;
slotName: string;
slotProps: object;
depth: number;
href?: string;
deprecated?: boolean;
pagePerSection?: boolean;
}
Expand All @@ -19,13 +19,9 @@ const SectionHeading: React.FunctionComponent<SectionHeadingProps> = ({
slotProps,
children,
id,
pagePerSection,
href,
...rest
}) => {
const href = pagePerSection
? getUrl({ slug: id, id: rest.depth !== 1, takeHash: true })
: getUrl({ slug: id, anchor: true });

return (
<SectionHeadingRenderer
toolbar={<Slot name={slotName} props={slotProps} />}
Expand Down

0 comments on commit 9b6b6f6

Please sign in to comment.