From 134f2658f0e73478d84b9caa942748dcef1b4192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arma=C4=9Fan?= Date: Fri, 21 Jul 2023 08:13:50 +1000 Subject: [PATCH] Remove `DropdownButton` and `DropdownMenu` from deprecated and update the usages across (#3544) * Remove DropdownMenu and DropdownButton and update usages across * copy ts expect from main and add changeset * clean up * unused ts expect --- .changeset/nine-news-walk.md | 7 + docs/content/deprecated/ActionList.mdx | 2 +- docs/content/deprecated/DropdownMenu.mdx | 127 --------- .../src/@primer/gatsby-theme-doctocat/nav.yml | 2 - src/ActionList/Item.tsx | 2 +- src/SelectPanel/SelectPanel.tsx | 13 +- .../__snapshots__/SelectPanel.test.tsx.snap | 269 ++++++++++++++---- .../__snapshots__/exports.test.ts.snap | 2 - .../deprecated/DropdownMenu.test.tsx | 120 -------- .../__snapshots__/DropdownMenu.test.tsx.snap | 104 ------- .../DropdownMenu/DropdownButton.tsx | 18 -- src/deprecated/DropdownMenu/DropdownMenu.tsx | 113 -------- src/deprecated/DropdownMenu/index.ts | 4 - src/deprecated/index.ts | 2 - src/stories/deprecated/ActionMenu.stories.tsx | 12 +- .../deprecated/DropdownMenu.stories.tsx | 84 ------ 16 files changed, 248 insertions(+), 633 deletions(-) create mode 100644 .changeset/nine-news-walk.md delete mode 100644 docs/content/deprecated/DropdownMenu.mdx delete mode 100644 src/__tests__/deprecated/DropdownMenu.test.tsx delete mode 100644 src/__tests__/deprecated/__snapshots__/DropdownMenu.test.tsx.snap delete mode 100644 src/deprecated/DropdownMenu/DropdownButton.tsx delete mode 100644 src/deprecated/DropdownMenu/DropdownMenu.tsx delete mode 100644 src/deprecated/DropdownMenu/index.ts delete mode 100644 src/stories/deprecated/DropdownMenu.stories.tsx diff --git a/.changeset/nine-news-walk.md b/.changeset/nine-news-walk.md new file mode 100644 index 00000000000..c5ae12cd1cb --- /dev/null +++ b/.changeset/nine-news-walk.md @@ -0,0 +1,7 @@ +--- +'@primer/react': major +--- + +Remove `DropdownButton` and `DropdownMenu` from deprecated and update the usages across + + diff --git a/docs/content/deprecated/ActionList.mdx b/docs/content/deprecated/ActionList.mdx index 13bfce59100..cccbdbe6642 100644 --- a/docs/content/deprecated/ActionList.mdx +++ b/docs/content/deprecated/ActionList.mdx @@ -4,7 +4,7 @@ status: Deprecated source: https://github.com/primer/react/tree/main/src/deprecated/ActionList --- -An `ActionList` is a list of items which can be activated or selected. `ActionList` is the base component for many of our menu-type components, including `DropdownMenu` and `ActionMenu`. +An `ActionList` is a list of items which can be activated or selected. `ActionList` is the base component for many of our menu-type components, including and `ActionMenu`. ## Deprecation diff --git a/docs/content/deprecated/DropdownMenu.mdx b/docs/content/deprecated/DropdownMenu.mdx deleted file mode 100644 index bd4ba02bc67..00000000000 --- a/docs/content/deprecated/DropdownMenu.mdx +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: DropdownMenu -status: Deprecated ---- - -A `DropdownMenu` provides an anchor (button by default) that will open a floating menu of selectable items. The menu can be opened and navigated using keyboard or mouse. When an item is selected, the menu will close and the `onChange` callback will be called. If the default anchor button is used, the anchor contents will be updated with the selection. - -## Deprecation - -Use [new version of ActionMenu](/ActionMenu#with-selection) with composable API, design updates and accessibility fixes. - -### Before - -```jsx -const fieldTypes = [ - {key: 0, text: 'Text'}, - {key: 1, text: 'Number'}, - {key: 3, text: 'Date'}, - {key: 4, text: 'Single select'}, - {key: 5, text: 'Iteration'}, -] - -const Example = () => { - const [selectedType, setSelectedType] = React.useState() - - return ( - ( - - {children} - - )} - placeholder="Field type" - items={fieldTypes} - selectedItem={selectedType} - onChange={setSelectedType} - /> - ) -} -``` - -### After - -Instead of `DropdownMenu`, you can use the `ActionMenu` with `ActionList selectionVariant=single`, this will give menu items the correct semantics: - -```jsx -const fieldTypes = [ - {id: 0, text: 'Text'}, - {id: 1, text: 'Number'}, - {id: 3, text: 'Date'}, - {id: 4, text: 'Single select'}, - {id: 5, text: 'Iteration'}, -] - -const Example = () => { - const [selectedType, setSelectedType] = React.useState() - - render( - - {selectedType.name || 'Field type'} - - - {fieldTypes.map(type => ( - setSelectedType(type)} - > - {type.name} - - ))} - - - , - ) -} -``` - -Or continue using deprecated API: - -```js -import {DropdownMenu} from '@primer/react/deprecated' -``` - -## Example - -```javascript live noinline deprecated -function DemoComponent() { - const items = React.useMemo( - () => [ - {text: '🔵 Cyan', id: 5, key: 'cyan'}, - {text: '🔴 Magenta', key: 'magenta'}, - {text: '🟡 Yellow', key: 'yellow'}, - ], - [], - ) - const [selectedItem, setSelectedItem] = React.useState() - - return ( - ( - - {children} - - )} - placeholder="🎨" - items={items} - selectedItem={selectedItem} - onChange={setSelectedItem} - /> - ) -} - -render() -``` - -## Component props - -| Name | Type | Default | Description | -| :------------ | :-------------------------------------------- | :---------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| items | `ItemProps[]` | `undefined` | Required. A list of item objects to display in the menu | -| selectedItem | `ItemInput` | `undefined` | An `ItemProps` item from the list of `items` which is currently selected. This item will receive a checkmark next to it in the menu. | -| onChange? | (item?: ItemInput) => unknown | `undefined` | A callback which receives the selected item or `undefined` when an item is activated in the menu. If the activated item is the same as the current `selectedItem`, `undefined` will be passed. | -| placeholder | `string` | `undefined` | Optional. A placeholder value to display when there is no current selection. | -| renderAnchor | `(props: DropdownButtonProps) => JSX.Element` | `DropdownButton` | Optional. If defined, provided component will be used to render the menu anchor. Will receive the selected `Item` text as `children` prop when an item is activated. | -| renderItem | `(props: ItemProps) => JSX.Element` | `ActionList.Item` | Optional. If defined, each item in `items` will be passed to this function, allowing for custom item rendering. | -| groupMetadata | `GroupProps[]` | `undefined` | Optional. If defined, `DropdownMenu` will group `items` into `ActionList.Group`s separated by `ActionList.Divider` according to their `groupId` property. | diff --git a/docs/src/@primer/gatsby-theme-doctocat/nav.yml b/docs/src/@primer/gatsby-theme-doctocat/nav.yml index e1c13da496a..1d72a4742d2 100644 --- a/docs/src/@primer/gatsby-theme-doctocat/nav.yml +++ b/docs/src/@primer/gatsby-theme-doctocat/nav.yml @@ -176,8 +176,6 @@ url: /deprecated/ActionMenu - title: Buttons (legacy) url: /deprecated/Buttons - - title: DropdownMenu - url: /deprecated/DropdownMenu - title: FilteredSearch url: /deprecated/FilteredSearch - title: FilterList diff --git a/src/ActionList/Item.tsx b/src/ActionList/Item.tsx index 90ead90ac58..3c25f839911 100644 --- a/src/ActionList/Item.tsx +++ b/src/ActionList/Item.tsx @@ -61,7 +61,7 @@ export const Item = React.forwardRef( /** Infer item role based on the container */ let itemRole: ActionListItemProps['role'] - if (container === 'ActionMenu' || container === 'DropdownMenu') { + if (container === 'ActionMenu') { if (selectionVariant === 'single') itemRole = 'menuitemradio' else if (selectionVariant === 'multiple') itemRole = 'menuitemcheckbox' else itemRole = 'menuitem' diff --git a/src/SelectPanel/SelectPanel.tsx b/src/SelectPanel/SelectPanel.tsx index 0075069d50d..2c79006ccfa 100644 --- a/src/SelectPanel/SelectPanel.tsx +++ b/src/SelectPanel/SelectPanel.tsx @@ -1,4 +1,4 @@ -import {SearchIcon} from '@primer/octicons-react' +import {SearchIcon, TriangleDownIcon} from '@primer/octicons-react' import React, {useCallback, useMemo} from 'react' import {AnchoredOverlay, AnchoredOverlayProps} from '../AnchoredOverlay' import {AnchoredOverlayWrapperAnchorProps} from '../AnchoredOverlay/AnchoredOverlay' @@ -9,7 +9,7 @@ import {OverlayProps} from '../Overlay' import {TextInputProps} from '../TextInput' import {ItemProps} from '../deprecated/ActionList' import {ItemInput} from '../deprecated/ActionList/List' -import {DropdownButton} from '../deprecated/DropdownMenu' +import {Button} from '../Button' import {useProvidedRefOrCreate} from '../hooks' import {FocusZoneHookSettings} from '../hooks/useFocusZone' import {useId} from '../hooks/useId' @@ -60,7 +60,14 @@ const focusZoneSettings: Partial = { export function SelectPanel({ open, onOpenChange, - renderAnchor = props => , + renderAnchor = props => { + const {children, ...rest} = props + return ( + + ) + }, anchorRef: externalAnchorRef, placeholder, placeholderText = 'Filter items', diff --git a/src/SelectPanel/__snapshots__/SelectPanel.test.tsx.snap b/src/SelectPanel/__snapshots__/SelectPanel.test.tsx.snap index ed7d0dc6442..6c41121a56b 100644 --- a/src/SelectPanel/__snapshots__/SelectPanel.test.tsx.snap +++ b/src/SelectPanel/__snapshots__/SelectPanel.test.tsx.snap @@ -8,71 +8,225 @@ exports[`SelectPanel renders consistently 1`] = ` } .c2 { - margin-left: 4px; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; +} + +.c3 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + pointer-events: none; } .c1 { - position: relative; - display: inline-block; - padding: 6px 16px; + border-radius: 6px; + border: 1px solid; + border-color: rgba(31,35,40,0.15); font-family: inherit; - font-weight: 600; - line-height: 20px; - white-space: nowrap; - vertical-align: middle; + font-weight: 500; + font-size: 14px; 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; - font-size: 14px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + height: 32px; + padding: 0 12px; + gap: 8px; + min-width: -webkit-max-content; + min-width: -moz-max-content; + min-width: max-content; + -webkit-transition: 80ms cubic-bezier(0.65,0,0.35,1); + transition: 80ms cubic-bezier(0.65,0,0.35,1); + -webkit-transition-property: color,fill,background-color,border-color; + transition-property: color,fill,background-color,border-color; color: #24292f; background-color: #f6f8fa; - border: 1px solid rgba(31,35,40,0.15); box-shadow: 0 1px 0 rgba(31,35,40,0.04),inset 0 1px 0 rgba(255,255,255,0.25); } -.c1:hover { +.c1:focus:not(:disabled) { + box-shadow: none; + outline: 2px solid #0969da; + outline-offset: -2px; +} + +.c1:focus:not(:disabled):not(:focus-visible) { + outline: solid 1px transparent; +} + +.c1:focus-visible:not(:disabled) { + box-shadow: none; + outline: 2px solid #0969da; + outline-offset: -2px; +} + +.c1[href] { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c1[href]:hover { -webkit-text-decoration: none; text-decoration: none; } -.c1:focus { - outline: none; +.c1:hover { + -webkit-transition-duration: 80ms; + transition-duration: 80ms; +} + +.c1:active { + -webkit-transition: none; + transition: none; } .c1:disabled { - cursor: default; + cursor: not-allowed; + box-shadow: none; + color: #8c959f; } -.c1:disabled svg { - opacity: 0.6; +.c1:disabled [data-component=ButtonCounter] { + color: inherit; } -.c1:hover { +.c1 [data-component=ButtonCounter] { + font-size: 14px; +} + +.c1[data-component=IconButton] { + display: inline-grid; + padding: unset; + place-content: center; + width: 32px; + min-width: unset; +} + +.c1[data-size="small"] { + padding: 0 8px; + height: 28px; + gap: 4px; + font-size: 12px; +} + +.c1[data-size="small"] [data-component="text"] { + line-height: calc(20 / 12); +} + +.c1[data-size="small"] [data-component=ButtonCounter] { + font-size: 12px; +} + +.c1[data-size="small"] [data-component="buttonContent"] > :not(:last-child) { + margin-right: 4px; +} + +.c1[data-size="small"][data-component=IconButton] { + width: 28px; + padding: unset; +} + +.c1[data-size="large"] { + padding: 0 16px; + height: 40px; + gap: 8px; +} + +.c1[data-size="large"] [data-component="buttonContent"] > :not(:last-child) { + margin-right: 8px; +} + +.c1[data-size="large"][data-component=IconButton] { + width: 40px; + padding: unset; +} + +.c1[data-block="block"] { + width: 100%; +} + +.c1 [data-component="leadingVisual"] { + grid-area: leadingVisual; +} + +.c1 [data-component="text"] { + grid-area: text; + line-height: calc(20/14); + white-space: nowrap; +} + +.c1 [data-component="trailingVisual"] { + grid-area: trailingVisual; +} + +.c1 [data-component="trailingAction"] { + margin-right: -4px; +} + +.c1 [data-component="buttonContent"] { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + display: grid; + grid-template-areas: "leadingVisual text trailingVisual"; + grid-template-columns: min-content minmax(0,auto) min-content; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; +} + +.c1 [data-component="buttonContent"] > :not(:last-child) { + margin-right: 8px; +} + +.c1:hover:not([disabled]) { background-color: #f3f4f6; border-color: rgba(31,35,40,0.15); } -.c1:focus { - outline: solid 2px #0969da; +.c1:active:not([disabled]) { + background-color: hsla(220,14%,93%,1); + border-color: rgba(31,35,40,0.15); } -.c1:active { - background-color: hsla(220,14%,94%,1); +.c1[aria-expanded=true] { + background-color: hsla(220,14%,93%,1); + border-color: rgba(31,35,40,0.15); } -.c1:disabled { - color: #8c959f; - background-color: #f6f8fa; - border-color: rgba(31,35,40,0.15); +@media (forced-colors:active) { + .c1:focus { + outline: solid 1px transparent; + } }
- Select Items - + + Select Items + + + + +
() - - return ( - - - -
X
- -
-
-
-
- ) -} - -describe('DropdownMenu', () => { - afterEach(() => { - jest.clearAllMocks() - }) - - behavesAsComponent({ - Component: DropdownMenu, - options: {skipAs: true, skipSx: true}, - toRender: () => ( - - - - ), - }) - - checkExports('deprecated/DropdownMenu', { - default: undefined, - DropdownMenu, - DropdownButton, - }) - - it('should have no axe violations', async () => { - const {container} = HTMLRender() - const results = await axe(container) - expect(results).toHaveNoViolations() - }) - - 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('Select an Option') - fireEvent.click(anchor) - portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__') - expect(portalRoot).toBeTruthy() - const itemText = items - .map((i: ItemInput) => { - if (i.hasOwnProperty('text')) { - return i.text - } - }) - .join('') - expect(portalRoot?.textContent?.trim()).toEqual(itemText) - }) - - it('should dismiss the overlay on dropdown item click', async () => { - const menu = HTMLRender() - let portalRoot = await menu.baseElement.querySelector('#__primerPortalRoot__') - expect(portalRoot).toBeNull() - const anchor = await menu.findByText('Select an Option') - fireEvent.click(anchor) - portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__') - expect(portalRoot).toBeTruthy() - const menuItem = menu.queryByText('Baz') - fireEvent.click(menuItem as Element) - // portal is closed after click - expect(portalRoot?.textContent).toEqual('') // menu items are hidden - }) - - it('option should be selected when chosen from the dropdown menu', async () => { - const menu = HTMLRender() - let portalRoot = await menu.baseElement.querySelector('#__primerPortalRoot__') - expect(portalRoot).toBeNull() - const anchor = await menu.findByText('Select an Option') - fireEvent.click(anchor) - portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__') - expect(portalRoot).toBeTruthy() - const menuItem = menu.queryByText('Baz') - fireEvent.click(menuItem as Element) - expect(anchor.textContent).toEqual('Baz') - }) - - 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('Select an Option') - fireEvent.click(anchor) - portalRoot = menu.baseElement.querySelector('#__primerPortalRoot__') - expect(portalRoot).toBeTruthy() - const somethingElse = (await menu.baseElement.querySelector('#something-else')) as HTMLElement - fireEvent.mouseDown(somethingElse) - // portal is closed after click - expect(portalRoot?.textContent).toEqual('') // menu items are hidden - }) -}) diff --git a/src/__tests__/deprecated/__snapshots__/DropdownMenu.test.tsx.snap b/src/__tests__/deprecated/__snapshots__/DropdownMenu.test.tsx.snap deleted file mode 100644 index e8ff59eba75..00000000000 --- a/src/__tests__/deprecated/__snapshots__/DropdownMenu.test.tsx.snap +++ /dev/null @@ -1,104 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`DropdownMenu renders consistently 1`] = ` -.c1 { - margin-left: 4px; -} - -.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(31,35,40,0.15); - box-shadow: 0 1px 0 rgba(31,35,40,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(31,35,40,0.15); -} - -.c0:focus { - outline: solid 2px #0969da; -} - -.c0:active { - background-color: hsla(220,14%,94%,1); -} - -.c0:disabled { - color: #8c959f; - background-color: #f6f8fa; - border-color: rgba(31,35,40,0.15); -} - - -`; diff --git a/src/deprecated/DropdownMenu/DropdownButton.tsx b/src/deprecated/DropdownMenu/DropdownButton.tsx deleted file mode 100644 index 8764123c07a..00000000000 --- a/src/deprecated/DropdownMenu/DropdownButton.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react' -import {TriangleDownIcon} from '@primer/octicons-react' -import Button, {ButtonProps} from '../Button/Button' -import Octicon from '../../Octicon' - -export type DropdownButtonProps = ButtonProps - -/** - * @deprecated Use Button with Octicons instead. See https://primer.style/react/drafts/Button2#appending-an-icon for more details. - */ -export const DropdownButton = React.forwardRef>( - ({children, ...props}: React.PropsWithChildren, ref): JSX.Element => ( - - ), -) diff --git a/src/deprecated/DropdownMenu/DropdownMenu.tsx b/src/deprecated/DropdownMenu/DropdownMenu.tsx deleted file mode 100644 index 8fd6fd4dc73..00000000000 --- a/src/deprecated/DropdownMenu/DropdownMenu.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React, {useCallback, useMemo} from 'react' -import {List, GroupedListProps, ListPropsBase, ItemInput} from '../ActionList/List' -import {DropdownButton, DropdownButtonProps} from './DropdownButton' -import {ItemProps} from '../ActionList/Item' -import {AnchoredOverlay} from '../../AnchoredOverlay' -import {OverlayProps} from '../../Overlay' -import {AnchoredOverlayWrapperAnchorProps} from '../../AnchoredOverlay/AnchoredOverlay' -import {useProvidedRefOrCreate} from '../../hooks/useProvidedRefOrCreate' -import {useProvidedStateOrCreate} from '../../hooks/useProvidedStateOrCreate' - -interface DropdownMenuBaseProps extends Partial>, ListPropsBase { - /** - * A placeholder value to display on the trigger button when no selection has been made. - */ - placeholder?: string - - /** - * An `ItemProps` item from the list of `items` which is currently selected. This item will receive a checkmark next to it in the menu. - */ - selectedItem?: ItemInput - - /** - * A callback which receives the selected item or `undefined` when an item is activated in the menu. If the activated item is the same as the current - * `selectedItem`, `undefined` will be passed. - */ - onChange?: (item?: ItemInput) => unknown - - /** - * Props to be spread on the internal `Overlay` component. - */ - overlayProps?: Partial - - /** - * If defined, will control the open/closed state of the overlay. If not defined, the overlay will manage its own state (in other words, an - * uncontrolled component). Must be used in conjunction with `onOpenChange`. - */ - open?: boolean - - /** - * If defined, will control the open/closed state of the overlay. Must be used in conjunction with `open`. - */ - onOpenChange?: (open: boolean) => void -} - -export type DropdownMenuProps = DropdownMenuBaseProps & AnchoredOverlayWrapperAnchorProps - -/** - * @deprecated Use ActionMenu with ActionList instead. See https://primer.style/react/ActionMenu#with-selection for more details. - */ -export function DropdownMenu({ - renderAnchor = (props: T) => , - anchorRef: externalAnchorRef, - placeholder, - selectedItem, - onChange, - overlayProps, - items, - open, - onOpenChange, - ...listProps -}: DropdownMenuProps): JSX.Element { - const [combinedOpenState, setCombinedOpenState] = useProvidedStateOrCreate(open, onOpenChange, false) - const onOpen = useCallback(() => setCombinedOpenState(true), [setCombinedOpenState]) - const onClose = useCallback(() => setCombinedOpenState(false), [setCombinedOpenState]) - - const anchorRef = useProvidedRefOrCreate(externalAnchorRef) - - const renderMenuAnchor = useMemo(() => { - if (renderAnchor === null) { - return null - } - return >(props: T) => - renderAnchor({ - ...props, - children: selectedItem?.text ?? placeholder, - }) - }, [placeholder, renderAnchor, selectedItem?.text]) - - const itemsToRender = useMemo(() => { - return items.map(item => { - return { - ...item, - role: 'option', - selected: item === selectedItem, - onAction: (itemFromAction, event) => { - item.onAction?.(itemFromAction, event) - - if (event.defaultPrevented) { - return - } - - onClose() - onChange?.(item === selectedItem ? undefined : item) - }, - } as ItemProps - }) - }, [items, onChange, onClose, selectedItem]) - - return ( - - - - ) -} - -DropdownMenu.displayName = 'DropdownMenu' diff --git a/src/deprecated/DropdownMenu/index.ts b/src/deprecated/DropdownMenu/index.ts deleted file mode 100644 index ba93a7e16a8..00000000000 --- a/src/deprecated/DropdownMenu/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export {DropdownMenu} from './DropdownMenu' -export type {DropdownMenuProps} from './DropdownMenu' -export {DropdownButton} from './DropdownButton' -export type {DropdownButtonProps} from './DropdownButton' diff --git a/src/deprecated/index.ts b/src/deprecated/index.ts index 52dcd0b6832..bff199c3864 100644 --- a/src/deprecated/index.ts +++ b/src/deprecated/index.ts @@ -12,9 +12,7 @@ export {ActionList} from './ActionList' export type {ActionListProps} from './ActionList' export {ActionMenu} from './ActionMenu' export type {ActionMenuProps} from './ActionMenu' -export {DropdownButton, DropdownMenu} from './DropdownMenu' // (copied over from src/index) not exporting new DropdownMenu types yet due to conflict with Dropdown types above -// export type {DropdownButtonProps, DropdownMenuProps} from './DropdownMenu' export { default as Button, ButtonDanger, diff --git a/src/stories/deprecated/ActionMenu.stories.tsx b/src/stories/deprecated/ActionMenu.stories.tsx index ebf770066f1..e9d64190677 100644 --- a/src/stories/deprecated/ActionMenu.stories.tsx +++ b/src/stories/deprecated/ActionMenu.stories.tsx @@ -9,15 +9,18 @@ import { FilterIcon, GearIcon, ArrowRightIcon, + TriangleDownIcon, } from '@primer/octicons-react' 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 {ActionMenu, ActionMenuProps, ActionList, DropdownButton, Button} from '../../deprecated' +import {ActionMenu, ActionMenuProps, ActionList, Button} from '../../deprecated' import {ItemProps} from '../../deprecated/ActionList' import BaseStyles from '../../BaseStyles' +// We can remove the as as DropdownButton, after updating the deprecated Button usages +import {Button as DropdownButton} from '../../Button' const meta: Meta = { title: 'Deprecated/Components/ActionMenu', @@ -281,6 +284,7 @@ export function ActionMenuWithExternalAnchor(): JSX.Element { const DoubleClickableAnchor: Exclude = ({ onClick: callback, + children, ...rest }) => { const onClick = useCallback( @@ -292,7 +296,11 @@ const DoubleClickableAnchor: Exclude + return ( + + {children} + + ) } export function ActionMenuWithDoubleClickStory(): JSX.Element { return ( diff --git a/src/stories/deprecated/DropdownMenu.stories.tsx b/src/stories/deprecated/DropdownMenu.stories.tsx deleted file mode 100644 index dce65ee703c..00000000000 --- a/src/stories/deprecated/DropdownMenu.stories.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import {Meta} from '@storybook/react' -import React from 'react' -import {theme, ThemeProvider} from '../..' -import {ItemInput} from '../../deprecated/ActionList/List' -import BaseStyles from '../../BaseStyles' -import Box from '../../Box' -import {DropdownMenu, DropdownButton} from '../../deprecated' -import TextInput from '../../TextInput' - -const meta: Meta = { - title: 'Deprecated/Components/DropdownMenu', - component: DropdownMenu, - decorators: [ - (Story: React.ComponentType>): JSX.Element => { - return ( - - - - - - ) - }, - ], - parameters: { - controls: { - disable: true, - }, - }, -} -export default meta - -export function FavoriteColorStory(): JSX.Element { - const items = React.useMemo(() => [{text: '🔵 Cyan'}, {text: '🔴 Magenta'}, {text: '🟡 Yellow'}], []) - const [selectedItem, setSelectedItem] = React.useState() - - return ( - <> -

Favorite Color

- -
Please select your favorite color:
- ( - - {children} - - )} - placeholder="🎨" - items={items} - selectedItem={selectedItem} - onChange={setSelectedItem} - /> - - ) -} -FavoriteColorStory.storyName = 'Favorite Color' - -export function ExternalAnchorStory(): JSX.Element { - const items = React.useMemo(() => [{text: '🔵 Cyan'}, {text: '🔴 Magenta'}, {text: '🟡 Yellow'}], []) - const [selectedItem, setSelectedItem] = React.useState() - const anchorRef = React.useRef(null) - const [open, setOpen] = React.useState(false) - - return ( - - - setOpen(true)}>Click me to open the dropdown - - Anchored on me! - - - - - ) -} -ExternalAnchorStory.storyName = 'DropdownMenu with External Anchor'