fix(theme-classic): allow tabs with number as value (#5732)

This commit is contained in:
Joshua Chen 2021-10-21 18:43:56 +08:00 committed by GitHub
parent 2398943c17
commit c2eda4aac1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 53 deletions

View file

@ -41,6 +41,6 @@ module.exports = {
// Maybe point to a fixture? // Maybe point to a fixture?
'@generated/.*': '<rootDir>/jest/emptyModule.js', '@generated/.*': '<rootDir>/jest/emptyModule.js',
// TODO maybe use "projects" + multiple configs if we plan to add tests to another theme? // TODO maybe use "projects" + multiple configs if we plan to add tests to another theme?
'@theme/(.*)': '@docusaurus/theme-classic/lib-next/theme/$1', '@theme/(.*)': '@docusaurus/theme-classic/src/theme/$1',
}, },
}; };

View file

@ -9,6 +9,8 @@ import React from 'react';
import renderer from 'react-test-renderer'; import renderer from 'react-test-renderer';
import Tabs from '../index'; import Tabs from '../index';
import TabItem from '../../TabItem'; import TabItem from '../../TabItem';
import UserPreferencesProvider from '@theme/UserPreferencesProvider';
import {ScrollControllerProvider} from '@docusaurus/theme-common';
describe('Tabs', () => { describe('Tabs', () => {
test('Should reject bad Tabs child', () => { test('Should reject bad Tabs child', () => {
@ -54,53 +56,74 @@ describe('Tabs', () => {
test('Should accept valid Tabs config', () => { test('Should accept valid Tabs config', () => {
expect(() => { expect(() => {
renderer.create( renderer.create(
<> <ScrollControllerProvider>
<Tabs> <UserPreferencesProvider>
<TabItem value="v1">Tab 1</TabItem> <Tabs>
<TabItem value="v2">Tab 2</TabItem> <TabItem value="v1">Tab 1</TabItem>
</Tabs> <TabItem value="v2">Tab 2</TabItem>
<Tabs> </Tabs>
<TabItem value="v1">Tab 1</TabItem> <Tabs>
<TabItem value="v2" default> <TabItem value="v1">Tab 1</TabItem>
Tab 2 <TabItem value="v2" default>
</TabItem> Tab 2
</Tabs> </TabItem>
<Tabs defaultValue="v1"> </Tabs>
<TabItem value="v1" label="V1"> <Tabs defaultValue="v1">
Tab 1 <TabItem value="v1" label="V1">
</TabItem> Tab 1
<TabItem value="v2" label="V2"> </TabItem>
Tab 2 <TabItem value="v2" label="V2">
</TabItem> Tab 2
</Tabs> </TabItem>
<Tabs </Tabs>
defaultValue="v1" <Tabs
values={[ defaultValue="v1"
{value: 'v1', label: 'V1'}, values={[
{value: 'v2', label: 'V2'}, {value: 'v1', label: 'V1'},
]}> {value: 'v2', label: 'V2'},
<TabItem>Tab 1</TabItem> ]}>
<TabItem>Tab 2</TabItem> <TabItem value="v1">Tab 1</TabItem>
</Tabs> <TabItem value="v2">Tab 2</TabItem>
<Tabs </Tabs>
defaultValue={null} <Tabs
values={[ defaultValue={null}
{value: 'v1', label: 'V1'}, values={[
{value: 'v2', label: 'V2'}, {value: 'v1', label: 'V1'},
]}> {value: 'v2', label: 'V2'},
<TabItem>Tab 1</TabItem> ]}>
<TabItem>Tab 2</TabItem> <TabItem value="v1">Tab 1</TabItem>
</Tabs> <TabItem value="v2">Tab 2</TabItem>
<Tabs defaultValue={null}> </Tabs>
<TabItem value="v1" label="V1"> <Tabs defaultValue={null}>
Tab 1 <TabItem value="v1" label="V1">
</TabItem> Tab 1
<TabItem value="v2" label="V2"> </TabItem>
Tab 2 <TabItem value="v2" label="V2">
</TabItem> Tab 2
</Tabs> </TabItem>
</>, </Tabs>
</UserPreferencesProvider>
</ScrollControllerProvider>,
); );
}).toMatchInlineSnapshot(`[Function]`); // This is just a check that the function returns. We don't care about the actual DOM. }).not.toThrow(); // TODO Better Jest infrastructure to mock the Layout
});
// https://github.com/facebook/docusaurus/issues/5729
test('Should accept dynamic Tabs with number values', () => {
expect(() => {
const tabs = ['Apple', 'Banana', 'Carrot'];
renderer.create(
<ScrollControllerProvider>
<UserPreferencesProvider>
<Tabs
values={tabs.map((t, idx) => ({label: t, value: idx}))}
defaultValue={0}>
{tabs.map((t, idx) => (
<TabItem value={idx}>{t}</TabItem>
))}
</Tabs>
</UserPreferencesProvider>
</ScrollControllerProvider>,
);
}).not.toThrow();
}); });
}); });

View file

@ -25,7 +25,7 @@ import styles from './styles.module.css';
// A very rough duck type, but good enough to guard against mistakes while // A very rough duck type, but good enough to guard against mistakes while
// allowing customization // allowing customization
function isTabItem(comp: ReactElement): comp is ReactElement<TabItemProps> { function isTabItem(comp: ReactElement): comp is ReactElement<TabItemProps> {
return typeof comp.props.value === 'string'; return typeof comp.props.value !== 'undefined';
} }
function TabsComponent(props: Props): JSX.Element { function TabsComponent(props: Props): JSX.Element {
@ -51,10 +51,7 @@ function TabsComponent(props: Props): JSX.Element {
); );
}); });
const values = const values =
valuesProp ?? valuesProp ?? children.map(({props: {value, label}}) => ({value, label}));
children.map(({props: {value, label}}) => {
return {value, label};
});
const dup = duplicates(values, (a, b) => a.value === b.value); const dup = duplicates(values, (a, b) => a.value === b.value);
if (dup.length > 0) { if (dup.length > 0) {
throw new Error( throw new Error(