Skip to content

Commit

Permalink
featIui): added menu component
Browse files Browse the repository at this point in the history
  • Loading branch information
akhilmhdh committed Jan 23, 2023
1 parent 89ba807 commit c4ee03c
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 0 deletions.
107 changes: 107 additions & 0 deletions frontend/src/components/v2/Menu/Menu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { faKey, faUser } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
// eslint-disable-next-line import/no-extraneous-dependencies
import { Meta, StoryObj } from '@storybook/react';

import { Menu, MenuGroup, MenuItem } from './Menu';

const meta: Meta<typeof Menu> = {
title: 'Components/Menu',
component: Menu,
tags: ['v2'],
argTypes: {}
};

export default meta;
type Story = StoryObj<typeof Menu>;

export const Basic: Story = {
render: (args) => (
<Menu {...args}>
<MenuItem>Secrets</MenuItem>
<MenuItem>Members</MenuItem>
<MenuItem>Integrations</MenuItem>
</Menu>
),
args: {}
};

export const SelectedItem: Story = {
render: (args) => (
<Menu {...args}>
<MenuItem isSelected>Secrets</MenuItem>
<MenuItem>Members</MenuItem>
<MenuItem>Integrations</MenuItem>
</Menu>
),
args: {}
};

export const GroupedItem: Story = {
render: (args) => (
<Menu {...args}>
<MenuGroup title="Group 1">
<MenuItem>Secrets</MenuItem>
<MenuItem>Members</MenuItem>
</MenuGroup>
<MenuGroup title="Group 2">
<MenuItem>Secrets</MenuItem>
<MenuItem>Members</MenuItem>
</MenuGroup>
</Menu>
),
args: {}
};

export const DisabledItem: Story = {
render: (args) => (
<Menu {...args}>
<MenuGroup title="Group 1">
<MenuItem isDisabled>Secrets</MenuItem>
<MenuItem>Members</MenuItem>
</MenuGroup>
<MenuGroup title="Group 2">
<MenuItem>Secrets</MenuItem>
<MenuItem>Members</MenuItem>
</MenuGroup>
</Menu>
),
args: {}
};

export const WithIcons: Story = {
render: (args) => (
<Menu {...args}>
<MenuGroup title="Group 1">
<MenuItem isDisabled icon={<FontAwesomeIcon icon={faKey} />}>
Secrets
</MenuItem>
<MenuItem icon={<FontAwesomeIcon icon={faUser} />}>Members</MenuItem>
</MenuGroup>
<MenuGroup title="Group 2">
<MenuItem icon={<FontAwesomeIcon icon={faKey} />}>Secrets</MenuItem>
<MenuItem icon={<FontAwesomeIcon icon={faKey} />}>Members</MenuItem>
</MenuGroup>
</Menu>
),
args: {}
};

export const WithDescription: Story = {
render: (args) => (
<Menu {...args}>
<MenuItem
isDisabled
icon={<FontAwesomeIcon icon={faKey} />}
description="Some random description"
>
Secrets
</MenuItem>
<MenuItem icon={<FontAwesomeIcon icon={faUser} />} description="Some random description">
Members
</MenuItem>
</Menu>
),
args: {}
};
64 changes: 64 additions & 0 deletions frontend/src/components/v2/Menu/Menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ComponentPropsWithRef, ElementType, ReactNode, Ref } from 'react';
import { twMerge } from 'tailwind-merge';

export type MenuProps = {
children: ReactNode;
className?: string;
};

export const Menu = ({ children, className }: MenuProps): JSX.Element => {
return <ul className={twMerge('p-2', className)}>{children}</ul>;
};

export type MenuItemProps<T extends ElementType> = {
// Kudos to https://itnext.io/react-polymorphic-components-with-typescript-f7ce72ea7af2
as?: T;
children: ReactNode;
icon?: ReactNode;
description?: ReactNode;
isDisabled?: boolean;
isSelected?: boolean;
className?: string;
inputRef?: Ref<T>;
};

export const MenuItem = <T extends ElementType = 'button'>({
children,
icon,
className,
isDisabled,
isSelected,
as: Item = 'button',
description,
// wrapping in forward ref with generic component causes the loss of ts definitions on props
inputRef
}: MenuItemProps<T> & ComponentPropsWithRef<T>): JSX.Element => (
<li
className={twMerge(
'px-2 py-3 flex flex-col text-sm text-white transition-all rounded cursor-pointer hover:bg-gray-700',
isSelected && 'text-primary',
isDisabled && 'text-gray-500 hover:bg-transparent cursor-not-allowed',
className
)}
>
<Item type="button" role="menuitem" class="flex items-center" ref={inputRef}>
{icon && <span className="mr-2">{icon}</span>}
<span className="flex-grow text-left">{children}</span>
</Item>
{description && <span className="mt-2 text-xs">{description}</span>}
</li>
);

MenuItem.displayName = 'MenuItem';

export type MenuGroupProps = {
children: ReactNode;
title: ReactNode;
};

export const MenuGroup = ({ children, title }: MenuGroupProps): JSX.Element => (
<>
<li className="p-2 text-xs text-gray-400">{title}</li>
{children}
</>
);
2 changes: 2 additions & 0 deletions frontend/src/components/v2/Menu/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { MenuGroupProps, MenuItemProps, MenuProps } from './Menu';
export { Menu, MenuGroup, MenuItem } from './Menu';
1 change: 1 addition & 0 deletions frontend/src/components/v2/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export * from './Card';
export * from './FormControl';
export * from './IconButton';
export * from './Input';
export * from './Menu';
export * from './Modal';
export * from './Select';

0 comments on commit c4ee03c

Please sign in to comment.