Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add checkbox implem to Menu #16888

Merged
merged 86 commits into from
Feb 12, 2021
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
d48612b
Auto-generate react-menu
ling1726 Feb 5, 2021
abd8efa
use correct react-menu version
ling1726 Feb 5, 2021
fc46c5b
Update packages/react-menu/package.json
ling1726 Feb 5, 2021
101354c
update files
ling1726 Feb 5, 2021
65c03bf
Merge branch 'feat/converged-menu-implem' of https://github.com/ling1…
ling1726 Feb 5, 2021
3670150
remove unnecessary
ling1726 Feb 5, 2021
7d59397
modified codeowners
ling1726 Feb 5, 2021
ce9bc5e
update codeowners
ling1726 Feb 5, 2021
b2b8da7
modify codeowners
ling1726 Feb 5, 2021
8cf1b35
Change files
ling1726 Feb 5, 2021
95156a9
Merge branch 'feat/converged-menu-implem' of https://github.com/ling1…
ling1726 Feb 5, 2021
6cf5fca
Merge branch 'master' into feat/converged-menu-implem
ecraig12345 Feb 5, 2021
df206a4
Merge remote-tracking branch 'origin' into feat/converged-menu-implem
ling1726 Feb 8, 2021
5b80fc5
add react-hooks dep
ling1726 Feb 8, 2021
728ddf0
Merge branch 'feat/converged-menu-implem' of https://github.com/ling1…
ling1726 Feb 8, 2021
8d23df5
revert unnecessary files
ling1726 Feb 8, 2021
586aa07
fix deps
ling1726 Feb 8, 2021
816547c
remove unnecessary comments
ling1726 Feb 8, 2021
8f3ffa6
type ref
ling1726 Feb 8, 2021
b1ca465
use state for selectors
ling1726 Feb 8, 2021
b094bed
add mising type
ling1726 Feb 8, 2021
7a397c2
remove cast
ling1726 Feb 8, 2021
a8f65f2
remove readme
ling1726 Feb 8, 2021
e2fa3f1
remove default div
ling1726 Feb 8, 2021
5242063
remove default div
ling1726 Feb 8, 2021
7586a93
fix story type
ling1726 Feb 8, 2021
e3f3da0
follow package structure
ling1726 Feb 8, 2021
4a33072
add export
ling1726 Feb 8, 2021
936ff5d
remove placeholder
ling1726 Feb 8, 2021
a387452
revert package.json
ling1726 Feb 8, 2021
52cefa2
Merge branch 'master' into feat/converged-menu-implem
ling1726 Feb 8, 2021
1b48ea2
type refs
ling1726 Feb 8, 2021
eb3fb95
Change files
ling1726 Feb 8, 2021
1aab8b6
Merge branch 'feat/converged-menu-implem' of https://github.com/ling1…
ling1726 Feb 8, 2021
61415e6
fix deps
ling1726 Feb 8, 2021
e7a0693
move conformance to devdep
ling1726 Feb 8, 2021
82f9f6b
update api
ling1726 Feb 8, 2021
f28db02
fixes to types
ling1726 Feb 8, 2021
f26b13d
remove coment
ling1726 Feb 8, 2021
8581e47
fix imports
ling1726 Feb 8, 2021
9f54bc5
update api
ling1726 Feb 8, 2021
a018d7d
update API
ling1726 Feb 8, 2021
6aa381f
fix types
ling1726 Feb 8, 2021
af33671
fix conformance tests
ling1726 Feb 8, 2021
c0fd3a5
fix exports
ling1726 Feb 8, 2021
2759105
Merge remote-tracking branch 'origin' into feat/converged-menu-implem
ling1726 Feb 8, 2021
79b953e
add checkbox and tests
ling1726 Feb 9, 2021
633c345
add snapshots
ling1726 Feb 9, 2021
4be7c77
use resolution for pretty-format
ling1726 Feb 9, 2021
176d7c9
Merge remote-tracking branch 'origin' into feat/converged-menu-implem
ling1726 Feb 9, 2021
57c4fff
add checkbox story
ling1726 Feb 9, 2021
561cf82
fix types
ling1726 Feb 9, 2021
3be1572
add comment
ling1726 Feb 9, 2021
0503840
use storybook decorator
ling1726 Feb 9, 2021
00e0301
remove imports, fix types
ling1726 Feb 9, 2021
a2d0408
make checkmark mandatory for checkbox
ling1726 Feb 9, 2021
83bc003
make value/item naming more consistent
ling1726 Feb 9, 2021
681e63b
move checked to selectable state
ling1726 Feb 9, 2021
1ab72c8
Change files
ling1726 Feb 9, 2021
6c58230
update codeowner for react-menu
ling1726 Feb 9, 2021
d4077aa
hide checkmark instead of not rendering
ling1726 Feb 9, 2021
1f0329a
add testing-library/hooks
ling1726 Feb 9, 2021
6530e24
revert codeowners
ling1726 Feb 9, 2021
786b361
correct tsdoc
ling1726 Feb 9, 2021
8d850dd
fix imports
ling1726 Feb 9, 2021
283c7d3
add icons to checkboxes, fix hover/focus styles
ling1726 Feb 9, 2021
4e12708
downgrade testing library and resolove testing-library/dom instead
ling1726 Feb 10, 2021
b934315
add checkbox implem
ling1726 Feb 11, 2021
5afd4d2
Merge branch 'master' into feat/converged-menu-implem
ling1726 Feb 11, 2021
3f93461
update md
ling1726 Feb 11, 2021
654b5e0
use styling hooks
ling1726 Feb 11, 2021
4c586d7
add comment
ling1726 Feb 11, 2021
c766a1f
add events
ling1726 Feb 11, 2021
d2fc8f5
use separate events for keyboard/click
ling1726 Feb 11, 2021
33a3916
remove e.persist
ling1726 Feb 11, 2021
33bf919
Update packages/react-menu/src/components/MenuItemRadio/MenuItemRadio…
ling1726 Feb 11, 2021
3608597
fix tests
ling1726 Feb 11, 2021
59127e9
fix tests
ling1726 Feb 11, 2021
96e3855
fix test
ling1726 Feb 11, 2021
f5cdcec
add required props for conformance test
ling1726 Feb 11, 2021
f09661b
Merge remote-tracking branch 'origin' into feat/converged-menu-implem
ling1726 Feb 11, 2021
7865164
ignore confirmance types for now
ling1726 Feb 11, 2021
68b4a63
update snapshots
ling1726 Feb 12, 2021
c6788b5
Merge branch 'master' into feat/converged-menu-implem
ling1726 Feb 12, 2021
4ee5d51
fix change file
ling1726 Feb 12, 2021
675bc60
deduplicate @babel/* entires
layershifter Feb 12, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@
"@types/react": "16.9.42",
"@types/react-dom": "16.9.10",
"eslint": "^7.1.0",
"//": "pretty-format contains typing only supported by TS 3.8+ remove when support in this repo is available",
"pretty-format": "^24.9.0",
"copy-to-clipboard": "3.2.0"
},
"syncpack": {
Expand Down
6 changes: 5 additions & 1 deletion packages/react-examples/.storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { withInfo } from '@storybook/addon-info';
import { withA11y } from '@storybook/addon-a11y';
import { withKnobs } from '@storybook/addon-knobs';
import { withPerformance } from 'storybook-addon-performance';
import { withKeytipLayer, withStrictMode, withCompatThemeProvider } from '@fluentui/storybook';
import { withKeytipLayer, withStrictMode, withCompatThemeProvider, withFluentProvider } from '@fluentui/storybook';

addDecorator(withPerformance);
addDecorator(withInfo());
Expand All @@ -29,6 +29,10 @@ if (
addDecorator(withCompatThemeProvider);
addDecorator(withStrictMode);
}
if (['react-menu'].includes('PACKAGE_NAME')) {
addDecorator(withFluentProvider);
addDecorator(withStrictMode);
}

addParameters({
a11y: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import * as React from 'react';

import { MenuList, MenuItem } from '@fluentui/react-menu';
import { teamsLightTheme } from '@fluentui/react-theme';
import { FluentProvider } from '@fluentui/react-provider';
import { CutIcon, PasteIcon, EditIcon } from '@fluentui/react-icons-mdl2';
import { MenuList, MenuItem, MenuItemCheckbox } from '@fluentui/react-menu';
import { CutIcon, PasteIcon, EditIcon, AcceptIcon } from '@fluentui/react-icons-mdl2';
import { makeStyles } from '@fluentui/react-make-styles';

const useContainerStyles = makeStyles([
Expand All @@ -27,25 +25,45 @@ const Container: React.FC = props => {
};

export const MenuListExample = () => (
<FluentProvider theme={teamsLightTheme}>
<Container>
<MenuList>
<MenuItem>Item</MenuItem>
<MenuItem>Item</MenuItem>
<MenuItem>Item</MenuItem>
</MenuList>
</Container>
</FluentProvider>
<Container>
<MenuList>
<MenuItem>Item</MenuItem>
<MenuItem>Item</MenuItem>
<MenuItem>Item</MenuItem>
</MenuList>
</Container>
);

export const MenuListWithIconsExample = () => (
<FluentProvider theme={teamsLightTheme}>
<Container>
<MenuList>
<MenuItem icon={<CutIcon />}>Item</MenuItem>
<MenuItem icon={<PasteIcon />}>Item</MenuItem>
<MenuItem icon={<EditIcon />}>Item</MenuItem>
</MenuList>
</Container>
);

export const MenuListWithCheckboxes = () => {
const checkmark = <AcceptIcon />;
const [checkedValues, setCheckedValues] = React.useState<Record<string, string[]>>({});
const onChange = (name: string, items: string[]) => {
setCheckedValues(s => ({ ...s, [name]: items }));
};

return (
<Container>
<MenuList>
<MenuItem icon={<CutIcon />}>Item</MenuItem>
<MenuItem icon={<PasteIcon />}>Item</MenuItem>
<MenuItem icon={<EditIcon />}>Item</MenuItem>
<MenuList checkedValues={checkedValues} onCheckedValuesChange={onChange}>
<MenuItemCheckbox name="checkbox" value="1" checkmark={checkmark}>
Item
</MenuItemCheckbox>
<MenuItemCheckbox name="checkbox" value="2" checkmark={checkmark}>
Item
</MenuItemCheckbox>
<MenuItemCheckbox name="checkbox" value="3" checkmark={checkmark}>
Item
</MenuItemCheckbox>
</MenuList>
</Container>
</FluentProvider>
);
);
};
32 changes: 32 additions & 0 deletions packages/react-menu/etc/react-menu.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ import { ShorthandProps } from '@fluentui/react-utils';
// @public
export const MenuItem: React.ForwardRefExoticComponent<Pick<MenuItemProps, React.ReactText> & React.RefAttributes<HTMLElement>>;

// @public
export const MenuItemCheckbox: React.ForwardRefExoticComponent<Pick<MenuItemCheckboxProps, React.ReactText> & React.RefAttributes<HTMLElement>>;

// Warning: (ae-forgotten-export) The symbol "MenuItemSelectableProps" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export interface MenuItemCheckboxProps extends ComponentProps, React.HTMLAttributes<HTMLElement>, MenuItemProps, MenuItemSelectableProps {
checkmark?: ShorthandProps;
icon?: ShorthandProps;
}

// @public
export const menuItemCheckboxShorthandProps: string[];

// Warning: (ae-forgotten-export) The symbol "MenuItemSelectableState" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export interface MenuItemCheckboxState extends MenuItemCheckboxProps, MenuItemState, MenuItemSelectableState {
checkmark?: ObjectShorthandProps<HTMLSpanElement>;
icon?: ObjectShorthandProps<HTMLElement>;
// (undocumented)
ref: React.MutableRefObject<HTMLElement>;
}

// @public (undocumented)
export interface MenuItemProps extends ComponentProps, React.HTMLAttributes<HTMLElement> {
icon?: ShorthandProps;
Expand All @@ -31,6 +55,8 @@ export const MenuList: React.ForwardRefExoticComponent<Pick<MenuListProps, React

// @public (undocumented)
export interface MenuListProps extends ComponentProps, React.HTMLAttributes<HTMLElement> {
checkedValues?: Record<string, string[]>;
onCheckedValuesChange?: (name: string, value: string[]) => void;
}

// @public (undocumented)
Expand All @@ -41,6 +67,9 @@ export interface MenuListState extends MenuListProps {
// @public
export const renderMenuItem: (state: MenuItemState) => JSX.Element;

// @public
export const renderMenuItemCheckbox: (state: MenuItemCheckboxState) => JSX.Element;

// @public
export const renderMenuList: (state: MenuListState) => JSX.Element;

Expand All @@ -50,6 +79,9 @@ export const useIconStyles: (selectors: MenuItemState) => string;
// @public
export const useMenuItem: (props: MenuItemProps, ref: React.Ref<HTMLElement>, defaultProps?: MenuItemProps | undefined) => MenuItemState;

// @public
export const useMenuItemCheckbox: (props: MenuItemCheckboxProps, ref: React.Ref<HTMLElement>, defaultProps?: MenuItemCheckboxProps | undefined) => MenuItemCheckboxState;

// @public
export const useMenuItemStyles: (state: MenuItemState) => void;

Expand Down
4 changes: 3 additions & 1 deletion packages/react-menu/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@
"update-snapshots": "just-scripts jest -u"
},
"devDependencies": {
"@fluentui/react-conformance": "^1.0.0",
"@fluentui/eslint-plugin": "^1.0.0-beta.1",
"@fluentui/react-conformance": "^1.0.0",
"@fluentui/scripts": "^1.0.0",
"@testing-library/react": "^11.2.5",
"@types/enzyme": "3.10.3",
"@types/enzyme-adapter-react-16": "1.0.3",
"@types/jest": "~24.9.0",
Expand All @@ -44,6 +45,7 @@
"react-test-renderer": "^16.3.0"
},
"dependencies": {
"@fluentui/keyboard-key": "^0.2.13",
"@fluentui/react-hooks": "^8.0.0-beta.10",
"@fluentui/react-make-styles": "^0.2.4",
"@fluentui/react-theme": "^0.3.1",
Expand Down
1 change: 1 addition & 0 deletions packages/react-menu/src/MenuItemCheckbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './components/MenuItemCheckbox/index';
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import * as React from 'react';
import * as renderer from 'react-test-renderer';
import { render, fireEvent } from '@testing-library/react';
import { ReactWrapper } from 'enzyme';
import { isConformant } from '../../common/isConformant';
import { MenuItemCheckbox } from './MenuItemCheckbox';
import { MenuListContext, MenuListProvider } from '../../menuListContext';

describe('MenuItemCheckbox conformance', () => {
isConformant({
Component: MenuItemCheckbox,
displayName: 'MenuItemCheckbox',
});

let wrapper: ReactWrapper | undefined;

afterEach(() => {
if (wrapper) {
wrapper.unmount();
wrapper = undefined;
}
});

/**
* Note: see more visual regression tests for MenuItemCheckbox in /apps/vr-tests.
*/
it('renders a default state', () => {
const component = renderer.create(<MenuItemCheckbox>Default MenuItemCheckbox</MenuItemCheckbox>);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});

