diff --git a/.changeset/drafts-dropdownmenu2-merged-into-actionmenu2.md b/.changeset/drafts-dropdownmenu2-merged-into-actionmenu2.md new file mode 100644 index 00000000000..4bb977a8f0e --- /dev/null +++ b/.changeset/drafts-dropdownmenu2-merged-into-actionmenu2.md @@ -0,0 +1,5 @@ +--- +'@primer/react': patch +--- + +Merges drafts/DropdownMenu2 into drafts/ActionMenu2 diff --git a/docs/content/drafts/ActionMenu2.mdx b/docs/content/drafts/ActionMenu2.mdx index 4a8a8250ebf..4ccc9b3c5a3 100644 --- a/docs/content/drafts/ActionMenu2.mdx +++ b/docs/content/drafts/ActionMenu2.mdx @@ -175,6 +175,44 @@ You can choose to have a different _anchor_ for the Menu dependending on the app ``` +### With selection + +Use `selectionVariant` on `ActionList` to create a menu with single or multiple selection. + +```javascript live noinline drafts +const fieldTypes = [ + {icon: TypographyIcon, name: 'Text'}, + {icon: NumberIcon, name: 'Number'}, + {icon: CalendarIcon, name: 'Date'}, + {icon: SingleSelectIcon, name: 'Single select'}, + {icon: IterationsIcon, name: 'Iteration'} +] + +const Example = () => { + const [selectedIndex, setSelectedIndex] = React.useState(1) + const selectedType = fieldTypes[selectedIndex] + + return ( + + + {selectedType.name} + + + + {fieldTypes.map((type, index) => ( + setSelectedIndex(index)}> + {type.name} + + ))} + + + + ) +} + +render() +``` + ### With External Anchor To create an anchor outside of the menu, you need to switch to controlled mode for the menu and pass it as `anchorRef` to `ActionMenu`. Make sure you add `aria-expanded` and `aria-haspopup` to the external anchor: @@ -241,12 +279,6 @@ render( ) ``` - - -Use `ActionMenu` to choose an action from a list. If you’re looking for single or multiple selection, use [DropdownMenu](/DropdownMenu) or [SelectPanel](/SelectPanel) instead. - - - ## Props / API reference ### ActionMenu @@ -305,6 +337,5 @@ Use `ActionMenu` to choose an action from a list. If you’re looking for single ## Related components - [ActionList](/drafts/ActionList2) -- [DropdownMenu](/DropdownMenu) - [SelectPanel](/SelectPanel) - [Button](/drafts/Button2) diff --git a/src/ActionList2/ActionListContainerContext.tsx b/src/ActionList2/ActionListContainerContext.tsx index 23cc31e9720..ddba7448833 100644 --- a/src/ActionList2/ActionListContainerContext.tsx +++ b/src/ActionList2/ActionListContainerContext.tsx @@ -5,8 +5,7 @@ import React from 'react' type ContextProps = { container?: string listRole?: string - itemRole?: string - selectionVariant?: 'single' | 'multiple' + selectionVariant?: 'single' | 'multiple' // TODO: Remove after DropdownMenu2 deprecation selectionAttribute?: 'aria-selected' | 'aria-checked' listLabelledBy?: string // This can be any function, we don't know anything about the arguments diff --git a/src/ActionList2/Item.tsx b/src/ActionList2/Item.tsx index ed93839c053..be233836fa6 100644 --- a/src/ActionList2/Item.tsx +++ b/src/ActionList2/Item.tsx @@ -7,7 +7,8 @@ import Box, {BoxProps} from '../Box' import sx, {SxProp, merge} from '../sx' import createSlots from '../utils/create-slots' import {AriaRole} from '../utils/types' -import {ListContext} from './List' +import {ListContext, ListProps} from './List' +import {GroupContext, GroupProps} from './Group' import {ActionListContainerContext} from './ActionListContainerContext' import {Selection} from './Selection' @@ -101,8 +102,21 @@ export const Item = React.forwardRef( }, forwardedRef ): JSX.Element => { - const {variant: listVariant, showDividers} = React.useContext(ListContext) - const {itemRole, afterSelect, selectionAttribute = 'aria-selected'} = React.useContext(ActionListContainerContext) + const {variant: listVariant, showDividers, selectionVariant: listSelectionVariant} = React.useContext(ListContext) + const {selectionVariant: groupSelectionVariant} = React.useContext(GroupContext) + const {container, afterSelect, selectionAttribute = 'aria-selected'} = React.useContext(ActionListContainerContext) + + let selectionVariant: ListProps['selectionVariant'] | GroupProps['selectionVariant'] + if (typeof groupSelectionVariant !== 'undefined') selectionVariant = groupSelectionVariant + else selectionVariant = listSelectionVariant + + /** Infer item role based on the container */ + let itemRole: ItemProps['role'] + if (container === 'ActionMenu' || container === 'DropdownMenu') { + if (selectionVariant === 'single') itemRole = 'menuitemradio' + else if (selectionVariant === 'multiple') itemRole = 'menuitemcheckbox' + else itemRole = 'menuitem' + } const {theme} = useTheme() diff --git a/src/ActionList2/List.tsx b/src/ActionList2/List.tsx index 1182b5107e9..de1363ed419 100644 --- a/src/ActionList2/List.tsx +++ b/src/ActionList2/List.tsx @@ -41,7 +41,11 @@ export const List = React.forwardRef( } /** if list is inside a Menu, it will get a role from the Menu */ - const {listRole, listLabelledBy} = React.useContext(ActionListContainerContext) + const { + listRole, + listLabelledBy, + selectionVariant: containerSelectionVariant // TODO: Remove after DropdownMenu2 deprecation + } = React.useContext(ActionListContainerContext) return ( ( {...props} ref={forwardedRef} > - {props.children} + + {props.children} + ) } diff --git a/src/ActionList2/Selection.tsx b/src/ActionList2/Selection.tsx index bb938ffb83c..acc86509893 100644 --- a/src/ActionList2/Selection.tsx +++ b/src/ActionList2/Selection.tsx @@ -1,8 +1,7 @@ import React from 'react' import {CheckIcon} from '@primer/octicons-react' -import {ListContext} from './List' -import {GroupContext} from './Group' -import {ActionListContainerContext} from './ActionListContainerContext' +import {ListContext, ListProps} from './List' +import {GroupContext, GroupProps} from './Group' import {ItemProps} from './Item' import {LeadingVisualContainer} from './Visuals' @@ -10,34 +9,23 @@ type SelectionProps = Pick export const Selection: React.FC = ({selected}) => { const {selectionVariant: listSelectionVariant} = React.useContext(ListContext) const {selectionVariant: groupSelectionVariant} = React.useContext(GroupContext) - const {container, selectionVariant: menuSelectionVariant} = React.useContext(ActionListContainerContext) /** selectionVariant in Group can override the selectionVariant in List root */ - /** fallback to selectionVariant from container menu if any (ActionMenu, DropdownMenu, SelectPanel ) */ - let selectionVariant + /** fallback to selectionVariant from container menu if any (ActionMenu, SelectPanel ) */ + let selectionVariant: ListProps['selectionVariant'] | GroupProps['selectionVariant'] if (typeof groupSelectionVariant !== 'undefined') selectionVariant = groupSelectionVariant - else selectionVariant = listSelectionVariant || menuSelectionVariant + else selectionVariant = listSelectionVariant - // if selectionVariant is not set on List, don't show selection if (!selectionVariant) { - // to avoid confusion, fail loudly instead of silently ignoring - if (selected) + // if selectionVariant is not set on List, but Item is selected + // fail loudly instead of silently ignoring + if (selected) { throw new Error( 'For Item to be selected, ActionList or ActionList.Group needs to have a selectionVariant defined' ) - return null - } - - if (container === 'ActionMenu') { - throw new Error( - 'ActionList cannot have a selectionVariant inside ActionMenu, please use DropdownMenu or SelectPanel instead. More information: https://primer.style/design/components/action-list#application' - ) - } - - if (container === 'DropdownMenu' && selectionVariant === 'multiple') { - throw new Error( - 'selectionVariant multiple cannot be used in DropdownMenu, please use SelectPanel instead. More information: https://primer.style/design/components/action-list#application' - ) + } else { + return null + } } if (selectionVariant === 'single') { diff --git a/src/ActionMenu2.tsx b/src/ActionMenu2.tsx index d01a04f7a13..5819eeca3d6 100644 --- a/src/ActionMenu2.tsx +++ b/src/ActionMenu2.tsx @@ -113,8 +113,8 @@ const Overlay: React.FC = ({children, ...overlayProps}) => { value={{ container: 'ActionMenu', listRole: 'menu', - itemRole: 'menuitem', listLabelledBy: anchorId, + selectionAttribute: 'aria-checked', // Should this be here? afterSelect: onClose }} > diff --git a/src/DropdownMenu2.tsx b/src/DropdownMenu2.tsx index 20f48e6e032..0e552bc5cef 100644 --- a/src/DropdownMenu2.tsx +++ b/src/DropdownMenu2.tsx @@ -117,7 +117,6 @@ const Overlay: React.FC = ({children, ...overlayProps}) => { value={{ container: 'DropdownMenu', listRole: 'menu', - itemRole: 'menuitemradio', listLabelledBy: anchorId, selectionVariant: 'single', selectionAttribute: 'aria-checked', diff --git a/src/__tests__/ActionMenu2.test.tsx b/src/__tests__/ActionMenu2.test.tsx index 88f9d35b6d9..20cfb9fcda2 100644 --- a/src/__tests__/ActionMenu2.test.tsx +++ b/src/__tests__/ActionMenu2.test.tsx @@ -7,10 +7,11 @@ import {ActionMenu} from '../ActionMenu2' import {ActionList} from '../ActionList2' import {behavesAsComponent, checkExports, checkStoriesForAxeViolations} from '../utils/testing' import {BaseStyles, ThemeProvider, SSRProvider} from '..' +import {SingleSelection, MixedSelection} from '../../src/stories/ActionMenu2/examples.stories' import '@testing-library/jest-dom' expect.extend(toHaveNoViolations) -function SimpleActionMenu(): JSX.Element { +function Example(): JSX.Element { return ( @@ -39,7 +40,7 @@ describe('ActionMenu', () => { behavesAsComponent({ Component: ActionList, options: {skipAs: true, skipSx: true}, - toRender: () => + toRender: () => }) checkExports('ActionMenu2', { @@ -48,7 +49,7 @@ describe('ActionMenu', () => { }) it('should open Menu on MenuButton click', async () => { - const component = HTMLRender() + const component = HTMLRender() const button = component.getByText('Toggle Menu') fireEvent.click(button) expect(component.getByRole('menu')).toBeInTheDocument() @@ -56,7 +57,7 @@ describe('ActionMenu', () => { }) it('should open Menu on MenuButton keypress', async () => { - const component = HTMLRender() + const component = HTMLRender() const button = component.getByText('Toggle Menu') // We pass keycode here to navigate a implementation detail in react-testing-library @@ -67,7 +68,7 @@ describe('ActionMenu', () => { }) it('should close Menu on selecting an action with click', async () => { - const component = HTMLRender() + const component = HTMLRender() const button = component.getByText('Toggle Menu') fireEvent.click(button) @@ -79,7 +80,7 @@ describe('ActionMenu', () => { }) it('should close Menu on selecting an action with Enter', async () => { - const component = HTMLRender() + const component = HTMLRender() const button = component.getByText('Toggle Menu') fireEvent.click(button) @@ -91,7 +92,7 @@ describe('ActionMenu', () => { }) it('should not close Menu if event is prevented', async () => { - const component = HTMLRender() + const component = HTMLRender() const button = component.getByText('Toggle Menu') fireEvent.click(button) @@ -103,38 +104,42 @@ describe('ActionMenu', () => { cleanup() }) - it('should throw when selectionVariant is provided to ActionList within ActionMenu', async () => { - // we expect console.error to be called, so we suppress that in the test - const mockError = jest.spyOn(console, 'error').mockImplementation(() => jest.fn()) - - expect(() => { - const component = HTMLRender( - - - - - Toggle Menu - - - Primer React - - - - - - - ) - - const button = component.getByText('Toggle Menu') - fireEvent.click(button) - }).toThrow('ActionList cannot have a selectionVariant inside ActionMenu') + it('should be able to select an Item with selectionVariant', async () => { + const component = HTMLRender( + + + + ) + const button = component.getByLabelText('Select field type') + fireEvent.click(button) + + // select first item by role, that would close the menu + fireEvent.click(component.getAllByRole('menuitemradio')[0]) + expect(component.queryByRole('menu')).not.toBeInTheDocument() + + // open menu again and check if the first option is checked + fireEvent.click(button) + expect(component.getAllByRole('menuitemradio')[0]).toHaveAttribute('aria-checked', 'true') + cleanup() + }) + + it('should assign the right roles with groups & mixed selectionVariant', async () => { + const component = HTMLRender( + + + + ) + const button = component.getByLabelText('Select field type to group by') + fireEvent.click(button) + + expect(component.getByLabelText('Status')).toHaveAttribute('role', 'menuitemradio') + expect(component.getByLabelText('Clear Group by')).toHaveAttribute('role', 'menuitem') cleanup() - mockError.mockRestore() }) it('should have no axe violations', async () => { - const {container} = HTMLRender() + const {container} = HTMLRender() const results = await axe(container) expect(results).toHaveNoViolations() cleanup() diff --git a/src/__tests__/DropdownMenu2.test.tsx b/src/__tests__/DropdownMenu2.test.tsx index 1379cecd6ed..04917c6e3a4 100644 --- a/src/__tests__/DropdownMenu2.test.tsx +++ b/src/__tests__/DropdownMenu2.test.tsx @@ -96,36 +96,6 @@ describe('DropdownMenu', () => { cleanup() }) - it('should throw when selectionVariant=multiple is provided to ActionList within DropdownMenu', async () => { - // we expect console.error to be called, so we suppress that in the test - const mockError = jest.spyOn(console, 'error').mockImplementation(() => jest.fn()) - - expect(() => { - const component = HTMLRender( - - - - - Select a field - - - Primer React - - - - - - - ) - - const button = component.getByText('Select a field') - fireEvent.click(button) - }).toThrow('selectionVariant multiple cannot be used in DropdownMenu') - - cleanup() - mockError.mockRestore() - }) - checkStoriesForAxeViolations('DropdownMenu2/fixtures') checkStoriesForAxeViolations('DropdownMenu2/examples') }) diff --git a/src/stories/ActionList2/examples.stories.tsx b/src/stories/ActionList2/examples.stories.tsx index 5f27efe6da5..3f8ed97b884 100644 --- a/src/stories/ActionList2/examples.stories.tsx +++ b/src/stories/ActionList2/examples.stories.tsx @@ -108,7 +108,7 @@ export function SingleSelection(): JSX.Element { <>

