Skip to content

Commit

Permalink
Remember Tab Selection (#3699)
Browse files Browse the repository at this point in the history
  • Loading branch information
tobias-tengler authored May 22, 2021
1 parent 8b204f7 commit 00931b1
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 53 deletions.
78 changes: 42 additions & 36 deletions website/src/components/doc-page/doc-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
ArticleTitle,
} from "../articles/article-elements";
import { ArticleSections } from "../articles/article-sections";
import { TabGroupProvider } from "../mdx/tabs/tab-groups";
import {
ArticleWrapper,
ArticleWrapperElement,
Expand Down Expand Up @@ -85,42 +86,47 @@ export const DocPage: FunctionComponent<DocPageProperties> = ({
}, [hasScrolled$]);

return (
<Container>
<DocPageNavigation
data={data}
selectedPath={path}
selectedProduct={selectedProduct}
selectedVersion={selectedVersion}
/>
<ArticleWrapper>
<ArticleContainer>
<Article>
{false && <DocPageLegacy />}
<ArticleHeader kind="doc">
<ResponsiveMenuWrapper>
<ResponsiveMenu ref={responsiveMenuRef}>
<Button onClick={handleToggleTOC} className="toc-toggle">
<ListAltIconSvg /> Table of contents
</Button>
<Button onClick={handleToggleAside} className="aside-toggle">
<NewspaperIconSvg /> About this article
</Button>
</ResponsiveMenu>
</ResponsiveMenuWrapper>
<ArticleTitle>{title}</ArticleTitle>
</ArticleHeader>
<ArticleContent>
<MDXRenderer>{body}</MDXRenderer>
</ArticleContent>
</Article>
{false && <ArticleComments data={data} path={path} title={title} />}
</ArticleContainer>
</ArticleWrapper>
<DocPageAside>
<DocPageCommunity data={data} originPath={originPath} />
<ArticleSections data={data.file!.childMdx!} />
</DocPageAside>
</Container>
<TabGroupProvider>
<Container>
<DocPageNavigation
data={data}
selectedPath={path}
selectedProduct={selectedProduct}
selectedVersion={selectedVersion}
/>
<ArticleWrapper>
<ArticleContainer>
<Article>
{false && <DocPageLegacy />}
<ArticleHeader kind="doc">
<ResponsiveMenuWrapper>
<ResponsiveMenu ref={responsiveMenuRef}>
<Button onClick={handleToggleTOC} className="toc-toggle">
<ListAltIconSvg /> Table of contents
</Button>
<Button
onClick={handleToggleAside}
className="aside-toggle"
>
<NewspaperIconSvg /> About this article
</Button>
</ResponsiveMenu>
</ResponsiveMenuWrapper>
<ArticleTitle>{title}</ArticleTitle>
</ArticleHeader>
<ArticleContent>
<MDXRenderer>{body}</MDXRenderer>
</ArticleContent>
</Article>
{false && <ArticleComments data={data} path={path} title={title} />}
</ArticleContainer>
</ArticleWrapper>
<DocPageAside>
<DocPageCommunity data={data} originPath={originPath} />
<ArticleSections data={data.file!.childMdx!} />
</DocPageAside>
</Container>
</TabGroupProvider>
);
};

Expand Down
2 changes: 1 addition & 1 deletion website/src/components/mdx/example-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const ExampleTabs: FunctionComponent & ExampleTabsComposition = ({
children,
}) => {
return (
<Tabs defaultValue={"annotation"}>
<Tabs defaultValue={"annotation"} groupId="code-style">
<Tabs.List>
<Tabs.Tab value="annotation">Annotation-based</Tabs.Tab>
<Tabs.Tab value="code">Code-first</Tabs.Tab>
Expand Down
87 changes: 87 additions & 0 deletions website/src/components/mdx/tabs/tab-groups.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, {
createContext,
FC,
useCallback,
useContext,
useEffect,
useState,
} from "react";

interface GroupMap {
readonly [groupId: string]: string | undefined;
}

type SetGroup = (groupId: string, value: string) => void;

interface TabGroupContextShape {
readonly groups: GroupMap;
readonly setGroup: SetGroup;
}

const TabGroupContext = createContext<TabGroupContextShape>({
groups: {},
setGroup: () => {},
});

const getLocalStorageKey = (groupId: string) => `tab-${groupId}`;

export const TabGroupProvider: FC = ({ children }) => {
const [groups, setGroups] = useState<GroupMap>({});

const handleSetGroup: SetGroup = (groupId, value) => {
if (groups[groupId] === value) {
return;
}

setGroups({ ...groups, [groupId]: value });

if (typeof window !== "undefined" && window.localStorage) {
window.localStorage.setItem(getLocalStorageKey(groupId), value);
}
};

return (
<TabGroupContext.Provider value={{ groups, setGroup: handleSetGroup }}>
{children}
</TabGroupContext.Provider>
);
};

type TabGroupReturn = [string, (value: string) => void];

export function useActiveTab(
defaultValue: string,
groupId?: string
): TabGroupReturn {
const { groups, setGroup } = useContext(TabGroupContext);
const [activeTab, setActiveTab] = useState(defaultValue);

useEffect(() => {
if (!groupId) {
return;
}

let value: string | null | undefined = groups[groupId];

if (!value && typeof window !== "undefined" && window.localStorage) {
value = window.localStorage.getItem(getLocalStorageKey(groupId));
}

if (value) {
setActiveTab(value);
}
}, [groups, groupId, setActiveTab]);

if (!groupId) {
return [activeTab, setActiveTab];
}

const setGroupTab = useCallback(
(value: string) => {
setGroup(groupId, value);
},
[groupId, setGroup]
);

return [activeTab, setGroupTab];
}
30 changes: 14 additions & 16 deletions website/src/components/mdx/tabs/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ import React, {
FunctionComponent,
ReactNode,
useContext,
useMemo,
useState,
} from "react";
import { Tab, TabProps } from "./tab";
import { Panel, PanelProps } from "./panel";
import { List } from "./list";
import { Panel, PanelProps } from "./panel";
import { Tab, TabProps } from "./tab";
import { useActiveTab } from "./tab-groups";

interface TabsContext {
activeTab: string;
Expand All @@ -24,26 +23,25 @@ export interface TabsComposition {
const TabsContext = createContext<TabsContext | undefined>(undefined);

export interface TabsProps {
defaultValue: string;
children: ReactNode;
readonly defaultValue: string;
readonly groupId?: string;
readonly children: ReactNode;
}

export const Tabs: FunctionComponent<TabsProps> & TabsComposition = ({
defaultValue,
groupId,
children,
}) => {
const [activeTab, setActiveTab] = useState(defaultValue);

const memoizedContextValue = useMemo(
() => ({
activeTab,
setActiveTab,
}),
[activeTab, setActiveTab]
);
const [activeTab, setActiveTab] = useActiveTab(defaultValue, groupId);

return (
<TabsContext.Provider value={memoizedContextValue}>
<TabsContext.Provider
value={{
activeTab,
setActiveTab,
}}
>
{children}
</TabsContext.Provider>
);
Expand Down

0 comments on commit 00931b1

Please sign in to comment.