describe('MenuItemCheckbox', () => {
const TestMenuListContext = (props: { children: React.ReactNode; context?: Partial<MenuListContext> }) => {
const contextValue: MenuListContext = {
checkedValues: {},
onCheckedValuesChange: jest.fn(),
...(props.context && props.context),
};

return <MenuListProvider value={contextValue}>{props.children}</MenuListProvider>;
};

it('should render checkmark slot if checked', () => {
// Arrange
const checkedValues = { test: ['1'] };
const checkmark = 'xxx';
const { getByText } = render(
<TestMenuListContext context={{ checkedValues }}>
<MenuItemCheckbox name="test" value="1" checkmark={checkmark}>
Checkbox
</MenuItemCheckbox>
</TestMenuListContext>,
);

// Assert
expect(getByText(checkmark)).not.toBeNull();
});

it('should render icon slot', () => {
// Arrange
const icon = 'xxx';
const { getByText } = render(
<TestMenuListContext>
<MenuItemCheckbox name="test" value="1" icon={icon}>
Checkbox
</MenuItemCheckbox>
</TestMenuListContext>,
);

// Assert
expect(getByText(icon)).not.toBeNull();
});

it('should set aria-checked value to true if value is checked', () => {
// Arrange
const checkedValues = { test: ['1'] };
const checkmark = 'xxx';
const { container } = render(
<TestMenuListContext context={{ checkedValues }}>
<MenuItemCheckbox name="test" value="1" checkmark={checkmark}>
Checkbox
</MenuItemCheckbox>
</TestMenuListContext>,
);

// Assert
expect(container.querySelector('[role="menuitemcheckbox"')?.getAttribute('aria-checked')).toEqual('true');
});

it.each([
['uncheck', ['1'], []],
['check', [], ['1']],
])('should %s checkbox on click', (_, checkedItems, expectedResult) => {
// Arrange
const checkboxName = 'name';
const checkedValues = { [checkboxName]: checkedItems };
const spy = jest.fn();
const { container } = render(
<TestMenuListContext context={{ checkedValues, onCheckedValuesChange: spy }}>
<MenuItemCheckbox name={checkboxName} value={'1'}>
Checkbox
</MenuItemCheckbox>
</TestMenuListContext>,
);

// Act
const menuitem = container.querySelector('[role="menuitemcheckbox"');
menuitem && fireEvent.click(menuitem);

// Assert
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(checkboxName, [...expectedResult]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react';
import { useMenuItemCheckbox } from './useMenuItemCheckbox';
import { MenuItemCheckboxProps } from './MenuItemCheckbox.types';
import { renderMenuItemCheckbox } from './renderMenuItemCheckbox';
import { useCheckmarkStyles } from '../../selectable';
import { useMenuItemStyles } from '../MenuItem/useMenuItemStyles';

/**
* Define a styled MenuItemCheckbox, using the `useMenuItemCheckbox` hook.
* {@docCategory MenuItemCheckbox}
*/
export const MenuItemCheckbox = React.forwardRef<HTMLElement, MenuItemCheckboxProps>((props, ref) => {
const state = useMenuItemCheckbox(props, ref);
useMenuItemStyles(state);
useCheckmarkStyles(state);

return renderMenuItemCheckbox(state);
});

MenuItemCheckbox.displayName = 'MenuItemCheckbox';
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';
import { ComponentProps, ObjectShorthandProps, ShorthandProps } from '@fluentui/react-utils';
import { MenuItemSelectableProps, MenuItemSelectableState } from '../../selectable';
import { MenuItemProps, MenuItemState } from '../MenuItem/MenuItem.types';

export interface MenuItemCheckboxProps
extends ComponentProps,
React.HTMLAttributes<HTMLElement>,
MenuItemProps,
MenuItemSelectableProps {
/**
* Icon slot rendered before children content
*/
icon?: ShorthandProps;

/**
* Slot for the checkmark indicator
*/
checkmark?: ShorthandProps;
}

export interface MenuItemCheckboxState extends MenuItemCheckboxProps, MenuItemState, MenuItemSelectableState {
ref: React.MutableRefObject<HTMLElement>;

/**
* Icon slot rendered before children content
*/
icon?: ObjectShorthandProps<HTMLElement>;

/**
* Slot for the checkmark indicator
*/
checkmark: ObjectShorthandProps<HTMLElement>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`MenuItemCheckbox conformance renders a default state 1`] = `
<div
aria-checked={false}
className=""
onClick={[Function]}
onKeyDown={[Function]}
role="menuitemcheckbox"
tabIndex={0}
>
Default MenuItemCheckbox
</div>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './MenuItemCheckbox.types';
export * from './MenuItemCheckbox';
export * from './renderMenuItemCheckbox';
export * from './useMenuItemCheckbox';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';
import { getSlots } from '@fluentui/react-utils';
import { MenuItemCheckboxState } from './MenuItemCheckbox.types';
import { menuItemCheckboxShorthandProps } from './useMenuItemCheckbox';

/** Function that renders the final JSX of the component */
export const renderMenuItemCheckbox = (state: MenuItemCheckboxState) => {
const { slots, slotProps } = getSlots(state, menuItemCheckboxShorthandProps);

return (
<slots.root {...slotProps.root}>
{state.checked && <slots.checkmark {...slotProps.checkmark} />}
<slots.icon {...slotProps.icon} />
{state.children}
</slots.root>
);
};
Loading