Skip to content

Commit

Permalink
[DST-111]: enhance styling tabs (#3250)
Browse files Browse the repository at this point in the history
* enhance tabs

* Test styles added via theme

* moving classNames to tab panel

* Add styles via theme

* Fixing tests

* Remove styling via `className` over components

* Add changes

* Fixing tests

* Create itchy-spies-greet.md

---------

Co-authored-by: Sebastian Sebald <[email protected]>
  • Loading branch information
OsamaAbdellateef and sebald authored Aug 16, 2023
1 parent 202ea43 commit 989f094
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 32 deletions.
8 changes: 8 additions & 0 deletions .changeset/itchy-spies-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@marigold/components": minor
"@marigold/system": minor
"@marigold/theme-b2b": minor
"@marigold/theme-core": minor
---

[DST-111]: enhance styling tabs
7 changes: 6 additions & 1 deletion packages/components/src/Tabs/Context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { createContext, useContext } from 'react';

export const TabContext = createContext<{
classNames: { tabs: string; tab: string };
classNames: {
container: string;
tabs: string;
tab: string;
tabpanel: string;
};
}>({} as any);
export const useTabContext = () => useContext(TabContext);
9 changes: 7 additions & 2 deletions packages/components/src/Tabs/TabPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React from 'react';
import { useRef } from 'react';
import { AriaTabPanelProps, useTabPanel } from '@react-aria/tabs';
import { TabListState } from '@react-stately/tabs';
import { useTabContext } from './Context';
import { cn } from '@marigold/system';

export interface TabPanelProps extends AriaTabPanelProps {
state: TabListState<object>;
Expand All @@ -10,9 +12,12 @@ export interface TabPanelProps extends AriaTabPanelProps {
export const TabPanel = ({ state, ...props }: TabPanelProps) => {
const ref = useRef(null);
const { tabPanelProps } = useTabPanel(props, state, ref);
const selectedItemProps = state.selectedItem?.props;
const { classNames } = useTabContext();

return (
<div {...tabPanelProps} ref={ref}>
{state.selectedItem?.props.children}
<div className={cn(classNames.tabpanel)} ref={ref} {...tabPanelProps}>
{selectedItemProps?.children}
</div>
);
};
56 changes: 38 additions & 18 deletions packages/components/src/Tabs/Tabs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ const theme: Theme = {
name: 'tabs test',
components: {
Tabs: {
container: cva('flex'),
tabpanel: cva('border-3 border-solid border-red-400'),
tabs: cva('mb-[10px]'),
tab: cva(
[
'min-h-[40px]',
'data-[hover]:text-tabs-tab-text data-[hover]:border-b-tabs-tab-hover data-[hover]:border-b-8 data-[hover]:border-solid',
' disabled:text-tabs-tab-disabled',
' selected:border-b-primary-600 selected:border-b-8 selected:border-solid ',
'selected:border-red-500 selected:border-b-8 selected:border-solid ',
],
{
variants: {
Expand Down Expand Up @@ -62,7 +61,7 @@ test('Supporting default size', () => {
</Tabs>
);
expect(screen.getByText('tab').className).toMatchInlineSnapshot(
`"flex cursor-pointer justify-center aria-disabled:cursor-not-allowed min-h-[40px] data-[hover]:text-tabs-tab-text data-[hover]:border-b-tabs-tab-hover data-[hover]:border-b-8 data-[hover]:border-solid disabled:text-tabs-tab-disabled selected:border-b-primary-600 selected:border-b-8 selected:border-solid px-2 pb-2 text-lg"`
`"flex cursor-pointer justify-center aria-disabled:cursor-not-allowed selected:border-red-500 selected:border-b-8 selected:border-solid px-2 pb-2 text-lg"`
);
});

Expand Down Expand Up @@ -97,9 +96,28 @@ test('set defaultValue via props in tabs', () => {
expect(screen.getByText('tab-2 content')).toBeVisible();
});

test('cursor indicates interactivity', () => {
test('open tabpanel when its tab controller is clicked', () => {
render(
<Tabs disabledKeys={['2']}>
<Tabs>
<Tabs.Item title="tab1" key="1">
tab-1 content
</Tabs.Item>
<Tabs.Item title="tab2" key="2">
tab-2 content
</Tabs.Item>
</Tabs>
);
const tab = screen.getByText('tab2');
fireEvent.click(tab);
expect(tab.className).toMatchInlineSnapshot(
`"flex cursor-pointer justify-center aria-disabled:cursor-not-allowed selected:border-red-500 selected:border-b-8 selected:border-solid px-2 pb-2 text-lg"`
);
expect(screen.getByText('tab-2 content')).toBeVisible();
});

test('allows styling "focus" state via theme', () => {
render(
<Tabs selectedKey={3} disabledKeys={['2']}>
<Tabs.Item key="1" title={'tab1'}>
tab-1 content
</Tabs.Item>
Expand All @@ -110,28 +128,30 @@ test('cursor indicates interactivity', () => {
);
const tabs = screen.getAllByRole('tab');
expect(tabs[0].className).toMatchInlineSnapshot(
`"flex cursor-pointer justify-center aria-disabled:cursor-not-allowed min-h-[40px] data-[hover]:text-tabs-tab-text data-[hover]:border-b-tabs-tab-hover data-[hover]:border-b-8 data-[hover]:border-solid disabled:text-tabs-tab-disabled selected:border-b-primary-600 selected:border-b-8 selected:border-solid px-2 pb-2 text-lg"`
`"flex cursor-pointer justify-center aria-disabled:cursor-not-allowed selected:border-red-500 selected:border-b-8 selected:border-solid px-2 pb-2 text-lg"`
);
expect(tabs[1].className).toMatchInlineSnapshot(
`"flex cursor-pointer justify-center aria-disabled:cursor-not-allowed min-h-[40px] data-[hover]:text-tabs-tab-text data-[hover]:border-b-tabs-tab-hover data-[hover]:border-b-8 data-[hover]:border-solid disabled:text-tabs-tab-disabled selected:border-b-primary-600 selected:border-b-8 selected:border-solid px-2 pb-2 text-lg"`
`"flex cursor-pointer justify-center aria-disabled:cursor-not-allowed selected:border-red-500 selected:border-b-8 selected:border-solid px-2 pb-2 text-lg"`
);
});

test('open tabpanel when its tab controller is clicked', () => {
test('allow styling TabPanel & container via theme', () => {
render(
<Tabs>
<Tabs.Item title="tab1" key="1">
<Tabs aria-label="tabs container" disabledKeys={['2']}>
<Tabs.Item key="1" title={'tab1'}>
tab-1 content
</Tabs.Item>
<Tabs.Item title="tab2" key="2">
<Tabs.Item key="2" title={'tab2'}>
tab-2 content
</Tabs.Item>
</Tabs>
);
const tab = screen.getByText('tab2');
fireEvent.click(tab);
expect(tab.className).toMatchInlineSnapshot(
`"flex cursor-pointer justify-center aria-disabled:cursor-not-allowed min-h-[40px] data-[hover]:text-tabs-tab-text data-[hover]:border-b-tabs-tab-hover data-[hover]:border-b-8 data-[hover]:border-solid disabled:text-tabs-tab-disabled selected:border-b-primary-600 selected:border-b-8 selected:border-solid px-2 pb-2 text-lg"`
const tabPanel = screen.getByText('tab-1 content');
const container = screen.getByLabelText('tabs container');

expect(container.className).toMatchSnapshot('flex gap-2');

expect(tabPanel.className).toMatchInlineSnapshot(
`"border-3 border-solid border-red-400"`
);
expect(screen.getByText('tab-2 content')).toBeVisible();
});
27 changes: 17 additions & 10 deletions packages/components/src/Tabs/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ import { TabContext } from './Context';
import { Tab } from './Tab';
import { TabPanel } from './TabPanel';

//props
// ----------------------
interface TabsProps
extends Omit<AriaTabListProps<object>, 'orientation' | 'isDisabled'>,
GapSpaceProp {
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
variant?: string;
}

// component
// ----------------------
export const Tabs = ({
space = 2,
size = 'medium',
Expand All @@ -40,19 +45,21 @@ export const Tabs = ({
size,
variant,
});

return (
<TabContext.Provider value={{ classNames }}>
<div
className={cn('flex', gapSpace[space], classNames.tabs)}
{...tabListProps}
ref={ref}
>
{[...state.collection].map(item => {
return <Tab key={item.key} item={item} state={state} />;
})}
{/* tabs container */}
<div className={cn(classNames.container)}>
<div
className={cn('flex', gapSpace[space], classNames.tabs)}
{...tabListProps}
ref={ref}
>
{[...state.collection].map(item => {
return <Tab key={item.key} item={item} state={state} />;
})}
</div>
<TabPanel key={state.selectedItem?.key} state={state} />
</div>
<TabPanel key={state.selectedItem?.key} state={state} />
</TabContext.Provider>
);
};
Expand Down
3 changes: 3 additions & 0 deletions packages/components/src/Tabs/__snapshots__/Tabs.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`allow styling TabPanel & container via theme: flex gap-2 1`] = `"flex gap-2 mb-[10px]"`;
5 changes: 4 additions & 1 deletion packages/system/src/types/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,10 @@ export type Theme = {
'container' | 'arrow',
ComponentStyleFunction<string, string>
>;
Tabs?: Record<'tabs' | 'tab', ComponentStyleFunction<string, string>>;
Tabs?: Record<
'tabs' | 'container' | 'tabpanel' | 'tab',
ComponentStyleFunction<string, string>
>;
Underlay?: ComponentStyleFunction<string, string>;
Calendar?: Record<
'calendar' | 'calendarCell' | 'calendarControllers',
Expand Down
2 changes: 2 additions & 0 deletions themes/theme-b2b/src/components/Tabs.styles.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ThemeComponent, cva } from '@marigold/system';

export const Tabs: ThemeComponent<'Tabs'> = {
container: cva(''),
tabs: cva('mb-[10px]'),
tabpanel: cva(''),
tab: cva(
[
'min-h-[40px]',
Expand Down
2 changes: 2 additions & 0 deletions themes/theme-core/src/components/Tabs.styles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ThemeComponent, cva } from '@marigold/system';

export const Tabs: ThemeComponent<'Tabs'> = {
container: cva(''),
tabpanel: cva(''),
tabs: cva('mb-[10px]'),
tab: cva(
[
Expand Down

2 comments on commit 989f094

@vercel
Copy link

@vercel vercel bot commented on 989f094 Aug 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

marigold-storybook – ./

marigold-storybook-marigold.vercel.app
marigold-latest.vercel.app
marigold-storybook-git-main-marigold.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 989f094 Aug 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

marigold-docs – ./

marigold-docs-marigold.vercel.app
marigold-docs-git-main-marigold.vercel.app
marigold-docs.vercel.app

Please sign in to comment.