Skip to content
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

Toc #226

Merged
merged 2 commits into from
Nov 27, 2020
Merged

Toc #226

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import React from 'react';
import { BulletLink } from './BulletLink';

export default {
Expand All @@ -10,7 +9,7 @@ export default {

const currentPath = '/path-1';
// Bullet links should always be used in a series
export const Series = (args) => (
export const Series = () => (
<ul>
<BulletLink currentPath={currentPath} item={{ path: '/path-1', title: 'Link 1' }} />
<BulletLink currentPath={currentPath} item={{ path: '/path-2', title: 'Link 2' }} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import React from 'react';
import styled from 'styled-components';
import { color, typography } from '../shared/styles';
import { Link } from '../Link';

const StyledBulletLink = styled(({ isActive, ...rest }) => <Link {...rest} />)`
type StyledBulletLinkProps = React.ComponentProps<typeof Link> & {
isActive?: boolean;
};

const StyledBulletLink = styled(({ isActive, ...rest }) => <Link {...rest} />)<
StyledBulletLinkProps
>`
outline: none;
display: inline-block;
padding: 6px 0;
Expand Down Expand Up @@ -53,7 +58,7 @@ const BulletLinkWrapper = styled.li`
}
`;

const Bullet = styled.span`
const Bullet = styled.span<{ isActive?: boolean }>`
display: inline-block;
margin-bottom: 1px;
margin-right: 16px;
Expand All @@ -67,7 +72,18 @@ const Bullet = styled.span`
${(props) => props.isActive && `background: ${color.secondary};`}
`;

export function BulletLink({ currentPath, item, ...rest }) {
export interface BulletLinkItem {
path: string;
title: string;
LinkWrapper?: Pick<React.ComponentProps<typeof Link>, 'LinkWrapper'>;
}

interface BulletLinkProps {
currentPath: string;
item: BulletLinkItem;
}

export function BulletLink({ currentPath, item, ...rest }: BulletLinkProps) {
const isActive = currentPath === item.path;

return (
Expand All @@ -77,19 +93,11 @@ export function BulletLink({ currentPath, item, ...rest }) {
href={item.path}
LinkWrapper={item.LinkWrapper}
tertiary={!isActive}
{...rest}
>
<Bullet isActive={isActive} />
{item.title}
</StyledBulletLink>
</BulletLinkWrapper>
);
}

BulletLink.propTypes = {
currentPath: PropTypes.string.isRequired,
item: PropTypes.shape({
LinkWrapper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
path: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
}).isRequired,
};
38 changes: 0 additions & 38 deletions src/components/table-of-contents/ItemLink.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import React from 'react';
import { ItemLink } from './ItemLink';

export default {
Expand Down
37 changes: 37 additions & 0 deletions src/components/table-of-contents/ItemLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import styled from 'styled-components';
import { MenuLink } from './MenuLink';
import { LinkProps } from '../Link';

const ItemLinkWrapper = styled.li`
list-style-type: none;

&:last-of-type ${MenuLink} {
margin-bottom: 0;
}
`;

export interface LinkItem extends Pick<LinkProps, 'LinkWrapper'> {
path: string;
title: string;
}

interface ItemLinkProps {
currentPath: string;
item: LinkItem;
}

export function ItemLink({ currentPath, item }: ItemLinkProps) {
return (
<ItemLinkWrapper>
<MenuLink
isActive={currentPath === item.path}
href={item.path}
LinkWrapper={item.LinkWrapper}
tertiary
>
{item.title}
</MenuLink>
</ItemLinkWrapper>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import React from 'react';
import { MenuLink } from './MenuLink';

export default {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import React from 'react';
import styled from 'styled-components';
import { color, typography } from '../shared/styles';
import { typography } from '../shared/styles';
import { Link } from '../Link';

export const MenuLink = styled(({ isActive, ...rest }) => <Link {...rest} />)`
type MenuLinkProps = React.ComponentProps<typeof Link> & {
isActive?: boolean;
};

export const MenuLink = styled(({ isActive, ...rest }) => <Link {...rest} tertiary={!isActive} />)<
MenuLinkProps
>`
outline: none;
color: ${(props) => (props.isActive ? color.secondary : color.darker)};
font-weight: ${(props) => (props.isActive ? typography.weight.bold : typography.weight.regular)};
line-height: 24px;
text-align: left;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,121 +1,172 @@
import React, { useState } from 'react';
import { TableOfContents } from './TableOfContents';
import { TableOfContents, TableOfContentsProps } from './TableOfContents';
import { Item, ItemType } from './TableOfContentsItems';
// @ts-ignore
import { StoryLinkWrapper } from '../StoryLinkWrapper';

export default {
title: 'TableOfContents',
component: TableOfContents,
decorators: [
(storyFn) => <div style={{ width: 240, outline: '1px dotted grey' }}>{storyFn()}</div>,
(storyFn: any) => <div style={{ width: 240, outline: '1px dotted grey' }}>{storyFn()}</div>,
],
};

const items = [
const items: Item[] = [
{
title: 'Get Started',
type: 'menu',
type: ItemType.MENU,
children: [
{
path: '/introduction',
title: 'Introduction',
type: 'bullet-link',
type: ItemType.BULLET_LINK,
},
{
path: '/setup',
title: 'Setup',
type: 'bullet-link',
type: ItemType.BULLET_LINK,
},
{
path: '/write-a-story',
title: 'Write a story',
type: 'bullet-link',
type: ItemType.BULLET_LINK,
},
],
},
{
title: '😀 Customize',
type: 'menu',
type: ItemType.MENU,
children: [
{
title: 'User interface',
type: 'menu',
type: ItemType.MENU,
children: [
{
path: '/features-and-behavior',
title: 'features and behavior dolor sit amet consectatur',
type: 'link',
type: ItemType.LINK,
},
{
path: '/theming',
title: 'Theming',
type: 'link',
type: ItemType.LINK,
},
],
},
{
title: 'Head tags',
type: 'link',
type: ItemType.LINK,
path: '/head-tags',
},
{
title: 'Body tags',
type: 'link',
type: ItemType.LINK,
path: '/body-tags',
},
],
},
{
title: '⚙️ Super long root heading that is very long',
type: 'menu',
type: ItemType.MENU,
children: [
{
title: 'Verbose menu link that is extremely lengthy',
type: 'menu',
type: ItemType.MENU,
children: [
{
path: '/lorem-ipsum',
title: 'Lorem ipsum dolor sit amet consectatur',
type: 'link',
type: ItemType.LINK,
},
{
path: '/theming',
title: 'Theming',
type: 'link',
type: ItemType.LINK,
},
],
},
{
title: 'Head tags',
type: 'link',
type: ItemType.LINK,
path: '/head-tags',
},
{
title: 'Body tags',
type: 'link',
type: ItemType.LINK,
path: '/body-tags',
},
],
},
];

const findPaths = (pathItems) => pathItems.flatMap((item) => item.path || findPaths(item.children));
const findPaths = (pathItems: Item[]) =>
// @ts-ignore
pathItems.flatMap((item: Item) => item.path || findPaths(item.children));
const paths = findPaths(items);

export const Basic = (args) => <TableOfContents {...args} />;
Basic.args = { currentPath: paths[0], items };
export const BasicFlat = (args: TableOfContentsProps) => <TableOfContents {...args} />;
BasicFlat.args = {
currentPath: '/essentials',
items: [
{
title: '⭐️ Popular',
path: '/popular',
type: ItemType.LINK,
},
{
title: '🧩 Essentials',
path: '/essentials',
type: ItemType.LINK,
},
{
title: '🛠 Code',
path: '/code',
type: ItemType.LINK,
},
{
title: '⚡️ Data & state',
path: '/Data & state',
type: ItemType.LINK,
},
{
title: '💅 Style',
path: '/style',
type: ItemType.LINK,
},
{
title: '🎨 Design',
path: '/design',
type: ItemType.LINK,
},
{
title: '⚙️ Appearance',
path: '/appearance',
type: ItemType.LINK,
},
{
title: '🗄 Organize',
path: '/organize',
type: ItemType.LINK,
},
],
};

export const BasicNested = (args: TableOfContentsProps) => <TableOfContents {...args} />;
BasicNested.args = { currentPath: paths[0], items };

export const NestedActivePath = Basic.bind();
export const NestedActivePath = BasicNested.bind({});
NestedActivePath.args = { currentPath: '/features-and-behavior', items };

const addLinkWrappers = (itemsToCompose) =>
itemsToCompose.map((item) => {
if (item.type === 'link' || item.type === 'bullet-link')
const addLinkWrappers = (itemsToCompose: Item[]): Item[] =>
itemsToCompose.map((item: Item) => {
if (item.type === ItemType.LINK || item.type === ItemType.BULLET_LINK)
return { ...item, LinkWrapper: StoryLinkWrapper };
if (item.children) return { ...item, children: addLinkWrappers(item.children) };
return item;
});
const itemsWithLinkWrappers = addLinkWrappers(items);
export const LinkWrappers = Basic.bind();
export const LinkWrappers = BasicNested.bind({});
LinkWrappers.args = { currentPath: paths[0], items: itemsWithLinkWrappers };

export const WithOpenControls = () => (
Expand Down
Loading