()
+ const {openWithFocus} = useMenuInitialFocus(open, onOpen, containerRef)
+ useTypeaheadFocus(open, containerRef)
+
+ return (
+
+
+
+ )
+}
+
+Menu.displayName = 'ActionMenu'
+export const ActionMenu = Object.assign(Menu, {Button: MenuButton, Anchor, Overlay, Divider})
diff --git a/src/Autocomplete/AutocompleteMenu.tsx b/src/Autocomplete/AutocompleteMenu.tsx
index 22e99c61652..4151132ef47 100644
--- a/src/Autocomplete/AutocompleteMenu.tsx
+++ b/src/Autocomplete/AutocompleteMenu.tsx
@@ -1,7 +1,7 @@
import React, {useContext, useEffect, useMemo, useRef, useState} from 'react'
import {scrollIntoView} from '@primer/behaviors'
import type {ScrollIntoViewOptions} from '@primer/behaviors'
-import {ActionList, ItemProps} from '../deprecated/ActionList'
+import {ActionList, ItemProps} from '../ActionList'
import {useFocusZone} from '../hooks/useFocusZone'
import {ComponentProps, MandateProps} from '../utils/types'
import {Box, Spinner, useSSRSafeId} from '../'
diff --git a/src/DropdownMenu/DropdownMenu.tsx b/src/DropdownMenu/DropdownMenu.tsx
index 3e084002318..37b2687449e 100644
--- a/src/DropdownMenu/DropdownMenu.tsx
+++ b/src/DropdownMenu/DropdownMenu.tsx
@@ -1,7 +1,7 @@
import React, {useCallback, useMemo} from 'react'
-import {List, GroupedListProps, ListPropsBase, ItemInput} from '../deprecated/ActionList/List'
+import {List, GroupedListProps, ListPropsBase, ItemInput} from '../ActionList/List'
import {DropdownButton, DropdownButtonProps} from './DropdownButton'
-import {ItemProps} from '../deprecated/ActionList/Item'
+import {ItemProps} from '../ActionList/Item'
import {AnchoredOverlay} from '../AnchoredOverlay'
import {OverlayProps} from '../Overlay'
import {AnchoredOverlayWrapperAnchorProps} from '../AnchoredOverlay/AnchoredOverlay'
diff --git a/src/DropdownMenu2.tsx b/src/DropdownMenu2.tsx
index 02697c66c66..1773eb8e85e 100644
--- a/src/DropdownMenu2.tsx
+++ b/src/DropdownMenu2.tsx
@@ -5,8 +5,8 @@ import {Button, ButtonProps} from './Button2'
import {AnchoredOverlay, AnchoredOverlayProps} from './AnchoredOverlay'
import {OverlayProps} from './Overlay'
import {useProvidedRefOrCreate, useProvidedStateOrCreate, useMenuInitialFocus, useTypeaheadFocus} from './hooks'
-import {Divider} from './ActionList/Divider'
-import {ActionListContainerContext} from './ActionList/ActionListContainerContext'
+import {Divider} from './ActionList2/Divider'
+import {ActionListContainerContext} from './ActionList2/ActionListContainerContext'
import {MandateProps} from './utils/types'
type MenuContextProps = Pick<
diff --git a/src/FilteredActionList/FilteredActionList.tsx b/src/FilteredActionList/FilteredActionList.tsx
index 665cf6a9c12..ebbb16c59d3 100644
--- a/src/FilteredActionList/FilteredActionList.tsx
+++ b/src/FilteredActionList/FilteredActionList.tsx
@@ -1,9 +1,9 @@
import React, {KeyboardEventHandler, useCallback, useEffect, useRef} from 'react'
import {useSSRSafeId} from '@react-aria/ssr'
-import {GroupedListProps, ListPropsBase} from '../deprecated/ActionList/List'
+import {GroupedListProps, ListPropsBase} from '../ActionList/List'
import TextInput, {TextInputProps} from '../TextInput'
import Box from '../Box'
-import {ActionList} from '../deprecated/ActionList'
+import {ActionList} from '../ActionList'
import Spinner from '../Spinner'
import {useFocusZone} from '../hooks/useFocusZone'
import {useProvidedStateOrCreate} from '../hooks/useProvidedStateOrCreate'
diff --git a/src/SelectPanel/SelectPanel.tsx b/src/SelectPanel/SelectPanel.tsx
index be8477f1786..5d688a55ae1 100644
--- a/src/SelectPanel/SelectPanel.tsx
+++ b/src/SelectPanel/SelectPanel.tsx
@@ -1,10 +1,10 @@
import React, {useCallback, useMemo} from 'react'
import {FilteredActionList, FilteredActionListProps} from '../FilteredActionList'
import {OverlayProps} from '../Overlay'
-import {ItemInput} from '../deprecated/ActionList/List'
+import {ItemInput} from '../ActionList/List'
import {FocusZoneHookSettings} from '../hooks/useFocusZone'
import {DropdownButton} from '../DropdownMenu'
-import {ItemProps} from '../deprecated/ActionList'
+import {ItemProps} from '../ActionList'
import {AnchoredOverlay, AnchoredOverlayProps} from '../AnchoredOverlay'
import {TextInputProps} from '../TextInput'
import {useProvidedStateOrCreate} from '../hooks/useProvidedStateOrCreate'
diff --git a/src/__tests__/ActionList.test.tsx b/src/__tests__/ActionList.test.tsx
index 8325fa7d17c..0003873d445 100644
--- a/src/__tests__/ActionList.test.tsx
+++ b/src/__tests__/ActionList.test.tsx
@@ -1,63 +1,36 @@
-import {cleanup, render as HTMLRender, waitFor, fireEvent} from '@testing-library/react'
+import {cleanup, render as HTMLRender} from '@testing-library/react'
import 'babel-polyfill'
import {axe, toHaveNoViolations} from 'jest-axe'
import React from 'react'
import theme from '../theme'
import {ActionList} from '../ActionList'
-import {behavesAsComponent, checkExports, checkStoriesForAxeViolations} from '../utils/testing'
-import {BaseStyles, ThemeProvider, SSRProvider} from '..'
-import '@testing-library/jest-dom'
+import {behavesAsComponent, checkExports} from '../utils/testing'
+import {BaseStyles, ThemeProvider} from '..'
expect.extend(toHaveNoViolations)
function SimpleActionList(): JSX.Element {
return (
-
-
-
- New file
-
- Copy link
- Edit file
- Delete file
-
-
-
+
+
+
)
}
-const projects = [
- {name: 'Primer Backlog', scope: 'GitHub'},
- {name: 'Primer React', scope: 'github/primer'},
- {name: 'Disabled Project', scope: 'github/primer', disabled: true}
-]
-function SingleSelectListStory(): JSX.Element {
- const [selectedIndex, setSelectedIndex] = React.useState(0)
-
- return (
-
- {projects.map((project, index) => (
- setSelectedIndex(index)}
- disabled={project.disabled}
- >
- {project.name}
-
- ))}
-
- )
-}
-
describe('ActionList', () => {
behavesAsComponent({
Component: ActionList,
options: {skipAs: true, skipSx: true},
- toRender: () =>
+ toRender: () =>
})
checkExports('ActionList', {
@@ -71,88 +44,10 @@ describe('ActionList', () => {
expect(results).toHaveNoViolations()
cleanup()
})
+})
- it('should fire onSelect on click and keypress', async () => {
- const component = HTMLRender( )
- const options = await waitFor(() => component.getAllByRole('option'))
-
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[1]).toHaveAttribute('aria-selected', 'false')
-
- fireEvent.click(options[1])
-
- expect(options[0]).toHaveAttribute('aria-selected', 'false')
- expect(options[1]).toHaveAttribute('aria-selected', 'true')
-
- // We pass keycode here to navigate a implementation detail in react-testing-library
- // https://github.com/testing-library/react-testing-library/issues/269#issuecomment-455854112
- fireEvent.keyPress(options[0], {key: 'Enter', charCode: 13})
-
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[1]).toHaveAttribute('aria-selected', 'false')
-
- fireEvent.keyPress(options[1], {key: ' ', charCode: 32})
-
- expect(options[0]).toHaveAttribute('aria-selected', 'false')
- expect(options[1]).toHaveAttribute('aria-selected', 'true')
-
- cleanup()
- })
-
- it('should skip onSelect on disabled items', async () => {
- const component = HTMLRender( )
- const options = await waitFor(() => component.getAllByRole('option'))
-
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[2]).toHaveAttribute('aria-selected', 'false')
-
- fireEvent.click(options[2])
-
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[2]).toHaveAttribute('aria-selected', 'false')
-
- fireEvent.keyPress(options[2], {key: 'Enter', charCode: 13})
-
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[2]).toHaveAttribute('aria-selected', 'false')
-
- cleanup()
- })
-
- it('should throw when selected is provided without a selectionVariant on parent', 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(() => {
- HTMLRender(
-
-
- Primer React
-
-
- )
- }).toThrow('For Item to be selected, ActionList or ActionList.Group needs to have a selectionVariant defined')
-
- cleanup()
- mockError.mockRestore()
- })
-
- it('should not crash when clicking an item without an onSelect', async () => {
- const component = HTMLRender(
-
- Primer React
-
- )
- const option = await waitFor(() => component.getByRole('option'))
- expect(option).toBeInTheDocument()
-
- fireEvent.click(option)
- fireEvent.keyPress(option, {key: 'Enter', charCode: 13})
- expect(option).toBeInTheDocument()
-
- cleanup()
+describe('ActionList.Item', () => {
+ behavesAsComponent({
+ Component: ActionList.Item
})
-
- checkStoriesForAxeViolations('ActionList/fixtures')
- checkStoriesForAxeViolations('ActionList/examples')
})
diff --git a/src/__tests__/deprecated/ActionList.types.test.tsx b/src/__tests__/ActionList.types.test.tsx
similarity index 93%
rename from src/__tests__/deprecated/ActionList.types.test.tsx
rename to src/__tests__/ActionList.types.test.tsx
index bcb1f2e3cb7..8ff53f3d295 100644
--- a/src/__tests__/deprecated/ActionList.types.test.tsx
+++ b/src/__tests__/ActionList.types.test.tsx
@@ -1,5 +1,5 @@
import React from 'react'
-import {ActionList} from '../../deprecated/ActionList'
+import {ActionList} from '..'
export function emptyList() {
return
diff --git a/src/__tests__/ActionList2.test.tsx b/src/__tests__/ActionList2.test.tsx
new file mode 100644
index 00000000000..d266852fb20
--- /dev/null
+++ b/src/__tests__/ActionList2.test.tsx
@@ -0,0 +1,158 @@
+import {cleanup, render as HTMLRender, waitFor, fireEvent} from '@testing-library/react'
+import 'babel-polyfill'
+import {axe, toHaveNoViolations} from 'jest-axe'
+import React from 'react'
+import theme from '../theme'
+import {ActionList} from '../ActionList2'
+import {behavesAsComponent, checkExports, checkStoriesForAxeViolations} from '../utils/testing'
+import {BaseStyles, ThemeProvider, SSRProvider} from '..'
+import '@testing-library/jest-dom'
+expect.extend(toHaveNoViolations)
+
+function SimpleActionList(): JSX.Element {
+ return (
+
+
+
+
+ New file
+
+ Copy link
+ Edit file
+ Delete file
+
+
+
+
+ )
+}
+
+const projects = [
+ {name: 'Primer Backlog', scope: 'GitHub'},
+ {name: 'Primer React', scope: 'github/primer'},
+ {name: 'Disabled Project', scope: 'github/primer', disabled: true}
+]
+function SingleSelectListStory(): JSX.Element {
+ const [selectedIndex, setSelectedIndex] = React.useState(0)
+
+ return (
+
+ {projects.map((project, index) => (
+ setSelectedIndex(index)}
+ disabled={project.disabled}
+ >
+ {project.name}
+
+ ))}
+
+ )
+}
+
+describe('ActionList', () => {
+ behavesAsComponent({
+ Component: ActionList,
+ options: {skipAs: true, skipSx: true},
+ toRender: () =>
+ })
+
+ checkExports('ActionList2', {
+ default: undefined,
+ ActionList
+ })
+
+ it('should have no axe violations', async () => {
+ const {container} = HTMLRender( )
+ const results = await axe(container)
+ expect(results).toHaveNoViolations()
+ cleanup()
+ })
+
+ it('should fire onSelect on click and keypress', async () => {
+ const component = HTMLRender( )
+ const options = await waitFor(() => component.getAllByRole('option'))
+
+ expect(options[0]).toHaveAttribute('aria-selected', 'true')
+ expect(options[1]).toHaveAttribute('aria-selected', 'false')
+
+ fireEvent.click(options[1])
+
+ expect(options[0]).toHaveAttribute('aria-selected', 'false')
+ expect(options[1]).toHaveAttribute('aria-selected', 'true')
+
+ // We pass keycode here to navigate a implementation detail in react-testing-library
+ // https://github.com/testing-library/react-testing-library/issues/269#issuecomment-455854112
+ fireEvent.keyPress(options[0], {key: 'Enter', charCode: 13})
+
+ expect(options[0]).toHaveAttribute('aria-selected', 'true')
+ expect(options[1]).toHaveAttribute('aria-selected', 'false')
+
+ fireEvent.keyPress(options[1], {key: ' ', charCode: 32})
+
+ expect(options[0]).toHaveAttribute('aria-selected', 'false')
+ expect(options[1]).toHaveAttribute('aria-selected', 'true')
+
+ cleanup()
+ })
+
+ it('should skip onSelect on disabled items', async () => {
+ const component = HTMLRender( )
+ const options = await waitFor(() => component.getAllByRole('option'))
+
+ expect(options[0]).toHaveAttribute('aria-selected', 'true')
+ expect(options[2]).toHaveAttribute('aria-selected', 'false')
+
+ fireEvent.click(options[2])
+
+ expect(options[0]).toHaveAttribute('aria-selected', 'true')
+ expect(options[2]).toHaveAttribute('aria-selected', 'false')
+
+ fireEvent.keyPress(options[2], {key: 'Enter', charCode: 13})
+
+ expect(options[0]).toHaveAttribute('aria-selected', 'true')
+ expect(options[2]).toHaveAttribute('aria-selected', 'false')
+
+ cleanup()
+ })
+
+ it('should throw when selected is provided without a selectionVariant on parent', 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(() => {
+ HTMLRender(
+
+
+ Primer React
+
+
+ )
+ }).toThrow('For Item to be selected, ActionList or ActionList.Group needs to have a selectionVariant defined')
+
+ cleanup()
+ mockError.mockRestore()
+ })
+
+ it('should not crash when clicking an item without an onSelect', async () => {
+ const component = HTMLRender(
+
+ Primer React
+
+ )
+ const option = await waitFor(() => component.getByRole('option'))
+ expect(option).toBeInTheDocument()
+
+ fireEvent.click(option)
+ fireEvent.keyPress(option, {key: 'Enter', charCode: 13})
+ expect(option).toBeInTheDocument()
+
+ cleanup()
+ })
+
+ checkStoriesForAxeViolations('ActionList2/fixtures')
+ checkStoriesForAxeViolations('ActionList2/examples')
+})
diff --git a/src/__tests__/ActionMenu.test.tsx b/src/__tests__/ActionMenu.test.tsx
index 7aa30c560ec..504693992eb 100644
--- a/src/__tests__/ActionMenu.test.tsx
+++ b/src/__tests__/ActionMenu.test.tsx
@@ -1,33 +1,31 @@
-import {cleanup, render as HTMLRender, waitFor, fireEvent} from '@testing-library/react'
+import {cleanup, render as HTMLRender, act, fireEvent} from '@testing-library/react'
import 'babel-polyfill'
import {axe, toHaveNoViolations} from 'jest-axe'
import React from 'react'
import theme from '../theme'
-import {ActionMenu, ActionList, BaseStyles, ThemeProvider, SSRProvider} from '..'
-import {behavesAsComponent, checkExports, checkStoriesForAxeViolations} from '../utils/testing'
-import {SingleSelection, MixedSelection} from '../stories/ActionMenu/examples.stories'
-import '@testing-library/jest-dom'
+import {ActionMenu} from '../ActionMenu'
+import {behavesAsComponent, checkExports} from '../utils/testing'
+import {BaseStyles, SSRProvider, ThemeProvider} from '..'
+import {ItemProps} from '../ActionList/Item'
expect.extend(toHaveNoViolations)
-function Example(): JSX.Element {
+const items = [
+ {text: 'New file'},
+ {text: 'Copy link'},
+ {text: 'Edit file'},
+ {text: 'Delete file', variant: 'danger'}
+] as ItemProps[]
+
+const mockOnActivate = jest.fn()
+
+function SimpleActionMenu(): JSX.Element {
return (
-
- Toggle Menu
-
-
- New file
-
- Copy link
- Edit file
- event.preventDefault()}>
- Delete file
-
-
-
-
+ X
+
+
@@ -35,10 +33,18 @@ function Example(): JSX.Element {
}
describe('ActionMenu', () => {
+ afterEach(() => {
+ jest.clearAllMocks()
+ })
+
behavesAsComponent({
- Component: ActionList,
+ Component: ActionMenu,
options: {skipAs: true, skipSx: true},
- toRender: () =>
+ toRender: () => (
+
+
+
+ )
})
checkExports('ActionMenu', {
@@ -46,103 +52,85 @@ describe('ActionMenu', () => {
ActionMenu
})
- it('should open Menu on MenuButton click', async () => {
- const component = HTMLRender( )
- const button = component.getByText('Toggle Menu')
- fireEvent.click(button)
- expect(component.getByRole('menu')).toBeInTheDocument()
- cleanup()
- })
-
- it('should open Menu on MenuButton keypress', async () => {
- const component = HTMLRender( )
- const button = component.getByText('Toggle Menu')
-
- // We pass keycode here to navigate a implementation detail in react-testing-library
- // https://github.com/testing-library/react-testing-library/issues/269#issuecomment-455854112
- fireEvent.keyDown(button, {key: 'Enter', charCode: 13})
- expect(component.getByRole('menu')).toBeInTheDocument()
- cleanup()
- })
-
- it('should close Menu on selecting an action with click', async () => {
- const component = HTMLRender( )
- const button = component.getByText('Toggle Menu')
-
- fireEvent.click(button)
- const menuItems = await waitFor(() => component.getAllByRole('menuitem'))
- fireEvent.click(menuItems[0])
- expect(component.queryByRole('menu')).toBeNull()
-
- cleanup()
- })
-
- it('should close Menu on selecting an action with Enter', async () => {
- const component = HTMLRender( )
- const button = component.getByText('Toggle Menu')
-
- fireEvent.click(button)
- const menuItems = await waitFor(() => component.getAllByRole('menuitem'))
- fireEvent.keyPress(menuItems[0], {key: 'Enter', charCode: 13})
- expect(component.queryByRole('menu')).toBeNull()
-
+ it('should have no axe violations', async () => {
+ const {container} = HTMLRender( )
+ const results = await axe(container)
+ expect(results).toHaveNoViolations()
cleanup()
})
- it('should not close Menu if event is prevented', async () => {
- const component = HTMLRender( )
- const button = component.getByText('Toggle Menu')
-
- fireEvent.click(button)
- const menuItems = await waitFor(() => component.getAllByRole('menuitem'))
- fireEvent.click(menuItems[3])
- // menu should still be open
- expect(component.getByRole('menu')).toBeInTheDocument()
-
- cleanup()
+ it('should trigger the overlay on trigger click', async () => {
+ const menu = HTMLRender( )
+ let portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__')
+ expect(portalRoot).toBeNull()
+ const anchor = await menu.findByText('Menu')
+ act(() => {
+ fireEvent.click(anchor)
+ })
+ portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__')
+ expect(portalRoot).toBeTruthy()
+ const itemText = items
+ .map((i: ItemProps) => {
+ if (i.hasOwnProperty('text')) {
+ return i.text
+ }
+ })
+ .join('')
+ expect(portalRoot?.textContent?.trim()).toEqual(itemText)
})
- 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 dismiss the overlay on menuitem click', async () => {
+ const menu = HTMLRender( )
+ let portalRoot = await menu.baseElement.querySelector('#__primerPortalRoot__')
+ expect(portalRoot).toBeNull()
+ const anchor = await menu.findByText('Menu')
+ act(() => {
+ fireEvent.click(anchor)
+ })
+ portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__')
+ expect(portalRoot).toBeTruthy()
+ const menuItem = await menu.queryByText(items[0].text!)
+ act(() => {
+ fireEvent.click(menuItem as Element)
+ })
+ expect(portalRoot?.textContent).toEqual('') // menu items are hidden
})
- 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()
+ it('should dismiss the overlay on clicking outside overlay', async () => {
+ const menu = HTMLRender( )
+ let portalRoot = await menu.baseElement.querySelector('#__primerPortalRoot__')
+ expect(portalRoot).toBeNull()
+ const anchor = await menu.findByText('Menu')
+ act(() => {
+ fireEvent.click(anchor)
+ })
+ portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__')
+ expect(portalRoot).toBeTruthy()
+ const somethingElse = (await menu.baseElement.querySelector('#something-else')) as HTMLElement
+ act(() => {
+ fireEvent.mouseDown(somethingElse)
+ })
+ expect(portalRoot?.textContent).toEqual('') // menu items are hidden
})
- it('should have no axe violations', async () => {
- const {container} = HTMLRender( )
- const results = await axe(container)
- expect(results).toHaveNoViolations()
- cleanup()
+ it('should pass correct values to onAction on menu click', async () => {
+ const menu = HTMLRender( )
+ let portalRoot = await menu.baseElement.querySelector('#__primerPortalRoot__')
+ expect(portalRoot).toBeNull()
+ const anchor = await menu.findByText('Menu')
+ act(() => {
+ fireEvent.click(anchor)
+ })
+ portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__')
+ expect(portalRoot).toBeTruthy()
+ const menuItem = (await portalRoot?.querySelector("[role='menuitem']")) as HTMLElement
+ act(() => {
+ fireEvent.click(menuItem)
+ })
+
+ // onAction has been called with correct argument
+ expect(mockOnActivate).toHaveBeenCalledTimes(1)
+ const arg = mockOnActivate.mock.calls[0][0]
+ expect(arg.text).toEqual(items[0].text)
})
-
- checkStoriesForAxeViolations('ActionMenu/fixtures')
- checkStoriesForAxeViolations('ActionMenu/examples')
})
diff --git a/src/__tests__/ActionMenu2.test.tsx b/src/__tests__/ActionMenu2.test.tsx
new file mode 100644
index 00000000000..20cfb9fcda2
--- /dev/null
+++ b/src/__tests__/ActionMenu2.test.tsx
@@ -0,0 +1,150 @@
+import {cleanup, render as HTMLRender, waitFor, fireEvent} from '@testing-library/react'
+import 'babel-polyfill'
+import {axe, toHaveNoViolations} from 'jest-axe'
+import React from 'react'
+import theme from '../theme'
+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 Example(): JSX.Element {
+ return (
+
+
+
+
+ Toggle Menu
+
+
+ New file
+
+ Copy link
+ Edit file
+ event.preventDefault()}>
+ Delete file
+
+
+
+
+
+
+
+ )
+}
+
+describe('ActionMenu', () => {
+ behavesAsComponent({
+ Component: ActionList,
+ options: {skipAs: true, skipSx: true},
+ toRender: () =>
+ })
+
+ checkExports('ActionMenu2', {
+ default: undefined,
+ ActionMenu
+ })
+
+ it('should open Menu on MenuButton click', async () => {
+ const component = HTMLRender( )
+ const button = component.getByText('Toggle Menu')
+ fireEvent.click(button)
+ expect(component.getByRole('menu')).toBeInTheDocument()
+ cleanup()
+ })
+
+ it('should open Menu on MenuButton keypress', async () => {
+ const component = HTMLRender( )
+ const button = component.getByText('Toggle Menu')
+
+ // We pass keycode here to navigate a implementation detail in react-testing-library
+ // https://github.com/testing-library/react-testing-library/issues/269#issuecomment-455854112
+ fireEvent.keyDown(button, {key: 'Enter', charCode: 13})
+ expect(component.getByRole('menu')).toBeInTheDocument()
+ cleanup()
+ })
+
+ it('should close Menu on selecting an action with click', async () => {
+ const component = HTMLRender( )
+ const button = component.getByText('Toggle Menu')
+
+ fireEvent.click(button)
+ const menuItems = await waitFor(() => component.getAllByRole('menuitem'))
+ fireEvent.click(menuItems[0])
+ expect(component.queryByRole('menu')).toBeNull()
+
+ cleanup()
+ })
+
+ it('should close Menu on selecting an action with Enter', async () => {
+ const component = HTMLRender( )
+ const button = component.getByText('Toggle Menu')
+
+ fireEvent.click(button)
+ const menuItems = await waitFor(() => component.getAllByRole('menuitem'))
+ fireEvent.keyPress(menuItems[0], {key: 'Enter', charCode: 13})
+ expect(component.queryByRole('menu')).toBeNull()
+
+ cleanup()
+ })
+
+ it('should not close Menu if event is prevented', async () => {
+ const component = HTMLRender( )
+ const button = component.getByText('Toggle Menu')
+
+ fireEvent.click(button)
+ const menuItems = await waitFor(() => component.getAllByRole('menuitem'))
+ fireEvent.click(menuItems[3])
+ // menu should still be open
+ expect(component.getByRole('menu')).toBeInTheDocument()
+
+ cleanup()
+ })
+
+ 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()
+ })
+
+ it('should have no axe violations', async () => {
+ const {container} = HTMLRender( )
+ const results = await axe(container)
+ expect(results).toHaveNoViolations()
+ cleanup()
+ })
+
+ checkStoriesForAxeViolations('ActionMenu2/fixtures')
+ checkStoriesForAxeViolations('ActionMenu2/examples')
+})
diff --git a/src/__tests__/Autocomplete.test.tsx b/src/__tests__/Autocomplete.test.tsx
index 3e17f37dcb0..8c2b89d046c 100644
--- a/src/__tests__/Autocomplete.test.tsx
+++ b/src/__tests__/Autocomplete.test.tsx
@@ -10,7 +10,7 @@ import BaseStyles from '../BaseStyles'
import {ThemeProvider} from '../ThemeProvider'
import userEvent from '@testing-library/user-event'
import {AutocompleteMenuInternalProps} from '../Autocomplete/AutocompleteMenu'
-import {ItemProps} from '../deprecated/ActionList'
+import {ItemProps} from '../ActionList'
import {MandateProps} from '../utils/types'
expect.extend(toHaveNoViolations)
diff --git a/src/__tests__/ConfirmationDialog.test.tsx b/src/__tests__/ConfirmationDialog.test.tsx
index 7e7cff17bb6..ef91e86786b 100644
--- a/src/__tests__/ConfirmationDialog.test.tsx
+++ b/src/__tests__/ConfirmationDialog.test.tsx
@@ -3,7 +3,7 @@ import {render as HTMLRender, cleanup, act, fireEvent} from '@testing-library/re
import {axe, toHaveNoViolations} from 'jest-axe'
import React, {useCallback, useRef, useState} from 'react'
-import {ActionMenu} from '../deprecated/ActionMenu'
+import {ActionMenu} from '../ActionMenu'
import BaseStyles from '../BaseStyles'
import Box from '../Box'
import Button from '../Button/Button'
diff --git a/src/__tests__/DropdownMenu.test.tsx b/src/__tests__/DropdownMenu.test.tsx
index 3a8665ccc55..aa93d7f5fb7 100644
--- a/src/__tests__/DropdownMenu.test.tsx
+++ b/src/__tests__/DropdownMenu.test.tsx
@@ -6,7 +6,7 @@ import theme from '../theme'
import {DropdownMenu, DropdownButton} from '../DropdownMenu'
import {behavesAsComponent, checkExports} from '../utils/testing'
import {BaseStyles, ThemeProvider, SSRProvider} from '..'
-import {ItemInput} from '../deprecated/ActionList/List'
+import {ItemInput} from '../ActionList/List'
expect.extend(toHaveNoViolations)
diff --git a/src/__tests__/DropdownMenu2.test.tsx b/src/__tests__/DropdownMenu2.test.tsx
index 2e91a21b0c0..04917c6e3a4 100644
--- a/src/__tests__/DropdownMenu2.test.tsx
+++ b/src/__tests__/DropdownMenu2.test.tsx
@@ -4,7 +4,7 @@ import {toHaveNoViolations} from 'jest-axe'
import React from 'react'
import theme from '../theme'
import {DropdownMenu} from '../DropdownMenu2'
-import {ActionList} from '../ActionList'
+import {ActionList} from '../ActionList2'
import {behavesAsComponent, checkExports, checkStoriesForAxeViolations} from '../utils/testing'
import {BaseStyles, ThemeProvider, SSRProvider} from '..'
import '@testing-library/jest-dom'
diff --git a/src/__tests__/SelectPanel.test.tsx b/src/__tests__/SelectPanel.test.tsx
index b88cb8f3149..aefd92e25b8 100644
--- a/src/__tests__/SelectPanel.test.tsx
+++ b/src/__tests__/SelectPanel.test.tsx
@@ -6,7 +6,7 @@ import theme from '../theme'
import {SelectPanel} from '../SelectPanel'
import {behavesAsComponent, checkExports} from '../utils/testing'
import {BaseStyles, SSRProvider, ThemeProvider} from '..'
-import {ItemInput} from '../deprecated/ActionList/List'
+import {ItemInput} from '../ActionList/List'
expect.extend(toHaveNoViolations)
diff --git a/src/__tests__/__snapshots__/ActionList.test.tsx.snap b/src/__tests__/__snapshots__/ActionList.test.tsx.snap
index 5bbbad35875..852201546b0 100644
--- a/src/__tests__/__snapshots__/ActionList.test.tsx.snap
+++ b/src/__tests__/__snapshots__/ActionList.test.tsx.snap
@@ -1,14 +1,223 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ActionList renders consistently 1`] = `
+.c1 {
+ margin-top: 8px;
+ margin-bottom: 8px;
+}
+
.c0 {
- margin: 0;
- padding-inline-start: 0;
- padding-top: 8px;
- padding-bottom: 8px;
+ font-size: 14px;
+ line-height: 20px;
+}
+
+.c0[data-has-active-descendant],
+.c0:focus-within {
+ --item-hover-bg-override: none;
+ --item-hover-divider-border-color-override: hsla(210,18%,87%,1);
}
-
+>
+
+
+`;
+
+exports[`ActionList.Item renders consistently 1`] = `
+.c3 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ min-width: 0;
+ position: relative;
+ -webkit-box-flex: 1;
+ -webkit-flex-grow: 1;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+}
+
+.c4 {
+ -webkit-align-items: baseline;
+ -webkit-box-align: baseline;
+ -ms-flex-align: baseline;
+ align-items: baseline;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ min-width: 0;
+ -webkit-flex-direction: var(--main-content-flex-direction);
+ -ms-flex-direction: var(--main-content-flex-direction);
+ flex-direction: var(--main-content-flex-direction);
+ -webkit-box-flex: 1;
+ -webkit-flex-grow: 1;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+}
+
+.c5:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) {
+ margin-top: 0;
+}
+
+.c5:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) .c2::before {
+ content: ' ';
+ display: block;
+ position: absolute;
+ width: 100%;
+ top: -7px;
+ border: 0 solid hsla(210,18%,87%,1);
+ border-top-width: 0;
+}
+
+.c5:hover .c2::before,
+.c5:hover + * .c2::before {
+ border-color: var(--item-hover-divider-border-color-override,transparent) !important;
+}
+
+.c5:focus .c2::before,
+.c5:focus + * .c2::before,
+.c5[data-is-active-descendant] .c2::before,
+[data-is-active-descendant] + .c5 .c2::before {
+ border-color: transparent !important;
+}
+
+.c6:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) {
+ margin-top: 0;
+}
+
+.c6:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) .c2::before {
+ content: ' ';
+ display: block;
+ position: absolute;
+ width: 100%;
+ top: -7px;
+ border: 0 solid hsla(210,18%,87%,1);
+ border-top-width: 0;
+}
+
+.c6:hover .c2::before,
+.c6:hover + * .c2::before {
+ border-color: var(--item-hover-divider-border-color-override,transparent) !important;
+}
+
+.c6:focus .c2::before,
+.c6:focus + * .c2::before,
+.c6[data-is-active-descendant] .c2::before,
+[data-is-active-descendant] + .c6 .c2::before {
+ border-color: transparent !important;
+}
+
+.c7:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) {
+ margin-top: 0;
+}
+
+.c7:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) .c2::before {
+ content: ' ';
+ display: block;
+ position: absolute;
+ width: 100%;
+ top: -7px;
+ border: 0 solid hsla(210,18%,87%,1);
+ border-top-width: 0;
+}
+
+.c7:hover .c2::before,
+.c7:hover + * .c2::before {
+ border-color: var(--item-hover-divider-border-color-override,transparent) !important;
+}
+
+.c7:focus .c2::before,
+.c7:focus + * .c2::before,
+.c7[data-is-active-descendant] .c2::before,
+[data-is-active-descendant] + .c7 .c2::before {
+ border-color: transparent !important;
+}
+
+.c1 {
+ padding: 6px 8px;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ border-radius: 6px;
+ color: #24292f;
+ -webkit-transition: background 33.333ms linear;
+ transition: background 33.333ms linear;
+ -webkit-text-decoration: none;
+ text-decoration: none;
+}
+
+.c1:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) {
+ margin-top: 0;
+}
+
+.c1:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) .c2::before {
+ content: ' ';
+ display: block;
+ position: absolute;
+ width: 100%;
+ top: -7px;
+ border: 0 solid hsla(210,18%,87%,1);
+ border-top-width: 0;
+}
+
+.c1:hover .c2::before,
+.c1:hover + * .c2::before {
+ border-color: var(--item-hover-divider-border-color-override,transparent) !important;
+}
+
+.c1:focus .c2::before,
+.c1:focus + * .c2::before,
+.c1[data-is-active-descendant] .c2::before,
+[data-is-active-descendant] + .c1 .c2::before {
+ border-color: transparent !important;
+}
+
+.c1[data-is-active-descendant='activated-directly'] {
+ background: rgba(208,215,222,0.48);
+}
+
+.c1[data-is-active-descendant='activated-indirectly'] {
+ background: rgba(208,215,222,0.32);
+}
+
+.c1:focus {
+ background: rgba(208,215,222,0.48);
+ outline: none;
+}
+
+.c1:active {
+ background: rgba(208,215,222,0.48);
+}
+
+@media (hover:hover) and (pointer:fine) {
+ .c1:hover {
+ background: var( --item-hover-bg-override,rgba(208,215,222,0.32) );
+ cursor: pointer;
+ }
+}
+
+
`;
diff --git a/src/__tests__/__snapshots__/ActionList2.test.tsx.snap b/src/__tests__/__snapshots__/ActionList2.test.tsx.snap
new file mode 100644
index 00000000000..5bbbad35875
--- /dev/null
+++ b/src/__tests__/__snapshots__/ActionList2.test.tsx.snap
@@ -0,0 +1,14 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ActionList renders consistently 1`] = `
+.c0 {
+ margin: 0;
+ padding-inline-start: 0;
+ padding-top: 8px;
+ padding-bottom: 8px;
+}
+
+
+`;
diff --git a/src/__tests__/__snapshots__/ActionMenu.test.tsx.snap b/src/__tests__/__snapshots__/ActionMenu.test.tsx.snap
index ccf46de1387..7581ef44ec9 100644
--- a/src/__tests__/__snapshots__/ActionMenu.test.tsx.snap
+++ b/src/__tests__/__snapshots__/ActionMenu.test.tsx.snap
@@ -2,143 +2,79 @@
exports[`ActionMenu renders consistently 1`] = `
.c0 {
- font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";
- line-height: 1.5;
- color: #24292f;
-}
-
-.c2 {
+ position: relative;
display: inline-block;
- margin-left: 8px;
-}
-
-.c1 {
- border-radius: 6px;
- border: 1px solid;
- border-color: rgba(27,31,36,0.15);
+ padding: 6px 16px;
font-family: inherit;
font-weight: 600;
line-height: 20px;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
+ border-radius: 6px;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
-webkit-text-decoration: none;
text-decoration: none;
text-align: center;
- display: grid;
- grid-template-areas: "leadingIcon text trailingIcon";
- padding-top: 5px;
- padding-bottom: 5px;
- padding-left: 16px;
- padding-right: 16px;
font-size: 14px;
color: #24292f;
background-color: #f6f8fa;
+ border: 1px solid rgba(27,31,36,0.15);
box-shadow: 0 1px 0 rgba(27,31,36,0.04),inset 0 1px 0 rgba(255,255,255,0.25);
}
-.c1:focus {
+.c0:hover {
+ -webkit-text-decoration: none;
+ text-decoration: none;
+}
+
+.c0:focus {
outline: none;
}
-.c1:disabled {
+.c0:disabled {
cursor: default;
- color: #8c959f;
- background-color: btn.disabledBg;
}
-.c1:disabled svg {
+.c0:disabled svg {
opacity: 0.6;
}
-.c1 > :not(:last-child) {
- margin-right: 8px;
-}
-
-.c1 [data-component="leadingIcon"] {
- grid-area: leadingIcon;
-}
-
-.c1 [data-component="text"] {
- grid-area: text;
-}
-
-.c1 [data-component="trailingIcon"] {
- grid-area: trailingIcon;
-}
-
-.c1 [data-component="ButtonCounter"] {
- font-size: 14px;
-}
-
-.c1:hover:not([disabled]) {
+.c0:hover {
background-color: #f3f4f6;
+ border-color: rgba(27,31,36,0.15);
}
-.c1:focus:not([disabled]) {
+.c0:focus {
+ border-color: rgba(27,31,36,0.15);
box-shadow: 0 0 0 3px rgba(9,105,218,0.3);
}
-.c1:active:not([disabled]) {
+.c0:active {
background-color: hsla(220,14%,94%,1);
box-shadow: inset 0 0.15em 0.3em rgba(27,31,36,0.15);
}
-
-
-
- Toggle Menu
-
-
- ",
- }
- }
- fill="currentColor"
- height={16}
- role="img"
- style={
- Object {
- "display": "inline-block",
- "overflow": "visible",
- "userSelect": "none",
- "verticalAlign": "text-bottom",
- }
- }
- viewBox="0 0 16 16"
- width={16}
- />
-
-
-
+ id="react-aria-1"
+ onClick={[Function]}
+ onKeyDown={[Function]}
+ tabIndex={0}
+/>
`;
diff --git a/src/__tests__/__snapshots__/ActionMenu2.test.tsx.snap b/src/__tests__/__snapshots__/ActionMenu2.test.tsx.snap
new file mode 100644
index 00000000000..ccf46de1387
--- /dev/null
+++ b/src/__tests__/__snapshots__/ActionMenu2.test.tsx.snap
@@ -0,0 +1,144 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ActionMenu renders consistently 1`] = `
+.c0 {
+ font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";
+ line-height: 1.5;
+ color: #24292f;
+}
+
+.c2 {
+ display: inline-block;
+ margin-left: 8px;
+}
+
+.c1 {
+ border-radius: 6px;
+ border: 1px solid;
+ border-color: rgba(27,31,36,0.15);
+ font-family: inherit;
+ font-weight: 600;
+ line-height: 20px;
+ white-space: nowrap;
+ vertical-align: middle;
+ cursor: pointer;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ -webkit-text-decoration: none;
+ text-decoration: none;
+ text-align: center;
+ display: grid;
+ grid-template-areas: "leadingIcon text trailingIcon";
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-left: 16px;
+ padding-right: 16px;
+ font-size: 14px;
+ color: #24292f;
+ background-color: #f6f8fa;
+ box-shadow: 0 1px 0 rgba(27,31,36,0.04),inset 0 1px 0 rgba(255,255,255,0.25);
+}
+
+.c1:focus {
+ outline: none;
+}
+
+.c1:disabled {
+ cursor: default;
+ color: #8c959f;
+ background-color: btn.disabledBg;
+}
+
+.c1:disabled svg {
+ opacity: 0.6;
+}
+
+.c1 > :not(:last-child) {
+ margin-right: 8px;
+}
+
+.c1 [data-component="leadingIcon"] {
+ grid-area: leadingIcon;
+}
+
+.c1 [data-component="text"] {
+ grid-area: text;
+}
+
+.c1 [data-component="trailingIcon"] {
+ grid-area: trailingIcon;
+}
+
+.c1 [data-component="ButtonCounter"] {
+ font-size: 14px;
+}
+
+.c1:hover:not([disabled]) {
+ background-color: #f3f4f6;
+}
+
+.c1:focus:not([disabled]) {
+ box-shadow: 0 0 0 3px rgba(9,105,218,0.3);
+}
+
+.c1:active:not([disabled]) {
+ background-color: hsla(220,14%,94%,1);
+ box-shadow: inset 0 0.15em 0.3em rgba(27,31,36,0.15);
+}
+
+
+
+
+ Toggle Menu
+
+
+ ",
+ }
+ }
+ fill="currentColor"
+ height={16}
+ role="img"
+ style={
+ Object {
+ "display": "inline-block",
+ "overflow": "visible",
+ "userSelect": "none",
+ "verticalAlign": "text-bottom",
+ }
+ }
+ viewBox="0 0 16 16"
+ width={16}
+ />
+
+
+
+`;
diff --git a/src/__tests__/deprecated/ActionList.test.tsx b/src/__tests__/deprecated/ActionList.test.tsx
deleted file mode 100644
index 7432588b70a..00000000000
--- a/src/__tests__/deprecated/ActionList.test.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import {cleanup, render as HTMLRender} from '@testing-library/react'
-import 'babel-polyfill'
-import {axe, toHaveNoViolations} from 'jest-axe'
-import React from 'react'
-import theme from '../../theme'
-import {ActionList} from '../../deprecated/ActionList'
-import {behavesAsComponent, checkExports} from '../../utils/testing'
-import {BaseStyles, ThemeProvider} from '../..'
-expect.extend(toHaveNoViolations)
-
-function SimpleActionList(): JSX.Element {
- return (
-
-
-
-
-
- )
-}
-
-describe('ActionList', () => {
- behavesAsComponent({
- Component: ActionList,
- options: {skipAs: true, skipSx: true},
- toRender: () =>
- })
-
- checkExports('deprecated/ActionList', {
- default: undefined,
- ActionList
- })
-
- it('should have no axe violations', async () => {
- const {container} = HTMLRender( )
- const results = await axe(container)
- expect(results).toHaveNoViolations()
- cleanup()
- })
-})
-
-describe('ActionList.Item', () => {
- behavesAsComponent({
- Component: ActionList.Item
- })
-})
diff --git a/src/__tests__/deprecated/ActionMenu.test.tsx b/src/__tests__/deprecated/ActionMenu.test.tsx
deleted file mode 100644
index e535bce9c84..00000000000
--- a/src/__tests__/deprecated/ActionMenu.test.tsx
+++ /dev/null
@@ -1,136 +0,0 @@
-import {cleanup, render as HTMLRender, act, fireEvent} from '@testing-library/react'
-import 'babel-polyfill'
-import {axe, toHaveNoViolations} from 'jest-axe'
-import React from 'react'
-import theme from '../../theme'
-import {ActionMenu} from '../../deprecated'
-import {behavesAsComponent, checkExports} from '../../utils/testing'
-import {BaseStyles, SSRProvider, ThemeProvider} from '../..'
-import {ItemProps} from '../../deprecated/ActionList/Item'
-expect.extend(toHaveNoViolations)
-
-const items = [
- {text: 'New file'},
- {text: 'Copy link'},
- {text: 'Edit file'},
- {text: 'Delete file', variant: 'danger'}
-] as ItemProps[]
-
-const mockOnActivate = jest.fn()
-
-function SimpleActionMenu(): JSX.Element {
- return (
-
-
-
- X
-
-
-
-
-
- )
-}
-
-describe('ActionMenu', () => {
- afterEach(() => {
- jest.clearAllMocks()
- })
-
- behavesAsComponent({
- Component: ActionMenu,
- options: {skipAs: true, skipSx: true},
- toRender: () => (
-
-
-
- )
- })
-
- checkExports('deprecated/ActionMenu', {
- default: undefined,
- ActionMenu
- })
-
- it('should have no axe violations', async () => {
- const {container} = HTMLRender( )
- const results = await axe(container)
- expect(results).toHaveNoViolations()
- cleanup()
- })
-
- it('should trigger the overlay on trigger click', async () => {
- const menu = HTMLRender( )
- let portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__')
- expect(portalRoot).toBeNull()
- const anchor = await menu.findByText('Menu')
- act(() => {
- fireEvent.click(anchor)
- })
- portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__')
- expect(portalRoot).toBeTruthy()
- const itemText = items
- .map((i: ItemProps) => {
- if (i.hasOwnProperty('text')) {
- return i.text
- }
- })
- .join('')
- expect(portalRoot?.textContent?.trim()).toEqual(itemText)
- })
-
- it('should dismiss the overlay on menuitem click', async () => {
- const menu = HTMLRender( )
- let portalRoot = await menu.baseElement.querySelector('#__primerPortalRoot__')
- expect(portalRoot).toBeNull()
- const anchor = await menu.findByText('Menu')
- act(() => {
- fireEvent.click(anchor)
- })
- portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__')
- expect(portalRoot).toBeTruthy()
- const menuItem = await menu.queryByText(items[0].text!)
- act(() => {
- fireEvent.click(menuItem as Element)
- })
- expect(portalRoot?.textContent).toEqual('') // menu items are hidden
- })
-
- it('should dismiss the overlay on clicking outside overlay', async () => {
- const menu = HTMLRender( )
- let portalRoot = await menu.baseElement.querySelector('#__primerPortalRoot__')
- expect(portalRoot).toBeNull()
- const anchor = await menu.findByText('Menu')
- act(() => {
- fireEvent.click(anchor)
- })
- portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__')
- expect(portalRoot).toBeTruthy()
- const somethingElse = (await menu.baseElement.querySelector('#something-else')) as HTMLElement
- act(() => {
- fireEvent.mouseDown(somethingElse)
- })
- expect(portalRoot?.textContent).toEqual('') // menu items are hidden
- })
-
- it('should pass correct values to onAction on menu click', async () => {
- const menu = HTMLRender( )
- let portalRoot = await menu.baseElement.querySelector('#__primerPortalRoot__')
- expect(portalRoot).toBeNull()
- const anchor = await menu.findByText('Menu')
- act(() => {
- fireEvent.click(anchor)
- })
- portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__')
- expect(portalRoot).toBeTruthy()
- const menuItem = (await portalRoot?.querySelector("[role='menuitem']")) as HTMLElement
- act(() => {
- fireEvent.click(menuItem)
- })
-
- // onAction has been called with correct argument
- expect(mockOnActivate).toHaveBeenCalledTimes(1)
- const arg = mockOnActivate.mock.calls[0][0]
- expect(arg.text).toEqual(items[0].text)
- })
-})
diff --git a/src/__tests__/deprecated/__snapshots__/ActionList.test.tsx.snap b/src/__tests__/deprecated/__snapshots__/ActionList.test.tsx.snap
deleted file mode 100644
index 852201546b0..00000000000
--- a/src/__tests__/deprecated/__snapshots__/ActionList.test.tsx.snap
+++ /dev/null
@@ -1,223 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ActionList renders consistently 1`] = `
-.c1 {
- margin-top: 8px;
- margin-bottom: 8px;
-}
-
-.c0 {
- font-size: 14px;
- line-height: 20px;
-}
-
-.c0[data-has-active-descendant],
-.c0:focus-within {
- --item-hover-bg-override: none;
- --item-hover-divider-border-color-override: hsla(210,18%,87%,1);
-}
-
-
-`;
-
-exports[`ActionList.Item renders consistently 1`] = `
-.c3 {
- display: -webkit-box;
- display: -webkit-flex;
- display: -ms-flexbox;
- display: flex;
- min-width: 0;
- position: relative;
- -webkit-box-flex: 1;
- -webkit-flex-grow: 1;
- -ms-flex-positive: 1;
- flex-grow: 1;
-}
-
-.c4 {
- -webkit-align-items: baseline;
- -webkit-box-align: baseline;
- -ms-flex-align: baseline;
- align-items: baseline;
- display: -webkit-box;
- display: -webkit-flex;
- display: -ms-flexbox;
- display: flex;
- min-width: 0;
- -webkit-flex-direction: var(--main-content-flex-direction);
- -ms-flex-direction: var(--main-content-flex-direction);
- flex-direction: var(--main-content-flex-direction);
- -webkit-box-flex: 1;
- -webkit-flex-grow: 1;
- -ms-flex-positive: 1;
- flex-grow: 1;
-}
-
-.c5:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) {
- margin-top: 0;
-}
-
-.c5:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) .c2::before {
- content: ' ';
- display: block;
- position: absolute;
- width: 100%;
- top: -7px;
- border: 0 solid hsla(210,18%,87%,1);
- border-top-width: 0;
-}
-
-.c5:hover .c2::before,
-.c5:hover + * .c2::before {
- border-color: var(--item-hover-divider-border-color-override,transparent) !important;
-}
-
-.c5:focus .c2::before,
-.c5:focus + * .c2::before,
-.c5[data-is-active-descendant] .c2::before,
-[data-is-active-descendant] + .c5 .c2::before {
- border-color: transparent !important;
-}
-
-.c6:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) {
- margin-top: 0;
-}
-
-.c6:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) .c2::before {
- content: ' ';
- display: block;
- position: absolute;
- width: 100%;
- top: -7px;
- border: 0 solid hsla(210,18%,87%,1);
- border-top-width: 0;
-}
-
-.c6:hover .c2::before,
-.c6:hover + * .c2::before {
- border-color: var(--item-hover-divider-border-color-override,transparent) !important;
-}
-
-.c6:focus .c2::before,
-.c6:focus + * .c2::before,
-.c6[data-is-active-descendant] .c2::before,
-[data-is-active-descendant] + .c6 .c2::before {
- border-color: transparent !important;
-}
-
-.c7:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) {
- margin-top: 0;
-}
-
-.c7:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) .c2::before {
- content: ' ';
- display: block;
- position: absolute;
- width: 100%;
- top: -7px;
- border: 0 solid hsla(210,18%,87%,1);
- border-top-width: 0;
-}
-
-.c7:hover .c2::before,
-.c7:hover + * .c2::before {
- border-color: var(--item-hover-divider-border-color-override,transparent) !important;
-}
-
-.c7:focus .c2::before,
-.c7:focus + * .c2::before,
-.c7[data-is-active-descendant] .c2::before,
-[data-is-active-descendant] + .c7 .c2::before {
- border-color: transparent !important;
-}
-
-.c1 {
- padding: 6px 8px;
- display: -webkit-box;
- display: -webkit-flex;
- display: -ms-flexbox;
- display: flex;
- border-radius: 6px;
- color: #24292f;
- -webkit-transition: background 33.333ms linear;
- transition: background 33.333ms linear;
- -webkit-text-decoration: none;
- text-decoration: none;
-}
-
-.c1:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) {
- margin-top: 0;
-}
-
-.c1:not(:first-of-type):not(.c9 + .c0):not(.c8 + .c0) .c2::before {
- content: ' ';
- display: block;
- position: absolute;
- width: 100%;
- top: -7px;
- border: 0 solid hsla(210,18%,87%,1);
- border-top-width: 0;
-}
-
-.c1:hover .c2::before,
-.c1:hover + * .c2::before {
- border-color: var(--item-hover-divider-border-color-override,transparent) !important;
-}
-
-.c1:focus .c2::before,
-.c1:focus + * .c2::before,
-.c1[data-is-active-descendant] .c2::before,
-[data-is-active-descendant] + .c1 .c2::before {
- border-color: transparent !important;
-}
-
-.c1[data-is-active-descendant='activated-directly'] {
- background: rgba(208,215,222,0.48);
-}
-
-.c1[data-is-active-descendant='activated-indirectly'] {
- background: rgba(208,215,222,0.32);
-}
-
-.c1:focus {
- background: rgba(208,215,222,0.48);
- outline: none;
-}
-
-.c1:active {
- background: rgba(208,215,222,0.48);
-}
-
-@media (hover:hover) and (pointer:fine) {
- .c1:hover {
- background: var( --item-hover-bg-override,rgba(208,215,222,0.32) );
- cursor: pointer;
- }
-}
-
-
-`;
diff --git a/src/__tests__/deprecated/__snapshots__/ActionMenu.test.tsx.snap b/src/__tests__/deprecated/__snapshots__/ActionMenu.test.tsx.snap
deleted file mode 100644
index 7581ef44ec9..00000000000
--- a/src/__tests__/deprecated/__snapshots__/ActionMenu.test.tsx.snap
+++ /dev/null
@@ -1,80 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ActionMenu renders consistently 1`] = `
-.c0 {
- position: relative;
- display: inline-block;
- padding: 6px 16px;
- font-family: inherit;
- font-weight: 600;
- line-height: 20px;
- white-space: nowrap;
- vertical-align: middle;
- cursor: pointer;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- border-radius: 6px;
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- -webkit-text-decoration: none;
- text-decoration: none;
- text-align: center;
- font-size: 14px;
- color: #24292f;
- background-color: #f6f8fa;
- border: 1px solid rgba(27,31,36,0.15);
- box-shadow: 0 1px 0 rgba(27,31,36,0.04),inset 0 1px 0 rgba(255,255,255,0.25);
-}
-
-.c0:hover {
- -webkit-text-decoration: none;
- text-decoration: none;
-}
-
-.c0:focus {
- outline: none;
-}
-
-.c0:disabled {
- cursor: default;
-}
-
-.c0:disabled svg {
- opacity: 0.6;
-}
-
-.c0:hover {
- background-color: #f3f4f6;
- border-color: rgba(27,31,36,0.15);
-}
-
-.c0:focus {
- border-color: rgba(27,31,36,0.15);
- box-shadow: 0 0 0 3px rgba(9,105,218,0.3);
-}
-
-.c0:active {
- background-color: hsla(220,14%,94%,1);
- box-shadow: inset 0 0.15em 0.3em rgba(27,31,36,0.15);
-}
-
-.c0:disabled {
- color: #8c959f;
- background-color: #f6f8fa;
- border-color: rgba(27,31,36,0.15);
-}
-
-
-`;
diff --git a/src/deprecated/ActionList/Divider.tsx b/src/deprecated/ActionList/Divider.tsx
deleted file mode 100644
index 48102214ba8..00000000000
--- a/src/deprecated/ActionList/Divider.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react'
-import styled from 'styled-components'
-import {get} from '../../constants'
-
-export const StyledDivider = styled.div`
- height: 1px;
- background: ${get('colors.border.muted')};
- margin-top: calc(${get('space.2')} - 1px);
- margin-bottom: ${get('space.2')};
-`
-
-/**
- * Visually separates `Item`s or `Group`s in an `ActionList`.
- */
-export function Divider(): JSX.Element {
- return
-}
-
-/**
- * `Divider` fulfills the `ItemPropsWithCustomRenderer` contract,
- * so it can be used inline in an `ActionList`’s `items` prop.
- * In other words, `items={[ActionList.Divider]}` is supported as a concise
- * alternative to `items={[{renderItem: () => }]}`.
- */
-Divider.renderItem = Divider
diff --git a/src/deprecated/ActionList/Group.tsx b/src/deprecated/ActionList/Group.tsx
deleted file mode 100644
index 638ea4abf64..00000000000
--- a/src/deprecated/ActionList/Group.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import React from 'react'
-import styled from 'styled-components'
-import sx, {SxProp} from '../../sx'
-import {Header, HeaderProps} from './Header'
-
-/**
- * Contract for props passed to the `Group` component.
- */
-export interface GroupProps extends React.ComponentPropsWithoutRef<'div'>, SxProp {
- /**
- * Props for a `Header` to render in the `Group`.
- */
- header?: HeaderProps
-
- /**
- * The id of the group.
- */
- groupId?: string
-
- /**
- * `Items` to render in the `Group`.
- */
- items?: JSX.Element[]
-
- /**
- * Whether to display a divider above each `Item` in this `Group` when it does not follow a `Header` or `Divider`.
- */
- showItemDividers?: boolean
-}
-
-const StyledGroup = styled.div`
- ${sx}
-`
-
-/**
- * Collects related `Items` in an `ActionList`.
- */
-export function Group({header, items, ...props}: GroupProps): JSX.Element {
- return (
-
- {header && }
- {items}
-
- )
-}
diff --git a/src/deprecated/ActionList/Item.tsx b/src/deprecated/ActionList/Item.tsx
deleted file mode 100644
index 70f4a3db399..00000000000
--- a/src/deprecated/ActionList/Item.tsx
+++ /dev/null
@@ -1,481 +0,0 @@
-import {CheckIcon, IconProps} from '@primer/octicons-react'
-import React, {useCallback} from 'react'
-import {get} from '../../constants'
-import sx, {SxProp} from '../../sx'
-import Truncate from '../../Truncate'
-import {ItemInput} from './List'
-import styled from 'styled-components'
-import {StyledHeader} from './Header'
-import {StyledDivider} from './Divider'
-import {useTheme} from '../../ThemeProvider'
-import {
- activeDescendantActivatedDirectly,
- activeDescendantActivatedIndirectly,
- isActiveDescendantAttribute
-} from '@primer/behaviors'
-import {useSSRSafeId} from '@react-aria/ssr'
-import {ForwardRefComponent as PolymorphicForwardRefComponent} from '@radix-ui/react-polymorphic'
-import {AriaRole} from '../../utils/types'
-
-/**
- * Contract for props passed to the `Item` component.
- */
-export interface ItemProps extends SxProp {
- /**
- * Primary text which names an `Item`.
- */
- text?: string
-
- /**
- * Secondary text which provides additional information about an `Item`.
- */
- description?: string
-
- /**
- * Secondary text style variations. Usage is discretionary.
- *
- * - `"inline"` - Secondary text is positioned beside primary text.
- * - `"block"` - Secondary text is positioned below primary text.
- */
- descriptionVariant?: 'inline' | 'block'
-
- /**
- * Icon (or similar) positioned before `Item` text.
- */
- leadingVisual?: React.FunctionComponent
-
- /**
- * @deprecated Use `trailingVisual` instead
- * Icon (or similar) positioned after `Item` text.
- */
- trailingIcon?: React.FunctionComponent
-
- /**
- * @deprecated Use `trailingVisual` instead
- * Text positioned after `Item` text and optional trailing icon.
- */
- trailingText?: string
-
- /**
- * Icon or text positioned after `Item` text.
- */
- trailingVisual?: React.ReactNode
-
- /**
- * Style variations associated with various `Item` types.
- *
- * - `"default"` - An action `Item`.
- * - `"danger"` - A destructive action `Item`.
- */
- variant?: 'default' | 'danger'
-
- /**
- * Whether to display a divider above the `Item` when it does not follow a `Header` or `Divider`.
- */
- showDivider?: boolean
-
- /**
- * For `Item`s which can be selected, whether the `Item` is currently selected.
- */
- selected?: boolean
-
- /**
- * For `Item`s which can be selected, whether `multiple` `Item`s or a `single` `Item` can be selected
- */
- selectionVariant?: 'single' | 'multiple'
-
- /**
- * Designates the group that an item belongs to.
- */
- groupId?: string
-
- /**
- * Items that are disabled can not be clicked, selected, or navigated through.
- */
- disabled?: boolean
-
- /**
- * Callback that will trigger both on click selection and keyboard selection.
- */
- onAction?: (item: ItemProps, event: React.MouseEvent | React.KeyboardEvent) => void
-
- /**
- * An id associated with this item. Should be unique between items
- */
- id?: number | string
-
- /**
- * Node to be included inside the item before the text.
- */
- children?: React.ReactNode
-
- /**
- * The ARIA role describing the function of `List` component. `option` is a common value.
- */
- role?: AriaRole
-
- /**
- * An item to pass back in the `onAction` callback, meant as
- */
- item?: ItemInput
-}
-
-const getItemVariant = (variant = 'default', disabled?: boolean) => {
- if (disabled) {
- return {
- color: get('colors.primer.fg.disabled'),
- iconColor: get('colors.primer.fg.disabled'),
- annotationColor: get('colors.primer.fg.disabled'),
- hoverCursor: 'default'
- }
- }
-
- switch (variant) {
- case 'danger':
- return {
- color: get('colors.danger.fg'),
- iconColor: get('colors.danger.fg'),
- annotationColor: get('colors.fg.muted'),
- hoverCursor: 'pointer',
- hoverBg: get('colors.actionListItem.danger.hoverBg'),
- focusBg: get('colors.actionListItem.danger.activeBg'),
- hoverText: get('colors.actionListItem.danger.hoverText')
- }
- default:
- return {
- color: get('colors.fg.default'),
- iconColor: get('colors.fg.muted'),
- annotationColor: get('colors.fg.muted'),
- hoverCursor: 'pointer',
- hoverBg: get('colors.actionListItem.default.hoverBg'),
- focusBg: get('colors.actionListItem.default.activeBg')
- }
- }
-}
-
-const DividedContent = styled.div`
- display: flex;
- min-width: 0;
-
- /* Required for dividers */
- position: relative;
- flex-grow: 1;
-`
-
-const MainContent = styled.div`
- align-items: baseline;
- display: flex;
- min-width: 0;
- flex-direction: var(--main-content-flex-direction);
- flex-grow: 1;
-`
-
-const StyledItem = styled.div<
- {
- variant: ItemProps['variant']
- showDivider: ItemProps['showDivider']
- item?: ItemInput
- } & SxProp
->`
- /* 6px vertical padding + 20px line height = 32px total height
- *
- * TODO: When rem-based spacing on a 4px scale lands, replace
- * hardcoded '6px' with 'calc((${get('space.s32')} - ${get('space.20')}) / 2)'.
- */
- padding: 6px ${get('space.2')};
- display: flex;
- border-radius: ${get('radii.2')};
- color: ${({variant, item}) => getItemVariant(variant, item?.disabled).color};
- // 2 frames on a 60hz monitor
- transition: background 33.333ms linear;
- text-decoration: none;
-
- @media (hover: hover) and (pointer: fine) {
- :hover {
- // allow override in case another item in the list is active/focused
- background: var(
- --item-hover-bg-override,
- ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverBg}
- );
- color: ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverText};
- cursor: ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverCursor};
- }
- }
-
- // Item dividers
- :not(:first-of-type):not(${StyledDivider} + &):not(${StyledHeader} + &) {
- margin-top: ${({showDivider}) => (showDivider ? `1px` : '0')};
-
- ${DividedContent}::before {
- content: ' ';
- display: block;
- position: absolute;
- width: 100%;
- top: -7px;
- // NB: This 'get' won’t execute if it’s moved into the arrow function below.
- border: 0 solid ${get('colors.border.muted')};
- border-top-width: ${({showDivider}) => (showDivider ? `1px` : '0')};
- }
- }
-
- // Item dividers should not be visible:
- // - above Hovered
- &:hover ${DividedContent}::before,
- // - below Hovered
- // '*' instead of '&' because '&' maps to separate class names depending on 'variant'
- :hover + * ${DividedContent}::before {
- // allow override in case another item in the list is active/focused
- border-color: var(--item-hover-divider-border-color-override, transparent) !important;
- }
-
- // - above Focused
- &:focus ${DividedContent}::before,
- // - below Focused
- // '*' instead of '&' because '&' maps to separate class names depending on 'variant'
- :focus + * ${DividedContent}::before,
- // - above Active Descendent
- &[${isActiveDescendantAttribute}] ${DividedContent}::before,
- // - below Active Descendent
- [${isActiveDescendantAttribute}] + & ${DividedContent}::before {
- // '!important' because all the ':not's above give higher specificity
- border-color: transparent !important;
- }
-
- // Active Descendant
- &[${isActiveDescendantAttribute}='${activeDescendantActivatedDirectly}'] {
- background: ${({variant, item}) => getItemVariant(variant, item?.disabled).focusBg};
- }
- &[${isActiveDescendantAttribute}='${activeDescendantActivatedIndirectly}'] {
- background: ${({variant, item}) => getItemVariant(variant, item?.disabled).hoverBg};
- }
-
- &:focus {
- background: ${({variant, item}) => getItemVariant(variant, item?.disabled).focusBg};
- outline: none;
- }
-
- &:active {
- background: ${({variant, item}) => getItemVariant(variant, item?.disabled).focusBg};
- }
-
- ${sx}
-`
-
-export const TextContainer = styled.span<{
- dangerouslySetInnerHtml?: React.DOMAttributes['dangerouslySetInnerHTML']
-}>``
-
-const BaseVisualContainer = styled.div<{variant?: ItemProps['variant']; disabled?: boolean}>`
- // Match visual height to adjacent text line height.
- // TODO: When rem-based spacing on a 4px scale lands, replace
- // hardcoded '20px' with '${get('space.s20')}'.
- height: 20px;
- width: ${get('space.3')};
- margin-right: ${get('space.2')};
- display: flex;
- justify-content: center;
- align-items: center;
- flex-shrink: 0;
-`
-
-const ColoredVisualContainer = styled(BaseVisualContainer)`
- svg {
- fill: ${({variant, disabled}) => getItemVariant(variant, disabled).iconColor};
- font-size: ${get('fontSizes.0')};
- }
-`
-
-const LeadingVisualContainer = styled(ColoredVisualContainer)`
- display: flex;
- flex-direction: column;
- justify-content: center;
-`
-
-const TrailingContent = styled(ColoredVisualContainer)`
- color: ${({variant, disabled}) => getItemVariant(variant, disabled).annotationColor}};
- margin-left: ${get('space.2')};
- margin-right: 0;
- width: auto;
- div:nth-child(2) {
- margin-left: ${get('space.2')};
- }
-`
-
-const DescriptionContainer = styled.span`
- color: ${get('colors.fg.muted')};
- font-size: ${get('fontSizes.0')};
- // TODO: When rem-based spacing on a 4px scale lands, replace
- // hardcoded '16px' with '${get('lh-12')}'.
- line-height: 16px;
- margin-left: var(--description-container-margin-left);
- min-width: 0;
- flex-grow: 1;
- flex-basis: var(--description-container-flex-basis);
-`
-
-const MultiSelectIcon = styled.svg<{selected?: boolean}>`
- rect {
- fill: ${({selected}) => (selected ? get('colors.accent.fg') : get('colors.canvas.default'))};
- stroke: ${({selected}) => (selected ? get('colors.accent.fg') : get('colors.border.default'))};
- shape-rendering: auto; // this is a workaround to override global style in github/github, see primer/react#1666
- }
- path {
- fill: ${get('colors.fg.onEmphasis')};
- boxshadow: ${get('shadow.small')};
- opacity: ${({selected}) => (selected ? 1 : 0)};
- }
-`
-
-/**
- * An actionable or selectable `Item` with an optional icon and description.
- */
-export const Item = React.forwardRef((itemProps, ref) => {
- const {
- as: Component,
- text,
- description,
- descriptionVariant = 'inline',
- selected,
- selectionVariant,
- leadingVisual: LeadingVisual,
- trailingIcon: TrailingIcon,
- trailingVisual: TrailingVisual,
- trailingText,
- variant = 'default',
- showDivider,
- disabled,
- onAction,
- onKeyPress,
- children,
- onClick,
- id,
- ...props
- } = itemProps
-
- const labelId = useSSRSafeId()
- const descriptionId = useSSRSafeId()
-
- const keyPressHandler = useCallback(
- event => {
- if (disabled) {
- return
- }
- onKeyPress?.(event)
-
- if (!event.defaultPrevented && [' ', 'Enter'].includes(event.key)) {
- onAction?.(itemProps, event)
- }
- },
- [onAction, disabled, itemProps, onKeyPress]
- )
-
- const clickHandler = useCallback(
- event => {
- if (disabled) {
- return
- }
- onClick?.(event)
- if (!event.defaultPrevented) {
- onAction?.(itemProps, event)
- }
- },
- [onAction, disabled, itemProps, onClick]
- )
-
- const {theme} = useTheme()
-
- return (
-
- {!!selected === selected && (
-
- {selectionVariant === 'multiple' ? (
- <>
- {/**
- * we use a svg instead of an input because there should not
- * be an interactive element inside an option
- * svg copied from primer/css
- */}
-
-
-
-
- >
- ) : (
- selected &&
- )}
-
- )}
- {LeadingVisual && (
-
-
-
- )}
-
-
- {children}
- {text ? {text} : null}
- {description ? (
-
- {descriptionVariant === 'block' ? (
- description
- ) : (
-
- {description}
-
- )}
-
- ) : null}
-
- {/* backward compatibility: prefer TrailingVisual but fallback to TrailingIcon */}
- {TrailingVisual ? (
-
- {typeof TrailingVisual === 'function' ? : TrailingVisual}
-
- ) : TrailingIcon || trailingText ? (
-
- {trailingText}
- {TrailingIcon && }
-
- ) : null}
-
-
- )
-}) as PolymorphicForwardRefComponent<'div', ItemProps>
-
-Item.displayName = 'ActionList.Item'
diff --git a/src/deprecated/ActionList/List.tsx b/src/deprecated/ActionList/List.tsx
deleted file mode 100644
index 0af5190d7c1..00000000000
--- a/src/deprecated/ActionList/List.tsx
+++ /dev/null
@@ -1,258 +0,0 @@
-import React, {Key} from 'react'
-import type {AriaRole} from '../../utils/types'
-import {Group, GroupProps} from './Group'
-import {Item, ItemProps} from './Item'
-import {Divider} from './Divider'
-import styled from 'styled-components'
-import {get} from '../../constants'
-import {SystemCssProperties} from '@styled-system/css'
-import {hasActiveDescendantAttribute} from '@primer/behaviors'
-import {Merge} from '../../utils/types/Merge'
-
-type RenderItemFn = (props: ItemProps) => React.ReactElement
-
-export type ItemInput =
- | Merge, ItemProps>
- | ((Partial & {renderItem: RenderItemFn}) & {key?: Key})
-
-/**
- * Contract for props passed to the `List` component.
- */
-export interface ListPropsBase {
- /**
- * A collection of `Item` props and `Item`-level custom `Item` renderers.
- */
- items: ItemInput[]
-
- /**
- * The ARIA role describing the function of `List` component. `listbox` is a common value.
- */
- role?: AriaRole
-
- /**
- * id to attach to the base DOM node of the list
- */
- id?: string
-
- /**
- * A `List`-level custom `Item` renderer. Every `Item` within this `List`
- * without a `Group`-level or `Item`-level custom `Item` renderer will be
- * rendered using this function component.
- */
- renderItem?: RenderItemFn
-
- /**
- * A `List`-level custom `Group` renderer. Every `Group` within this `List`
- * without a `Group`-level custom `Item` renderer will be rendered using
- * this function component.
- */
- renderGroup?: typeof Group
-
- /**
- * Style variations. Usage is discretionary.
- *
- * - `"inset"` - `List` children are offset (vertically and horizontally) from `List`’s edges
- * - `"full"` - `List` children are flush (vertically and horizontally) with `List` edges
- */
- variant?: 'inset' | 'full'
-
- /**
- * For `Item`s which can be selected, whether `multiple` `Item`s or a `single` `Item` can be selected
- */
- selectionVariant?: 'single' | 'multiple'
-
- /**
- * Whether to display a divider above each `Item` in this `List` when it does not follow a `Header` or `Divider`.
- */
- showItemDividers?: boolean
-}
-
-/**
- * Contract for props passed to the `List` component, when its `Item`s are collected in `Group`s.
- */
-export interface GroupedListProps extends ListPropsBase {
- /**
- * A collection of `Group` props (except `items`), plus a unique group identifier
- * and `Group`-level custom `Item` or `Group` renderers.
- */
- groupMetadata: ((
- | Omit
- | Omit & {renderItem?: RenderItemFn; renderGroup?: typeof Group}, 'items'>
- ) & {groupId: string})[]
-
- /**
- * A collection of `Item` props, plus associated group identifiers
- * and `Item`-level custom `Item` renderers.
- */
- items: ((ItemProps | (Partial & {renderItem: RenderItemFn})) & {groupId: string})[]
-}
-
-/**
- * Asserts that the given value fulfills the `GroupedListProps` contract.
- * @param props A value which fulfills either the `ListPropsBase` or the `GroupedListProps` contract.
- */
-function isGroupedListProps(props: ListProps): props is GroupedListProps {
- return 'groupMetadata' in props
-}
-
-/**
- * Contract for props passed to the `List` component.
- */
-export type ListProps = ListPropsBase | GroupedListProps
-
-const StyledList = styled.div`
- font-size: ${get('fontSizes.1')};
- /* 14px font-size * 1.428571429 = 20px line height
- *
- * TODO: When rem-based spacing on a 4px scale lands, replace
- * hardcoded '20px'
- */
- line-height: 20px;
-
- &[${hasActiveDescendantAttribute}], &:focus-within {
- --item-hover-bg-override: none;
- --item-hover-divider-border-color-override: ${get('colors.border.muted')};
- }
-`
-
-/**
- * Returns `sx` prop values for `List` children matching the given `List` style variation.
- * @param variant `List` style variation.
- */
-function useListVariant(variant: ListProps['variant'] = 'inset'): {
- firstGroupStyle?: SystemCssProperties
- lastGroupStyle?: SystemCssProperties
- headerStyle?: SystemCssProperties
- itemStyle?: SystemCssProperties
-} {
- switch (variant) {
- case 'full':
- return {
- headerStyle: {paddingX: get('space.2')},
- itemStyle: {borderRadius: 0}
- }
- default:
- return {
- firstGroupStyle: {marginTop: get('space.2')},
- lastGroupStyle: {marginBottom: get('space.2')},
- itemStyle: {marginX: get('space.2')}
- }
- }
-}
-
-/**
- * Lists `Item`s, either grouped or ungrouped, with a `Divider` between each `Group`.
- */
-export const List = React.forwardRef((props, forwardedRef): JSX.Element => {
- // Get `sx` prop values for `List` children matching the given `List` style variation.
- const {firstGroupStyle, lastGroupStyle, headerStyle, itemStyle} = useListVariant(props.variant)
-
- /**
- * Render a `Group` using the first of the following renderers that is defined:
- * A `Group`-level or `List`-level custom `Group` renderer, or
- * the default `Group` renderer.
- */
- const renderGroup = (
- groupProps: GroupProps | (Partial & {renderItem?: typeof Item; renderGroup?: typeof Group})
- ) => {
- const GroupComponent = (('renderGroup' in groupProps && groupProps.renderGroup) ?? props.renderGroup) || Group
- return
- }
-
- /**
- * Render an `Item` using the first of the following renderers that is defined:
- * An `Item`-level, `Group`-level, or `List`-level custom `Item` renderer,
- * or the default `Item` renderer.
- */
- const renderItem = (itemProps: ItemInput, item: ItemInput, itemIndex: number) => {
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- const ItemComponent = ('renderItem' in itemProps && itemProps.renderItem) || props.renderItem || Item
- const key = ('key' in itemProps ? itemProps.key : undefined) ?? itemProps.id?.toString() ?? itemIndex.toString()
- return (
-
- )
- }
-
- /**
- * An array of `Group`s, each with an associated `Header` and with an array of `Item`s belonging to that `Group`.
- */
- let groups: (GroupProps | (Partial & {renderItem?: typeof Item; renderGroup?: typeof Group}))[] = []
-
- // Collect rendered `Item`s into `Group`s, avoiding excess iteration over the lists of `items` and `groupMetadata`:
- if (!isGroupedListProps(props)) {
- // When no `groupMetadata`s is provided, collect rendered `Item`s into a single anonymous `Group`.
- groups = [{items: props.items.map((item, index) => renderItem(item, item, index)), groupId: '0'}]
- } else {
- // When `groupMetadata` is provided, collect rendered `Item`s into their associated `Group`s.
-
- /**
- * A map of group identifiers to `Group`s, each with an associated array of `Item`s belonging to that `Group`.
- */
- const groupMap = props.groupMetadata.reduce(
- (groupAccumulator, groupMetadata) => groupAccumulator.set(groupMetadata.groupId, groupMetadata),
- new Map & {renderItem?: typeof Item; renderGroup?: typeof Group})>()
- )
-
- for (const itemProps of props.items) {
- // Look up the group associated with the current item.
- const group = groupMap.get(itemProps.groupId)
- const itemIndex = group?.items?.length ?? 0
-
- // Upsert the group to include the current item (rendered).
- groupMap.set(itemProps.groupId, {
- ...group,
- items: [
- ...(group?.items ?? []),
- renderItem(
- {
- showDivider: group?.showItemDividers,
- ...(group && 'renderItem' in group && {renderItem: group.renderItem}),
- ...itemProps
- },
- itemProps,
- itemIndex
- )
- ]
- })
- }
-
- groups = [...groupMap.values()]
- }
-
- return (
-
- {groups.map(({header, ...groupProps}, index) => {
- const hasFilledHeader = header?.variant === 'filled'
- const shouldShowDivider = index > 0 && !hasFilledHeader
- return (
-
- {shouldShowDivider ? : null}
- {renderGroup({
- sx: {
- ...(index === 0 && firstGroupStyle),
- ...(index === groups.length - 1 && lastGroupStyle),
- ...(index > 0 && !shouldShowDivider && {mt: 2})
- },
- ...(header && {
- header: {
- ...header,
- sx: {...headerStyle, ...header.sx}
- }
- }),
- ...groupProps
- })}
-
- )
- })}
-
- )
-})
-
-List.displayName = 'ActionList'
diff --git a/src/deprecated/ActionList/index.ts b/src/deprecated/ActionList/index.ts
deleted file mode 100644
index 71eb71708f8..00000000000
--- a/src/deprecated/ActionList/index.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import {List} from './List'
-import {Group} from './Group'
-import {Item} from './Item'
-import {Divider} from './Divider'
-export type {ListProps as ActionListProps} from './List'
-export type {GroupProps} from './Group'
-export type {ItemProps} from './Item'
-
-/**
- * Collection of list-related components.
- */
-export const ActionList = Object.assign(List, {
- /** Collects related `Items` in an `ActionList`. */
- Group,
-
- /** An actionable or selectable `Item` with an optional icon and description. */
- Item,
-
- /** Visually separates `Item`s or `Group`s in an `ActionList`. */
- Divider
-})
diff --git a/src/deprecated/ActionMenu.tsx b/src/deprecated/ActionMenu.tsx
deleted file mode 100644
index c2a34dcff42..00000000000
--- a/src/deprecated/ActionMenu.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-import {GroupedListProps, List, ListPropsBase} from './ActionList/List'
-import {Item, ItemProps} from './ActionList/Item'
-import {Divider} from './ActionList/Divider'
-import Button, {ButtonProps} from '../Button'
-import React, {useCallback, useMemo} from 'react'
-import {AnchoredOverlay} from '../AnchoredOverlay'
-import {useProvidedStateOrCreate} from '../hooks/useProvidedStateOrCreate'
-import {OverlayProps} from '../Overlay'
-import {useProvidedRefOrCreate} from '../hooks'
-import {AnchoredOverlayWrapperAnchorProps} from '../AnchoredOverlay/AnchoredOverlay'
-
-interface ActionMenuBaseProps extends Partial>, ListPropsBase {
- /**
- * Content that is passed into the renderAnchor component, which is a button by default.
- */
- anchorContent?: React.ReactNode
-
- /**
- * A callback that triggers both on clicks and keyboard events. This callback will be overridden by item level `onAction` callbacks.
- */
- onAction?: (props: ItemProps, event?: React.MouseEvent | React.KeyboardEvent) => void
-
- /**
- * If defined, will control the open/closed state of the overlay. Must be used in conjuction with `setOpen`.
- */
- open?: boolean
-
- /**
- * If defined, will control the open/closed state of the overlay. Must be used in conjuction with `open`.
- */
- setOpen?: (s: boolean) => void
-
- /**
- * Props to be spread on the internal `Overlay` component.
- */
- overlayProps?: Partial
-}
-
-export type ActionMenuProps = ActionMenuBaseProps & AnchoredOverlayWrapperAnchorProps
-
-const ActionMenuItem = (props: ItemProps) =>
-
-ActionMenuItem.displayName = 'ActionMenu.Item'
-
-const ActionMenuBase = ({
- anchorContent,
- renderAnchor = (props: T) => ,
- anchorRef: externalAnchorRef,
- onAction,
- open,
- setOpen,
- overlayProps,
- items,
- ...listProps
-}: ActionMenuProps): JSX.Element => {
- const [combinedOpenState, setCombinedOpenState] = useProvidedStateOrCreate(open, setOpen, false)
- const anchorRef = useProvidedRefOrCreate(externalAnchorRef)
- const onOpen = useCallback(() => setCombinedOpenState(true), [setCombinedOpenState])
- const onClose = useCallback(() => setCombinedOpenState(false), [setCombinedOpenState])
-
- const renderMenuAnchor = useMemo(() => {
- if (renderAnchor === null) {
- return null
- }
- return >(props: T) => {
- return renderAnchor({
- 'aria-label': 'menu',
- children: anchorContent,
- ...props
- })
- }
- }, [anchorContent, renderAnchor])
-
- const itemsToRender = useMemo(() => {
- return items.map(item => {
- return {
- ...item,
- role: 'menuitem',
- onAction: (props, event) => {
- const actionCallback = item.onAction ?? onAction
- actionCallback?.(props as ItemProps, event)
- if (!event.defaultPrevented) {
- onClose()
- }
- }
- } as ItemProps
- })
- }, [items, onAction, onClose])
-
- return (
-
-
-
- )
-}
-
-ActionMenuBase.displayName = 'ActionMenu'
-
-/**
- * @deprecated Use ActionMenu with composable API instead. See https://primer.style/react/ActionMenu for more details.
- */
-export const ActionMenu = Object.assign(ActionMenuBase, {Divider, Item: ActionMenuItem})
diff --git a/src/deprecated/index.ts b/src/deprecated/index.ts
index b5641b8e5c1..df51794a190 100644
--- a/src/deprecated/index.ts
+++ b/src/deprecated/index.ts
@@ -37,7 +37,3 @@ export type {
SelectMenuTabPanelProps,
SelectMenuLoadingAnimationProps
} from '../SelectMenu'
-export {ActionList} from './ActionList'
-export type {ActionListProps} from './ActionList'
-export {ActionMenu} from './ActionMenu'
-export type {ActionMenuProps} from './ActionMenu'
diff --git a/src/drafts/index.ts b/src/drafts/index.ts
index 69143a5a08b..efa9ff196c5 100644
--- a/src/drafts/index.ts
+++ b/src/drafts/index.ts
@@ -6,6 +6,8 @@
*/
// Components
+export * from '../ActionList2'
export * from '../Button2'
+export * from '../ActionMenu2'
export * from '../DropdownMenu2'
export * from '../Label2'
diff --git a/src/index.ts b/src/index.ts
index 082749a9535..69129d62591 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -30,16 +30,8 @@ export {useConfirm} from './Dialog/ConfirmationDialog'
export {default as Radio} from './Radio'
export type {RadioProps} from './Radio'
export {ActionList} from './ActionList'
-export type {
- ActionListProps,
- ActionListGroupProps,
- ActionListItemProps,
- ActionListDescriptionProps,
- ActionListLeadingVisualProps,
- ActionListTrailingVisualProps
-} from './ActionList'
export {ActionMenu} from './ActionMenu'
-export type {ActionMenuProps, ActionMenuAnchorProps, ActionMenuButtonProps} from './ActionMenu'
+export type {ActionMenuProps} from './ActionMenu'
export {default as Autocomplete} from './Autocomplete'
export type {AutocompleteMenuProps, AutocompleteInputProps, AutocompleteOverlayProps} from './Autocomplete'
export {default as Avatar} from './Avatar'
diff --git a/src/stories/deprecated/ActionList.stories.tsx b/src/stories/ActionList.stories.tsx
similarity index 97%
rename from src/stories/deprecated/ActionList.stories.tsx
rename to src/stories/ActionList.stories.tsx
index 615511ac43d..46e21e2c660 100644
--- a/src/stories/deprecated/ActionList.stories.tsx
+++ b/src/stories/ActionList.stories.tsx
@@ -14,18 +14,18 @@ import {
import {Meta} from '@storybook/react'
import React, {forwardRef} from 'react'
import styled from 'styled-components'
-import {Label, ThemeProvider} from '../..'
-import {ActionList as _ActionList} from '../../deprecated/ActionList'
-import {Header} from '../../deprecated/ActionList/Header'
-import BaseStyles from '../../BaseStyles'
-import sx from '../../sx'
+import {Label, ThemeProvider} from '..'
+import {ActionList as _ActionList} from '../ActionList'
+import {Header} from '../ActionList/Header'
+import BaseStyles from '../BaseStyles'
+import sx from '../sx'
const ActionList = Object.assign(_ActionList, {
Header
})
const meta: Meta = {
- title: 'Deprecated components/ActionList',
+ title: 'Composite components/ActionList',
component: ActionList,
decorators: [
(Story: React.ComponentType): JSX.Element => (
diff --git a/src/stories/ActionList/examples.stories.tsx b/src/stories/ActionList2/examples.stories.tsx
similarity index 98%
rename from src/stories/ActionList/examples.stories.tsx
rename to src/stories/ActionList2/examples.stories.tsx
index fdf306934bd..bb923be9c4f 100644
--- a/src/stories/ActionList/examples.stories.tsx
+++ b/src/stories/ActionList2/examples.stories.tsx
@@ -16,7 +16,8 @@ import {
} from '@primer/octicons-react'
import {ThemeProvider} from '../..'
-import {ActionList} from '../../ActionList'
+import {ActionList as _ActionList} from '../../ActionList2'
+import {Header} from '../../ActionList/Header'
import BaseStyles from '../../BaseStyles'
import Avatar from '../../Avatar'
import TextInput from '../../TextInput'
@@ -24,8 +25,12 @@ import Spinner from '../../Spinner'
import Box from '../../Box'
import Text from '../../Text'
+const ActionList = Object.assign(_ActionList, {
+ Header
+})
+
const meta: Meta = {
- title: 'Composite components/ActionList/examples',
+ title: 'Composite components/ActionList2/examples',
component: ActionList,
decorators: [
(Story: React.ComponentType): JSX.Element => (
diff --git a/src/stories/ActionList/fixtures.stories.tsx b/src/stories/ActionList2/fixtures.stories.tsx
similarity index 98%
rename from src/stories/ActionList/fixtures.stories.tsx
rename to src/stories/ActionList2/fixtures.stories.tsx
index 301b58684b7..a7fbcddd3c9 100644
--- a/src/stories/ActionList/fixtures.stories.tsx
+++ b/src/stories/ActionList2/fixtures.stories.tsx
@@ -28,15 +28,20 @@ import styled from 'styled-components'
import {DndProvider, useDrag, useDrop} from 'react-dnd'
import {HTML5Backend} from 'react-dnd-html5-backend'
import {Label, ThemeProvider} from '../..'
-import {ActionList, ActionListItemProps} from '../../ActionList'
+import {ActionList as _ActionList, ItemProps} from '../../ActionList2'
+import {Header} from '../../ActionList/Header'
import BaseStyles from '../../BaseStyles'
import Avatar from '../../Avatar'
import {ButtonInvisible} from '../../Button'
import Box from '../../Box'
import {AnchoredOverlay} from '../../AnchoredOverlay'
+const ActionList = Object.assign(_ActionList, {
+ Header
+})
+
const meta: Meta = {
- title: 'Composite components/ActionList/fixtures',
+ title: 'Composite components/ActionList2/fixtures',
component: ActionList,
decorators: [
(Story: React.ComponentType): JSX.Element => (
@@ -471,14 +476,14 @@ export function LinkItemStory(): JSX.Element {
as ReactRouterLink
-
+
@@ -486,7 +491,7 @@ export function LinkItemStory(): JSX.Element {
NextJS style Link
-
+
@@ -990,8 +995,8 @@ MemexSortable.storyName = 'Memex Sortable List'
type SortableItemProps = {
option: Option
- role: ActionListItemProps['role']
- onSelect: ActionListItemProps['onSelect']
+ role: ItemProps['role']
+ onSelect: ItemProps['onSelect']
reorder: ({optionToMove, moveAfterOption}: {optionToMove: Option; moveAfterOption: Option}) => void
}
const SortableItem: React.FC = ({option, role, onSelect, reorder}) => {
diff --git a/src/stories/deprecated/ActionMenu.stories.tsx b/src/stories/ActionMenu.stories.tsx
similarity index 96%
rename from src/stories/deprecated/ActionMenu.stories.tsx
rename to src/stories/ActionMenu.stories.tsx
index 05ec0a193bb..5d9165b5969 100644
--- a/src/stories/deprecated/ActionMenu.stories.tsx
+++ b/src/stories/ActionMenu.stories.tsx
@@ -13,16 +13,16 @@ import {
import {Meta} from '@storybook/react'
import React, {useCallback, useState, useRef} from 'react'
import styled from 'styled-components'
-import {ThemeProvider} from '../..'
-import Link, {LinkProps} from '../../Link'
-import Button from '../../Button'
-import {ActionMenu, ActionMenuProps, ActionList} from '../../deprecated'
-import {ItemProps} from '../../deprecated/ActionList'
-import BaseStyles from '../../BaseStyles'
-import {DropdownButton} from '../../DropdownMenu'
+import {ThemeProvider} from '..'
+import {ActionMenu, ActionMenuProps} from '../ActionMenu'
+import Link, {LinkProps} from '../Link'
+import Button from '../Button'
+import {ActionList, ItemProps} from '../ActionList'
+import BaseStyles from '../BaseStyles'
+import {DropdownButton} from '../DropdownMenu'
const meta: Meta = {
- title: 'Deprecated components/ActionMenu',
+ title: 'Composite components/ActionMenu',
component: ActionMenu,
decorators: [
(Story: React.ComponentType): JSX.Element => (
diff --git a/src/stories/ActionMenu/examples.stories.tsx b/src/stories/ActionMenu2/examples.stories.tsx
similarity index 98%
rename from src/stories/ActionMenu/examples.stories.tsx
rename to src/stories/ActionMenu2/examples.stories.tsx
index 9bce65fcc8b..8f3592d316b 100644
--- a/src/stories/ActionMenu/examples.stories.tsx
+++ b/src/stories/ActionMenu2/examples.stories.tsx
@@ -1,6 +1,7 @@
import React from 'react'
import {Meta} from '@storybook/react'
-import {ThemeProvider, BaseStyles, Box, Text, Avatar, ActionMenu, ActionList} from '../..'
+import {ThemeProvider, BaseStyles, Box, Text, Avatar} from '../..'
+import {ActionMenu, ActionList} from '../../drafts'
import {
GearIcon,
MilestoneIcon,
@@ -16,7 +17,7 @@ import {
} from '@primer/octicons-react'
const meta: Meta = {
- title: 'Composite components/ActionMenu/examples',
+ title: 'Composite components/ActionMenu2/examples',
component: ActionMenu,
decorators: [
(Story: React.ComponentType): JSX.Element => (
diff --git a/src/stories/ActionMenu/fixtures.stories.tsx b/src/stories/ActionMenu2/fixtures.stories.tsx
similarity index 99%
rename from src/stories/ActionMenu/fixtures.stories.tsx
rename to src/stories/ActionMenu2/fixtures.stories.tsx
index ebf881eea59..bc867f300f4 100644
--- a/src/stories/ActionMenu/fixtures.stories.tsx
+++ b/src/stories/ActionMenu2/fixtures.stories.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import {Meta} from '@storybook/react'
-import {ThemeProvider, BaseStyles, Box, Text, TextInput, StyledOcticon, FormGroup, ActionMenu, ActionList} from '../..'
-import {Button, IconButton} from '../../drafts'
+import {ThemeProvider, BaseStyles, Box, Text, TextInput, StyledOcticon, FormGroup} from '../..'
+import {ActionMenu, ActionList, Button, IconButton} from '../../drafts'
import {
ServerIcon,
PlusCircleIcon,
@@ -25,7 +25,7 @@ import {
} from '@primer/octicons-react'
const meta: Meta = {
- title: 'Composite components/ActionMenu/fixtures',
+ title: 'Composite components/ActionMenu2/fixtures',
component: ActionMenu,
decorators: [
(Story: React.ComponentType): JSX.Element => (
diff --git a/src/stories/ConfirmationDialog.stories.tsx b/src/stories/ConfirmationDialog.stories.tsx
index 8e91c8a9f39..69e9619f4e9 100644
--- a/src/stories/ConfirmationDialog.stories.tsx
+++ b/src/stories/ConfirmationDialog.stories.tsx
@@ -3,7 +3,7 @@ import {Meta} from '@storybook/react'
import {BaseStyles, Button, Box, ThemeProvider, useTheme} from '..'
import {ConfirmationDialog, useConfirm} from '../Dialog/ConfirmationDialog'
-import {ActionMenu} from '../deprecated/ActionMenu'
+import {ActionMenu} from '../ActionMenu'
export default {
title: 'Internal components/ConfirmationDialog',
diff --git a/src/stories/DropdownMenu.stories.tsx b/src/stories/DropdownMenu.stories.tsx
index 13812de3b11..1f524c0de8d 100644
--- a/src/stories/DropdownMenu.stories.tsx
+++ b/src/stories/DropdownMenu.stories.tsx
@@ -1,7 +1,7 @@
import {Meta} from '@storybook/react'
import React from 'react'
import {theme, ThemeProvider} from '..'
-import {ItemInput} from '../deprecated/ActionList/List'
+import {ItemInput} from '../ActionList/List'
import BaseStyles from '../BaseStyles'
import Box from '../Box'
import {DropdownMenu, DropdownButton} from '../DropdownMenu'
diff --git a/src/stories/DropdownMenu2/examples.stories.tsx b/src/stories/DropdownMenu2/examples.stories.tsx
index c72878f65e1..a461dbbf47f 100644
--- a/src/stories/DropdownMenu2/examples.stories.tsx
+++ b/src/stories/DropdownMenu2/examples.stories.tsx
@@ -3,7 +3,7 @@ import {Meta} from '@storybook/react'
import {ThemeProvider} from '../..'
import BaseStyles from '../../BaseStyles'
import {DropdownMenu} from '../../DropdownMenu2'
-import {ActionList} from '../../ActionList'
+import {ActionList} from '../../ActionList2'
import Box from '../../Box'
import Text from '../../Text'
import {
diff --git a/src/stories/DropdownMenu2/fixtures.stories.tsx b/src/stories/DropdownMenu2/fixtures.stories.tsx
index b0c462b9b17..d39782e46c1 100644
--- a/src/stories/DropdownMenu2/fixtures.stories.tsx
+++ b/src/stories/DropdownMenu2/fixtures.stories.tsx
@@ -3,7 +3,7 @@ import {Meta} from '@storybook/react'
import {ThemeProvider} from '../..'
import BaseStyles from '../../BaseStyles'
import {DropdownMenu} from '../../DropdownMenu2'
-import {ActionList} from '../../ActionList'
+import {ActionList} from '../../ActionList2'
import {Button} from '../../Button2'
import Box from '../../Box'
import Text from '../../Text'
diff --git a/src/stories/Overlay.stories.tsx b/src/stories/Overlay.stories.tsx
index d5235581ff6..dd635c6338a 100644
--- a/src/stories/Overlay.stories.tsx
+++ b/src/stories/Overlay.stories.tsx
@@ -17,14 +17,14 @@ import {
Checkbox,
ChoiceInputField,
TextInput,
- Link,
- Label,
ActionList,
- ActionMenu
+ Link,
+ Label
} from '..'
import type {AnchorSide} from '@primer/behaviors'
import {DropdownMenu, DropdownButton} from '../DropdownMenu'
-import {ItemInput} from '../deprecated/ActionList/List'
+import {ActionMenu, ActionList as ActionList2} from '../drafts'
+import {ItemInput} from '../ActionList/List'
export default {
title: 'Internal components/Overlay',
@@ -367,13 +367,13 @@ export const MemexNestedOverlays = () => {
{duration}
-
+
{durations.map(item => (
- setDuration(item)}>
+ setDuration(item)}>
{item}
-
+
))}
-
+