Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ramonjd committed Nov 17, 2021
1 parent 2c42299 commit e17628a
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 25 deletions.
2 changes: 1 addition & 1 deletion packages/components/src/dropdown/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
*/
import Dropdown from '../';
import Button from '../../button';
import MenuGroup from '../../menu-group';
import { MenuGroup } from '../../menu-group';
import MenuItem from '../../menu-item';
import DropdownMenu from '../../dropdown-menu';

Expand Down
79 changes: 79 additions & 0 deletions packages/components/src/menu-group/component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* External dependencies
*/
// eslint-disable-next-line no-restricted-imports
import type { Ref } from 'react';

/**
* WordPress dependencies
*/
import { Children } from '@wordpress/element';
import { useInstanceId } from '@wordpress/compose';

/**
* Internal dependencies
*/
import { contextConnect, WordPressComponentProps } from '../ui/context';
import { useMenuGroup } from './hook';
import type { MenuGroupProps } from './types';
import { View } from '../view';

function useUniqueId() {
return `components-menu-group-label-${ useInstanceId( MenuGroup ) }`;
}

function MenuGroup(
props: WordPressComponentProps< MenuGroupProps, 'div' >,
forwardedRef: Ref< any >
) {
const {
children,
label,
menuGroupClassName,
menuGroupLabelClassName,
} = useMenuGroup( props );

const labelId = useUniqueId();

if ( ! Children.count( children ) ) {
return null;
}

return (
<View className={ menuGroupClassName } ref={ forwardedRef }>
{ label && (
<View
className={ menuGroupLabelClassName }
id={ labelId }
aria-hidden="true"
>
{ label }
</View>
) }
<View role="group" aria-labelledby={ label ? labelId : undefined }>
{ children }
</View>
</View>
);
}

/**
* `MenuGroup` wraps a series of related `MenuItem` components into a common section.
*
* @example
* ```jsx
* import { MenuGroup } from `@wordpress/components`
*
* function Example() {
* return (
* <MenuGroup label="Settings">
* <MenuItem>Setting 1</MenuItem>
* <MenuItem>Setting 2</MenuItem>
* </MenuGroup>
* );
* }
* ```
*/
const ConnectedMenuGroup = contextConnect( MenuGroup, 'MenuGroup' );

export default ConnectedMenuGroup;
54 changes: 54 additions & 0 deletions packages/components/src/menu-group/hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { useMemo } from '@wordpress/element';

/**
* Internal dependencies
*/
import { useCx } from '../utils/hooks/use-cx';

/**
* Internal dependencies
*/
import { useContextSystem, WordPressComponentProps } from '../ui/context';
import * as styles from './styles';
import type { MenuGroupProps } from './types';

export function useMenuGroup(
props: WordPressComponentProps< MenuGroupProps, 'div' >
) {
const {
className = '',
children,
hideSeparator = false,
label = '',
...otherProps
} = useContextSystem( props, 'MenuGroup' );

const classNames = classnames( className, 'components-menu-group' );
const cx = useCx();
const menuGroupClassName = useMemo( () => {
return cx(
styles.MenuGroup,
hideSeparator && styles.MenuGroupWithHiddenSeparator,
classNames
);
}, [ className, hideSeparator ] );
const menuGroupLabelClassName = useMemo( () => {
return cx( styles.MenuGroupLabel, 'components-menu-group__label' );
}, [] );

return {
...otherProps,
menuGroupClassName,
menuGroupLabelClassName,
label,
children,
};
}
1 change: 1 addition & 0 deletions packages/components/src/menu-group/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as MenuGroup } from './component';
33 changes: 33 additions & 0 deletions packages/components/src/menu-group/stories/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* External dependencies
*/
import { boolean, text } from '@storybook/addon-knobs';

/**
* Internal dependencies
*/
import { MenuGroup } from '../';

export default {
title: 'Components/MenuGroup',
component: MenuGroup,
parameters: {
knobs: { disabled: false },
},
};

export const _default = () => {
const label = text( 'Label', 'MenuGroup label text' );
const hideSeparator = boolean( 'Hide top border separator', false );
const className = text( 'ClassName', 'menu-group-story' );

return (
<MenuGroup
hideSeparator={ hideSeparator }
label={ label }
className={ className }
>
<p>MenuGroup item</p>
</MenuGroup>
);
};
22 changes: 0 additions & 22 deletions packages/components/src/menu-group/style.scss

This file was deleted.

33 changes: 33 additions & 0 deletions packages/components/src/menu-group/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* External dependencies
*/
import { css } from '@emotion/react';

/**
* Internal dependencies
*/
import { COLORS, CONFIG } from '../utils';
import { space } from '../ui/utils/space';

export const MenuGroup = css`
margin-top: ${ space( 2 ) };
padding-top: ${ space( 2 ) };
border-top: ${ CONFIG.borderWidth } solid ${ COLORS.gray[ 900 ] };
`;

export const MenuGroupWithHiddenSeparator = css`
border-top: none;
margin-top: 0;
padding-top: 0;
`;

export const MenuGroupLabel = css`
padding: 0 ${ space( 2 ) };
margin-top: ${ space( 1 ) };
margin-bottom: ${ space( 3 ) };
color: ${ COLORS.gray[ 700 ] };
text-transform: uppercase;
font-size: ${ CONFIG.fontSizeSmall };
font-weight: ${ CONFIG.fontWeightHeading };
white-space: nowrap;
`;
28 changes: 28 additions & 0 deletions packages/components/src/menu-group/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* External dependencies
*/
// eslint-disable-next-line no-restricted-imports
import type { ReactNode } from 'react';

export type MenuGroupProps = {
/**
* Whether to hide top border on the MenuGroup container.
*
* @default false
*/
hideSeparator?: boolean;
/**
* Text to be displayed as the menu group header.
*/
label?: string;
/**
* An optional class name for the MenuGroup container.
*
* @default ''
*/
className?: string;
/**
* Child elements.
*/
children: ReactNode;
};
1 change: 0 additions & 1 deletion packages/components/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
@import "./form-token-field/style.scss";
@import "./guide/style.scss";
@import "./higher-order/navigate-regions/style.scss";
@import "./menu-group/style.scss";
@import "./menu-item/style.scss";
@import "./menu-items-choice/style.scss";
@import "./modal/style.scss";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { __, _x, sprintf } from '@wordpress/i18n';
* Internal dependencies
*/
import DropdownMenu from '../../dropdown-menu';
import MenuGroup from '../../menu-group';
import { MenuGroup } from '../../menu-group';
import MenuItem from '../../menu-item';
import { HStack } from '../../h-stack';
import { Heading } from '../../heading';
Expand Down

0 comments on commit e17628a

Please sign in to comment.