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: tab as nav link #576

Merged
merged 1 commit into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 51 additions & 9 deletions src/components/tabs/tab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,90 @@ import { useTabContext, getTabPanelId } from '../tabContext';
import { StyledTab } from './style';

export interface TabProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
/**
* 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<React.RefObject<HTMLButtonElement>, 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 (
<StyledTab
activeClassName='selected'
as={as}
style={style}
role='tab'
disabled={disabled}
aria-selected={isSelected}
aria-controls={getTabPanelId(context, index)}
tabIndex={isSelected ? 0 : -1}
aria-controls={context ? getTabPanelId(context, index) : undefined}
className={cx(className, disabled && 'disabled', isSelected && 'selected')}
onClick={handleClick}
ref={combinedRef}
tabIndex={context ? tabIndex : undefined}
{...props}
>
{icon &&
Expand Down
10 changes: 10 additions & 0 deletions src/components/tabs/tablist/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
173 changes: 92 additions & 81 deletions stories/tabs/Tabs.stories.tsx
Original file line number Diff line number Diff line change
@@ -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: {
Expand All @@ -15,92 +15,103 @@ export default {
},
controls: { disabled: true },
},
} as ComponentMeta<typeof Tabs>;
subcomponents: {
Tab,
BookmarkTab,
},
} as ComponentMeta<typeof Tablist>;

const Template: ComponentStory<typeof Tabs> = (args) => <Tabs {...args} />;
const Template: ComponentStory<typeof Tablist> = (args) => (
<TabContext>
<Tablist {...args}>
<Tab index={0} icon='settings'>
Tab 1
</Tab>
<Tab index={1} icon='settings'>
Tab 2
</Tab>
<Tab index={2} icon='settings'>
Tab 3
</Tab>
</Tablist>
<Tabpanel index={0}>Tab panel 1</Tabpanel>
<Tabpanel index={1}>Tab panel 2</Tabpanel>
<Tabpanel index={2}>Tab panel 3</Tabpanel>
</TabContext>
);

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<typeof Tablist> = (args) => (
<TabContext>
<Tablist {...args}>
<Tab index={0} icon='settings'>
Tab 1
</Tab>
<Tab index={1} icon='settings' disabled>
Tab 2
</Tab>
<Tab index={2} icon='settings'>
Tab 3
</Tab>
</Tablist>
<Tabpanel index={0}>Tab panel 1</Tabpanel>
<Tabpanel index={1}>Tab panel 2</Tabpanel>
<Tabpanel index={2}>Tab panel 3</Tabpanel>
</TabContext>
);

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<typeof Tablist> = (args) => (
<TabContext>
<Tablist {...args}>
<BookmarkTab index={0} icon='settings'>
Tab 1
</BookmarkTab>
<BookmarkTab index={1} icon='settings'>
Tab 2
</BookmarkTab>
<BookmarkTab index={2} icon='settings'>
Tab 3
</BookmarkTab>
</Tablist>
<Tabpanel index={0}>Tab panel 1</Tabpanel>
<Tabpanel index={1}>Tab panel 2</Tabpanel>
<Tabpanel index={2}>Tab panel 3</Tabpanel>
</TabContext>
);

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<typeof Tablist> = (args) => (
<BrowserRouter>
<Tablist {...args}>
<Tab index={0} icon='settings' as={NavLink} to='/tab1'>
Tab 1
</Tab>
<Tab index={1} icon='settings' as={NavLink} to='/tab2'>
Tab 2
</Tab>
<Tab index={2} icon='settings' as={NavLink} to='/tab3'>
Tab 3
</Tab>
</Tablist>
<Route path='/tab1'>
<div>Route 1</div>
</Route>
<Route path='/tab2'>
<div>Route 2</div>
</Route>
<Route path='/tab3'>
<div>Route 3</div>
</Route>
</BrowserRouter>
);

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({});
22 changes: 0 additions & 22 deletions stories/tabs/Tabs.tsx

This file was deleted.