Skip to content

Commit

Permalink
feat(toc): scroll the active item into view on mount (#173)
Browse files Browse the repository at this point in the history
* chore: add fake navigation to the ToC story

* feat: scroll active item into view on initial render
  • Loading branch information
Marcell Toth authored Jun 23, 2020
1 parent 1e11c32 commit e0459e6
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 11 deletions.
15 changes: 14 additions & 1 deletion src/TableOfContents/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,14 @@ function DefaultRowImpl<T extends TableOfContentsItem>({ item, isExpanded, toggl
const isActive = item.isActive && !showSkeleton;
const isDisabled = item.isDisabled;

const holderCallbackRef = React.useCallback((e: HTMLDivElement) => {
if (e && isActive) {
e.scrollIntoView({ block: 'center' });
}
// we only want this on initial render
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

let icon = item.icon;
if (item.activeIcon && (isActive || isSelected)) {
icon = item.activeIcon;
Expand Down Expand Up @@ -289,7 +297,12 @@ function DefaultRowImpl<T extends TableOfContentsItem>({ item, isExpanded, toggl
) : null;

return (
<div onClick={onClick} className={outerClassName} style={{ marginLeft: (item.depth ?? 0) * 24 }}>
<div
onClick={onClick}
className={outerClassName}
style={{ marginLeft: (item.depth ?? 0) * 24 }}
ref={holderCallbackRef}
>
<div className={cn('-ml-px', innerClassName, { 'opacity-75': isDisabled })}>
<div className="flex flex-row items-center">
{icon && (
Expand Down
50 changes: 40 additions & 10 deletions src/__stories__/TableOfContents/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ const styles = {
storiesOf('TableOfContents', module)
.addDecorator(withKnobs)
.add('studio /w custom RowComponent', () => {
return (
<div style={styles}>
<TableOfContents className="h-full" contents={studioContents} rowComponent={RowComponent} />
</div>
);
return <CustomComponentStory />;
})
.add('studio without rowComponent', () => {
return (
Expand Down Expand Up @@ -52,8 +48,42 @@ const MobileStory = () => {
);
};

const RowComponent: RowComponentType<ITableOfContentsLink> = props => (
<a href={props.item.to}>
<DefaultRow {...props} />
</a>
);
const NavigationContext = React.createContext({ path: '', setPath: (_: string) => {} });

const CustomComponentStory = () => {
const [path, setPath] = React.useState('/reference/petstore/openapi.v1.yaml/paths/~1pets~1{petId}/get');

const contextValue = React.useMemo(() => ({ path, setPath }), [path, setPath]);

const contentsWithDynamicIsActive = React.useMemo(() => {
return studioContents.map(c => ({ ...c, isActive: c.to === path }));
}, [path]);

return (
<NavigationContext.Provider value={contextValue}>
<div style={styles}>
<TableOfContents className="h-full" contents={contentsWithDynamicIsActive} rowComponent={RowComponent} />
</div>
</NavigationContext.Provider>
);
};

const RowComponent: RowComponentType<ITableOfContentsLink> = props => {
const { setPath } = React.useContext(NavigationContext);

const handleClick = React.useCallback(
(e: React.MouseEvent) => {
if (props.item.to) {
setPath(props.item.to);
}
e.preventDefault();
},
[props.item.to, setPath],
);

return (
<a href={props.item.to} onClick={handleClick}>
<DefaultRow {...props} />
</a>
);
};

0 comments on commit e0459e6

Please sign in to comment.