Skip to content

Commit

Permalink
Merge pull request #558 from santanasara/feat/RA-3888-add-menu-component
Browse files Browse the repository at this point in the history
[RA-3888] feat(menu): adds menu component based on mui menu
  • Loading branch information
MarcosViniciusPC authored May 3, 2024
2 parents 5289649 + cb27ba9 commit fc066e9
Show file tree
Hide file tree
Showing 12 changed files with 532 additions and 0 deletions.
Binary file added .loki/reference/chrome_iphone7_Menu_Default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .loki/reference/chrome_laptop_Menu_Default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions components/Menu/Menu.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Generated with scripts/create-component.js
import PropTypes from 'prop-types';
import MaterialMenu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import { createTheme } from '@mui/material/styles';
import { baseFontSize, colors } from '../shared/theme';

const materialThemeOverride = createTheme({
typography: {
fontFamily: `"Nunito Sans", sans-serif`,
fontSize: baseFontSize,
allVariants: {
color: colors.neutral[700],
},
},
palette: {
text: {
primary: colors.neutral[700],
},
},
});

const Menu = (props) => {
const {
open,
anchorEl,
anchorOrigin,
transformOrigin,
onClose,
items,
keepMounted,
} = props;

return (
<MaterialMenu
id="menu"
data-testid="menu"
theme={materialThemeOverride}
aria-labelledby="button"
anchorReference="anchorEl"
anchorEl={anchorEl}
open={open}
onClose={onClose}
onBlur={onClose}
anchorOrigin={anchorOrigin}
transformOrigin={transformOrigin}
keepMounted={keepMounted}
>
{items.map((item) => {
const onClickFunc = () => {
const { onClick } = item;
onClose();
onClick();
};
return (
<MenuItem
key={item.id}
onClick={onClickFunc}
theme={materialThemeOverride}
color="textPrimary"
>
{item.content}
</MenuItem>
);
})}
</MaterialMenu>
);
};

Menu.propTypes = {
/** If true, the component is shown. */
open: PropTypes.bool,
/** Menu contents, has a content parameter, an id and a handleClick function. */
items: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
content: PropTypes.node.isRequired,
onClick: PropTypes.func,
}),
),
/** An HTML element, or a function that returns one. It's used to set the position of the menu. */
anchorEl: PropTypes.object,
/** The point on the anchor where the popover's anchorEl will attach to.
* - vertical: "top", "bottom", "center"
* - horizontal: "left", "center", "right"
* */
anchorOrigin: PropTypes.object,
/** The point on the popover which will attach to the anchor's origin.
* - vertical: "top", "bottom", "center"
* - horizontal: "left", "center", "right"
* */
transformOrigin: PropTypes.object,
/** Always keep the children in the DOM. */
keepMounted: PropTypes.bool,
/** Callback fired when the component requests to be closed. */
onClose: PropTypes.func,
};

Menu.defaultProps = {
open: false,
items: [],
anchorEl: null,
anchorOrigin: {
vertical: 'bottom',
horizontal: 'left',
},
transformOrigin: {
vertical: 'top',
horizontal: 'left',
},
keepMounted: false,
onClose: () => {},
};

export default Menu;
94 changes: 94 additions & 0 deletions components/Menu/Menu.unit.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Generated with scripts/create-component.js
import { render, fireEvent } from '@testing-library/react';
import Menu from './Menu';

