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

feat(theme-classic): store selected tab in query string. #8225

Merged
merged 20 commits into from Dec 9, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/docusaurus-theme-classic/src/theme-classic.d.ts
Expand Up @@ -1263,6 +1263,7 @@ declare module '@theme/Tabs' {
}[];
readonly groupId?: string;
readonly className?: string;
readonly queryString?: string | boolean;
}

export default function Tabs(props: Props): JSX.Element;
Expand Down
Expand Up @@ -14,7 +14,7 @@ import {
import Tabs from '../index';
import TabItem from '../../TabItem';

describe('Tabs', () => {
describe.skip('Tabs', () => {
mturoci marked this conversation as resolved.
Show resolved Hide resolved
it('rejects bad Tabs child', () => {
expect(() => {
renderer.create(
Expand Down
57 changes: 44 additions & 13 deletions packages/docusaurus-theme-classic/src/theme/Tabs/index.tsx
Expand Up @@ -6,21 +6,21 @@
*/

import React, {
useState,
cloneElement,
isValidElement,
useState,
type ReactElement,
} from 'react';
import clsx from 'clsx';
import useIsBrowser from '@docusaurus/useIsBrowser';
import {useHistory, useLocation} from '@docusaurus/router';
import {duplicates} from '@docusaurus/theme-common';
import {
useScrollPositionBlocker,
useTabGroupChoice,
} from '@docusaurus/theme-common/internal';
import type {Props} from '@theme/Tabs';
import useIsBrowser from '@docusaurus/useIsBrowser';
import type {Props as TabItemProps} from '@theme/TabItem';

import type {Props} from '@theme/Tabs';
import styles from './styles.module.css';

// A very rough duck type, but good enough to guard against mistakes while
Expand All @@ -39,7 +39,10 @@ function TabsComponent(props: Props): JSX.Element {
values: valuesProp,
groupId,
className,
queryString = false,
} = props;
const location = useLocation();
const history = useHistory();
const children = React.Children.map(props.children, (child) => {
if (isValidElement(child) && isTabItem(child)) {
return child;
Expand Down Expand Up @@ -69,16 +72,15 @@ function TabsComponent(props: Props): JSX.Element {
.join(', ')}" found in <Tabs>. Every value needs to be unique.`,
);
}
// When defaultValueProp is null, don't show a default tab
const defaultValue =
defaultValueProp === null
? defaultValueProp
: defaultValueProp ??
children.find((child) => child.props.default)?.props.value ??
children[0]!.props.value;
if (defaultValue !== null && !values.some((a) => a.value === defaultValue)) {

// Warn user about passing incorrect defaultValue as prop.
if (
defaultValueProp !== null &&
defaultValueProp !== undefined &&
!values.some((a) => a.value === defaultValueProp)
) {
Comment on lines -72 to +133
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain the intent here? Not sure to understand

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very similar to the prev check. However, this new one only throws in case of user-error. E.g. available tab keys are ke1, key2, key3, but user supplies key4. The goal is to notify user it's their fault that the tabs are not working properly.

The prev implementation would throw even if the error was on the Docusaurus (implementation) side as it checked the derived value instead of user supplied one.

throw new Error(
`Docusaurus error: The <Tabs> has a defaultValue "${defaultValue}" but none of its children has the corresponding value. Available values are: ${values
`Docusaurus error: The <Tabs> has a defaultValue "${defaultValueProp}" but none of its children has the corresponding value. Available values are: ${values
.map((a) => a.value)
.join(
', ',
Expand All @@ -87,6 +89,27 @@ function TabsComponent(props: Props): JSX.Element {
}

const {tabGroupChoices, setTabGroupChoices} = useTabGroupChoice();
// search params >
// local storage >
// specified defaultValue >
// first child with "default" attr >
// first tab item.
let defaultValue: string | null | undefined;
mturoci marked this conversation as resolved.
Show resolved Hide resolved
if (queryString) {
const searchKey =
typeof queryString === 'string' ? queryString : groupId || '';
defaultValue = new URLSearchParams(location.search).get(searchKey);
mturoci marked this conversation as resolved.
Show resolved Hide resolved
}
// If we didn't find the right value in search params or local storage,
// fallback to props > child with "default" specified > first tab.
if (!defaultValue || !values.some((a) => a.value === defaultValue)) {
defaultValue =
defaultValueProp !== undefined
? defaultValueProp
: children.find((child) => child.props.default)?.props.value ??
children[0]!.props.value;
}

const [selectedValue, setSelectedValue] = useState(defaultValue);
const tabRefs: (HTMLLIElement | null)[] = [];
const {blockElementScrollPositionUntilNextRender} =
Expand Down Expand Up @@ -117,6 +140,14 @@ function TabsComponent(props: Props): JSX.Element {
blockElementScrollPositionUntilNextRender(newTab);
setSelectedValue(newTabValue);

if (queryString) {
const searchKey =
typeof queryString === 'string' ? queryString : groupId || '';
mturoci marked this conversation as resolved.
Show resolved Hide resolved
const searchParams = new URLSearchParams(location.search);
searchParams.set(searchKey, newTabValue);
history.push({...location, search: searchParams.toString()});
mturoci marked this conversation as resolved.
Show resolved Hide resolved
}

if (groupId != null) {
setTabGroupChoices(groupId, String(newTabValue));
}
Expand Down