Skip to content

Commit

Permalink
Adjust studioContentMenu
Browse files Browse the repository at this point in the history
  • Loading branch information
standeren committed Oct 30, 2024
1 parent 3962016 commit 9fc28fc
Show file tree
Hide file tree
Showing 59 changed files with 408 additions and 368 deletions.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
.tabsContainer {
background-color: var(--fds-semantic-surface-action-second-subtle);
.menuContainer {
height: 100%;
background-color: var(--fds-semantic-surface-action-second-subtle);
}

.tabsContainer {
display: flex;
flex-direction: column;
gap: var(--fds-border_width-default);
background-color: var(--fds-semantic-border-action-second-subtle);
padding: var(--fds-border_width-default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ const meta: Meta<StudioContentMenuWrapperProps<StudioMenuTabName>> = {
control: 'object',
description: 'Array of button menu tabs with icons, names, and ids.',
table: {
type: { summary: 'StudioButtonTabType<TabId>[]' },
type: { summary: 'StudioContentMenuButtonTabProps<TabId>[]' },
},
},
linkTabs: {
control: 'object',
description:
'Array of link menu tabs with icons, names, and ids. Prop `to` defines the url to navigate to.',
'Array of link menu tabs with icons, names, and ids. Provide an optional link-element to return `props` in renderTab.',
table: {
type: { summary: 'StudioLinkTabType<TabId>[]' },
type: { summary: 'StudioContentMenuLinkTabProps<TabId>[]' },
},
},
selectedTabId: {
Expand Down Expand Up @@ -65,7 +65,7 @@ Preview.args = {
tabId: 'tabAsLink',
tabName: 'Gå til Designsystemet',
icon: <ExternalLinkIcon />,
to: 'https://next.storybook.designsystemet.no',
renderTab: (props) => <a href={'https://next.storybook.designsystemet.no'} {...props} />,
},
],
onChangeTab: () => {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@ import React from 'react';
import { render, screen } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import { StudioContentMenu } from './';
import type { StudioMenuTabType } from './types/StudioMenuTabType';
import type { StudioContentMenuWrapperProps } from './StudioContentMenuWrapper';
import type { StudioContentMenuButtonTabProps } from './StudioContentMenuButtonTab';

type StudioMenuTabName = 'tab1' | 'tab2' | 'tab3';

const onChangeTabMock = jest.fn();

const tab1Name = 'My tab';
const tab1Id: StudioMenuTabName = 'tab1';
const tab1: StudioMenuTabType<StudioMenuTabName> = {
const tab1: StudioContentMenuButtonTabProps<StudioMenuTabName> = {
tabName: tab1Name,
tabId: tab1Id,
icon: <svg />,
};
const tab2Name = 'My second tab';
const tab2Id: StudioMenuTabName = 'tab2';
const tab2: StudioMenuTabType<StudioMenuTabName> = {
const tab2: StudioContentMenuButtonTabProps<StudioMenuTabName> = {
tabName: tab2Name,
tabId: tab2Id,
icon: <svg />,
Expand All @@ -27,18 +27,18 @@ const tab2: StudioMenuTabType<StudioMenuTabName> = {
describe('StudioContentMenu', () => {
afterEach(jest.clearAllMocks);

it('renders an empty contentMenu when there is no provided tabs', () => {
renderStudioContentMenu({ buttonTabs: [] });
const emptyMenu = screen.getByRole('tablist');
expect(emptyMenu).toBeInTheDocument();
});

it('renders first tab as selected if selectedTab is not provided', () => {
renderStudioContentMenu({
buttonTabs: [tab1, tab2],
});
const firstTab = screen.getByRole('tab', { name: tab1Name });
expect(firstTab).toHaveClass('selectedTab');
expect(firstTab).toHaveClass('selected');
});

it('renders an empty contentMenu when there is no provided tabs', () => {
renderStudioContentMenu({ buttonTabs: [] });
const emptyMenu = screen.getByRole('tablist');
expect(emptyMenu).toBeInTheDocument();
});

it('renders the title and icon of a given menu tab', () => {
Expand All @@ -63,13 +63,11 @@ describe('StudioContentMenu', () => {
linkTabs: [
{
...tab1,
to: link,
renderTab: (props) => <a href={link} {...props} />,
},
],
});
const menuTab = screen.getByRole('tab', { name: tab1Name });
const linkTab = screen.getByRole('link', { name: tab1Name });
expect(menuTab).toBeInTheDocument();
const linkTab = screen.getByRole('tab', { name: tab1Name });
expect(linkTab).toBeInTheDocument();
expect(linkTab).toHaveAttribute('href', link);
});
Expand Down Expand Up @@ -148,23 +146,6 @@ describe('StudioContentMenu', () => {
expect(onChangeTabMock).toHaveBeenCalledTimes(1);
expect(onChangeTabMock).toHaveBeenCalledWith(tab1Id);
});

it('calls onChangeTab when clicking on a menu tab with link', async () => {
const link = 'url-link';
const user = userEvent.setup();
renderStudioContentMenu({
linkTabs: [
{
...tab1,
to: link,
},
],
});
const menuTab = screen.getByRole('tab', { name: tab1Name });
await user.click(menuTab);
expect(onChangeTabMock).toHaveBeenCalledTimes(1);
expect(onChangeTabMock).toHaveBeenCalledWith(tab1Id);
});
});

const renderStudioContentMenu = ({
Expand All @@ -174,10 +155,21 @@ const renderStudioContentMenu = ({
render(
<StudioContentMenu selectedTabId={undefined} onChangeTab={onChangeTabMock}>
{buttonTabs.map((buttonTab) => (
<StudioContentMenu.ButtonTab key={buttonTab.tabId} contentTab={buttonTab} />
<StudioContentMenu.ButtonTab
key={buttonTab.tabId}
icon={buttonTab.icon}
tabId={buttonTab.tabId}
tabName={buttonTab.tabName}
/>
))}
{linkTabs.map((linkTab) => (
<StudioContentMenu.LinkTab key={linkTab.tabId} contentTab={linkTab} />
<StudioContentMenu.LinkTab
key={linkTab.tabId}
icon={linkTab.icon}
tabId={linkTab.tabId}
tabName={linkTab.tabName}
renderTab={linkTab.renderTab}
/>
))}
</StudioContentMenu>,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,25 @@ function StudioContentMenuForwarded<TabId extends string>(
ref: React.Ref<HTMLDivElement>,
): ReactElement {
const firstTabId = getFirstTabId(children);
const [selectedTab, setSelectedTab] = useState<string>(selectedTabId ?? firstTabId);
const [selectedTab, setSelectedTab] = useState<TabId>(selectedTabId ?? firstTabId);

const handleChangeTab = (tabId: TabId) => {
onChangeTab(tabId);
setSelectedTab(tabId);
};

const isTabSelected = (tabId: TabId) => selectedTab === tabId;

return (
<div ref={ref} className={classes.tabsContainer} role='tablist'>
<StudioContentMenuContextProvider selectedTabId={selectedTab} onChangeTab={handleChangeTab}>
{children}
</StudioContentMenuContextProvider>
<div ref={ref} className={classes.menuContainer}>
<div ref={ref} className={classes.tabsContainer} role='tablist'>
<StudioContentMenuContextProvider
isTabSelected={isTabSelected}
onChangeTab={handleChangeTab}
>
{children}
</StudioContentMenuContextProvider>
</div>
</div>
);
}
Expand All @@ -36,5 +44,5 @@ export const StudioContentMenu = forwardRef<HTMLDivElement, StudioContentMenuPro
const getFirstTabId = (children: ReactNode) => {
return Children.toArray(children).filter((child): child is ReactElement =>
React.isValidElement(child),
)[0]?.props.contentTab.tabId;
)[0]?.props.tabId;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.buttonTab {
all: unset;
display: var(--tab-display);
align-items: var(--tab-align-items);
width: var(--tab-width);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import type { ReactNode } from 'react';
import { useTabProps } from '../hooks/useTabProps';
import { StudioButton } from '@studio/components';

export type StudioContentMenuButtonTabProps<TabId extends string> = {
icon: ReactNode;
tabName: string;
tabId: TabId;
};

export function StudioContentMenuButtonTab<TabId extends string>({
icon,
tabName,
tabId,
}: StudioContentMenuButtonTabProps<TabId>): React.ReactElement {
const props = useTabProps(icon, tabName, tabId);

return <StudioButton {...props} color='first' />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { StudioContentMenuButtonTab } from './StudioContentMenuButtonTab';
export type { StudioContentMenuButtonTabProps } from './StudioContentMenuButtonTab';
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import type { ReactNode } from 'react';
import { useTabProps } from '../hooks/useTabProps';
import { StudioButton } from '@studio/components';

export type StudioContentMenuLinkTabProps<TabId extends string> = {
icon: ReactNode;
tabName: string;
tabId: TabId;
renderTab: (props: React.HTMLAttributes<HTMLAnchorElement>) => React.ReactElement;
};

export function StudioContentMenuLinkTab<TabId extends string>({
icon,
tabName,
tabId,
renderTab,
}: StudioContentMenuLinkTabProps<TabId>): React.ReactElement {
const props = useTabProps(icon, tabName, tabId);

return <StudioButton asChild>{renderTab(props)}</StudioButton>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { StudioContentMenuLinkTab } from './StudioContentMenuLinkTab';
export type { StudioContentMenuLinkTabProps } from './StudioContentMenuLinkTab';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.contentMenuWrapper {
height: 300px;
width: 200px;
width: 20vw;
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react';
import { StudioContentMenu } from './';
import classes from './StudioContentMenuWrapper.module.css';
import type { StudioButtonTabType, StudioLinkTabType } from './types/StudioMenuTabType';
import type { StudioContentMenuButtonTabProps } from './StudioContentMenuButtonTab';
import type { StudioContentMenuLinkTabProps } from './StudioContentMenuLinkTab';

export type StudioContentMenuWrapperProps<TabId extends string> = {
buttonTabs: StudioButtonTabType<TabId>[];
linkTabs: StudioLinkTabType<TabId>[];
buttonTabs: StudioContentMenuButtonTabProps<TabId>[];
linkTabs: StudioContentMenuLinkTabProps<TabId>[];
selectedTabId: TabId;
onChangeTab: (tabId: TabId) => void;
};
Expand All @@ -19,10 +20,27 @@ export function StudioContentMenuWrapper<TabId extends string>({
return (
<div className={classes.contentMenuWrapper}>
<StudioContentMenu selectedTabId={selectedTabId} onChangeTab={onChangeTab}>
<StudioContentMenu.ButtonTab contentTab={buttonTabs[0]} />
<StudioContentMenu.ButtonTab contentTab={buttonTabs[1]} />
<StudioContentMenu.ButtonTab contentTab={buttonTabs[2]} />
<StudioContentMenu.LinkTab contentTab={linkTabs[0]} />
<StudioContentMenu.ButtonTab
icon={buttonTabs[0].icon}
tabId={buttonTabs[0].tabId}
tabName={buttonTabs[0].tabName}
/>
<StudioContentMenu.ButtonTab
icon={buttonTabs[1].icon}
tabId={buttonTabs[1].tabId}
tabName={buttonTabs[1].tabName}
/>
<StudioContentMenu.ButtonTab
icon={buttonTabs[2].icon}
tabId={buttonTabs[2].tabId}
tabName={buttonTabs[2].tabName}
/>
<StudioContentMenu.LinkTab
icon={linkTabs[0].icon}
tabId={linkTabs[0].tabId}
tabName={linkTabs[0].tabName}
renderTab={linkTabs[0].renderTab}
/>
</StudioContentMenu>
</div>
);
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit 9fc28fc

Please sign in to comment.