Skip to content

Commit

Permalink
Feat/UI contained tabs (#2291)
Browse files Browse the repository at this point in the history
* feat: add tab spacing and border behaviour as well as background color

* fix: fix border for items

* fix: remove border logic and rename contained story

* chore: add changeset
  • Loading branch information
r-mulder authored Jun 20, 2024
1 parent 16f0b17 commit 6d9900e
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/wet-bobcats-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kadena/react-ui": patch
---

add contained prop to tabs component, this will make the content the same color as the tab
2 changes: 1 addition & 1 deletion packages/libs/react-ui/src/components/Tabs/Tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function Tab<T>({
className={classNames(
className,
tabItemClass({
inverse,
inverse: inverse.toString(),
borderPosition,
size: isCompact ? 'compact' : 'default',
}),
Expand Down
18 changes: 14 additions & 4 deletions packages/libs/react-ui/src/components/Tabs/Tabs.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export const tabListClass = style({
minWidth: '100%',
});

export const tabListGap = style({
gap: '2px',
});

// Prevent button from increasing the tab size and having the outline conflict with label
globalStyle(`${tabListClass} button`, {
paddingBlock: 0,
Expand All @@ -76,23 +80,23 @@ export const tabItemClass = recipe({
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
outline: 'none',
paddingBlock: token('size.n2'),
paddingInline: token('size.n4'),
gap: token('size.n2'),
backgroundColor: 'transparent',
backgroundColor: token('color.background.base.default'),
color: token('color.text.base.default'),
outline: 'none',
zIndex: 3,
minWidth: 'fit-content',
borderBlock: `2px solid transparent`,
borderBlockStart: `2px solid transparent`,
borderBlockEnd: `2px solid ${token('color.border.base.subtle')}`,
borderTopLeftRadius: token('radius.xs'),
borderTopRightRadius: token('radius.xs'),
transition:
'background-color .4s ease, color .4s, border-bottom .4s ease-in-out',
whiteSpace: 'nowrap',
selectors: {
'&[data-selected="true"]': {
backgroundColor: token('color.background.base.@active'),
color: token('color.text.base.@active'),
},
'&[data-hovered="true"]': {
Expand Down Expand Up @@ -204,3 +208,9 @@ export const closeButtonClass = style({
},
},
});

export const containedTabContent = style({
backgroundColor: token('color.background.base.@active'),
margin: 0,
paddingBlock: token('size.n4'),
});
29 changes: 29 additions & 0 deletions packages/libs/react-ui/src/components/Tabs/Tabs.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ const meta: Meta<ITabsProps<object>> = {
type: 'boolean',
},
},
isContained: {
control: {
type: 'boolean',
},
},
},
};

Expand Down Expand Up @@ -152,6 +157,30 @@ export const DefaultSelectedTabsStory: Story = {
},
};

export const ContainedVariant: Story = {
name: 'Contained scrollable Tabs with defaultSelectedTab',
args: {
['aria-label']: 'generic tabs story',
defaultSelectedKey: ExampleManyTabs[5].title,
isContained: true,
},
render: (props) => {
return (
<Tabs
{...props}
aria-label={props['aria-label']}
defaultSelectedKey={props.defaultSelectedKey}
>
{ExampleManyTabs.map((tab) => (
<TabItem key={tab.title} title={tab.title}>
{tab.content}
</TabItem>
))}
</Tabs>
);
},
};

export const ControlledTabsStory: Story = {
name: 'Tabs',
render: () => {
Expand Down
17 changes: 14 additions & 3 deletions packages/libs/react-ui/src/components/Tabs/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import type { Node as ITabNode } from 'react-stately';
import { Item as TabItem, useTabListState } from 'react-stately';
import { Tab } from './Tab';
import { TabPanel } from './TabPanel';
import { scrollContainer, tabListClass, tabsContainerClass } from './Tabs.css';
import {
containedTabContent,
scrollContainer,
tabListClass,
tabListGap,
tabsContainerClass,
} from './Tabs.css';
import { TabsPagination } from './TabsPagination';

export { ITabNode, TabItem };
Expand All @@ -23,15 +29,17 @@ export interface ITabsProps<T>
onClose?: (item: ITabNode<T>) => void;
isCompact?: boolean;
tabPanelClassName?: string;
isContained?: boolean;
}

export function Tabs<T extends object>({
className,
borderPosition = 'bottom',
borderPosition = 'top',
inverse = false,
onClose,
isCompact,
tabPanelClassName,
isContained,
...props
}: ITabsProps<T>): ReactNode {
const state = useTabListState(props);
Expand Down Expand Up @@ -79,6 +87,7 @@ export function Tabs<T extends object>({
<div className={scrollContainer} ref={scrollRef}>
<div
className={classNames(tabListClass, {
[tabListGap]: isContained,
focusVisible: isFocusVisible,
})}
{...mergeProps(tabListProps, focusProps)}
Expand All @@ -101,7 +110,9 @@ export function Tabs<T extends object>({
<TabPanel
key={state.selectedItem?.key}
state={state}
className={tabPanelClassName}
className={classNames(tabPanelClassName, {
[containedTabContent]: isContained,
})}
/>
</div>
);
Expand Down

0 comments on commit 6d9900e

Please sign in to comment.