Single Selection

-

This pattern appears inside a nested DropdownMenu in Memex view options.

+

This pattern appears inside a nested menu in Memex view options.

{options.map((option, index) => ( @@ -266,7 +266,7 @@ export function MixedSelection(): JSX.Element {

In this list, there is a ActionList.Group with single selection for picking one option, followed by a Item that - is an action. This pattern appears inside a DropdownMenu for selection view options in Memex + is an action. This pattern appears inside a menu for selection view options in Memex

diff --git a/src/stories/ActionMenu2/examples.stories.tsx b/src/stories/ActionMenu2/examples.stories.tsx index 0130751049a..3412c22e6e7 100644 --- a/src/stories/ActionMenu2/examples.stories.tsx +++ b/src/stories/ActionMenu2/examples.stories.tsx @@ -1,9 +1,20 @@ import React from 'react' import {Meta} from '@storybook/react' -import {ThemeProvider} from '../..' -import BaseStyles from '../../BaseStyles' -import {ActionMenu} from '../../ActionMenu2' -import {ActionList} from '../../ActionList2' +import {ThemeProvider, BaseStyles, Box, Text, Avatar} from '../..' +import {ActionMenu, ActionList} from '../../drafts' +import { + GearIcon, + MilestoneIcon, + CalendarIcon, + IterationsIcon, + NumberIcon, + SingleSelectIcon, + TypographyIcon, + IssueOpenedIcon, + TableIcon, + PeopleIcon, + XIcon +} from '@primer/octicons-react' const meta: Meta = { title: 'Composite components/ActionMenu2/examples', @@ -25,7 +36,7 @@ const meta: Meta = { } export default meta -export function SimpleListStory(): JSX.Element { +export function MenuWithActions(): JSX.Element { const [actionFired, fireAction] = React.useState('') const onSelect = (name: string) => fireAction(name) @@ -60,4 +71,285 @@ export function SimpleListStory(): JSX.Element { ) } -SimpleListStory.storyName = 'Simple Menu' + +const fieldTypes = [ + {icon: TypographyIcon, name: 'Text'}, + {icon: NumberIcon, name: 'Number'}, + {icon: CalendarIcon, name: 'Date'}, + {icon: SingleSelectIcon, name: 'Single select'}, + {icon: IterationsIcon, name: 'Iteration'} +] + +export function SingleSelection(): JSX.Element { + const [selectedIndex, setSelectedIndex] = React.useState(0) + const selectedType = fieldTypes[selectedIndex] + return ( + <> +

Single Selection

+ +

This pattern has a single section with the selected value shown in the button

+ + + + {selectedType.name} + + + + {fieldTypes.map((type, index) => ( + setSelectedIndex(index)} + disabled={index === 3} + > + {type.name} + + ))} + + + + + ) +} + +export function SingleSelectionWithPlaceholder(): JSX.Element { + const [selectedIndex, setSelectedIndex] = React.useState(-1) + const selectedType = fieldTypes[selectedIndex] || {} + + return ( + <> +

With placeholder

+ +

This pattern has a placeholder in menu button when no value is selected yet

+ + + + {selectedType.name || 'Pick a field type'} + + + + {fieldTypes.map((type, index) => ( + setSelectedIndex(index)}> + {type.name} + + ))} + + + + + ) +} + +const milestones = [ + {name: 'FY21 - Q2', due: 'December 31, 2021', progress: 90}, + {name: 'FY22 - Q3', due: 'March 31, 2022', progress: 10}, + {name: 'FY23 - Q1', due: 'June 30, 2022', progress: 0}, + {name: 'FY23 - Q2', due: 'December 30, 2022', progress: 0} +] + +export function GroupsAndDescription(): JSX.Element { + const [selectedMilestone, setSelectedMilestone] = React.useState() + + return ( + <> +

Milestone selector

+ + + + Milestone + + + + + {milestones + .filter(milestone => !milestone.name.includes('21')) + .map((milestone, index) => ( + setSelectedMilestone(milestone)} + > + + + + {milestone.name} + Due by {milestone.due} + + ))} + + + {milestones + .filter(milestone => milestone.name.includes('21')) + .map((milestone, index) => ( + setSelectedMilestone(milestone)} + > + + + + {milestone.name} + Due by {milestone.due} + + ))} + + + + + {selectedMilestone ? ( + + {selectedMilestone.name} + + ) : ( + + No milestone + + )} + + + ) +} + +const users = [ + {login: 'pksjce', name: 'Pavithra Kodmad'}, + {login: 'jfuchs', name: 'Jonathan Fuchs'}, + {login: 'colebemis', name: 'Cole Bemis'}, + {login: 'mperrotti', name: 'Mike Perrotti'}, + {login: 'dgreif', name: 'Dusty Greif'}, + {login: 'smockle', name: 'Clay Miller'}, + {login: 'siddharthkp', name: 'Siddharth Kshetrapal'} +] + +export function MultipleSelection(): JSX.Element { + const [assignees, setAssignees] = React.useState(users.slice(0, 2)) + + const toggleAssignee = (assignee: typeof users[number]) => { + const assigneeIndex = assignees.findIndex(a => a.login === assignee.login) + + if (assigneeIndex === -1) setAssignees([...assignees, assignee]) + else setAssignees(assignees.filter((_, index) => index !== assigneeIndex)) + } + + return ( + <> +

Multi Select List

+ +

ActionMenu with multiple selection is not seen in production. You see SelectPanel used instead.

+ + + + + Assignees + + + + {users.map(user => ( + assignee.login === user.login))} + onSelect={() => toggleAssignee(user)} + > + + + + {user.login} + {user.name} + + ))} + + + + + + ) +} + +export function MixedSelection(): JSX.Element { + const [selectedIndex, setSelectedIndex] = React.useState(1) + + const options = [ + {text: 'Status', icon: IssueOpenedIcon}, + {text: 'Stage', icon: TableIcon}, + {text: 'Assignee', icon: PeopleIcon}, + {text: 'Team', icon: TypographyIcon}, + {text: 'Estimate', icon: NumberIcon}, + {text: 'Due Date', icon: CalendarIcon} + ] + + const selectedOption = selectedIndex !== null && options[selectedIndex] + + return ( + <> +

List with mixed selection

+ +

+ In this list, there is a ActionList.Group with single selection for picking one option, followed by a Item that + is an action. This pattern appears inside a ActionMenu for selection view options in Memex +

+ + + + {selectedOption ? `Group by ${selectedOption.text}` : 'Group items by'} + + + + + {options.map((option, index) => ( + setSelectedIndex(index)} + > + + + + {option.text} + + ))} + + {typeof selectedIndex === 'number' && ( + + + setSelectedIndex(null)} role="menuitem"> + + + + Clear Group by + + + )} + + + + + ) +} diff --git a/src/stories/ActionMenu2/fixtures.stories.tsx b/src/stories/ActionMenu2/fixtures.stories.tsx index 3fbf73013dc..1a279b1d46b 100644 --- a/src/stories/ActionMenu2/fixtures.stories.tsx +++ b/src/stories/ActionMenu2/fixtures.stories.tsx @@ -1,16 +1,7 @@ import React from 'react' import {Meta} from '@storybook/react' -import {ThemeProvider} from '../..' -import BaseStyles from '../../BaseStyles' -import {ActionMenu} from '../../ActionMenu2' -import {ActionList} from '../../ActionList2' -import {Button} from '../../Button2' -import {IconButton} from '../../Button2/IconButton' -import Box from '../../Box' -import Text from '../../Text' -import TextInput from '../../TextInput' -import StyledOcticon from '../../StyledOcticon' -import FormGroup from '../../FormGroup' +import {ThemeProvider, BaseStyles, Box, Text, TextInput, StyledOcticon, FormGroup} from '../..' +import {ActionMenu, ActionList, Button, IconButton} from '../../drafts' import { ServerIcon, PlusCircleIcon, @@ -25,6 +16,11 @@ import { SearchIcon, VersionsIcon, TableIcon, + CalendarIcon, + IterationsIcon, + NumberIcon, + SingleSelectIcon, + TypographyIcon, IconProps } from '@primer/octicons-react' @@ -86,7 +82,6 @@ export function ActionsStory(): JSX.Element { ) } -ActionsStory.storyName = 'Actions' export function ExternalAnchor(): JSX.Element { const [actionFired, fireAction] = React.useState('') @@ -133,7 +128,6 @@ export function ExternalAnchor(): JSX.Element { ) } -ExternalAnchor.storyName = 'External Anchor' export function ControlledMenu(): JSX.Element { const [actionFired, fireAction] = React.useState('') @@ -191,7 +185,6 @@ export function ControlledMenu(): JSX.Element { ) } -ControlledMenu.storyName = 'Controlled Menu' export function CustomAnchor(): JSX.Element { const [actionFired, fireAction] = React.useState('') @@ -233,7 +226,6 @@ export function CustomAnchor(): JSX.Element { ) } -CustomAnchor.storyName = 'Custom Anchor' export function MemexTableMenu(): JSX.Element { const [name, setName] = React.useState('Estimate') @@ -297,7 +289,6 @@ export function MemexTableMenu(): JSX.Element { ) } -MemexTableMenu.storyName = 'Memex Table Menu' /* copied from github/memex */ const LayoutToggleItem = ({ @@ -408,19 +399,15 @@ export function MemexViewOptionsMenu(): JSX.Element { React - - - + + + @@ -495,7 +482,105 @@ export function MemexViewOptionsMenu(): JSX.Element { ) } -MemexViewOptionsMenu.storyName = 'Memex View Options Menu' + +export function MemexIteration(): JSX.Element { + const [duration, setDuration] = React.useState(1) + + return ( + <> +

Memex Iteration Menu

+ + + + {duration} {duration > 1 ? 'weeks' : 'week'} + + + + {[1, 2, 3, 4, 5, 6].map(weeks => ( + setDuration(weeks)}> + {weeks} {weeks > 1 ? 'weeks' : 'week'} + + ))} + + + + + ) +} + +const fieldTypes = [ + {icon: TypographyIcon, name: 'Text'}, + {icon: NumberIcon, name: 'Number'}, + {icon: CalendarIcon, name: 'Date'}, + {icon: SingleSelectIcon, name: 'Single select'}, + {icon: IterationsIcon, name: 'Iteration'} +] + +export function MemexAddColumn(): JSX.Element { + const [selectedIndex, setSelectedIndex] = React.useState(0) + const selectedType = fieldTypes[selectedIndex] + + const [duration, setDuration] = React.useState(1) + + return ( + <> +

Memex Add column

+ + + + + + {selectedType.name} + + + + {fieldTypes.map((type, index) => ( + setSelectedIndex(index)} + > + {type.icon} {type.name} + + ))} + + + + Options + + + Duration: + + + {duration} {duration > 1 ? 'weeks' : 'week'} + + + + {[1, 2, 3, 4, 5, 6].map(weeks => ( + setDuration(weeks)}> + {weeks} {weeks > 1 ? 'weeks' : 'week'} + + ))} + + + + + + + ) +} export function OverlayProps(): JSX.Element { const [open, setOpen] = React.useState(false) @@ -534,30 +619,7 @@ export function OverlayProps(): JSX.Element {


- - - ) -} -OverlayProps.storyName = 'Overlay Props' - -export function UnexpectedSelectionVariant(): JSX.Element { - return ( - <> -

Expect error if selectionVariant is passed

- - - Menu - - - Copy link - Quote reply - Edit comment - - Delete file - - - + ) } -UnexpectedSelectionVariant.storyName = 'Unexpected selectionVariant' diff --git a/src/stories/Overlay.stories.tsx b/src/stories/Overlay.stories.tsx index 5392d4c9a15..dd635c6338a 100644 --- a/src/stories/Overlay.stories.tsx +++ b/src/stories/Overlay.stories.tsx @@ -23,7 +23,7 @@ import { } from '..' import type {AnchorSide} from '@primer/behaviors' import {DropdownMenu, DropdownButton} from '../DropdownMenu' -import {DropdownMenu as DropdownMenu2, ActionList as ActionList2} from '../drafts' +import {ActionMenu, ActionList as ActionList2} from '../drafts' import {ItemInput} from '../ActionList/List' export default { @@ -362,20 +362,20 @@ export const MemexNestedOverlays = () => { Duration: - - + + {duration} - - - + + + {durations.map(item => ( setDuration(item)}> {item} ))} - - + + diff --git a/src/utils/types/AriaRole.ts b/src/utils/types/AriaRole.ts index 87471c6c67f..b1d1d1f4914 100644 --- a/src/utils/types/AriaRole.ts +++ b/src/utils/types/AriaRole.ts @@ -35,7 +35,7 @@ export type AriaRole = | 'menu' | 'menubar' | 'menuitem' - | 'menuitemcheckbox ' + | 'menuitemcheckbox' | 'menuitemradio' | 'navigation' | 'none'