Skip to content

Commit

Permalink
chore: merge latest master
Browse files Browse the repository at this point in the history
  • Loading branch information
jor-row committed Sep 26, 2024
2 parents 16a39b8 + 1973bef commit 19bbbb3
Show file tree
Hide file tree
Showing 16 changed files with 9,488 additions and 119 deletions.
15 changes: 14 additions & 1 deletion src/components/ContentSeparator/ContentSeparator.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */

import React from 'react';
import classnames from 'classnames';

import './ContentSeparator.style.scss';
import { Props } from './ContentSeparator.types';
import { STYLE } from './ContentSeparator.constants';
import { useSeparator } from '@react-aria/separator';
import { PartialNode } from '@react-stately/collections';

const ContentSeparator: React.FC<Props> = (props: Props) => {
const { className, children, gradient } = props;
Expand All @@ -24,8 +27,18 @@ const ContentSeparator: React.FC<Props> = (props: Props) => {
);
};

// This allows the ContentSeparator to be used within Menu
// @ts-ignore
ContentSeparator.getCollectionNode = function* getCollectionNode<T>(
props: Props
): Generator<PartialNode<T>> {
yield {
type: 'item',
props: { ...props, _isSeparator: true },
};
};

/**
* ContentSeparator with text/other component in the middle
*/

export default ContentSeparator;
37 changes: 37 additions & 0 deletions src/components/ContentSeparator/ContentSeparator.unit.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,41 @@ describe('ContentSeparator', () => {
expect(container.find('li').prop('data-gradient')).toEqual(true);
});
});

describe('menus', () => {
it('getCollectionNode should yield a single node with type "item" and have prop _isSeparator', () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - We don't export this method in the types
const generator = ContentSeparator.getCollectionNode();
const results = Array.from(generator);

expect(results).toHaveLength(1);
expect(results[0]).toEqual({
type: 'item',
props: {
_isSeparator: true,
},
});
});

it('getCollectionNode should yield a single node with props passed', () => {
const props = {
className: 'main-separator',
};

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - We don't export this method in the types
const generator = ContentSeparator.getCollectionNode(props);
const results = Array.from(generator);

expect(results).toHaveLength(1);
expect(results[0]).toEqual({
type: 'item',
props: {
_isSeparator: true,
...props,
},
});
});
});
});
93 changes: 28 additions & 65 deletions src/components/Menu/Menu.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import Documentation from './Menu.stories.docs.mdx';
import { action } from '@storybook/addon-actions';
import Flex from '../Flex';
import Avatar from '../Avatar';
import ContentSeparator from '../ContentSeparator';
import { PresenceType } from '../Avatar/Avatar.types';
import { ListHeader, ListItemBaseSection, Icon } from '..';
import { Icon, Text } from '..';
import './Menu.stories.style.scss';

