Skip to content

Commit

Permalink
feat: add a new UI component DropdownMenu (ENG-56423)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenle17 committed Jun 10, 2024
1 parent 226dc8e commit fb4aa64
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 0 deletions.
124 changes: 124 additions & 0 deletions src/components/DropdownMenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import {
Button,
Layer,
Menu,
MenuItem,
useLayer,
useMenuHandler,
useMenuPositioner,
} from '@bluecateng/pelagos';
import ChevronDown from '@carbon/icons-react/es/ChevronDown';
import lodash from 'lodash';
import PropTypes from 'prop-types';
import { forwardRef } from 'react';
import { createPortal } from 'react-dom';
import './Dropdown.less';

const setRefs =
(...refs) =>
(current) => {
for (const ref of refs) {
console.log(typeof ref);
if (typeof ref === 'function') {
ref(current);
} else {
ref.current = current;
}
}
};

/** A button with a pop-up menu. */
const DropdownMenu = forwardRef(
({ customValues, id, className, disabled, flipped, ...props }, ref) => {
id = lodash.uniqueId();
const menuId = `${id}-menu`;

const level = useLayer() + 1;

const setPopUpPosition = useMenuPositioner(flipped);

const { expanded, buttonProps, menuProps, buttonRef } =
useMenuHandler(setPopUpPosition);

return (
<>
<Button
{...buttonProps}
{...props}
aria-controls={expanded ? menuId : null}
aria-expanded={expanded}
aria-haspopup='true'
className={`DropdownMenu${
className ? ` ${className}` : ''
}`}
data-layer={expanded ? level : null}
disabled={disabled}
icon={ChevronDown}
id={id}
ref={ref ? setRefs(ref, buttonRef) : buttonRef}
/>
{expanded &&
createPortal(
<Layer className='DropdownMenu__popUp' level={level}>
<Menu
{...menuProps}
id={menuId}
aria-labelledby={id}>
{customValues?.map((item, index) => {
return (
<MenuItem
disable={item?.disable}
key={index}
onClick={item?.onClick}
text={item.text}
/>
);
})}
</Menu>
</Layer>,
document.body,
)}
</>
);
},
);

DropdownMenu.displayName = 'DropdownMenu';

DropdownMenu.propTypes = {
/** The component id. */
id: PropTypes.string,
/** The component class name(s). */
className: PropTypes.string,
/** The text to display. */
text: PropTypes.string,
/** The tooltip text to display. */
tooltipText: PropTypes.string,
/** The placement of the tooltip relative to the button. */
tooltipPlacement: PropTypes.oneOf(['left', 'right', 'top', 'bottom']),
/** The size of the button. */
size: PropTypes.oneOf(['small', 'medium', 'large']),
/** The button type. */
type: PropTypes.oneOf(['primary', 'tertiary', 'ghost']),
/** Whether the button is disabled. */
disabled: PropTypes.bool,
/** Whether the menu alignment should be flipped. */
flipped: PropTypes.bool,
/** The menu items. */
customValues: PropTypes.arrayOf(
PropTypes.shape({
text: PropTypes.string.isRequired,
onClick: PropTypes.func,
disable: PropTypes.bool,
}),
).isRequired,
};

PropTypes.func;

DropdownMenu.defaultProps = {
size: 'small',
type: 'ghost',
};

export default DropdownMenu;
18 changes: 18 additions & 0 deletions src/components/DropdownMenu.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@import '~@bluecateng/pelagos/less/elevations';

@layer pelagos {
.DropdownMenu {
& > .Button__icon {
transition: transform 0.15s ease-out;
}

&[aria-expanded='true'] > .Button__icon {
transform: rotate(180deg);
}

&__popUp {
@elv-04();
position: absolute;
}
}
}

0 comments on commit fb4aa64

Please sign in to comment.