describe('<Menu />', () => {
const mockItems = [
{ id: 'item-1', content: 'Item 1', onClick: jest.fn() },
{ id: 'item-2', content: 'Item 2', onClick: jest.fn() },
];

const mockAnchorEl = document.createElement('div');
const mockHandleClose = jest.fn();

const renderMenu = (props) => render(<Menu {...props} />);

it('should match the snapshots', () => {
expect(render(<Menu />).asFragment()).toMatchSnapshot();
});
it('renders with correct items', () => {
const { getByText } = renderMenu({
open: true,
items: mockItems,
anchorEl: mockAnchorEl,
onClose: mockHandleClose,
});

mockItems.forEach((item) => {
const renderedItem = getByText(item.content);
expect(renderedItem).toBeInTheDocument();
});
});

it('calls onClose when an item is clicked', () => {
const { getByText } = renderMenu({
open: true,
items: mockItems,
anchorEl: mockAnchorEl,
onClose: mockHandleClose,
});

mockItems.forEach((item) => {
const renderedItem = getByText(item.content);
fireEvent.click(renderedItem);
expect(item.onClick).toHaveBeenCalledTimes(1);
});
});

it('renders with anchorEl when open is true', () => {
const { getByRole } = renderMenu({
open: true,
items: mockItems,
anchorEl: mockAnchorEl,
onClose: mockHandleClose,
});
const menu = getByRole('presentation');
expect(menu).toBeInTheDocument();
});

it('does not render with anchorEl when open is false', () => {
const { queryByRole } = renderMenu({
open: false,
items: mockItems,
anchorEl: mockAnchorEl,
onClose: mockHandleClose,
});
const menu = queryByRole('presentation');
expect(menu).toBeNull();
});

it('calls onClose when onBlur event is triggered', () => {
const { getByRole } = renderMenu({
open: true,
items: mockItems,
anchorEl: mockAnchorEl,
onClose: mockHandleClose,
});
const menu = getByRole('presentation');
fireEvent.blur(menu);
expect(mockHandleClose).toHaveBeenCalledTimes(1);
});

it('renders without items when items array is empty', () => {
const { queryByText } = renderMenu({
open: true,
items: [],
anchorEl: mockAnchorEl,
onClose: mockHandleClose,
});
mockItems.forEach((item) => {
const renderedItem = queryByText(item.content);
expect(renderedItem).toBeNull();
});
});
});
3 changes: 3 additions & 0 deletions components/Menu/__snapshots__/Menu.unit.test.jsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<Menu /> should match the snapshots 1`] = `<DocumentFragment />`;
27 changes: 27 additions & 0 deletions components/Menu/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Generated with scripts/create-component.js
import { FC } from 'react';

export interface MenuItemProps {
id: string | number;
content: React.ReactNode;
onClick: Function;
}

export interface MenuProps {
open?: boolean;
items?: MenuItemProps[];
anchorEl?: Element | (() => Element) | null;
anchorOrigin?: {
vertical: 'top' | 'bottom' | 'center';
horizontal: 'left' | 'center' | 'right';
};
transformOrigin?: {
vertical: 'top' | 'bottom' | 'center';
horizontal: 'left' | 'center' | 'right';
};
keepMounted?: boolean;
onClose?: (event: {}, reason: 'backdropClick' | 'escapeKeyDown') => void;
}

declare const Menu: FC<MenuProps>;
export default Menu;
4 changes: 4 additions & 0 deletions components/Menu/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Generated with scripts/create-component.js
import Menu from './Menu';

export default Menu;
2 changes: 2 additions & 0 deletions components/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { default as CarouselComponent, CarouselProps } from "./Carousel"
import { default as InfiniteScrollComponent, InfiniteScrollProps } from "./InfiniteScroll"
import { default as SkeletonComponent, SkeletonProps } from "./Skeleton";
import { default as ScoreComponent, ScoreProps } from './Score';
import { default as MenuComponent, MenuProps } from './Menu';

export { default as BREAKPOINTS } from './Breakpoints';
export { default as Colors } from "./Colors";
Expand Down Expand Up @@ -94,3 +95,4 @@ export const Carousel: StyledComponent<typeof CarouselComponent, CarouselProps>;
export const InfiniteScroll: StyledComponent<typeof InfiniteScrollComponent, InfiniteScrollProps>;
export const DropdownLight: StyledComponent<typeof DropdownLightComponent, DropdownLightProps>;
export const Score: StyledComponent<typeof ScoreComponent, ScoreProps>;
export const Menu: StyledComponent<typeof MenuComponent, MenuProps>;
2 changes: 2 additions & 0 deletions components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import Link from './Link';
import InfiniteScroll from './InfiniteScroll';
import DropdownLight from './DropdownLight';
import Score from './Score';
import Menu from './Menu';

export {
Accordion,
Expand Down Expand Up @@ -93,4 +94,5 @@ export {
InfiniteScroll,
DropdownLight,
Score,
Menu,
};
Loading

0 comments on commit fc066e9

Please sign in to comment.