export default {
Expand Down Expand Up @@ -82,39 +83,21 @@ Sections.parameters = {
onSelectionChange: menuOnSelectionChange,
onAction: menuOnAction,
children: [
<Section
key="0"
title={
<ListHeader outline={false}>
<ListItemBaseSection position="fill">Europe</ListItemBaseSection>
</ListHeader>
}
>
<Section key="0" title="Europe">
<Item key="00">Spain</Item>
<Item key="01">France</Item>
<Item key="02">Italy</Item>
</Section>,
<Section
key="1"
title={
<ListHeader outline={true} outlinePosition="top" outlineColor="secondary">
<ListItemBaseSection position="fill">Asia</ListItemBaseSection>
</ListHeader>
}
>
<ContentSeparator key="sep-0" />,
<Section key="1" title="Asia">
<Item key="10">India</Item>
<Item key="11">China</Item>
<Item key="12">Japan</Item>
</Section>,
<Section
key="2"
title={
<ListHeader outline={true} outlinePosition="top" outlineColor="secondary">
<ListItemBaseSection position="fill">America</ListItemBaseSection>
</ListHeader>
}
>
<ContentSeparator key="sep-1" />,
<Section key="2" title="America">
<Item key="13">USA</Item>
<ContentSeparator key="sep-21" />
<Item key="14">Mexico</Item>
<Item key="15">Canada</Item>
</Section>,
Expand Down Expand Up @@ -146,52 +129,42 @@ SelectionGroups.parameters = {
<SelectionGroup
key="0"
selectionMode="multiple"
aria-label="First group"
onSelectionChange={(...rest) => {
console.log('singleselection1', rest);
}}
onAction={(...rest) => {
console.log('selectionOnAction1', rest);
}}
title={
<ListHeader outline={false}>
<ListItemBaseSection position="start">
<Icon scale={16} name="speaker" strokeColor="none" />
</ListItemBaseSection>
<ListItemBaseSection position="fill">
Speaker (you can choose many)
</ListItemBaseSection>
</ListHeader>
<Flex direction="row" alignItems="center" xgap="0.25rem">
<Icon scale={16} name="speaker" strokeColor="none" />
<Text type="body-secondary">Speaker (you can choose many)</Text>
</Flex>
}
>
<Item key="00">System default speaker</Item>
<Item key="01">Default - External Headphones (Built-in)</Item>
<ContentSeparator key="sep-21" />
<Item key="02">Desk Pro Web Camera</Item>
<Item key="03">MacBook Pro Speakers</Item>
<Item key="04">Webex Media Audio Device</Item>
</SelectionGroup>,
<ContentSeparator key="sep-0" />,
<SelectionGroup
key="1"
selectionMode="single"
tickPosition="right"
aria-label="Second group"
onSelectionChange={(...rest) => {
console.log('singleselection2', rest);
}}
onAction={(...rest) => {
console.log('selectionOnAction2', rest);
}}
title={
<>
<ListHeader outline={true} outlinePosition="top" outlineColor="secondary">
<ListItemBaseSection position="start">
<Icon scale={16} name="microphone" strokeColor="none" />
</ListItemBaseSection>
<ListItemBaseSection position="fill">
Microphone (you can choose one)
</ListItemBaseSection>
</ListHeader>
</>
<Flex direction="row" alignItems="center" xgap="0.25rem">
<Icon scale={16} name="microphone" strokeColor="none" />
<Text type="body-secondary">Microphone (you can choose one)</Text>
</Flex>
}
>
<Item key="10">No Microphone</Item>
Expand All @@ -200,6 +173,7 @@ SelectionGroups.parameters = {
<Item key="13">MacBook Pro Microphone</Item>
<Item key="14">Webex Media Audio Device</Item>
</SelectionGroup>,
<ContentSeparator key="sep-1" />,
<SelectionGroup
key="2"
tickPosition="none"
Expand All @@ -210,24 +184,17 @@ SelectionGroups.parameters = {
{ key: '22', value: 'Music mode' },
]}
selectionMode="single"
aria-label="Second group"
onSelectionChange={(...rest) => {
console.log('singleselection3', rest);
}}
onAction={(...rest) => {
console.log('selectionOnAction3', rest);
}}
title={
<>
<ListHeader outline={true} outlinePosition="top" outlineColor="secondary">
<ListItemBaseSection position="start">
<Icon scale={16} name="adjust-microphone" strokeColor="none" />
</ListItemBaseSection>
<ListItemBaseSection position="fill">
Webex smart audio (You can choose one)
</ListItemBaseSection>
</ListHeader>
</>
<Flex direction="row" alignItems="center" xgap="0.25rem">
<Icon scale={16} name="adjust-microphone" strokeColor="none" />
<Text type="body-secondary">Webex smart audio (You can choose one)</Text>
</Flex>
}
>
{(item) => (
Expand All @@ -236,6 +203,7 @@ SelectionGroups.parameters = {
</Item>
)}
</SelectionGroup>,
<ContentSeparator key="sep-2" />,
<SelectionGroup
key="3"
tickPosition="none"
Expand All @@ -248,22 +216,17 @@ SelectionGroups.parameters = {
{ key: '32', value: 'Side by side' },
]}
selectionMode="single"
aria-label="Third group"
onSelectionChange={(...rest) => {
console.log('singleselection4', rest);
}}
onAction={(...rest) => {
console.log('selectionOnAction4', rest);
}}
title={
<>
<ListHeader outline={true} outlinePosition="top" outlineColor="secondary">
<ListItemBaseSection position="start">
<Icon scale={16} name="accessibility" strokeColor="none" />
</ListItemBaseSection>
<ListItemBaseSection position="fill">Layout</ListItemBaseSection>
</ListHeader>
</>
<Flex direction="row" alignItems="center" xgap="0.25rem">
<Icon scale={16} name="accessibility" strokeColor="none" />
<Text type="body-secondary">Layout</Text>
</Flex>
}
>
{(item) => (
Expand Down
20 changes: 18 additions & 2 deletions src/components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import MenuItem from '../MenuItem';
import { mergeProps } from '@react-aria/utils';
import MenuSection from '../MenuSection';
import MenuSelectionGroup from '../MenuSelectionGroup';
import ContentSeparator from '../ContentSeparator';

export const MenuContext = React.createContext<MenuContextValue>({});

Expand Down Expand Up @@ -63,10 +64,18 @@ const Menu = <T extends object>(props: Props<T>, providedRef: RefObject<HTMLDivE

const internalRef = useRef();
const ref = providedRef || internalRef;
const itemArray = Array.from(state.collection.getKeys());

const { menuProps } = useMenu(_props, state, ref);
// Ensure all separators are in the disabledKeys set so they are not interactable
// This may need copying instead of modifying due to how React works
itemArray.forEach((key) => {
const item = state.collection.getItem(key);
if (item.props?._isSeparator) {
state.disabledKeys.add(key);
}
});

const itemArray = Array.from(state.collection.getKeys());
const { menuProps } = useMenu(_props, state, ref);

const renderItem = useCallback(
<T extends object>(item: Node<T>, state: TreeState<T>) => {
Expand All @@ -90,6 +99,13 @@ const Menu = <T extends object>(props: Props<T>, providedRef: RefObject<HTMLDivE
return;
}

if (item.props?._isSeparator) {
const props = { ...item.props };
delete props._isSeparator;

return <ContentSeparator {...props} />;
}

let menuItem = (
<MenuItem key={item.key} item={item} state={state} onAction={_props.onAction} />
);
Expand Down
Loading

0 comments on commit 19bbbb3

Please sign in to comment.