Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
felixhabib committed Dec 16, 2024
1 parent 0571d6a commit 163fa36
Show file tree
Hide file tree
Showing 14 changed files with 137 additions and 32 deletions.
18 changes: 18 additions & 0 deletions .changeset/wicked-dancers-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'braid-design-system': minor
---

---
updated:
- MenuRenderer
---

**MenuRenderer:** Add `small` size.

Introduce a new `small` size for the `MenuRenderer` component.
This is available via the `size` prop, which supports the existing `standard` (default) and `small`.

**EXAMPLE USAGE:**
```jsx
<MenuRenderer size="small" ... />
```
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Menu } from '../MenuRenderer/MenuRenderer';
const defaultProps = {
offsetSpace: 'none',
align: 'left',
size: 'standard',
width: 'content',
highlightIndex: -1,
open: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import React, {
import type { BadgeProps } from '../Badge/Badge';
import { type BoxProps, Box } from '../Box/Box';
import { Text } from '../Text/Text';
import { touchableText } from '../../css/typography.css';
import { normalizeKey } from '../private/normalizeKey';
import { MenuRendererItemContext } from '../MenuRenderer/MenuRendererItemContext';
import { type Action, actionTypes } from '../MenuRenderer/MenuRenderer.actions';
Expand All @@ -25,6 +24,7 @@ import { MenuRendererContext } from '../MenuRenderer/MenuRendererContext';
import { useBraidTheme } from '../BraidProvider/BraidThemeContext';
import { iconSlotSpace } from '../private/iconSlotSpace';
import { badgeSlotSpace } from '../private/badgeSlotSpace';
import { virtualTouchable } from '../private/touchable/virtualTouchable.css';

const {
MENU_ITEM_UP,
Expand All @@ -37,8 +37,6 @@ const {
MENU_ITEM_HOVER,
} = actionTypes;

const menuItemChildrenSize = 'standard';

type MenuItemTone = 'critical' | undefined;

export interface UseMenuItemProps {
Expand All @@ -58,17 +56,19 @@ export function useMenuItem<MenuItemElement extends HTMLElement>({
id,
...restProps
}: UseMenuItemProps) {
const menuRendererContext = useContext(MenuRendererContext);
const menuRendererItemContext = useContext(MenuRendererItemContext);

assert(
menuRendererItemContext !== null,
menuRendererContext !== null && menuRendererItemContext !== null,
`${displayName} must be rendered as an immediate child of a menu. See the documentation for correct usage: https://seek-oss.github.io/braid-design-system/components/MenuItem`,
);

if (menuRendererItemContext === null) {
throw new Error(`${displayName} element rendered outside menu context`);
}

const { size } = menuRendererContext;
const { isHighlighted, index, dispatch, focusTrigger } =
menuRendererItemContext;
const menuItemRef = useRef<MenuItemElement>(null);
Expand Down Expand Up @@ -155,11 +155,14 @@ export function useMenuItem<MenuItemElement extends HTMLElement>({
background: isHighlighted ? hoverBackground : undefined,
className: [
styles.menuItem,
touchableText[menuItemChildrenSize],
size === 'small' ? virtualTouchable : undefined,
atoms({
display: 'block',
display: 'flex',
alignItems: 'center',
width: 'full',
paddingX: 'small',
paddingX: size === 'standard' ? 'small' : 'small', // todo - with 'small' size - small or xsmall?
paddingY: size === 'standard' ? undefined : 'xsmall',
height: size === 'standard' ? 'touchable' : undefined,
cursor: 'pointer',
textAlign: 'left',
outline: 'none',
Expand Down Expand Up @@ -201,23 +204,24 @@ function MenuItemChildren({
);

let leftSlot: ReactNode = null;
const { size, reserveIconSpace } = menuRendererContext;

if (!formElement) {
if (icon) {
leftSlot = (
<Text
size={menuItemChildrenSize}
baseline={false}
tone={tone === 'critical' ? tone : undefined}
>
{icon}
</Text>
);
} else if (menuRendererContext.reserveIconSpace) {
leftSlot = (
<Box component="span" display="block" className={iconSize()} />
);
}
// Todo - fix alignment issue. When reserveIconSpace is true, text is not aligned between items with and without icons

if (!formElement && (icon || reserveIconSpace)) {
leftSlot = (
<Text size={size} tone={tone === 'critical' ? tone : undefined}>
{icon || (
<Box
component="span"
display="block"
className={iconSize({ size, crop: true })}
>
&nbsp;
</Box>
)}
</Text>
);
}

return (
Expand All @@ -234,8 +238,7 @@ function MenuItemChildren({
) : null}
<Box component="span" minWidth={0}>
<Text
size={menuItemChildrenSize}
baseline={false}
size={size}
tone={tone === 'critical' ? tone : undefined}
maxLines={1}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Menu } from '../MenuRenderer/MenuRenderer';
const defaultProps = {
offsetSpace: 'none',
align: 'left',
size: 'standard',
width: 'content',
highlightIndex: -1,
open: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Menu } from '../MenuRenderer/MenuRenderer';
const defaultProps = {
offsetSpace: 'none',
align: 'left',
size: 'standard',
width: 'content',
highlightIndex: -1,
open: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useContext } from 'react';
import { Box } from '../Box/Box';
import { Divider } from '../Divider/Divider';
import { MenuRendererContext } from '../MenuRenderer/MenuRendererContext';
import { menuYPadding } from '../MenuRenderer/MenuRenderer.css';

export const MenuItemDivider = () => {
assert(
Expand All @@ -11,7 +12,7 @@ export const MenuItemDivider = () => {
);

return (
<Box paddingY="xxsmall">
<Box paddingY={menuYPadding}>
<Divider />
</Box>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
import { calc } from '@vanilla-extract/css-utils';
import { vars } from '../../themes/vars.css';

export const menuYPadding = 'xxsmall';

export const backdrop = style({
width: '100vw',
height: '100vh',
Expand Down Expand Up @@ -48,5 +50,8 @@ export const width = styleVariants({ small, medium, large }, (w) => [
]);

export const menuHeightLimit = style({
maxHeight: calc(vars.touchableSize).multiply(9.5).toString(),
maxHeight: calc(vars.touchableSize)
.multiply(9.5)
.add(calc(vars.space[menuYPadding]).multiply(2))
.toString(),
});
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,62 @@ const docs: ComponentDocs = {
</Inline>,
),
},
{
label: 'Sizes',
description: (
<Text>
You can customise the size of the menu via the <Strong>size</Strong>{' '}
prop, which accepts either <Strong>standard</Strong> or{' '}
<Strong>small</Strong>. When using a <Strong>small</Strong> trigger
element, it is recommended to use the <Strong>small</Strong> size for
menu.
</Text>
),
Example: () =>
source(
<Stack space="large">
<Inline space="none">
<MenuRenderer
offsetSpace="small"
trigger={(triggerProps, { open }) => (
<Box userSelect="none" cursor="pointer" {...triggerProps}>
<Text>
Standard trigger{' '}
<IconChevron
direction={open ? 'up' : 'down'}
alignY="lowercase"
/>
</Text>
</Box>
)}
>
<MenuItem onClick={() => {}}>Button</MenuItem>
<MenuItemLink href="#">Link</MenuItemLink>
</MenuRenderer>
</Inline>
<Inline space="none">
<MenuRenderer
size="small"
offsetSpace="xsmall"
trigger={(triggerProps, { open }) => (
<Box userSelect="none" cursor="pointer" {...triggerProps}>
<Text size="small">
Small trigger{' '}
<IconChevron
direction={open ? 'up' : 'down'}
alignY="lowercase"
/>
</Text>
</Box>
)}
>
<MenuItem onClick={() => {}}>Button</MenuItem>
<MenuItemLink href="#">Link</MenuItemLink>
</MenuRenderer>
</Inline>
</Stack>,
),
},
{
label: 'Width',
description: (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { calc } from '@vanilla-extract/css-utils';
const defaultProps = {
offsetSpace: 'none',
align: 'left',
size: 'standard',
width: 'content',
highlightIndex: -1,
open: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import buildDataAttributes, {
import * as styles from './MenuRenderer.css';
import { BraidPortal } from '../BraidPortal/BraidPortal';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import type { MenuSize } from './MenuRendererTypes';

interface TriggerProps {
'aria-haspopup': boolean;
Expand All @@ -51,6 +52,7 @@ export interface MenuRendererProps {
trigger: (props: TriggerProps, state: TriggerState) => ReactNode;
align?: 'left' | 'right';
offsetSpace?: ResponsiveSpace;
size?: MenuSize;
width?: keyof typeof styles.width | 'content';
placement?: 'top' | 'bottom';
onOpen?: () => void;
Expand Down Expand Up @@ -118,6 +120,7 @@ export const MenuRenderer = ({
onOpen,
onClose,
trigger,
size = 'standard',
width = 'content',
align = 'left',
offsetSpace = 'none',
Expand Down Expand Up @@ -344,6 +347,7 @@ export const MenuRenderer = ({
<BraidPortal>
<Menu
align={align}
size={size}
width={width}
placement={placement}
offsetSpace={offsetSpace}
Expand Down Expand Up @@ -385,6 +389,7 @@ const borderRadius = 'large';
interface MenuProps {
offsetSpace: NonNullable<MenuRendererProps['offsetSpace']>;
align: NonNullable<MenuRendererProps['align']>;
size: NonNullable<MenuRendererProps['size']>;
width: NonNullable<MenuRendererProps['width']>;
placement: NonNullable<MenuRendererProps['placement']>;
reserveIconSpace: NonNullable<MenuRendererProps['reserveIconSpace']>;
Expand All @@ -399,6 +404,7 @@ interface MenuProps {
export function Menu({
offsetSpace,
align,
size,
width,
placement,
children,
Expand All @@ -419,15 +425,14 @@ export function Menu({
});

return (
<MenuRendererContext.Provider value={{ reserveIconSpace }}>
<MenuRendererContext.Provider value={{ size, reserveIconSpace }}>
<Box
role="menu"
position={position}
zIndex="modal"
boxShadow={placement === 'top' ? 'small' : 'medium'}
borderRadius={borderRadius}
background="surface"
paddingY="xxsmall"
marginTop={placement === 'bottom' ? offsetSpace : undefined}
marginBottom={placement === 'top' ? offsetSpace : undefined}
transition="fast"
Expand All @@ -439,8 +444,11 @@ export function Menu({
width !== 'content' && styles.width[width],
]}
>
<ScrollContainer direction="vertical" fadeSize="small">
<Box className={styles.menuHeightLimit}>
<ScrollContainer direction="vertical" fadeSize="medium">
<Box
paddingY={styles.menuYPadding}
className={styles.menuHeightLimit}
>
{Children.map(children, (item, i) => {
if (isDivider(item)) {
dividerCount++;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { createContext } from 'react';
import type { MenuSize } from './MenuRendererTypes';

interface MenuRendererValues {
size: MenuSize;
reserveIconSpace: boolean;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// todo - move to existing file
export type MenuSize = 'standard' | 'small';
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { Box } from '../Box/Box';
import * as styles from './OverflowMenu.css';

export interface OverflowMenuProps
extends Omit<MenuRendererProps, 'trigger' | 'align' | 'offsetSpace'> {
extends Omit<
MenuRendererProps,
'size' | 'trigger' | 'align' | 'offsetSpace'
> {
label: string;
id?: string;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7418,6 +7418,9 @@ exports[`MenuRenderer 1`] = `
| "bottom"
| "top"
reserveIconSpace?: boolean
size?:
| "small"
| "standard"
trigger: (props: TriggerProps, state: TriggerState) => ReactNode
width?:
| "content"
Expand Down

0 comments on commit 163fa36

Please sign in to comment.