From dc30c388535448316fc0353540dc959ae59f6354 Mon Sep 17 00:00:00 2001 From: Roman Vozbrannyi Date: Wed, 8 Feb 2023 15:43:39 +0100 Subject: [PATCH] feat: tab as nav link --- src/components/tabs/tab/index.tsx | 60 +++++++-- src/components/tabs/tablist/index.tsx | 10 ++ stories/tabs/Tabs.stories.tsx | 173 ++++++++++++++------------ stories/tabs/Tabs.tsx | 22 ---- 4 files changed, 153 insertions(+), 112 deletions(-) delete mode 100644 stories/tabs/Tabs.tsx diff --git a/src/components/tabs/tab/index.tsx b/src/components/tabs/tab/index.tsx index 6fe1db21..c2a63f34 100644 --- a/src/components/tabs/tab/index.tsx +++ b/src/components/tabs/tab/index.tsx @@ -7,48 +7,90 @@ import { useTabContext, getTabPanelId } from '../tabContext'; import { StyledTab } from './style'; export interface TabProps extends React.ButtonHTMLAttributes { + /** + * The HTML element to render the tab as. + */ + as?: React.ElementType; + /** + * The content of the tab. + */ children: React.ReactNode; + /** + * The class name of the tab. + * @default '' + * */ className?: string; + /** + * Whether the tab is disabled. + * @default false + * */ disabled?: boolean; + /** + * The icon to display. + * */ icon?: IconType | keyof typeof icons.sm; + /** + * The class name of the icon. + * @default '' + * */ iconClassName?: string; + /** + * The index of the tab. + * */ index: number; + /** + * The style of the tab. + * */ selected?: boolean; + /** + * The `to` prop for the NavLink. + * */ + to?: string; } const BaseTab = React.forwardRef, TabProps>( ( - { index, className, iconClassName, icon, style, disabled, selected, onClick, children, ...props }: TabProps, + { as, index, className, iconClassName, icon, style, disabled, selected, onClick, children, ...props }: TabProps, ref, ): JSX.Element => { const tabRef = React.useRef(); const combinedRef = useCombinedRefs(tabRef, ref); - const context = useTabContext()!; - const { indexSelected, setIndexSelected } = context; + const context = useTabContext(); React.useEffect(() => { - if (selected) setIndexSelected(index); + if (context && selected) { + const { setIndexSelected } = context; + setIndexSelected(index); + } }, [selected]); - const isSelected = indexSelected === index; - const handleClick = (event): void => { - setIndexSelected(index); + if (context) { + const { setIndexSelected } = context; + setIndexSelected(index); + } + if (onClick) onClick(event); }; + const isSelected = context ? context.indexSelected === index : undefined; + + const tabIndex = isSelected ? 0 : -1; + return ( {icon && diff --git a/src/components/tabs/tablist/index.tsx b/src/components/tabs/tablist/index.tsx index 97f02c54..2c0b93b3 100644 --- a/src/components/tabs/tablist/index.tsx +++ b/src/components/tabs/tablist/index.tsx @@ -5,8 +5,18 @@ import { useCombinedRefs } from 'utils/hooks/combinedrefs'; import { StyledTablist } from './style'; export interface TablistProps { + /** + * Whether to automatically activate the next tab when the user presses the left or right arrow keys. + * @default false + * */ autoActivation?: boolean; + /** + * The content of the tablist. + * */ children: React.ReactNode; + /** + * The class name of the tablist. + * */ className?: string; } diff --git a/stories/tabs/Tabs.stories.tsx b/stories/tabs/Tabs.stories.tsx index 7a17f0fc..b063ad18 100644 --- a/stories/tabs/Tabs.stories.tsx +++ b/stories/tabs/Tabs.stories.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; import { ComponentStory, ComponentMeta } from '@storybook/react'; -import { Tab, BookmarkTab } from 'components'; +import { Tab, BookmarkTab, TabContext, Tablist, Tabpanel } from 'components'; +import { BrowserRouter, NavLink, Route } from 'react-router-dom'; import docs from './readme.md'; -import Tabs from './Tabs'; export default { title: 'Components/Tabs', - component: Tabs, + component: Tablist, parameters: { docs: { description: { @@ -15,92 +15,103 @@ export default { }, controls: { disabled: true }, }, -} as ComponentMeta; + subcomponents: { + Tab, + BookmarkTab, + }, +} as ComponentMeta; -const Template: ComponentStory = (args) => ; +const Template: ComponentStory = (args) => ( + + + + Tab 1 + + + Tab 2 + + + Tab 3 + + + Tab panel 1 + Tab panel 2 + Tab panel 3 + +); export const Default = Template.bind({}); -Default.args = { - style: { padding: '10px' }, - tabComponent: Tab, - tabs: [ - { index: 0, label: 'Tab 1', icon: 'settings' }, - { index: 1, label: 'Tab 2', icon: 'settings' }, - { index: 2, label: 'Tab 3', icon: 'settings' }, - ], - tabpanels: [ - { index: 0, content: 'Tab panel 1' }, - { index: 1, content: 'Tab panel 2' }, - { index: 2, content: 'Tab panel 3' }, - ], +export const AutoActivation = Template.bind({}); +AutoActivation.args = { + autoActivation: true, }; -export const DisabledTab = Template.bind({}); +const DisabledTabTemplate: ComponentStory = (args) => ( + + + + Tab 1 + + + Tab 2 + + + Tab 3 + + + Tab panel 1 + Tab panel 2 + Tab panel 3 + +); -DisabledTab.args = { - style: { padding: '10px' }, - tabComponent: Tab, - tabs: [ - { index: 0, label: 'Tab 1', icon: 'settings' }, - { index: 1, label: 'Tab 2', icon: 'settings' }, - { index: 2, label: 'Tab 3', icon: 'settings', disabled: true }, - ], - tabpanels: [ - { index: 0, content: 'Tab panel 1' }, - { index: 1, content: 'Tab panel 2' }, - { index: 2, content: 'Tab panel 3' }, - ], -}; +export const DisabledTab = DisabledTabTemplate.bind({}); -export const PreselectedTab = Template.bind({}); +const BookmarkTabsTemplate: ComponentStory = (args) => ( + + + + Tab 1 + + + Tab 2 + + + Tab 3 + + + Tab panel 1 + Tab panel 2 + Tab panel 3 + +); -PreselectedTab.args = { - style: { padding: '10px' }, - tabComponent: Tab, - tabs: [ - { index: 0, label: 'Tab 1', icon: 'settings' }, - { index: 1, label: 'Tab 2', icon: 'settings' }, - { index: 2, label: 'Tab 3', icon: 'settings', selected: true }, - ], - tabpanels: [ - { index: 0, content: 'Tab panel 1' }, - { index: 1, content: 'Tab panel 2' }, - { index: 2, content: 'Tab panel 3' }, - ], -}; +export const BookmarkTabs = BookmarkTabsTemplate.bind({}); -export const BookmarkTabs = Template.bind({}); +const NavLinkTabsTemplate: ComponentStory = (args) => ( + + + + Tab 1 + + + Tab 2 + + + Tab 3 + + + +
Route 1
+
+ +
Route 2
+
+ +
Route 3
+
+
+); -BookmarkTabs.args = { - style: { padding: '10px', backgroundColor: '#ECEFF1' }, - tabComponent: BookmarkTab, - tabs: [ - { index: 0, label: 'Tab 1' }, - { index: 1, label: 'Tab 2' }, - { index: 2, label: 'Tab 3' }, - ], - tabpanels: [ - { index: 0, content: 'Tab panel 1' }, - { index: 1, content: 'Tab panel 2' }, - { index: 2, content: 'Tab panel 3' }, - ], -}; - -export const AutoActivation = Template.bind({}); - -AutoActivation.args = { - style: { padding: '10px', backgroundColor: '#ECEFF1' }, - tabComponent: BookmarkTab, - autoActivation: true, - tabs: [ - { index: 0, label: 'Tab 1' }, - { index: 1, label: 'Tab 2' }, - { index: 2, label: 'Tab 3' }, - ], - tabpanels: [ - { index: 0, content: 'Tab panel 1' }, - { index: 1, content: 'Tab panel 2' }, - { index: 2, content: 'Tab panel 3' }, - ], -}; +export const NavLinkTabs = NavLinkTabsTemplate.bind({}); diff --git a/stories/tabs/Tabs.tsx b/stories/tabs/Tabs.tsx deleted file mode 100644 index 1a4d7b2e..00000000 --- a/stories/tabs/Tabs.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react'; -import { TabContext, Tablist, Tabpanel } from 'components'; - -const Tabs = ({ tabs, tabpanels, autoActivation, tabComponent }) => { - return ( - - - {tabs.map((tab) => { - const { index, label, icon, selected, disabled } = tab; - return React.createElement(tabComponent, { index, icon, selected, disabled }, label); - })} - - {tabpanels.map((panel) => ( - - {panel.content} - - ))} - - ); -}; - -export default Tabs;