-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
feat(theme-classic): store selected tab in query string. #8225
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
Changes from 2 commits
c621e42
634c0f2
52fa90b
04053c5
216119d
cabac96
80e978d
6e1fb03
7eb1334
66a3939
4213dc2
60813d8
5e38a1f
f8eb751
13aec5d
3d2a36f
0d48df6
5264f83
1bd6561
c2eca08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,16 @@ function isTabItem( | |
| return 'value' in comp.props; | ||
| } | ||
|
|
||
| const NON_GROUP_TAB_KEY = '__noGroup__'; | ||
| function getValueFromSearchParams(groupId = NON_GROUP_TAB_KEY): string | null { | ||
| if (typeof window === 'undefined') { | ||
| return null; // Ignore during SSR. | ||
| } | ||
| const searchParams = new URLSearchParams(window.location.search); | ||
| const prevSearchParams = searchParams.get('tabs'); | ||
| return prevSearchParams ? JSON.parse(prevSearchParams)[groupId] : null; | ||
mturoci marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| function TabsComponent(props: Props): JSX.Element { | ||
| const { | ||
| lazy, | ||
|
|
@@ -69,16 +79,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
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you explain the intent here? Not sure to understand
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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( | ||
| ', ', | ||
|
|
@@ -87,21 +96,37 @@ function TabsComponent(props: Props): JSX.Element { | |
| } | ||
|
|
||
| const {tabGroupChoices, setTabGroupChoices} = useTabGroupChoice(); | ||
| const [selectedValue, setSelectedValue] = useState(defaultValue); | ||
| const tabRefs: (HTMLLIElement | null)[] = []; | ||
| const {blockElementScrollPositionUntilNextRender} = | ||
| useScrollPositionBlocker(); | ||
|
|
||
| if (groupId != null) { | ||
| // search params > | ||
| // local storage > | ||
| // specified defaultValue > | ||
| // first child with "default" attr > | ||
| // first tab item. | ||
| let defaultValue: string | null | undefined = | ||
| getValueFromSearchParams(groupId); | ||
| if (!defaultValue && groupId != null) { | ||
| const relevantTabGroupChoice = tabGroupChoices[groupId]; | ||
| if ( | ||
| relevantTabGroupChoice != null && | ||
| relevantTabGroupChoice !== selectedValue && | ||
| relevantTabGroupChoice !== defaultValue && | ||
| values.some((value) => value.value === relevantTabGroupChoice) | ||
| ) { | ||
| setSelectedValue(relevantTabGroupChoice); | ||
| defaultValue = relevantTabGroupChoice; | ||
| } | ||
| } | ||
| // 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} = | ||
| useScrollPositionBlocker(); | ||
|
|
||
| const handleTabChange = ( | ||
| event: | ||
|
|
@@ -117,6 +142,18 @@ function TabsComponent(props: Props): JSX.Element { | |
| blockElementScrollPositionUntilNextRender(newTab); | ||
| setSelectedValue(newTabValue); | ||
|
|
||
| const searchParams = new URLSearchParams(window.location.search); | ||
| const prevSearchParams = searchParams.get('tabs'); | ||
| const prevVal = prevSearchParams ? JSON.parse(prevSearchParams) : null; | ||
| const newVal = {[groupId || NON_GROUP_TAB_KEY]: newTabValue}; | ||
| const url = new URL(window.location.origin + window.location.pathname); | ||
| searchParams.set( | ||
| 'tabs', | ||
| JSON.stringify(prevVal ? {...prevVal, ...newVal} : newVal), | ||
| ); | ||
| url.search = searchParams.toString(); | ||
mturoci marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| window.history.replaceState({}, '', url); | ||
mturoci marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| if (groupId != null) { | ||
| setTabGroupChoices(groupId, String(newTabValue)); | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.