From 8ea5a05af70e5d9dfc39060bcc01783db712a95a Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 09:14:25 +0200 Subject: [PATCH 01/27] feat(dropdownmenu): streamline API --- .../DropdownMenu/DropdownMenu.stories.tsx | 16 ++++++------- .../DropdownMenu/DropdownMenu.test.tsx | 2 +- .../DropdownMenu/DropdownMenuContent.tsx | 2 +- ...wnMenuRoot.tsx => DropdownMenuContext.tsx} | 24 +++++++++---------- .../DropdownMenu/DropdownMenuGroup.tsx | 2 +- .../DropdownMenu/DropdownMenuItem.tsx | 2 +- .../DropdownMenu/DropdownMenuTrigger.tsx | 2 +- .../src/components/DropdownMenu/index.ts | 10 ++++---- 8 files changed, 29 insertions(+), 31 deletions(-) rename packages/react/src/components/DropdownMenu/{DropdownMenuRoot.tsx => DropdownMenuContext.tsx} (77%) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx index b4cea2f7f2..d20374d516 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx @@ -18,7 +18,7 @@ export default { export const Preview: StoryFn = (args) => { return ( <> - + Dropdown @@ -26,7 +26,7 @@ export const Preview: StoryFn = (args) => { Button 2 - + ); }; @@ -40,7 +40,7 @@ Preview.decorators = [marginDecorator]; export const Icons: StoryFn = () => { return ( - + Dropdown @@ -66,7 +66,7 @@ export const Icons: StoryFn = () => { - + ); }; @@ -74,7 +74,7 @@ Icons.decorators = [marginDecorator]; export const InPortal: StoryFn = () => { return ( - + Dropdown @@ -100,7 +100,7 @@ export const InPortal: StoryFn = () => { - + ); }; @@ -109,7 +109,7 @@ export const Controlled: StoryFn = () => { return ( <> - setOpen(false)} portal> + setOpen(false)} portal> setOpen(!open)}> Dropdown @@ -137,7 +137,7 @@ export const Controlled: StoryFn = () => { - + ); }; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx index 4467d20303..294dbb977a 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx @@ -2,7 +2,7 @@ import { render as renderRtl, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { act } from 'react'; -import type { DropdownMenuRootProps } from './DropdownMenuRoot'; +import type { DropdownMenuRootProps } from './DropdownMenuContext'; import { DropdownMenu } from '.'; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx index d172e5188f..bff80fe1ca 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx @@ -18,7 +18,7 @@ import type { HTMLAttributes, ReactNode } from 'react'; import { useIsomorphicLayoutEffect } from '../../utilities'; -import { DropdownMenuContext } from './DropdownMenuRoot'; +import { DropdownMenuContext } from './DropdownMenuContext'; const GAP = 4; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuRoot.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx similarity index 77% rename from packages/react/src/components/DropdownMenu/DropdownMenuRoot.tsx rename to packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx index f135439d29..a7971aea3f 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuRoot.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx @@ -4,7 +4,7 @@ import type { ReactNode, RefObject } from 'react'; import type { PortalProps } from '../../types/Portal'; -export type DropdownMenuRootProps = { +export type DropdownMenuContextProps = { /** Whether the dropdown is open or not. * @default false */ @@ -24,7 +24,7 @@ export type DropdownMenuRootProps = { } & PortalProps; /** - * DropdownMenuRoot is the root component for the DropdownMenu component. + * DropdownMenuContext is the root component for the DropdownMenu component. * @example * * Dropdown @@ -35,14 +35,14 @@ export type DropdownMenuRootProps = { * * */ -export const DropdownMenuRoot = ({ +export function DropdownMenuContext({ open, onClose, placement = 'bottom-end', portal, size = 'md', children, -}: DropdownMenuRootProps) => { +}: DropdownMenuContextProps) { const triggerRef = useRef(null); const [internalOpen, setInternalOpen] = useState(open ?? false); @@ -54,7 +54,7 @@ export const DropdownMenuRoot = ({ }, [open]); return ( - {children} - + ); -}; +} type DropdownMenuContextType = { anchorEl: Element | null; triggerRef: RefObject; - size: NonNullable; + size: NonNullable; portal?: PortalProps['portal']; - placement?: DropdownMenuRootProps['placement']; + placement?: DropdownMenuContextProps['placement']; internalOpen: boolean; isControlled?: boolean; setInternalOpen: (open: boolean) => void; - onClose?: DropdownMenuRootProps['onClose']; + onClose?: DropdownMenuContextProps['onClose']; }; -export const DropdownMenuContext = createContext({ +export const DropdownMenuCtx = createContext({ triggerRef: { current: null }, size: 'md', anchorEl: null, internalOpen: false, setInternalOpen: () => {}, }); - -DropdownMenuRoot.displayName = 'DropdownMenuRoot'; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx index e55150845b..c9c475ef22 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx @@ -3,7 +3,7 @@ import type { HTMLAttributes, ReactNode } from 'react'; import { Paragraph } from '../Typography'; -import { DropdownMenuContext } from './DropdownMenuRoot'; +import { DropdownMenuContext } from './DropdownMenuContext'; export type DropdownMenuGroupProps = { /** diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx index 0bb517df54..03e40eba77 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx @@ -3,7 +3,7 @@ import { forwardRef, useContext } from 'react'; import type { ButtonProps } from '../Button'; import { Button } from '../Button'; -import { DropdownMenuContext } from './DropdownMenuRoot'; +import { DropdownMenuContext } from './DropdownMenuContext'; export type DropdownMenuItemProps = Omit< ButtonProps, diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx index 019869c9f0..7d65282abd 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx @@ -5,7 +5,7 @@ import type { ComponentPropsWithRef } from 'react'; import { Button } from '../Button'; -import { DropdownMenuContext } from './DropdownMenuRoot'; +import { DropdownMenuContext } from './DropdownMenuContext'; export type DropdownMenuTriggerProps = ComponentPropsWithRef; diff --git a/packages/react/src/components/DropdownMenu/index.ts b/packages/react/src/components/DropdownMenu/index.ts index 3e4fe4b58c..fff29c52cc 100644 --- a/packages/react/src/components/DropdownMenu/index.ts +++ b/packages/react/src/components/DropdownMenu/index.ts @@ -1,11 +1,11 @@ import { DropdownMenuContent } from './DropdownMenuContent'; +import { DropdownMenuContext } from './DropdownMenuContext'; import { DropdownMenuGroup } from './DropdownMenuGroup'; import { DropdownMenuItem } from './DropdownMenuItem'; -import { DropdownMenuRoot } from './DropdownMenuRoot'; import { DropdownMenuTrigger } from './DropdownMenuTrigger'; type DropdownMenuComponent = { - Root: typeof DropdownMenuRoot; + Context: typeof DropdownMenuContext; Content: typeof DropdownMenuContent; Group: typeof DropdownMenuGroup; Item: typeof DropdownMenuItem; @@ -25,7 +25,7 @@ type DropdownMenuComponent = { */ const DropdownMenu = {} as DropdownMenuComponent; -DropdownMenu.Root = DropdownMenuRoot; +DropdownMenu.Context = DropdownMenuContext; DropdownMenu.Content = DropdownMenuContent; DropdownMenu.Group = DropdownMenuGroup; DropdownMenu.Item = DropdownMenuItem; @@ -37,13 +37,13 @@ DropdownMenu.Group.displayName = 'DropdownMenu.Group'; DropdownMenu.Item.displayName = 'DropdownMenu.Item'; DropdownMenu.Trigger.displayName = 'DropdownMenu.Trigger'; -export type { DropdownMenuRootProps } from './DropdownMenuRoot'; +export type { DropdownMenuContextProps } from './DropdownMenuContext'; export type { DropdownMenuGroupProps } from './DropdownMenuGroup'; export type { DropdownMenuItemProps } from './DropdownMenuItem'; export type { DropdownMenuContentProps } from './DropdownMenuContent'; export { DropdownMenu, - DropdownMenuRoot, + DropdownMenuContext, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, From 034aeed7f630d52c74c55717b180733e33a9dd3d Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 09:21:25 +0200 Subject: [PATCH 02/27] functions and context --- .../DropdownMenu/DropdownMenu.stories.tsx | 6 +++--- .../DropdownMenu/DropdownMenuContent.tsx | 8 +++----- .../DropdownMenu/DropdownMenuContext.tsx | 16 +++++++++------- .../DropdownMenu/DropdownMenuGroup.tsx | 11 ++++++----- .../components/DropdownMenu/DropdownMenuItem.tsx | 8 +++----- .../DropdownMenu/DropdownMenuTrigger.tsx | 8 +++----- .../react/src/components/DropdownMenu/index.ts | 2 +- 7 files changed, 28 insertions(+), 31 deletions(-) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx index d20374d516..fea24f0739 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx @@ -12,10 +12,10 @@ const marginDecorator = (Story: StoryFn) => ( export default { title: 'Komponenter/DropdownMenu', - component: DropdownMenu.Root, + component: DropdownMenu.Context, } as Meta; -export const Preview: StoryFn = (args) => { +export const Preview: StoryFn = (args) => { return ( <> @@ -38,7 +38,7 @@ Preview.args = { Preview.decorators = [marginDecorator]; -export const Icons: StoryFn = () => { +export const Icons: StoryFn = () => { return ( Dropdown diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx index bff80fe1ca..13789f4cbf 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx @@ -18,7 +18,7 @@ import type { HTMLAttributes, ReactNode } from 'react'; import { useIsomorphicLayoutEffect } from '../../utilities'; -import { DropdownMenuContext } from './DropdownMenuContext'; +import { DropdownMenuCtx } from './DropdownMenuContext'; const GAP = 4; @@ -29,7 +29,7 @@ export type DropdownMenuContentProps = { export const DropdownMenuContent = forwardRef< HTMLUListElement, DropdownMenuContentProps ->(({ className, children, ...rest }, ref) => { +>(function DropddownMenuContent({ className, children, ...rest }, ref) { const { size, placement, @@ -39,7 +39,7 @@ export const DropdownMenuContent = forwardRef< internalOpen, setInternalOpen, onClose, - } = useContext(DropdownMenuContext); + } = useContext(DropdownMenuCtx); const Container = portal ? FloatingPortal : Fragment; const floatingEl = useRef(null); @@ -113,5 +113,3 @@ export const DropdownMenuContent = forwardRef< ); }); - -DropdownMenuContent.displayName = 'DropdownMenuContent'; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx index a7971aea3f..104ee12f31 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx @@ -26,23 +26,23 @@ export type DropdownMenuContextProps = { /** * DropdownMenuContext is the root component for the DropdownMenu component. * @example - * + * * Dropdown * * * Button 1 * * - * + * */ -export function DropdownMenuContext({ +export const DropdownMenuContext = ({ open, onClose, placement = 'bottom-end', portal, size = 'md', children, -}: DropdownMenuContextProps) { +}: DropdownMenuContextProps) => { const triggerRef = useRef(null); const [internalOpen, setInternalOpen] = useState(open ?? false); @@ -70,9 +70,11 @@ export function DropdownMenuContext({ {children} ); -} +}; + +DropdownMenuContext.displayName = 'DropdownMenuContext'; -type DropdownMenuContextType = { +type DropdownMenuCtxType = { anchorEl: Element | null; triggerRef: RefObject; size: NonNullable; @@ -84,7 +86,7 @@ type DropdownMenuContextType = { onClose?: DropdownMenuContextProps['onClose']; }; -export const DropdownMenuCtx = createContext({ +export const DropdownMenuCtx = createContext({ triggerRef: { current: null }, size: 'md', anchorEl: null, diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx index c9c475ef22..5b2946ead7 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx @@ -3,7 +3,7 @@ import type { HTMLAttributes, ReactNode } from 'react'; import { Paragraph } from '../Typography'; -import { DropdownMenuContext } from './DropdownMenuContext'; +import { DropdownMenuCtx } from './DropdownMenuContext'; export type DropdownMenuGroupProps = { /** @@ -15,8 +15,11 @@ export type DropdownMenuGroupProps = { export const DropdownMenuGroup = forwardRef< HTMLUListElement, DropdownMenuGroupProps ->(({ children, heading, className, style, ...rest }, ref) => { - const { size } = useContext(DropdownMenuContext); +>(function DropdownMenuGroup( + { children, heading, className, style, ...rest }, + ref, +) { + const { size } = useContext(DropdownMenuCtx); const headingId = useId(); return ( @@ -40,5 +43,3 @@ export const DropdownMenuGroup = forwardRef< ); }); - -DropdownMenuGroup.displayName = 'DropdownMenuGroup'; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx index 03e40eba77..d3e021e1b9 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx @@ -3,7 +3,7 @@ import { forwardRef, useContext } from 'react'; import type { ButtonProps } from '../Button'; import { Button } from '../Button'; -import { DropdownMenuContext } from './DropdownMenuContext'; +import { DropdownMenuCtx } from './DropdownMenuContext'; export type DropdownMenuItemProps = Omit< ButtonProps, @@ -13,8 +13,8 @@ export type DropdownMenuItemProps = Omit< export const DropdownMenuItem = forwardRef< HTMLButtonElement, DropdownMenuItemProps ->(({ children, className, style, ...rest }, ref) => { - const { size } = useContext(DropdownMenuContext); +>(function DropdownMenuItem({ children, className, style, ...rest }, ref) { + const { size } = useContext(DropdownMenuCtx); return (
  • @@ -31,5 +31,3 @@ export const DropdownMenuItem = forwardRef<
  • ); }); - -DropdownMenuItem.displayName = 'DropdownMenuItem'; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx index 7d65282abd..d4b90e1142 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx @@ -5,16 +5,16 @@ import type { ComponentPropsWithRef } from 'react'; import { Button } from '../Button'; -import { DropdownMenuContext } from './DropdownMenuContext'; +import { DropdownMenuCtx } from './DropdownMenuContext'; export type DropdownMenuTriggerProps = ComponentPropsWithRef; export const DropdownMenuTrigger = forwardRef< HTMLButtonElement, DropdownMenuTriggerProps ->(({ asChild, ...rest }, ref) => { +>(function DropdownMenuTrigger({ asChild, ...rest }, ref) { const { triggerRef, internalOpen, setInternalOpen, isControlled } = - useContext(DropdownMenuContext); + useContext(DropdownMenuCtx); const mergedRefs = useMergeRefs([ref, triggerRef]); const Component = asChild ? Slot : Button; @@ -31,5 +31,3 @@ export const DropdownMenuTrigger = forwardRef< /> ); }); - -DropdownMenuTrigger.displayName = 'DropdownMenuTrigger'; diff --git a/packages/react/src/components/DropdownMenu/index.ts b/packages/react/src/components/DropdownMenu/index.ts index fff29c52cc..5081db3430 100644 --- a/packages/react/src/components/DropdownMenu/index.ts +++ b/packages/react/src/components/DropdownMenu/index.ts @@ -31,7 +31,7 @@ DropdownMenu.Group = DropdownMenuGroup; DropdownMenu.Item = DropdownMenuItem; DropdownMenu.Trigger = DropdownMenuTrigger; -DropdownMenu.Root.displayName = 'DropdownMenu.Root'; +DropdownMenu.Context.displayName = 'DropdownMenu.Context'; DropdownMenu.Content.displayName = 'DropdownMenu.Content'; DropdownMenu.Group.displayName = 'DropdownMenu.Group'; DropdownMenu.Item.displayName = 'DropdownMenu.Item'; From 0287cc6fca4011cf0f93fe090349174d61a97a45 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 09:25:55 +0200 Subject: [PATCH 03/27] update where used --- .../components/Tokens/TokenList/TokenList.tsx | 8 ++++---- .../components/Previews/Components/Components.tsx | 4 ++-- .../react/src/components/Avatar/Avatar.stories.tsx | 4 ++-- .../react/src/components/DropdownMenu/DropdownMenu.mdx | 4 ++-- .../src/components/DropdownMenu/DropdownMenu.test.tsx | 10 +++++----- packages/react/src/components/DropdownMenu/index.ts | 4 ++-- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/storefront/components/Tokens/TokenList/TokenList.tsx b/apps/storefront/components/Tokens/TokenList/TokenList.tsx index 4e74647e20..a41b73190b 100644 --- a/apps/storefront/components/Tokens/TokenList/TokenList.tsx +++ b/apps/storefront/components/Tokens/TokenList/TokenList.tsx @@ -220,7 +220,7 @@ const TokenList = ({ {(showThemePicker || showModeSwitcher) && (
    {showThemePicker && ( - + Brand: {capitalizeString(brand)} @@ -238,10 +238,10 @@ const TokenList = ({ Brreg - + )} {showModeSwitcher && ( - + Mode: {capitalizeString(mode)} @@ -253,7 +253,7 @@ const TokenList = ({ Dark - + )}
    )} diff --git a/apps/theme/components/Previews/Components/Components.tsx b/apps/theme/components/Previews/Components/Components.tsx index c9a3585a89..c0ee77a18b 100644 --- a/apps/theme/components/Previews/Components/Components.tsx +++ b/apps/theme/components/Previews/Components/Components.tsx @@ -390,7 +390,7 @@ export const Components = () => {
    - + Velg språk Norsk @@ -401,7 +401,7 @@ export const Components = () => { Velg språk for å endre innholdet på siden - +
    diff --git a/packages/react/src/components/Avatar/Avatar.stories.tsx b/packages/react/src/components/Avatar/Avatar.stories.tsx index a6653f6cdb..8ddad8de4a 100644 --- a/packages/react/src/components/Avatar/Avatar.stories.tsx +++ b/packages/react/src/components/Avatar/Avatar.stories.tsx @@ -94,7 +94,7 @@ export const WithImage: Story = () => ( ); export const InDropdownMenu: Story = () => ( - + ON @@ -119,7 +119,7 @@ export const InDropdownMenu: Story = () => ( - + ); export const AsLink: Story = () => ( diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx index a223cb1572..a1e11b9ef6 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx @@ -16,14 +16,14 @@ import { DropdownMenu } from './'; ```tsx import { DropdownMenu } from '@digdir/designsystemet-react'; - + Trigger Item -; +; ``` ## Eksempler på bruk diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx index 294dbb977a..280d27177d 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx @@ -2,13 +2,13 @@ import { render as renderRtl, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { act } from 'react'; -import type { DropdownMenuRootProps } from './DropdownMenuContext'; +import type { DropdownMenuContextProps } from './DropdownMenuContext'; import { DropdownMenu } from '.'; -const Comp = (args: Partial) => { +const Comp = (args: Partial) => { return ( - + Dropdown @@ -16,11 +16,11 @@ const Comp = (args: Partial) => { {args.children} - + ); }; -const render = async (props: Partial = {}) => { +const render = async (props: Partial = {}) => { /* Flush microtasks */ await act(async () => {}); const user = userEvent.setup(); diff --git a/packages/react/src/components/DropdownMenu/index.ts b/packages/react/src/components/DropdownMenu/index.ts index 5081db3430..26c792267b 100644 --- a/packages/react/src/components/DropdownMenu/index.ts +++ b/packages/react/src/components/DropdownMenu/index.ts @@ -14,14 +14,14 @@ type DropdownMenuComponent = { /** * @example - * + * * Dropdown * * * Button 1 * * - * + * */ const DropdownMenu = {} as DropdownMenuComponent; From f031965a839065a4949abc9546fb4b4e6e10a225 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 10:39:09 +0200 Subject: [PATCH 04/27] use popover --- packages/css/dropdownmenu.css | 4 + .../DropdownMenu/DropdownMenuContent.tsx | 120 +++--------------- .../DropdownMenu/DropdownMenuContext.tsx | 3 +- .../DropdownMenu/DropdownMenuTrigger.tsx | 25 +--- .../react/src/components/Popover/Popover.tsx | 8 +- 5 files changed, 39 insertions(+), 121 deletions(-) diff --git a/packages/css/dropdownmenu.css b/packages/css/dropdownmenu.css index 206d581e38..46b948a881 100644 --- a/packages/css/dropdownmenu.css +++ b/packages/css/dropdownmenu.css @@ -15,6 +15,10 @@ border: 1px solid var(--ds-color-neutral-border-subtle); min-width: var(--dsc-dropdownmenu-min-width); + &::before { + display: none; + } + &[data-size='sm'] { --dsc-dropdownmenu-padding: var(--ds-spacing-2); --dsc-dropdownmenu-min-width: 15rem; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx index 13789f4cbf..58aed39c0b 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx @@ -1,115 +1,35 @@ -import { - FloatingFocusManager, - FloatingPortal, - autoUpdate, - offset, - shift, - useClick, - useDismiss, - useFloating, - useFocus, - useInteractions, - useMergeRefs, - useRole, -} from '@floating-ui/react'; import cl from 'clsx/lite'; -import { Fragment, forwardRef, useContext, useRef } from 'react'; +import { forwardRef, useContext } from 'react'; import type { HTMLAttributes, ReactNode } from 'react'; -import { useIsomorphicLayoutEffect } from '../../utilities'; - +import { Popover } from '../Popover'; import { DropdownMenuCtx } from './DropdownMenuContext'; -const GAP = 4; - export type DropdownMenuContentProps = { children: ReactNode; -} & HTMLAttributes; +} & HTMLAttributes; export const DropdownMenuContent = forwardRef< - HTMLUListElement, + HTMLDivElement, DropdownMenuContentProps >(function DropddownMenuContent({ className, children, ...rest }, ref) { - const { - size, - placement, - portal, - anchorEl, - isControlled, - internalOpen, - setInternalOpen, - onClose, - } = useContext(DropdownMenuCtx); - - const Container = portal ? FloatingPortal : Fragment; - const floatingEl = useRef(null); - - const { - context, - update, - refs, - placement: flPlacement, - floatingStyles, - } = useFloating({ - placement, - open: internalOpen, - onOpenChange: (localOpen) => { - if (!localOpen) onClose?.(); - if (!isControlled) setInternalOpen(localOpen); - }, - elements: { - reference: anchorEl, - floating: floatingEl.current, - }, - whileElementsMounted: autoUpdate, - middleware: [offset(GAP), shift()], - }); - - const { getFloatingProps } = useInteractions([ - useFocus(context), - useClick(context), - useDismiss(context), - useRole(context), - ]); - - useIsomorphicLayoutEffect(() => { - refs.setReference(anchorEl); - if (!refs.reference.current || !refs.floating.current || !internalOpen) - return; - const cleanup = autoUpdate( - refs.reference.current, - refs.floating.current, - update, - ); - return () => cleanup(); - }, [refs.floating, refs.reference, update, anchorEl, refs, internalOpen]); - - const floatingRef = useMergeRefs([refs.setFloating, ref]); + const { size, placement, onClose } = useContext(DropdownMenuCtx); return ( - <> - {internalOpen && ( - - -
      - {children} -
    -
    -
    - )} - + +
      + {children} +
    +
    ); }); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx index 104ee12f31..a979bcedbc 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx @@ -3,6 +3,7 @@ import { createContext, useEffect, useRef, useState } from 'react'; import type { ReactNode, RefObject } from 'react'; import type { PortalProps } from '../../types/Portal'; +import { PopoverContext } from '../Popover'; export type DropdownMenuContextProps = { /** Whether the dropdown is open or not. @@ -67,7 +68,7 @@ export const DropdownMenuContext = ({ setInternalOpen, }} > - {children} + {children} ); }; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx index d4b90e1142..65d4fbe8d7 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx @@ -1,33 +1,20 @@ import { useMergeRefs } from '@floating-ui/react'; -import { Slot } from '@radix-ui/react-slot'; import { forwardRef, useContext } from 'react'; import type { ComponentPropsWithRef } from 'react'; -import { Button } from '../Button'; - +import { PopoverTrigger } from '../Popover'; import { DropdownMenuCtx } from './DropdownMenuContext'; -export type DropdownMenuTriggerProps = ComponentPropsWithRef; +export type DropdownMenuTriggerProps = ComponentPropsWithRef< + typeof PopoverTrigger +>; export const DropdownMenuTrigger = forwardRef< HTMLButtonElement, DropdownMenuTriggerProps >(function DropdownMenuTrigger({ asChild, ...rest }, ref) { - const { triggerRef, internalOpen, setInternalOpen, isControlled } = - useContext(DropdownMenuCtx); + const { triggerRef } = useContext(DropdownMenuCtx); const mergedRefs = useMergeRefs([ref, triggerRef]); - const Component = asChild ? Slot : Button; - - return ( - { - if (!isControlled) setInternalOpen(!internalOpen); - }} - aria-haspopup='menu' - aria-expanded={internalOpen} - {...rest} - /> - ); + return ; }); diff --git a/packages/react/src/components/Popover/Popover.tsx b/packages/react/src/components/Popover/Popover.tsx index 50729e56f8..cd2365724a 100644 --- a/packages/react/src/components/Popover/Popover.tsx +++ b/packages/react/src/components/Popover/Popover.tsx @@ -7,6 +7,7 @@ import { } from '@floating-ui/dom'; import type { MiddlewareState, Placement } from '@floating-ui/dom'; import { useMergeRefs } from '@floating-ui/react'; +import { Slot } from '@radix-ui/react-slot'; import cl from 'clsx/lite'; import { forwardRef, useContext, useRef, useState } from 'react'; import type { HTMLAttributes } from 'react'; @@ -65,6 +66,8 @@ export type PopoverProps = { * Callback when the popover wants to close. */ onClose?: () => void; + + asChild?: boolean; } & HTMLAttributes; export const Popover = forwardRef( @@ -78,10 +81,13 @@ export const Popover = forwardRef( placement = 'top', size = 'md', variant = 'default', + asChild = false, ...rest }, ref, ) { + const Component = asChild ? Slot : 'div'; + const popoverRef = useRef(null); const mergedRefs = useMergeRefs([popoverRef, ref]); const { popoverId, setPopoverId } = useContext(Context); @@ -154,7 +160,7 @@ export const Popover = forwardRef( return ( -
    Date: Tue, 17 Sep 2024 12:00:58 +0200 Subject: [PATCH 05/27] update tests --- .../components/DropdownMenu/DropdownMenu.mdx | 6 -- .../DropdownMenu/DropdownMenu.stories.tsx | 42 ---------- .../DropdownMenu/DropdownMenu.test.tsx | 78 +------------------ .../DropdownMenu/DropdownMenuContent.tsx | 3 +- .../DropdownMenu/DropdownMenuContext.tsx | 29 +------ .../DropdownMenu/DropdownMenuTrigger.tsx | 5 +- 6 files changed, 6 insertions(+), 157 deletions(-) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx index a1e11b9ef6..72a241e90c 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx @@ -40,12 +40,6 @@ Du kan legge ikon rett inn i `DropdownMenu.Item`, dersom det blir mye mellomrom -### I portal - -Legg på `portal` for å rendere `DropdownMenu` i en portal. Dette flytter dropdownen til `body`. - - - ## Tilgjengelighet Det er innebygd tilgjengelighet i `DropdownMenu.Trigger` med `aria-expanded={true/false}` i henhold til åpne/lukket tilstand, og `aria-haspopup='menu'`. diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx index fea24f0739..e256125f20 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx @@ -4,12 +4,6 @@ import { useState } from 'react'; import { DropdownMenu } from '.'; -const marginDecorator = (Story: StoryFn) => ( -
    - -
    -); - export default { title: 'Komponenter/DropdownMenu', component: DropdownMenu.Context, @@ -36,8 +30,6 @@ Preview.args = { size: 'md', }; -Preview.decorators = [marginDecorator]; - export const Icons: StoryFn = () => { return ( @@ -70,40 +62,6 @@ export const Icons: StoryFn = () => { ); }; -Icons.decorators = [marginDecorator]; - -export const InPortal: StoryFn = () => { - return ( - - Dropdown - - - - - - Github - - - - - - Designsystemet.no - - - - - - ); -}; - export const Controlled: StoryFn = () => { const [open, setOpen] = useState(false); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx index 280d27177d..4efb8323a3 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx @@ -32,27 +32,7 @@ const render = async (props: Partial = {}) => { }; describe('Dropdown', () => { - it('should render dropdown on trigger-click when closed', async () => { - const { user } = await render(); - const dropdownTrigger = screen.getByRole('button'); - - expect(screen.queryByText('Item')).not.toBeInTheDocument(); - - await act(async () => await user.click(dropdownTrigger)); - - expect(screen.queryByText('Item')).toBeInTheDocument(); - }); - - it('should close when we click the button twitce', async () => { - const { user } = await render(); - const dropdownTrigger = screen.getByRole('button'); - - await act(async () => await user.click(dropdownTrigger)); - await act(async () => await user.click(dropdownTrigger)); - - expect(screen.queryByText('Item')).not.toBeInTheDocument(); - }); - + /* We are testing closing and opening in Popover.tests.tsx */ it('should render children', async () => { const { user } = await render({ children: Item 2, @@ -64,51 +44,6 @@ describe('Dropdown', () => { expect(screen.queryByText('Item 2')).toBeInTheDocument(); }); - it('should close when we click outside', async () => { - const { user } = await render(); - const dropdownTrigger = screen.getByRole('button'); - - await act(async () => { - await user.click(dropdownTrigger); - }); - - expect(screen.queryByText('Item')).toBeInTheDocument(); - - await act(async () => { - await user.click(document.body); - }); - - expect(screen.queryByText('Item')).not.toBeInTheDocument(); - }); - - it('should close when we press ESC', async () => { - const { user } = await render(); - const dropdownTrigger = screen.getByRole('button'); - - await act(async () => await user.click(dropdownTrigger)); - - expect(screen.queryByText('Item')).toBeInTheDocument(); - - await act(async () => await user.keyboard('[Escape]')); - - expect(screen.queryByText('Item')).not.toBeInTheDocument(); - }); - - it('should not close if we click inisde the dropdown', async () => { - const { user } = await render({ - children: Item 2, - }); - const dropdownTrigger = screen.getByRole('button'); - - await act(async () => await user.click(dropdownTrigger)); - - expect(screen.queryByText('Item')).toBeInTheDocument(); - - await act(async () => await user.click(screen.getByText('Item 2'))); - - expect(screen.queryByText('Item')).toBeInTheDocument(); - }); - it('should be able to render `Dropdown.Item` as a anchor element using asChild', async () => { const { user } = await render({ children: ( @@ -151,15 +86,4 @@ describe('Dropdown', () => { expect(screen.getByRole('group')).toHaveAttribute('aria-labelledby'); }); - - it('should focus the first item when we open the dropdown', async () => { - const { user } = await render(); - const dropdownTrigger = screen.getByRole('button'); - - await act(async () => await user.click(dropdownTrigger)); - - await vi.waitFor(() => { - expect(document.activeElement).toBe(screen.getByText('Item')); - }); - }); }); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx index 58aed39c0b..5cccefa1ec 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx @@ -13,7 +13,7 @@ export const DropdownMenuContent = forwardRef< HTMLDivElement, DropdownMenuContentProps >(function DropddownMenuContent({ className, children, ...rest }, ref) { - const { size, placement, onClose } = useContext(DropdownMenuCtx); + const { size, placement, onClose, open } = useContext(DropdownMenuCtx); return (
      { - const triggerRef = useRef(null); - const [internalOpen, setInternalOpen] = useState(open ?? false); - - const anchorEl = triggerRef.current; - const isControlled = typeof open === 'boolean'; - - useEffect(() => { - setInternalOpen(open ?? false); - }, [open]); - return ( {children} @@ -76,21 +60,12 @@ export const DropdownMenuContext = ({ DropdownMenuContext.displayName = 'DropdownMenuContext'; type DropdownMenuCtxType = { - anchorEl: Element | null; - triggerRef: RefObject; size: NonNullable; - portal?: PortalProps['portal']; placement?: DropdownMenuContextProps['placement']; - internalOpen: boolean; - isControlled?: boolean; - setInternalOpen: (open: boolean) => void; + open?: boolean; onClose?: DropdownMenuContextProps['onClose']; }; export const DropdownMenuCtx = createContext({ - triggerRef: { current: null }, size: 'md', - anchorEl: null, - internalOpen: false, - setInternalOpen: () => {}, }); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx index 65d4fbe8d7..140daeeea4 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx @@ -13,8 +13,5 @@ export const DropdownMenuTrigger = forwardRef< HTMLButtonElement, DropdownMenuTriggerProps >(function DropdownMenuTrigger({ asChild, ...rest }, ref) { - const { triggerRef } = useContext(DropdownMenuCtx); - const mergedRefs = useMergeRefs([ref, triggerRef]); - - return ; + return ; }); From 3d9a8e2e29d06ed433f024d9416759ee48ce4071 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 12:14:07 +0200 Subject: [PATCH 06/27] add proper focus --- .../DropdownMenu/DropdownMenu.stories.tsx | 10 ++++--- .../DropdownMenu/DropdownMenuContent.tsx | 3 ++- .../DropdownMenu/DropdownMenuContext.tsx | 4 +-- .../DropdownMenu/DropdownMenuItem.tsx | 26 +++++++++++-------- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx index e256125f20..7e9e22f949 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx @@ -15,9 +15,13 @@ export const Preview: StoryFn = (args) => { Dropdown - - Button 1 - Button 2 + + Button 1.1 + Button 1.2 + + + Button 2.1 + Button 2.2 diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx index 5cccefa1ec..fa154b22d0 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx @@ -2,6 +2,7 @@ import cl from 'clsx/lite'; import { forwardRef, useContext } from 'react'; import type { HTMLAttributes, ReactNode } from 'react'; +import { RovingFocusRoot } from '../../utilities'; import { Popover } from '../Popover'; import { DropdownMenuCtx } from './DropdownMenuContext'; @@ -29,7 +30,7 @@ export const DropdownMenuContent = forwardRef< className={cl('ds-dropdownmenu', className)} data-size={size} > - {children} + {children}
    ); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx index 523c0ad329..43daeb1533 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx @@ -1,6 +1,6 @@ import type { Placement } from '@floating-ui/react'; -import { createContext, useEffect, useRef, useState } from 'react'; -import type { ReactNode, RefObject } from 'react'; +import { createContext } from 'react'; +import type { ReactNode } from 'react'; import type { PortalProps } from '../../types/Portal'; import { PopoverContext } from '../Popover'; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx index d3e021e1b9..472756d8bc 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx @@ -1,8 +1,9 @@ -import { forwardRef, useContext } from 'react'; +import { forwardRef, useContext, useId } from 'react'; import type { ButtonProps } from '../Button'; import { Button } from '../Button'; +import { RovingFocusItem } from '../../utilities'; import { DropdownMenuCtx } from './DropdownMenuContext'; export type DropdownMenuItemProps = Omit< @@ -15,19 +16,22 @@ export const DropdownMenuItem = forwardRef< DropdownMenuItemProps >(function DropdownMenuItem({ children, className, style, ...rest }, ref) { const { size } = useContext(DropdownMenuCtx); + const value = useId(); return (
  • - + + +
  • ); }); From 0333325c4d473a7eca8339b748f92fc192b882fb Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 12:32:52 +0200 Subject: [PATCH 07/27] rename without .Content --- .../components/Tokens/TokenList/TokenList.tsx | 8 +-- .../Previews/Components/Components.tsx | 4 +- .../src/components/Avatar/Avatar.stories.tsx | 6 +- .../components/DropdownMenu/DropdownMenu.mdx | 4 +- .../DropdownMenu/DropdownMenu.stories.tsx | 22 +++--- .../DropdownMenu/DropdownMenu.test.tsx | 4 +- .../components/DropdownMenu/DropdownMenu.tsx | 69 +++++++++++++++++++ .../DropdownMenu/DropdownMenuContent.tsx | 37 ---------- .../DropdownMenu/DropdownMenuContext.tsx | 47 ++++--------- .../DropdownMenu/DropdownMenuTrigger.tsx | 4 +- .../src/components/DropdownMenu/index.ts | 31 +++------ 11 files changed, 116 insertions(+), 120 deletions(-) create mode 100644 packages/react/src/components/DropdownMenu/DropdownMenu.tsx delete mode 100644 packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx diff --git a/apps/storefront/components/Tokens/TokenList/TokenList.tsx b/apps/storefront/components/Tokens/TokenList/TokenList.tsx index a41b73190b..e1eeded7b0 100644 --- a/apps/storefront/components/Tokens/TokenList/TokenList.tsx +++ b/apps/storefront/components/Tokens/TokenList/TokenList.tsx @@ -224,7 +224,7 @@ const TokenList = ({ Brand: {capitalizeString(brand)} - + setBrand('digdir')}> Digdir @@ -237,7 +237,7 @@ const TokenList = ({ setBrand('portal')}> Brreg - +
    )} {showModeSwitcher && ( @@ -245,14 +245,14 @@ const TokenList = ({ Mode: {capitalizeString(mode)} - + setMode('light')}> Light setMode('dark')}> Dark - + )}
    diff --git a/apps/theme/components/Previews/Components/Components.tsx b/apps/theme/components/Previews/Components/Components.tsx index c0ee77a18b..8fed2dcfad 100644 --- a/apps/theme/components/Previews/Components/Components.tsx +++ b/apps/theme/components/Previews/Components/Components.tsx @@ -392,12 +392,12 @@ export const Components = () => {
    Velg språk - + Norsk Engelsk Spansk Fransk - + Velg språk for å endre innholdet på siden diff --git a/packages/react/src/components/Avatar/Avatar.stories.tsx b/packages/react/src/components/Avatar/Avatar.stories.tsx index 8ddad8de4a..4aa48907ca 100644 --- a/packages/react/src/components/Avatar/Avatar.stories.tsx +++ b/packages/react/src/components/Avatar/Avatar.stories.tsx @@ -94,14 +94,14 @@ export const WithImage: Story = () => ( ); export const InDropdownMenu: Story = () => ( - + ON Velg Profil - + @@ -118,7 +118,7 @@ export const InDropdownMenu: Story = () => ( Sogndal kommune - + ); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx index 72a241e90c..c97e12e4b4 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx @@ -18,11 +18,11 @@ import { DropdownMenu } from '@digdir/designsystemet-react'; Trigger - + Item - + ; ``` diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx index 7e9e22f949..97e8024b3b 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx @@ -6,15 +6,15 @@ import { DropdownMenu } from '.'; export default { title: 'Komponenter/DropdownMenu', - component: DropdownMenu.Context, + component: DropdownMenu, } as Meta; -export const Preview: StoryFn = (args) => { +export const Preview: StoryFn = (args) => { return ( <> - + Dropdown - + Button 1.1 Button 1.2 @@ -23,7 +23,7 @@ export const Preview: StoryFn = (args) => { Button 2.1 Button 2.2 - + ); @@ -34,11 +34,11 @@ Preview.args = { size: 'md', }; -export const Icons: StoryFn = () => { +export const Icons: StoryFn = (args) => { return ( Dropdown - + = () => { - + ); }; @@ -71,11 +71,11 @@ export const Controlled: StoryFn = () => { return ( <> - setOpen(false)} portal> + setOpen(!open)}> Dropdown - + setOpen(false)}> = () => { - + ); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx index 4efb8323a3..1bbae68e87 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx @@ -10,12 +10,12 @@ const Comp = (args: Partial) => { return ( Dropdown - + Item {args.children} - + ); }; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.tsx new file mode 100644 index 0000000000..bd1af6bee4 --- /dev/null +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.tsx @@ -0,0 +1,69 @@ +import cl from 'clsx/lite'; +import { forwardRef, useContext, useEffect } from 'react'; +import type { HTMLAttributes, ReactNode } from 'react'; + +import type { Placement } from '@floating-ui/react'; +import { RovingFocusRoot } from '../../utilities'; +import { Popover } from '../Popover'; +import { DropdownMenuCtx } from './DropdownMenuContext'; + +export type DropdownMenuProps = { + /** Whether the dropdown is open or not. + * @default undefined + */ + open?: boolean; + /** Callback function when dropdown closes */ + onClose?: () => void; + /** The placement of the dropdown + * @default bottom-end + */ + placement?: Placement; + /** + * The size of the dropdown + * @default md + **/ + size?: 'sm' | 'md' | 'lg'; + children: ReactNode; +} & HTMLAttributes; + +export const DropdownMenu = forwardRef( + function DropddownMenuContent( + { + open, + onClose, + placement = 'bottom-end', + size = 'md', + className, + children, + ...rest + }, + ref, + ) { + const { setSize } = useContext(DropdownMenuCtx); + + useEffect(() => { + if (setSize) { + setSize(size); + } + }, [size, setSize]); + + return ( + +
      + {children} +
    +
    + ); + }, +); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx deleted file mode 100644 index fa154b22d0..0000000000 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContent.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import cl from 'clsx/lite'; -import { forwardRef, useContext } from 'react'; -import type { HTMLAttributes, ReactNode } from 'react'; - -import { RovingFocusRoot } from '../../utilities'; -import { Popover } from '../Popover'; -import { DropdownMenuCtx } from './DropdownMenuContext'; - -export type DropdownMenuContentProps = { - children: ReactNode; -} & HTMLAttributes; - -export const DropdownMenuContent = forwardRef< - HTMLDivElement, - DropdownMenuContentProps ->(function DropddownMenuContent({ className, children, ...rest }, ref) { - const { size, placement, onClose, open } = useContext(DropdownMenuCtx); - - return ( - -
      - {children} -
    -
    - ); -}); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx index 43daeb1533..58fc08c799 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx @@ -1,55 +1,34 @@ -import type { Placement } from '@floating-ui/react'; -import { createContext } from 'react'; +import { createContext, useState } from 'react'; import type { ReactNode } from 'react'; -import type { PortalProps } from '../../types/Portal'; import { PopoverContext } from '../Popover'; +import type { DropdownMenuProps } from './DropdownMenu'; export type DropdownMenuContextProps = { - /** Whether the dropdown is open or not. - * @default false - */ - open?: boolean; - /** Callback function when dropdown closes */ - onClose?: () => void; - /** The placement of the dropdown - * @default bottom-end - */ - placement?: Placement; - /** - * The size of the dropdown - * @default md - **/ - size?: 'sm' | 'md' | 'lg'; children: ReactNode; -} & PortalProps; +}; /** * DropdownMenuContext is the root component for the DropdownMenu component. * @example * * Dropdown - * + * * * Button 1 * - * + * * */ -export const DropdownMenuContext = ({ - open, - onClose, - placement = 'bottom-end', - size = 'md', - children, -}: DropdownMenuContextProps) => { +export const DropdownMenuContext = ({ children }: DropdownMenuContextProps) => { + const [size, setSize] = + useState>('md'); + return ( {children} @@ -60,10 +39,8 @@ export const DropdownMenuContext = ({ DropdownMenuContext.displayName = 'DropdownMenuContext'; type DropdownMenuCtxType = { - size: NonNullable; - placement?: DropdownMenuContextProps['placement']; - open?: boolean; - onClose?: DropdownMenuContextProps['onClose']; + size: NonNullable; + setSize?: (size: NonNullable) => void; }; export const DropdownMenuCtx = createContext({ diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx index 140daeeea4..3ef5a762e5 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx @@ -1,9 +1,7 @@ -import { useMergeRefs } from '@floating-ui/react'; -import { forwardRef, useContext } from 'react'; +import { forwardRef } from 'react'; import type { ComponentPropsWithRef } from 'react'; import { PopoverTrigger } from '../Popover'; -import { DropdownMenuCtx } from './DropdownMenuContext'; export type DropdownMenuTriggerProps = ComponentPropsWithRef< typeof PopoverTrigger diff --git a/packages/react/src/components/DropdownMenu/index.ts b/packages/react/src/components/DropdownMenu/index.ts index 26c792267b..45a5d7b76d 100644 --- a/packages/react/src/components/DropdownMenu/index.ts +++ b/packages/react/src/components/DropdownMenu/index.ts @@ -1,38 +1,28 @@ -import { DropdownMenuContent } from './DropdownMenuContent'; +import { DropdownMenu as DropdownMenuRoot } from './DropdownMenu'; import { DropdownMenuContext } from './DropdownMenuContext'; import { DropdownMenuGroup } from './DropdownMenuGroup'; import { DropdownMenuItem } from './DropdownMenuItem'; import { DropdownMenuTrigger } from './DropdownMenuTrigger'; -type DropdownMenuComponent = { - Context: typeof DropdownMenuContext; - Content: typeof DropdownMenuContent; - Group: typeof DropdownMenuGroup; - Item: typeof DropdownMenuItem; - Trigger: typeof DropdownMenuTrigger; -}; - /** * @example * * Dropdown - * + * * * Button 1 * - * + * * */ -const DropdownMenu = {} as DropdownMenuComponent; - -DropdownMenu.Context = DropdownMenuContext; -DropdownMenu.Content = DropdownMenuContent; -DropdownMenu.Group = DropdownMenuGroup; -DropdownMenu.Item = DropdownMenuItem; -DropdownMenu.Trigger = DropdownMenuTrigger; +const DropdownMenu = Object.assign(DropdownMenuRoot, { + Context: DropdownMenuContext, + Group: DropdownMenuGroup, + Item: DropdownMenuItem, + Trigger: DropdownMenuTrigger, +}); DropdownMenu.Context.displayName = 'DropdownMenu.Context'; -DropdownMenu.Content.displayName = 'DropdownMenu.Content'; DropdownMenu.Group.displayName = 'DropdownMenu.Group'; DropdownMenu.Item.displayName = 'DropdownMenu.Item'; DropdownMenu.Trigger.displayName = 'DropdownMenu.Trigger'; @@ -40,11 +30,10 @@ DropdownMenu.Trigger.displayName = 'DropdownMenu.Trigger'; export type { DropdownMenuContextProps } from './DropdownMenuContext'; export type { DropdownMenuGroupProps } from './DropdownMenuGroup'; export type { DropdownMenuItemProps } from './DropdownMenuItem'; -export type { DropdownMenuContentProps } from './DropdownMenuContent'; +export type { DropdownMenuProps } from './DropdownMenu'; export { DropdownMenu, DropdownMenuContext, - DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuTrigger, From fc6656f8901f9369d17037075eb4df4ee2a262ee Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 12:33:08 +0200 Subject: [PATCH 08/27] fix build error --- apps/theme/components/Previews/Components/Components.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/theme/components/Previews/Components/Components.tsx b/apps/theme/components/Previews/Components/Components.tsx index 8fed2dcfad..63826ac3ba 100644 --- a/apps/theme/components/Previews/Components/Components.tsx +++ b/apps/theme/components/Previews/Components/Components.tsx @@ -390,9 +390,9 @@ export const Components = () => {
    - + Velg språk - + Norsk Engelsk Spansk From dca836107c909c3dbb7ed920ae900f126d0a6645 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 12:40:47 +0200 Subject: [PATCH 09/27] story without trigger --- .../src/components/DropdownMenu/DropdownMenu.mdx | 7 +++++++ .../DropdownMenu/DropdownMenu.stories.tsx | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx index c97e12e4b4..7e1ffe6266 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx @@ -40,9 +40,16 @@ Du kan legge ikon rett inn i `DropdownMenu.Item`, dersom det blir mye mellomrom +### Uten Trigger + +`DropdownMenu` bruker popover APIet, så du kan bruke `DropdownMenu` uten `DropdownMenu.Trigger`. + + + ## Tilgjengelighet Det er innebygd tilgjengelighet i `DropdownMenu.Trigger` med `aria-expanded={true/false}` i henhold til åpne/lukket tilstand, og `aria-haspopup='menu'`. +Du må da legge til `popovertarget={id}` på `DropdownMenu`, og `id` på `DropdownMenu`. ### `DropdownMenu.Group` diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx index 97e8024b3b..d8f8af74e3 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx @@ -3,6 +3,7 @@ import type { Meta, StoryFn } from '@storybook/react'; import { useState } from 'react'; import { DropdownMenu } from '.'; +import { Button } from '../Button'; export default { title: 'Komponenter/DropdownMenu', @@ -103,3 +104,16 @@ export const Controlled: StoryFn = () => { ); }; + +export const WithoutTrigger: StoryFn = () => { + return ( + <> + + + + Item + + + + ); +}; From a343fd28fc9c8b2a287b0042ec524c03fa498ed9 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 12:53:40 +0200 Subject: [PATCH 10/27] drop role menu --- .../components/DropdownMenu/DropdownMenu.mdx | 2 +- .../components/DropdownMenu/DropdownMenu.tsx | 31 ++++--------------- .../DropdownMenu/DropdownMenuGroup.tsx | 26 +++++++--------- .../DropdownMenu/DropdownMenuItem.tsx | 21 ++++++------- 4 files changed, 28 insertions(+), 52 deletions(-) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx index 7e1ffe6266..1ea698e797 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx @@ -43,13 +43,13 @@ Du kan legge ikon rett inn i `DropdownMenu.Item`, dersom det blir mye mellomrom ### Uten Trigger `DropdownMenu` bruker popover APIet, så du kan bruke `DropdownMenu` uten `DropdownMenu.Trigger`. +Du må da legge til `popovertarget={id}` på `DropdownMenu`, og `id` på `DropdownMenu`. ## Tilgjengelighet Det er innebygd tilgjengelighet i `DropdownMenu.Trigger` med `aria-expanded={true/false}` i henhold til åpne/lukket tilstand, og `aria-haspopup='menu'`. -Du må da legge til `popovertarget={id}` på `DropdownMenu`, og `id` på `DropdownMenu`. ### `DropdownMenu.Group` diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.tsx index bd1af6bee4..97fc5aac66 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.tsx @@ -1,30 +1,19 @@ import cl from 'clsx/lite'; import { forwardRef, useContext, useEffect } from 'react'; -import type { HTMLAttributes, ReactNode } from 'react'; +import type { ReactNode } from 'react'; import type { Placement } from '@floating-ui/react'; -import { RovingFocusRoot } from '../../utilities'; import { Popover } from '../Popover'; +import type { PopoverProps } from '../Popover'; import { DropdownMenuCtx } from './DropdownMenuContext'; export type DropdownMenuProps = { - /** Whether the dropdown is open or not. - * @default undefined - */ - open?: boolean; - /** Callback function when dropdown closes */ - onClose?: () => void; /** The placement of the dropdown * @default bottom-end */ placement?: Placement; - /** - * The size of the dropdown - * @default md - **/ - size?: 'sm' | 'md' | 'lg'; children: ReactNode; -} & HTMLAttributes; +} & Omit; export const DropdownMenu = forwardRef( function DropddownMenuContent( @@ -34,7 +23,6 @@ export const DropdownMenu = forwardRef( placement = 'bottom-end', size = 'md', className, - children, ...rest }, ref, @@ -49,21 +37,14 @@ export const DropdownMenu = forwardRef( return ( -
      - {children} -
    -
    + /> ); }, ); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx index 5b2946ead7..adbea63372 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx @@ -1,3 +1,4 @@ +import cl from 'clsx/lite'; import { forwardRef, useContext, useId } from 'react'; import type { HTMLAttributes, ReactNode } from 'react'; @@ -15,31 +16,28 @@ export type DropdownMenuGroupProps = { export const DropdownMenuGroup = forwardRef< HTMLUListElement, DropdownMenuGroupProps ->(function DropdownMenuGroup( - { children, heading, className, style, ...rest }, - ref, -) { +>(function DropdownMenuGroup({ children, heading, className, ...rest }, ref) { const { size } = useContext(DropdownMenuCtx); const headingId = useId(); return ( -
  • + <> + {heading && ( + +

    + {heading} +

    +
    + )}
      - {heading && ( - -

      - {heading} -

      -
      - )} {children}
    -
  • + ); }); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx index 472756d8bc..ef020e7871 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx @@ -20,18 +20,15 @@ export const DropdownMenuItem = forwardRef< return (
  • - - - +
  • ); }); From de99916e26476eada85f6ff7569e612ab5a4036f Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 12:54:09 +0200 Subject: [PATCH 11/27] update tests --- .../src/components/DropdownMenu/DropdownMenu.test.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx index 1bbae68e87..29c1261f69 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx @@ -60,15 +60,6 @@ describe('Dropdown', () => { expect(screen.getByText('Anchor').tagName).toBe('A'); }); - it('Item should have role="menuitem"', async () => { - const { user } = await render(); - const dropdownTrigger = screen.getByRole('button'); - - await act(async () => await user.click(dropdownTrigger)); - - expect(screen.getByText('Item')).toHaveAttribute('role', 'menuitem'); - }); - it('Group should have role="group"', async () => { const { user } = await render(); const dropdownTrigger = screen.getByRole('button'); From d204ba92bc13a029fd5286c4f775737ac4aa8559 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 13:02:18 +0200 Subject: [PATCH 12/27] move popover above dropdownmenu --- packages/css/index.css | 2 +- packages/react/src/components/DropdownMenu/DropdownMenu.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/css/index.css b/packages/css/index.css index f60c02ac0f..0406b3fe75 100644 --- a/packages/css/index.css +++ b/packages/css/index.css @@ -12,6 +12,7 @@ @import url('./utilities.css') layer(ds.utilities); @import url('./button.css') layer(ds.components); @import url('./alert.css') layer(ds.components); +@import url('./popover.css') layer(ds.components); @import url('./skiplink.css') layer(ds.components); @import url('./accordion.css') layer(ds.components); @import url('./switch.css') layer(ds.components); @@ -32,7 +33,6 @@ @import url('./divider.css') layer(ds.components); @import url('./tabs.css') layer(ds.components); @import url('./pagination.css') layer(ds.components); -@import url('./popover.css') layer(ds.components); @import url('./skeleton.css') layer(ds.components); @import url('./tag.css') layer(ds.components); @import url('./error-summary.css') layer(ds.components); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx index 1ea698e797..e44ec0e4b4 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx @@ -6,7 +6,7 @@ import { DropdownMenu } from './'; -# Dropdown +# DropdownMenu From 2bcdd20a0669c01100f63d7bdbdc61148bb48081 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 13:13:18 +0200 Subject: [PATCH 13/27] docs --- .../src/components/DropdownMenu/DropdownMenu.mdx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx index e44ec0e4b4..f726c5893a 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx @@ -16,6 +16,7 @@ import { DropdownMenu } from './'; ```tsx import { DropdownMenu } from '@digdir/designsystemet-react'; +// med context Trigger @@ -23,7 +24,15 @@ import { DropdownMenu } from '@digdir/designsystemet-react'; Item -; +
    + +// uten context + + + + Item + + ``` ## Eksempler på bruk From 3260384764153d4e62c8a8b64c593b3f521573f9 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 13:51:54 +0200 Subject: [PATCH 14/27] add dropdownmenuheading --- .../src/components/Avatar/Avatar.stories.tsx | 5 ++- .../components/DropdownMenu/DropdownMenu.mdx | 12 +++--- .../DropdownMenu/DropdownMenu.stories.tsx | 22 +++++----- .../DropdownMenu/DropdownMenu.test.tsx | 14 ++---- .../DropdownMenu/DropdownMenuContext.tsx | 5 ++- .../DropdownMenu/DropdownMenuGroup.tsx | 43 ------------------- .../DropdownMenu/DropdownMenuHeading.tsx | 25 +++++++++++ .../DropdownMenu/DropdownMenuList.tsx | 19 ++++++++ .../src/components/DropdownMenu/index.ts | 20 ++++++--- 9 files changed, 84 insertions(+), 81 deletions(-) delete mode 100644 packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx create mode 100644 packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx create mode 100644 packages/react/src/components/DropdownMenu/DropdownMenuList.tsx diff --git a/packages/react/src/components/Avatar/Avatar.stories.tsx b/packages/react/src/components/Avatar/Avatar.stories.tsx index 4aa48907ca..ab66d11ea2 100644 --- a/packages/react/src/components/Avatar/Avatar.stories.tsx +++ b/packages/react/src/components/Avatar/Avatar.stories.tsx @@ -102,7 +102,8 @@ export const InDropdownMenu: Story = () => ( Velg Profil - + Alle kontoer + @@ -117,7 +118,7 @@ export const InDropdownMenu: Story = () => ( Sogndal kommune - +
    ); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx index f726c5893a..01e04d9e43 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx @@ -20,18 +20,18 @@ import { DropdownMenu } from '@digdir/designsystemet-react'; Trigger - + Item - + // uten context - + Item - + ``` @@ -60,9 +60,9 @@ Du må da legge til `popovertarget={id}` på `DropdownMenu`, og `id` på `Dropdo Det er innebygd tilgjengelighet i `DropdownMenu.Trigger` med `aria-expanded={true/false}` i henhold til åpne/lukket tilstand, og `aria-haspopup='menu'`. -### `DropdownMenu.Group` +### `DropdownMenu.List` - + ### `DropdownMenu.Trigger` diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx index d8f8af74e3..b5ed8daea2 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx @@ -16,14 +16,16 @@ export const Preview: StoryFn = (args) => { Dropdown - + Heading 1 + Button 1.1 Button 1.2 - - + + Heading 2 + Button 2.1 Button 2.2 - + @@ -40,7 +42,7 @@ export const Icons: StoryFn = (args) => { Dropdown - + = (args) => { Designsystemet.no - + ); @@ -77,7 +79,7 @@ export const Controlled: StoryFn = () => { Dropdown setOpen(false)}> - + = () => { Designsystemet.no - + @@ -110,9 +112,9 @@ export const WithoutTrigger: StoryFn = () => { <> - + Item - + ); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx index 29c1261f69..31cbccf208 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx @@ -11,10 +11,11 @@ const Comp = (args: Partial) => { Dropdown - + Links + Item {args.children} - + ); @@ -68,13 +69,4 @@ describe('Dropdown', () => { expect(screen.getByRole('group')).toBeInTheDocument(); }); - - it('Group should be described by heading', async () => { - const { user } = await render(); - const dropdownTrigger = screen.getByRole('button'); - - await act(async () => user.click(dropdownTrigger)); - - expect(screen.getByRole('group')).toHaveAttribute('aria-labelledby'); - }); }); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx index 58fc08c799..cce4217335 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx @@ -14,9 +14,10 @@ export type DropdownMenuContextProps = { * * Dropdown * - * + * Heading + * * Button 1 - * + * * * */ diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx deleted file mode 100644 index adbea63372..0000000000 --- a/packages/react/src/components/DropdownMenu/DropdownMenuGroup.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import cl from 'clsx/lite'; -import { forwardRef, useContext, useId } from 'react'; -import type { HTMLAttributes, ReactNode } from 'react'; - -import { Paragraph } from '../Typography'; - -import { DropdownMenuCtx } from './DropdownMenuContext'; - -export type DropdownMenuGroupProps = { - /** - * Heading of the group - */ - heading?: ReactNode; -} & HTMLAttributes; - -export const DropdownMenuGroup = forwardRef< - HTMLUListElement, - DropdownMenuGroupProps ->(function DropdownMenuGroup({ children, heading, className, ...rest }, ref) { - const { size } = useContext(DropdownMenuCtx); - const headingId = useId(); - - return ( - <> - {heading && ( - -

    - {heading} -

    -
    - )} -
      - {children} -
    - - ); -}); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx new file mode 100644 index 0000000000..e85da116d1 --- /dev/null +++ b/packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx @@ -0,0 +1,25 @@ +import cl from 'clsx/lite'; +import { type HTMLAttributes, forwardRef, useContext } from 'react'; +import { Paragraph } from '../Typography'; +import { DropdownMenuCtx } from './DropdownMenuContext'; + +export type DropdownMenuHeadingProps = HTMLAttributes; + +export const DropdownMenuHeading = forwardRef< + HTMLHeadingElement, + DropdownMenuHeadingProps +>(function DropdownMenuHeading({ children, className, ...rest }, ref) { + const { size } = useContext(DropdownMenuCtx); + + return ( + +

    + {children} +

    +
    + ); +}); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx new file mode 100644 index 0000000000..2330747dd0 --- /dev/null +++ b/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx @@ -0,0 +1,19 @@ +import cl from 'clsx/lite'; +import { forwardRef } from 'react'; +import type { HTMLAttributes } from 'react'; + +export type DropdownMenuListProps = HTMLAttributes; + +export const DropdownMenuList = forwardRef< + HTMLUListElement, + DropdownMenuListProps +>(function DropdownMenuGroup({ className, ...rest }, ref) { + return ( +
      + ); +}); diff --git a/packages/react/src/components/DropdownMenu/index.ts b/packages/react/src/components/DropdownMenu/index.ts index 45a5d7b76d..93f2820f75 100644 --- a/packages/react/src/components/DropdownMenu/index.ts +++ b/packages/react/src/components/DropdownMenu/index.ts @@ -1,7 +1,8 @@ import { DropdownMenu as DropdownMenuRoot } from './DropdownMenu'; import { DropdownMenuContext } from './DropdownMenuContext'; -import { DropdownMenuGroup } from './DropdownMenuGroup'; +import { DropdownMenuHeading } from './DropdownMenuHeading'; import { DropdownMenuItem } from './DropdownMenuItem'; +import { DropdownMenuList } from './DropdownMenuList'; import { DropdownMenuTrigger } from './DropdownMenuTrigger'; /** @@ -9,32 +10,37 @@ import { DropdownMenuTrigger } from './DropdownMenuTrigger'; * * Dropdown * - * + * Heading + * * Button 1 - * + * * * */ const DropdownMenu = Object.assign(DropdownMenuRoot, { Context: DropdownMenuContext, - Group: DropdownMenuGroup, + Heading: DropdownMenuHeading, + List: DropdownMenuList, Item: DropdownMenuItem, Trigger: DropdownMenuTrigger, }); DropdownMenu.Context.displayName = 'DropdownMenu.Context'; -DropdownMenu.Group.displayName = 'DropdownMenu.Group'; +DropdownMenu.List.displayName = 'DropdownMenu.List'; +DropdownMenu.Heading.displayName = 'DropdownMenu.Heading'; DropdownMenu.Item.displayName = 'DropdownMenu.Item'; DropdownMenu.Trigger.displayName = 'DropdownMenu.Trigger'; export type { DropdownMenuContextProps } from './DropdownMenuContext'; -export type { DropdownMenuGroupProps } from './DropdownMenuGroup'; +export type { DropdownMenuListProps } from './DropdownMenuList'; +export type { DropdownMenuHeadingProps } from './DropdownMenuHeading'; export type { DropdownMenuItemProps } from './DropdownMenuItem'; export type { DropdownMenuProps } from './DropdownMenu'; export { DropdownMenu, DropdownMenuContext, - DropdownMenuGroup, + DropdownMenuList, + DropdownMenuHeading, DropdownMenuItem, DropdownMenuTrigger, }; From cc3aa650e23a91726ec51d6631ceaf3b804425c3 Mon Sep 17 00:00:00 2001 From: Tobias Barsnes Date: Tue, 17 Sep 2024 13:53:08 +0200 Subject: [PATCH 15/27] Create proud-walls-flash.md --- .changeset/proud-walls-flash.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/proud-walls-flash.md diff --git a/.changeset/proud-walls-flash.md b/.changeset/proud-walls-flash.md new file mode 100644 index 0000000000..1e70de27f5 --- /dev/null +++ b/.changeset/proud-walls-flash.md @@ -0,0 +1,9 @@ +--- +"@digdir/designsystemet-css": patch +"@digdir/designsystemet-react": patch +--- + +DropdownMenu: +- Change API and structure +- Rename `.Root` to `.Context` +- Rename `.Content` to `DropdownMenu` From f94486e48119188fc1d15350bd8da00322f442ee Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 13:55:07 +0200 Subject: [PATCH 16/27] update docs --- packages/react/src/components/DropdownMenu/DropdownMenu.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx index 01e04d9e43..669738d3d9 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx @@ -20,6 +20,7 @@ import { DropdownMenu } from '@digdir/designsystemet-react'; Trigger + Heading Item @@ -29,6 +30,7 @@ import { DropdownMenu } from '@digdir/designsystemet-react'; // uten context + Heading Item From 8d77cf45422a3b5a65d63e91537cc3e327134f8d Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 14:24:28 +0200 Subject: [PATCH 17/27] remove fragments --- .../DropdownMenu/DropdownMenu.stories.tsx | 92 +++++++++---------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx index b5ed8daea2..012c464bc2 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx @@ -12,23 +12,21 @@ export default { export const Preview: StoryFn = (args) => { return ( - <> - - Dropdown - - Heading 1 - - Button 1.1 - Button 1.2 - - Heading 2 - - Button 2.1 - Button 2.2 - - - - + + Dropdown + + Heading 1 + + Button 1.1 + Button 1.2 + + Heading 2 + + Button 2.1 + Button 2.2 + + + ); }; @@ -73,37 +71,35 @@ export const Controlled: StoryFn = () => { const [open, setOpen] = useState(false); return ( - <> - - setOpen(!open)}> - Dropdown - - setOpen(false)}> - - - - - Github - - - - - - Designsystemet.no - - - - - - + + setOpen(!open)}> + Dropdown + + setOpen(false)}> + + + + + Github + + + + + + Designsystemet.no + + + + + ); }; From ee284bd8ba321c8d9bb5b5ad8b63e9c22c92ef22 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 14:31:38 +0200 Subject: [PATCH 18/27] classname, spread --- packages/css/dropdownmenu.css | 2 +- .../src/components/DropdownMenu/DropdownMenu.tsx | 13 ++----------- .../components/DropdownMenu/DropdownMenuList.tsx | 2 +- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/css/dropdownmenu.css b/packages/css/dropdownmenu.css index 46b948a881..edbb35a817 100644 --- a/packages/css/dropdownmenu.css +++ b/packages/css/dropdownmenu.css @@ -36,7 +36,7 @@ width: 100%; } -.ds-dropdownmenu__group { +.ds-dropdownmenu__list { margin: 0; padding: 0; list-style: none; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.tsx index 97fc5aac66..8ecb3d927f 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.tsx @@ -17,14 +17,7 @@ export type DropdownMenuProps = { export const DropdownMenu = forwardRef( function DropddownMenuContent( - { - open, - onClose, - placement = 'bottom-end', - size = 'md', - className, - ...rest - }, + { placement = 'bottom-end', size = 'md', className, ...rest }, ref, ) { const { setSize } = useContext(DropdownMenuCtx); @@ -39,10 +32,8 @@ export const DropdownMenu = forwardRef( ); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx index 2330747dd0..5c541c62a8 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx @@ -12,7 +12,7 @@ export const DropdownMenuList = forwardRef<
        ); From 24d3ae54a9daa7779b4206a3aaaf430debac5593 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 14:37:25 +0200 Subject: [PATCH 19/27] size context on DropdownMenu --- .../components/DropdownMenu/DropdownMenu.tsx | 49 +++++++++++++------ .../DropdownMenu/DropdownMenuContext.tsx | 25 +--------- .../DropdownMenu/DropdownMenuHeading.tsx | 2 +- .../DropdownMenu/DropdownMenuItem.tsx | 3 +- 4 files changed, 37 insertions(+), 42 deletions(-) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.tsx index 8ecb3d927f..ba1a18d25f 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.tsx @@ -1,11 +1,16 @@ import cl from 'clsx/lite'; -import { forwardRef, useContext, useEffect } from 'react'; +import { + createContext, + forwardRef, + useContext, + useEffect, + useState, +} from 'react'; import type { ReactNode } from 'react'; import type { Placement } from '@floating-ui/react'; import { Popover } from '../Popover'; import type { PopoverProps } from '../Popover'; -import { DropdownMenuCtx } from './DropdownMenuContext'; export type DropdownMenuProps = { /** The placement of the dropdown @@ -17,25 +22,39 @@ export type DropdownMenuProps = { export const DropdownMenu = forwardRef( function DropddownMenuContent( - { placement = 'bottom-end', size = 'md', className, ...rest }, + { placement = 'bottom-end', className, ...rest }, ref, ) { - const { setSize } = useContext(DropdownMenuCtx); + const [size, setSize] = useState>( + rest.size || 'md', + ); useEffect(() => { - if (setSize) { - setSize(size); - } - }, [size, setSize]); + setSize(rest.size || 'md'); + }, [rest.size]); return ( - + + + ); }, ); + +type DropdownMenuCtxType = { + size: NonNullable; +}; + +export const DropdownMenuCtx = createContext({ + size: 'md', +}); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx index cce4217335..f432f04662 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx @@ -1,8 +1,6 @@ -import { createContext, useState } from 'react'; import type { ReactNode } from 'react'; import { PopoverContext } from '../Popover'; -import type { DropdownMenuProps } from './DropdownMenu'; export type DropdownMenuContextProps = { children: ReactNode; @@ -22,28 +20,7 @@ export type DropdownMenuContextProps = { * */ export const DropdownMenuContext = ({ children }: DropdownMenuContextProps) => { - const [size, setSize] = - useState>('md'); - - return ( - - {children} - - ); + return {children}; }; DropdownMenuContext.displayName = 'DropdownMenuContext'; - -type DropdownMenuCtxType = { - size: NonNullable; - setSize?: (size: NonNullable) => void; -}; - -export const DropdownMenuCtx = createContext({ - size: 'md', -}); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx index e85da116d1..5edeef8058 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx @@ -1,7 +1,7 @@ import cl from 'clsx/lite'; import { type HTMLAttributes, forwardRef, useContext } from 'react'; import { Paragraph } from '../Typography'; -import { DropdownMenuCtx } from './DropdownMenuContext'; +import { DropdownMenuCtx } from './DropdownMenu'; export type DropdownMenuHeadingProps = HTMLAttributes; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx index ef020e7871..d181004ed7 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx @@ -4,7 +4,7 @@ import type { ButtonProps } from '../Button'; import { Button } from '../Button'; import { RovingFocusItem } from '../../utilities'; -import { DropdownMenuCtx } from './DropdownMenuContext'; +import { DropdownMenuCtx } from './DropdownMenu'; export type DropdownMenuItemProps = Omit< ButtonProps, @@ -16,7 +16,6 @@ export const DropdownMenuItem = forwardRef< DropdownMenuItemProps >(function DropdownMenuItem({ children, className, style, ...rest }, ref) { const { size } = useContext(DropdownMenuCtx); - const value = useId(); return (
      • From be9289a325ff8126e1bd7101a6340b2b6dcad109 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 14:51:00 +0200 Subject: [PATCH 20/27] css, and remove group --- packages/css/dropdownmenu.css | 27 ++++++++++--------- .../DropdownMenu/DropdownMenuList.tsx | 1 - 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/css/dropdownmenu.css b/packages/css/dropdownmenu.css index edbb35a817..1355e31b7e 100644 --- a/packages/css/dropdownmenu.css +++ b/packages/css/dropdownmenu.css @@ -15,6 +15,7 @@ border: 1px solid var(--ds-color-neutral-border-subtle); min-width: var(--dsc-dropdownmenu-min-width); + /* Remove popover arrow */ &::before { display: none; } @@ -28,20 +29,20 @@ --dsc-dropdownmenu-padding: var(--ds-spacing-4) var(--ds-spacing-2); --dsc-dropdownmenu-min-width: 18rem; } -} -.ds-dropdownmenu__item { - justify-content: start; - padding: var(--dsc-dropdownmenu-item-padding); - width: 100%; -} + & :is(a, button, [role='button']) { + justify-content: start; + padding: var(--dsc-dropdownmenu-item-padding); + width: 100%; + } -.ds-dropdownmenu__list { - margin: 0; - padding: 0; - list-style: none; -} + .ds-dropdownmenu__list { + margin: 0; + padding: 0; + list-style: none; + } -.ds-dropdownmenu__heading { - padding: var(--dsc-dropdownmenu-header-padding); + .ds-dropdownmenu__heading { + padding: var(--dsc-dropdownmenu-header-padding); + } } diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx index 5c541c62a8..256f859a97 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx @@ -11,7 +11,6 @@ export const DropdownMenuList = forwardRef< return (
          From aa8366fb6e8eedb9d5a49964cbedd69a26634256 Mon Sep 17 00:00:00 2001 From: barsnes Date: Tue, 17 Sep 2024 15:11:15 +0200 Subject: [PATCH 21/27] remove failing test that is no longer releavant --- .../src/components/DropdownMenu/DropdownMenu.test.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx index 31cbccf208..4cd99489e9 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx @@ -60,13 +60,4 @@ describe('Dropdown', () => { expect(screen.getByText('Anchor')).toHaveAttribute('href', '/'); expect(screen.getByText('Anchor').tagName).toBe('A'); }); - - it('Group should have role="group"', async () => { - const { user } = await render(); - const dropdownTrigger = screen.getByRole('button'); - - await act(async () => await user.click(dropdownTrigger)); - - expect(screen.getByRole('group')).toBeInTheDocument(); - }); }); From 9ebc0dbb71fe93e1ee11dc11fde62ea8b1eb40fa Mon Sep 17 00:00:00 2001 From: barsnes Date: Wed, 18 Sep 2024 08:55:47 +0200 Subject: [PATCH 22/27] fix placement by removing relative and z --- packages/css/dropdownmenu.css | 2 -- .../react/src/components/DropdownMenu/DropdownMenuTrigger.tsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/css/dropdownmenu.css b/packages/css/dropdownmenu.css index 1355e31b7e..d8a094f74e 100644 --- a/packages/css/dropdownmenu.css +++ b/packages/css/dropdownmenu.css @@ -4,9 +4,7 @@ --dsc-dropdownmenu-item-padding: 0 var(--ds-spacing-4); --dsc-dropdownmenu-header-padding: var(--ds-spacing-2) var(--ds-spacing-4); - position: relative; padding: var(--dsc-dropdownmenu-padding); - z-index: 1500; margin: 0; list-style: none; border-radius: min(1rem, var(--ds-border-radius-md)); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx index 3ef5a762e5..cb1c971575 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx +++ b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx @@ -11,5 +11,5 @@ export const DropdownMenuTrigger = forwardRef< HTMLButtonElement, DropdownMenuTriggerProps >(function DropdownMenuTrigger({ asChild, ...rest }, ref) { - return ; + return ; }); From e936fc5cc00f59d7e0c435bdc8e9cf90e64130ed Mon Sep 17 00:00:00 2001 From: barsnes Date: Wed, 18 Sep 2024 08:56:45 +0200 Subject: [PATCH 23/27] move closer to button --- packages/css/dropdownmenu.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/css/dropdownmenu.css b/packages/css/dropdownmenu.css index d8a094f74e..4a2f877a14 100644 --- a/packages/css/dropdownmenu.css +++ b/packages/css/dropdownmenu.css @@ -5,7 +5,7 @@ --dsc-dropdownmenu-header-padding: var(--ds-spacing-2) var(--ds-spacing-4); padding: var(--dsc-dropdownmenu-padding); - margin: 0; + margin-top: -6px; list-style: none; border-radius: min(1rem, var(--ds-border-radius-md)); box-shadow: var(--ds-shadow-md); From 98bd5170c6b43fa0aa808a415839b326bb714933 Mon Sep 17 00:00:00 2001 From: barsnes Date: Wed, 18 Sep 2024 08:57:06 +0200 Subject: [PATCH 24/27] remove margin --- packages/css/dropdownmenu.css | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/css/dropdownmenu.css b/packages/css/dropdownmenu.css index 4a2f877a14..14d5a492b2 100644 --- a/packages/css/dropdownmenu.css +++ b/packages/css/dropdownmenu.css @@ -5,7 +5,6 @@ --dsc-dropdownmenu-header-padding: var(--ds-spacing-2) var(--ds-spacing-4); padding: var(--dsc-dropdownmenu-padding); - margin-top: -6px; list-style: none; border-radius: min(1rem, var(--ds-border-radius-md)); box-shadow: var(--ds-shadow-md); From 54a2a6b5e00c69c593bb23bc6d906fd50227d06b Mon Sep 17 00:00:00 2001 From: barsnes Date: Wed, 18 Sep 2024 10:28:43 +0200 Subject: [PATCH 25/27] rename to `Dropdown` --- .changeset/proud-walls-flash.md | 3 +- .../app/komponenter/component-list.ts | 4 +- .../components/Tokens/TokenList/TokenList.tsx | 50 ++++---- .../{DropdownMenu.svg => Dropdown.svg} | 0 .../Previews/Components/Components.tsx | 20 +-- packages/css/dropdown.css | 45 +++++++ packages/css/dropdownmenu.css | 45 ------- packages/css/index.css | 2 +- packages/react/CHANGELOG.md | 4 +- .../src/components/Avatar/Avatar.stories.tsx | 30 ++--- .../src/components/Dropdown/Dropdown.mdx | 81 ++++++++++++ .../components/Dropdown/Dropdown.stories.tsx | 117 ++++++++++++++++++ .../Dropdown.test.tsx} | 32 ++--- .../Dropdown.tsx} | 24 ++-- .../DropdownContext.tsx} | 8 +- .../components/Dropdown/DropdownHeading.tsx | 21 ++++ .../src/components/Dropdown/DropdownItem.tsx | 28 +++++ .../src/components/Dropdown/DropdownList.tsx | 13 ++ .../components/Dropdown/DropdownTrigger.tsx | 13 ++ .../react/src/components/Dropdown/index.ts | 46 +++++++ .../components/DropdownMenu/DropdownMenu.mdx | 81 ------------ .../DropdownMenu/DropdownMenu.stories.tsx | 117 ------------------ .../DropdownMenu/DropdownMenuHeading.tsx | 25 ---- .../DropdownMenu/DropdownMenuItem.tsx | 33 ----- .../DropdownMenu/DropdownMenuList.tsx | 18 --- .../DropdownMenu/DropdownMenuTrigger.tsx | 15 --- .../src/components/DropdownMenu/index.ts | 46 ------- packages/react/src/components/index.ts | 2 +- 28 files changed, 451 insertions(+), 472 deletions(-) rename apps/storefront/public/img/component-previews/{DropdownMenu.svg => Dropdown.svg} (100%) create mode 100644 packages/css/dropdown.css delete mode 100644 packages/css/dropdownmenu.css create mode 100644 packages/react/src/components/Dropdown/Dropdown.mdx create mode 100644 packages/react/src/components/Dropdown/Dropdown.stories.tsx rename packages/react/src/components/{DropdownMenu/DropdownMenu.test.tsx => Dropdown/Dropdown.test.tsx} (62%) rename packages/react/src/components/{DropdownMenu/DropdownMenu.tsx => Dropdown/Dropdown.tsx} (61%) rename packages/react/src/components/{DropdownMenu/DropdownMenuContext.tsx => Dropdown/DropdownContext.tsx} (66%) create mode 100644 packages/react/src/components/Dropdown/DropdownHeading.tsx create mode 100644 packages/react/src/components/Dropdown/DropdownItem.tsx create mode 100644 packages/react/src/components/Dropdown/DropdownList.tsx create mode 100644 packages/react/src/components/Dropdown/DropdownTrigger.tsx create mode 100644 packages/react/src/components/Dropdown/index.ts delete mode 100644 packages/react/src/components/DropdownMenu/DropdownMenu.mdx delete mode 100644 packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx delete mode 100644 packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx delete mode 100644 packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx delete mode 100644 packages/react/src/components/DropdownMenu/DropdownMenuList.tsx delete mode 100644 packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx delete mode 100644 packages/react/src/components/DropdownMenu/index.ts diff --git a/.changeset/proud-walls-flash.md b/.changeset/proud-walls-flash.md index 1e70de27f5..ab31bf63fe 100644 --- a/.changeset/proud-walls-flash.md +++ b/.changeset/proud-walls-flash.md @@ -4,6 +4,7 @@ --- DropdownMenu: +- Rename from `DropdownMenu` to `Dropdown` - Change API and structure - Rename `.Root` to `.Context` -- Rename `.Content` to `DropdownMenu` +- Rename `.Content` to `Dropdown` diff --git a/apps/storefront/app/komponenter/component-list.ts b/apps/storefront/app/komponenter/component-list.ts index d2f614bdd9..ab515f63f8 100644 --- a/apps/storefront/app/komponenter/component-list.ts +++ b/apps/storefront/app/komponenter/component-list.ts @@ -56,8 +56,8 @@ export const data = [ }, { title: 'Dropdown Menu', - image: 'DropdownMenu.svg', - url: 'https://storybook.designsystemet.no/?path=/docs/komponenter-dropdownmenu--docs', + image: 'Dropdown.svg', + url: 'https://storybook.designsystemet.no/?path=/docs/komponenter-dropdown--docs', }, { title: 'Error Summary', diff --git a/apps/storefront/components/Tokens/TokenList/TokenList.tsx b/apps/storefront/components/Tokens/TokenList/TokenList.tsx index e1eeded7b0..25304bf2aa 100644 --- a/apps/storefront/components/Tokens/TokenList/TokenList.tsx +++ b/apps/storefront/components/Tokens/TokenList/TokenList.tsx @@ -1,6 +1,6 @@ 'use client'; import { - DropdownMenu, + Dropdown, Heading, Link, Paragraph, @@ -220,40 +220,40 @@ const TokenList = ({ {(showThemePicker || showModeSwitcher) && (
          {showThemePicker && ( - - + + Brand: {capitalizeString(brand)} - - - setBrand('digdir')}> + + + setBrand('digdir')}> Digdir - - setBrand('altinn')}> + + setBrand('altinn')}> Altinn - - setBrand('tilsynet')}> + + setBrand('tilsynet')}> Tilsynet - - setBrand('portal')}> + + setBrand('portal')}> Brreg - - - + + + )} {showModeSwitcher && ( - - + + Mode: {capitalizeString(mode)} - - - setMode('light')}> + + + setMode('light')}> Light - - setMode('dark')}> + + setMode('dark')}> Dark - - - + + + )}
          )} diff --git a/apps/storefront/public/img/component-previews/DropdownMenu.svg b/apps/storefront/public/img/component-previews/Dropdown.svg similarity index 100% rename from apps/storefront/public/img/component-previews/DropdownMenu.svg rename to apps/storefront/public/img/component-previews/Dropdown.svg diff --git a/apps/theme/components/Previews/Components/Components.tsx b/apps/theme/components/Previews/Components/Components.tsx index 750d768ceb..51ed80bbef 100644 --- a/apps/theme/components/Previews/Components/Components.tsx +++ b/apps/theme/components/Previews/Components/Components.tsx @@ -6,7 +6,7 @@ import { Checkbox, Chip, Combobox, - DropdownMenu, + Dropdown, Fieldset, Heading, HelpText, @@ -390,18 +390,18 @@ export const Components = () => {
    - - Velg språk - - Norsk - Engelsk - Spansk - Fransk - + + Velg språk + + Norsk + Engelsk + Spansk + Fransk + Velg språk for å endre innholdet på siden - +
    diff --git a/packages/css/dropdown.css b/packages/css/dropdown.css new file mode 100644 index 0000000000..f3881b085f --- /dev/null +++ b/packages/css/dropdown.css @@ -0,0 +1,45 @@ +.ds-dropdown { + --dsc-dropdown-padding: var(--ds-spacing-3) var(--ds-spacing-2); + --dsc-dropdown-min-width: 16rem; + --dsc-dropdown-item-padding: 0 var(--ds-spacing-4); + --dsc-dropdown-header-padding: var(--ds-spacing-2) var(--ds-spacing-4); + + padding: var(--dsc-dropdown-padding); + list-style: none; + border-radius: min(1rem, var(--ds-border-radius-md)); + box-shadow: var(--ds-shadow-md); + background-color: var(--ds-color-neutral-background-default); + border: 1px solid var(--ds-color-neutral-border-subtle); + min-width: var(--dsc-dropdown-min-width); + + /* Remove popover arrow */ + &::before { + display: none; + } + + &[data-size='sm'] { + --dsc-dropdown-padding: var(--ds-spacing-2); + --dsc-dropdown-min-width: 15rem; + } + + &[data-size='lg'] { + --dsc-dropdown-padding: var(--ds-spacing-4) var(--ds-spacing-2); + --dsc-dropdown-min-width: 18rem; + } + + & :is(a, button, [role='button']) { + justify-content: start; + padding: var(--dsc-dropdown-item-padding); + width: 100%; + } + + .ds-dropdown__list { + margin: 0; + padding: 0; + list-style: none; + } + + .ds-dropdown__heading { + padding: var(--dsc-dropdown-header-padding); + } +} diff --git a/packages/css/dropdownmenu.css b/packages/css/dropdownmenu.css deleted file mode 100644 index 14d5a492b2..0000000000 --- a/packages/css/dropdownmenu.css +++ /dev/null @@ -1,45 +0,0 @@ -.ds-dropdownmenu { - --dsc-dropdownmenu-padding: var(--ds-spacing-3) var(--ds-spacing-2); - --dsc-dropdownmenu-min-width: 16rem; - --dsc-dropdownmenu-item-padding: 0 var(--ds-spacing-4); - --dsc-dropdownmenu-header-padding: var(--ds-spacing-2) var(--ds-spacing-4); - - padding: var(--dsc-dropdownmenu-padding); - list-style: none; - border-radius: min(1rem, var(--ds-border-radius-md)); - box-shadow: var(--ds-shadow-md); - background-color: var(--ds-color-neutral-background-default); - border: 1px solid var(--ds-color-neutral-border-subtle); - min-width: var(--dsc-dropdownmenu-min-width); - - /* Remove popover arrow */ - &::before { - display: none; - } - - &[data-size='sm'] { - --dsc-dropdownmenu-padding: var(--ds-spacing-2); - --dsc-dropdownmenu-min-width: 15rem; - } - - &[data-size='lg'] { - --dsc-dropdownmenu-padding: var(--ds-spacing-4) var(--ds-spacing-2); - --dsc-dropdownmenu-min-width: 18rem; - } - - & :is(a, button, [role='button']) { - justify-content: start; - padding: var(--dsc-dropdownmenu-item-padding); - width: 100%; - } - - .ds-dropdownmenu__list { - margin: 0; - padding: 0; - list-style: none; - } - - .ds-dropdownmenu__heading { - padding: var(--dsc-dropdownmenu-header-padding); - } -} diff --git a/packages/css/index.css b/packages/css/index.css index 0406b3fe75..fef99b2e5f 100644 --- a/packages/css/index.css +++ b/packages/css/index.css @@ -28,7 +28,7 @@ @import url('./card.css') layer(ds.components); @import url('./link.css') layer(ds.components); @import url('./fieldset.css') layer(ds.components); -@import url('./dropdownmenu.css') layer(ds.components); +@import url('./dropdown.css') layer(ds.components); @import url('./chip') layer(ds.components); @import url('./divider.css') layer(ds.components); @import url('./tabs.css') layer(ds.components); diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index bd1ba9ecaa..e316e1448d 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -199,7 +199,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - **Box:** Remove deprecated `as` prop ([#1896](https://github.com/digdir/designsystemet/issues/1896)) ([59dd231](https://github.com/digdir/designsystemet/commit/59dd2310cfb7c91e4be20a54412e036a8a268b73)) - **Button:** Remove deprecated `as` prop ([#1894](https://github.com/digdir/designsystemet/issues/1894)) ([c346203](https://github.com/digdir/designsystemet/commit/c34620344be7d25c4c73b147b136254b177cab2c)) - **Card:** Remove deprecated `as` prop ([#1897](https://github.com/digdir/designsystemet/issues/1897)) ([17ef8c6](https://github.com/digdir/designsystemet/commit/17ef8c64c3ad07d8a0bf036a032c1a5bd62a4233)) -- **DropdownMenuItem:** Remove deprecated `as` prop ([#1895](https://github.com/digdir/designsystemet/issues/1895)) ([2f705bb](https://github.com/digdir/designsystemet/commit/2f705bbe063317fd6f21d340ab222e966d912d7e)) +- **DropdownItem:** Remove deprecated `as` prop ([#1895](https://github.com/digdir/designsystemet/issues/1895)) ([2f705bb](https://github.com/digdir/designsystemet/commit/2f705bbe063317fd6f21d340ab222e966d912d7e)) - **ErrorMessage:** Remove deprecated `as` prop ([#1899](https://github.com/digdir/designsystemet/issues/1899)) ([b37584a](https://github.com/digdir/designsystemet/commit/b37584af07b2b8a4f536b5931213227fd1fc1c9d)) - **Heading:** Remove deprecated `as` prop ([#1900](https://github.com/digdir/designsystemet/issues/1900)) ([685e438](https://github.com/digdir/designsystemet/commit/685e43897775276c331b2cf548f70fa508806d9e)) - **Ingress:** Remove deprecated `as` prop ([#1903](https://github.com/digdir/designsystemet/issues/1903)) ([1139b31](https://github.com/digdir/designsystemet/commit/1139b312defa3411e9133c949dc19d48011fa74d)) @@ -529,7 +529,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ### Bug Fixes -- **DropDownMenuItem:** add list style none ([#1190](https://github.com/digdir/designsystemet/issues/1190)) ([11bd19b](https://github.com/digdir/designsystemet/commit/11bd19bfb6ac76b2c697a22e876117c4128be3bd)) +- **DropdownItem:** add list style none ([#1190](https://github.com/digdir/designsystemet/issues/1190)) ([11bd19b](https://github.com/digdir/designsystemet/commit/11bd19bfb6ac76b2c697a22e876117c4128be3bd)) - **List:** Wrap in `div` to allow access to `Heading` ([#1217](https://github.com/digdir/designsystemet/issues/1217)) ([afcadb7](https://github.com/digdir/designsystemet/commit/afcadb7c4cb4b368d247af0c41ed8debf53c4b66)) - **Pagination:** Only use needed space for buttons ([#1220](https://github.com/digdir/designsystemet/issues/1220)) ([4bf3d74](https://github.com/digdir/designsystemet/commit/4bf3d745888f500259df5aadf4edee97ec4f95bc)) - **Select:** Select not working properly in Modal ([#1195](https://github.com/digdir/designsystemet/issues/1195)) ([fb8be6a](https://github.com/digdir/designsystemet/commit/fb8be6a647ba0da8b5b23e65813508f34e09c8c1)) diff --git a/packages/react/src/components/Avatar/Avatar.stories.tsx b/packages/react/src/components/Avatar/Avatar.stories.tsx index ab66d11ea2..bdceb4b65d 100644 --- a/packages/react/src/components/Avatar/Avatar.stories.tsx +++ b/packages/react/src/components/Avatar/Avatar.stories.tsx @@ -3,7 +3,7 @@ import type { Meta, StoryFn } from '@storybook/react'; import { BriefcaseIcon } from '@navikt/aksel-icons'; import { Avatar } from '.'; -import { Badge, DropdownMenu } from '../'; +import { Badge, Dropdown } from '../'; type Story = StoryFn; @@ -93,34 +93,34 @@ export const WithImage: Story = () => ( ); -export const InDropdownMenu: Story = () => ( - - +export const InDropdown: Story = () => ( + + ON Velg Profil - - - Alle kontoer - - + + + Alle kontoer + + ON Ola Nordmann - - + + Sogndal kommune - - - - + + + + ); export const AsLink: Story = () => ( diff --git a/packages/react/src/components/Dropdown/Dropdown.mdx b/packages/react/src/components/Dropdown/Dropdown.mdx new file mode 100644 index 0000000000..22dedbccc9 --- /dev/null +++ b/packages/react/src/components/Dropdown/Dropdown.mdx @@ -0,0 +1,81 @@ +import { Meta, Canvas, Controls, Primary, ArgTypes } from '@storybook/blocks'; + +import * as DropdownStories from './Dropdown.stories'; + +import { Dropdown } from './'; + + + +# Dropdown + + + + +## Slik bruker du `Dropdown` + +```tsx +import { Dropdown } from '@digdir/designsystemet-react'; + +// med context + + Trigger + + Heading + + Item + + + + +// uten context + + + Heading + + Item + + +``` + +## Eksempler på bruk + +### Kontrollert + +Dersom du sender inn `open`, så bruker du `Dropdown` kontrollert. Du kan bruke `onClose` for å få beskjed når `Dropdown` vil lukkes. + + + +### Ikoner + +Du kan legge ikon rett inn i `Dropdown.Item`, dersom det blir mye mellomrom til kanten kan du legge på din egen klasse og endre på `padding`. + + + +### Uten Trigger + +`Dropdown` bruker popover APIet, så du kan bruke `Dropdown` uten `Dropdown.Trigger`. +Du må da legge til `popovertarget={id}` på `Dropdown`, og `id` på `Dropdown`. + + + +## Tilgjengelighet + +Det er innebygd tilgjengelighet i `Dropdown.Trigger` med `aria-expanded={true/false}` i henhold til åpne/lukket tilstand, og `aria-haspopup='menu'`. + +### `Dropdown.List` + + + +### `Dropdown.Trigger` + +Triggeren er en [Button](/docs/komponenter-button--docs) som standard. + +Bruk `Dropdown.Trigger` til å aktivere `Dropdown`. Du kan bruke `asChild` for å endre `Dropdown.Trigger` elementet. +Dersom du skal legge på funksjoner som `onClick`, legg det på ditt element, og legg `asChild` på `Dropdown.Trigger`. + +### Referanser + +Vi bruker `ul` og `li` tags i dropdownen, valget er basert på denne: + +- https://www.w3.org/WAI/tutorials/menus/flyout/#flyoutnavkbbtn +- https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/../Dropdown.stories. diff --git a/packages/react/src/components/Dropdown/Dropdown.stories.tsx b/packages/react/src/components/Dropdown/Dropdown.stories.tsx new file mode 100644 index 0000000000..2f4e813e96 --- /dev/null +++ b/packages/react/src/components/Dropdown/Dropdown.stories.tsx @@ -0,0 +1,117 @@ +import { LinkIcon } from '@navikt/aksel-icons'; +import type { Meta, StoryFn } from '@storybook/react'; +import { useState } from 'react'; + +import { Dropdown } from '.'; +import { Button } from '../Button'; + +export default { + title: 'Komponenter/Dropdown', + component: Dropdown, +} as Meta; + +export const Preview: StoryFn = (args) => { + return ( + + Dropdown + + Heading 1 + + Button 1.1 + Button 1.2 + + Heading 2 + + Button 2.1 + Button 2.2 + + + + ); +}; + +Preview.args = { + placement: 'bottom-end', + size: 'md', +}; + +export const Icons: StoryFn = (args) => { + return ( + + Dropdown + + + + + + Github + + + + + + Designsystemet.no + + + + + + ); +}; + +export const Controlled: StoryFn = () => { + const [open, setOpen] = useState(false); + + return ( + + setOpen(!open)}> + Dropdown + + setOpen(false)}> + + + + + Github + + + + + + Designsystemet.no + + + + + + ); +}; + +export const WithoutTrigger: StoryFn = () => { + return ( + <> + + + + Item + + + + ); +}; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx b/packages/react/src/components/Dropdown/Dropdown.test.tsx similarity index 62% rename from packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx rename to packages/react/src/components/Dropdown/Dropdown.test.tsx index 4cd99489e9..d9c283151c 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.test.tsx +++ b/packages/react/src/components/Dropdown/Dropdown.test.tsx @@ -2,26 +2,26 @@ import { render as renderRtl, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { act } from 'react'; -import type { DropdownMenuContextProps } from './DropdownMenuContext'; +import type { DropdownContextProps } from './DropdownContext'; -import { DropdownMenu } from '.'; +import { Dropdown } from '.'; -const Comp = (args: Partial) => { +const Comp = (args: Partial) => { return ( - - Dropdown - - Links - - Item + + Dropdown + + Links + + Item {args.children} - - - + + + ); }; -const render = async (props: Partial = {}) => { +const render = async (props: Partial = {}) => { /* Flush microtasks */ await act(async () => {}); const user = userEvent.setup(); @@ -36,7 +36,7 @@ describe('Dropdown', () => { /* We are testing closing and opening in Popover.tests.tsx */ it('should render children', async () => { const { user } = await render({ - children: Item 2, + children: Item 2, }); const dropdownTrigger = screen.getByRole('button'); @@ -48,9 +48,9 @@ describe('Dropdown', () => { it('should be able to render `Dropdown.Item` as a anchor element using asChild', async () => { const { user } = await render({ children: ( - + Anchor - + ), }); const dropdownTrigger = screen.getByRole('button'); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.tsx b/packages/react/src/components/Dropdown/Dropdown.tsx similarity index 61% rename from packages/react/src/components/DropdownMenu/DropdownMenu.tsx rename to packages/react/src/components/Dropdown/Dropdown.tsx index ba1a18d25f..039b7705f7 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.tsx +++ b/packages/react/src/components/Dropdown/Dropdown.tsx @@ -1,18 +1,12 @@ import cl from 'clsx/lite'; -import { - createContext, - forwardRef, - useContext, - useEffect, - useState, -} from 'react'; +import { createContext, forwardRef, useEffect, useState } from 'react'; import type { ReactNode } from 'react'; import type { Placement } from '@floating-ui/react'; import { Popover } from '../Popover'; import type { PopoverProps } from '../Popover'; -export type DropdownMenuProps = { +export type DropdownProps = { /** The placement of the dropdown * @default bottom-end */ @@ -20,12 +14,12 @@ export type DropdownMenuProps = { children: ReactNode; } & Omit; -export const DropdownMenu = forwardRef( +export const Dropdown = forwardRef( function DropddownMenuContent( { placement = 'bottom-end', className, ...rest }, ref, ) { - const [size, setSize] = useState>( + const [size, setSize] = useState>( rest.size || 'md', ); @@ -34,7 +28,7 @@ export const DropdownMenu = forwardRef( }, [rest.size]); return ( - ( ref={ref} placement={placement} size={size} - className={cl('ds-dropdownmenu', className)} + className={cl('ds-dropdown', className)} {...rest} /> - + ); }, ); type DropdownMenuCtxType = { - size: NonNullable; + size: NonNullable; }; -export const DropdownMenuCtx = createContext({ +export const DropdownCtx = createContext({ size: 'md', }); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx b/packages/react/src/components/Dropdown/DropdownContext.tsx similarity index 66% rename from packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx rename to packages/react/src/components/Dropdown/DropdownContext.tsx index f432f04662..0ed577de32 100644 --- a/packages/react/src/components/DropdownMenu/DropdownMenuContext.tsx +++ b/packages/react/src/components/Dropdown/DropdownContext.tsx @@ -2,12 +2,12 @@ import type { ReactNode } from 'react'; import { PopoverContext } from '../Popover'; -export type DropdownMenuContextProps = { +export type DropdownContextProps = { children: ReactNode; }; /** - * DropdownMenuContext is the root component for the DropdownMenu component. + * DropdownContext is the root component for the DropdownMenu component. * @example * * Dropdown @@ -19,8 +19,8 @@ export type DropdownMenuContextProps = { * * */ -export const DropdownMenuContext = ({ children }: DropdownMenuContextProps) => { +export const DropdownContext = ({ children }: DropdownContextProps) => { return {children}; }; -DropdownMenuContext.displayName = 'DropdownMenuContext'; +DropdownContext.displayName = 'DropdownContext'; diff --git a/packages/react/src/components/Dropdown/DropdownHeading.tsx b/packages/react/src/components/Dropdown/DropdownHeading.tsx new file mode 100644 index 0000000000..666acef743 --- /dev/null +++ b/packages/react/src/components/Dropdown/DropdownHeading.tsx @@ -0,0 +1,21 @@ +import cl from 'clsx/lite'; +import { type HTMLAttributes, forwardRef, useContext } from 'react'; +import { Paragraph } from '../Typography'; +import { DropdownCtx } from './Dropdown'; + +export type DropdownHeadingProps = HTMLAttributes; + +export const DropdownHeading = forwardRef< + HTMLHeadingElement, + DropdownHeadingProps +>(function DropdownHeading({ children, className, ...rest }, ref) { + const { size } = useContext(DropdownCtx); + + return ( + +

    + {children} +

    +
    + ); +}); diff --git a/packages/react/src/components/Dropdown/DropdownItem.tsx b/packages/react/src/components/Dropdown/DropdownItem.tsx new file mode 100644 index 0000000000..64103040f1 --- /dev/null +++ b/packages/react/src/components/Dropdown/DropdownItem.tsx @@ -0,0 +1,28 @@ +import { forwardRef, useContext } from 'react'; + +import type { ButtonProps } from '../Button'; +import { Button } from '../Button'; + +import { DropdownCtx } from './Dropdown'; + +export type DropdownItemProps = Omit; + +export const DropdownItem = forwardRef( + function DropdownItem({ children, className, style, ...rest }, ref) { + const { size } = useContext(DropdownCtx); + + return ( +
  • + +
  • + ); + }, +); diff --git a/packages/react/src/components/Dropdown/DropdownList.tsx b/packages/react/src/components/Dropdown/DropdownList.tsx new file mode 100644 index 0000000000..4fa1b4f1f2 --- /dev/null +++ b/packages/react/src/components/Dropdown/DropdownList.tsx @@ -0,0 +1,13 @@ +import cl from 'clsx/lite'; +import { forwardRef } from 'react'; +import type { HTMLAttributes } from 'react'; + +export type DropdownListProps = HTMLAttributes; + +export const DropdownList = forwardRef( + function DropdownList({ className, ...rest }, ref) { + return ( +
      + ); + }, +); diff --git a/packages/react/src/components/Dropdown/DropdownTrigger.tsx b/packages/react/src/components/Dropdown/DropdownTrigger.tsx new file mode 100644 index 0000000000..bdc42dfc0a --- /dev/null +++ b/packages/react/src/components/Dropdown/DropdownTrigger.tsx @@ -0,0 +1,13 @@ +import { forwardRef } from 'react'; +import type { ComponentPropsWithRef } from 'react'; + +import { PopoverTrigger } from '../Popover'; + +export type DropdownTriggerProps = ComponentPropsWithRef; + +export const DropdownTrigger = forwardRef< + HTMLButtonElement, + DropdownTriggerProps +>(function DropdownTrigger({ asChild, ...rest }, ref) { + return ; +}); diff --git a/packages/react/src/components/Dropdown/index.ts b/packages/react/src/components/Dropdown/index.ts new file mode 100644 index 0000000000..5f496ab8d8 --- /dev/null +++ b/packages/react/src/components/Dropdown/index.ts @@ -0,0 +1,46 @@ +import { Dropdown as DropdownRoot } from './Dropdown'; +import { DropdownContext } from './DropdownContext'; +import { DropdownHeading } from './DropdownHeading'; +import { DropdownItem } from './DropdownItem'; +import { DropdownList } from './DropdownList'; +import { DropdownTrigger } from './DropdownTrigger'; + +/** + * @example + * + * Dropdown + * + * Heading + * + * Button 1 + * + * + * + */ +const Dropdown = Object.assign(DropdownRoot, { + Context: DropdownContext, + Heading: DropdownHeading, + List: DropdownList, + Item: DropdownItem, + Trigger: DropdownTrigger, +}); + +Dropdown.Context.displayName = 'Dropdown.Context'; +Dropdown.List.displayName = 'Dropdown.List'; +Dropdown.Heading.displayName = 'Dropdown.Heading'; +Dropdown.Item.displayName = 'Dropdown.Item'; +Dropdown.Trigger.displayName = 'Dropdown.Trigger'; + +export type { DropdownContextProps } from './DropdownContext'; +export type { DropdownListProps } from './DropdownList'; +export type { DropdownHeadingProps } from './DropdownHeading'; +export type { DropdownItemProps } from './DropdownItem'; +export type { DropdownProps } from './Dropdown'; +export { + Dropdown, + DropdownContext, + DropdownList, + DropdownHeading, + DropdownItem, + DropdownTrigger, +}; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx b/packages/react/src/components/DropdownMenu/DropdownMenu.mdx deleted file mode 100644 index 669738d3d9..0000000000 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.mdx +++ /dev/null @@ -1,81 +0,0 @@ -import { Meta, Canvas, Controls, Primary, ArgTypes } from '@storybook/blocks'; - -import * as DropdownMenuStories from './DropdownMenu.stories'; - -import { DropdownMenu } from './'; - - - -# DropdownMenu - - - - -## Slik bruker du `DropdownMenu` - -```tsx -import { DropdownMenu } from '@digdir/designsystemet-react'; - -// med context - - Trigger - - Heading - - Item - - - - -// uten context - - - Heading - - Item - - -``` - -## Eksempler på bruk - -### Kontrollert - -Dersom du sender inn `open`, så bruker du `DropdownMenu` kontrollert. Du kan bruke `onClose` for å få beskjed når `DropdownMenu` vil lukkes. - - - -### Ikoner - -Du kan legge ikon rett inn i `DropdownMenu.Item`, dersom det blir mye mellomrom til kanten kan du legge på din egen klasse og endre på `padding`. - - - -### Uten Trigger - -`DropdownMenu` bruker popover APIet, så du kan bruke `DropdownMenu` uten `DropdownMenu.Trigger`. -Du må da legge til `popovertarget={id}` på `DropdownMenu`, og `id` på `DropdownMenu`. - - - -## Tilgjengelighet - -Det er innebygd tilgjengelighet i `DropdownMenu.Trigger` med `aria-expanded={true/false}` i henhold til åpne/lukket tilstand, og `aria-haspopup='menu'`. - -### `DropdownMenu.List` - - - -### `DropdownMenu.Trigger` - -Triggeren er en [Button](/docs/komponenter-button--docs) som standard. - -Bruk `DropdownMenu.Trigger` til å aktivere `DropdownMenu`. Du kan bruke `asChild` for å endre `DropdownMenu.Trigger` elementet. -Dersom du skal legge på funksjoner som `onClick`, legg det på ditt element, og legg `asChild` på `DropdownMenu.Trigger`. - -### Referanser - -Vi bruker `ul` og `li` tags i dropdownen, valget er basert på denne: - -- https://www.w3.org/WAI/tutorials/menus/flyout/#flyoutnavkbbtn -- https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/ diff --git a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx b/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx deleted file mode 100644 index 012c464bc2..0000000000 --- a/packages/react/src/components/DropdownMenu/DropdownMenu.stories.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { LinkIcon } from '@navikt/aksel-icons'; -import type { Meta, StoryFn } from '@storybook/react'; -import { useState } from 'react'; - -import { DropdownMenu } from '.'; -import { Button } from '../Button'; - -export default { - title: 'Komponenter/DropdownMenu', - component: DropdownMenu, -} as Meta; - -export const Preview: StoryFn = (args) => { - return ( - - Dropdown - - Heading 1 - - Button 1.1 - Button 1.2 - - Heading 2 - - Button 2.1 - Button 2.2 - - - - ); -}; - -Preview.args = { - placement: 'bottom-end', - size: 'md', -}; - -export const Icons: StoryFn = (args) => { - return ( - - Dropdown - - - - - - Github - - - - - - Designsystemet.no - - - - - - ); -}; - -export const Controlled: StoryFn = () => { - const [open, setOpen] = useState(false); - - return ( - - setOpen(!open)}> - Dropdown - - setOpen(false)}> - - - - - Github - - - - - - Designsystemet.no - - - - - - ); -}; - -export const WithoutTrigger: StoryFn = () => { - return ( - <> - - - - Item - - - - ); -}; diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx deleted file mode 100644 index 5edeef8058..0000000000 --- a/packages/react/src/components/DropdownMenu/DropdownMenuHeading.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import cl from 'clsx/lite'; -import { type HTMLAttributes, forwardRef, useContext } from 'react'; -import { Paragraph } from '../Typography'; -import { DropdownMenuCtx } from './DropdownMenu'; - -export type DropdownMenuHeadingProps = HTMLAttributes; - -export const DropdownMenuHeading = forwardRef< - HTMLHeadingElement, - DropdownMenuHeadingProps ->(function DropdownMenuHeading({ children, className, ...rest }, ref) { - const { size } = useContext(DropdownMenuCtx); - - return ( - -

      - {children} -

      -
      - ); -}); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx deleted file mode 100644 index d181004ed7..0000000000 --- a/packages/react/src/components/DropdownMenu/DropdownMenuItem.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { forwardRef, useContext, useId } from 'react'; - -import type { ButtonProps } from '../Button'; -import { Button } from '../Button'; - -import { RovingFocusItem } from '../../utilities'; -import { DropdownMenuCtx } from './DropdownMenu'; - -export type DropdownMenuItemProps = Omit< - ButtonProps, - 'variant' | 'size' | 'color' ->; - -export const DropdownMenuItem = forwardRef< - HTMLButtonElement, - DropdownMenuItemProps ->(function DropdownMenuItem({ children, className, style, ...rest }, ref) { - const { size } = useContext(DropdownMenuCtx); - - return ( -
    • - -
    • - ); -}); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx deleted file mode 100644 index 256f859a97..0000000000 --- a/packages/react/src/components/DropdownMenu/DropdownMenuList.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import cl from 'clsx/lite'; -import { forwardRef } from 'react'; -import type { HTMLAttributes } from 'react'; - -export type DropdownMenuListProps = HTMLAttributes; - -export const DropdownMenuList = forwardRef< - HTMLUListElement, - DropdownMenuListProps ->(function DropdownMenuGroup({ className, ...rest }, ref) { - return ( -
        - ); -}); diff --git a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx b/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx deleted file mode 100644 index cb1c971575..0000000000 --- a/packages/react/src/components/DropdownMenu/DropdownMenuTrigger.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { forwardRef } from 'react'; -import type { ComponentPropsWithRef } from 'react'; - -import { PopoverTrigger } from '../Popover'; - -export type DropdownMenuTriggerProps = ComponentPropsWithRef< - typeof PopoverTrigger ->; - -export const DropdownMenuTrigger = forwardRef< - HTMLButtonElement, - DropdownMenuTriggerProps ->(function DropdownMenuTrigger({ asChild, ...rest }, ref) { - return ; -}); diff --git a/packages/react/src/components/DropdownMenu/index.ts b/packages/react/src/components/DropdownMenu/index.ts deleted file mode 100644 index 93f2820f75..0000000000 --- a/packages/react/src/components/DropdownMenu/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { DropdownMenu as DropdownMenuRoot } from './DropdownMenu'; -import { DropdownMenuContext } from './DropdownMenuContext'; -import { DropdownMenuHeading } from './DropdownMenuHeading'; -import { DropdownMenuItem } from './DropdownMenuItem'; -import { DropdownMenuList } from './DropdownMenuList'; -import { DropdownMenuTrigger } from './DropdownMenuTrigger'; - -/** - * @example - * - * Dropdown - * - * Heading - * - * Button 1 - * - * - * - */ -const DropdownMenu = Object.assign(DropdownMenuRoot, { - Context: DropdownMenuContext, - Heading: DropdownMenuHeading, - List: DropdownMenuList, - Item: DropdownMenuItem, - Trigger: DropdownMenuTrigger, -}); - -DropdownMenu.Context.displayName = 'DropdownMenu.Context'; -DropdownMenu.List.displayName = 'DropdownMenu.List'; -DropdownMenu.Heading.displayName = 'DropdownMenu.Heading'; -DropdownMenu.Item.displayName = 'DropdownMenu.Item'; -DropdownMenu.Trigger.displayName = 'DropdownMenu.Trigger'; - -export type { DropdownMenuContextProps } from './DropdownMenuContext'; -export type { DropdownMenuListProps } from './DropdownMenuList'; -export type { DropdownMenuHeadingProps } from './DropdownMenuHeading'; -export type { DropdownMenuItemProps } from './DropdownMenuItem'; -export type { DropdownMenuProps } from './DropdownMenu'; -export { - DropdownMenu, - DropdownMenuContext, - DropdownMenuList, - DropdownMenuHeading, - DropdownMenuItem, - DropdownMenuTrigger, -}; diff --git a/packages/react/src/components/index.ts b/packages/react/src/components/index.ts index d1bbe9ad95..a4eec0aa5e 100644 --- a/packages/react/src/components/index.ts +++ b/packages/react/src/components/index.ts @@ -27,7 +27,7 @@ export * from './ToggleGroup'; export * from './Popover'; export * from './Divider'; export * from './Modal'; -export * from './DropdownMenu'; +export * from './Dropdown'; export * from './form/Search'; export * from './Card'; export * from './form/Combobox'; From 5a29da4f572a47dfa10d172ece4d59a09c36ff0f Mon Sep 17 00:00:00 2001 From: barsnes Date: Wed, 18 Sep 2024 12:00:33 +0200 Subject: [PATCH 26/27] don't change changelog --- packages/react/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index e316e1448d..fcd334652a 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -199,7 +199,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - **Box:** Remove deprecated `as` prop ([#1896](https://github.com/digdir/designsystemet/issues/1896)) ([59dd231](https://github.com/digdir/designsystemet/commit/59dd2310cfb7c91e4be20a54412e036a8a268b73)) - **Button:** Remove deprecated `as` prop ([#1894](https://github.com/digdir/designsystemet/issues/1894)) ([c346203](https://github.com/digdir/designsystemet/commit/c34620344be7d25c4c73b147b136254b177cab2c)) - **Card:** Remove deprecated `as` prop ([#1897](https://github.com/digdir/designsystemet/issues/1897)) ([17ef8c6](https://github.com/digdir/designsystemet/commit/17ef8c64c3ad07d8a0bf036a032c1a5bd62a4233)) -- **DropdownItem:** Remove deprecated `as` prop ([#1895](https://github.com/digdir/designsystemet/issues/1895)) ([2f705bb](https://github.com/digdir/designsystemet/commit/2f705bbe063317fd6f21d340ab222e966d912d7e)) +- **DropdownMenuItem:** Remove deprecated `as` prop ([#1895](https://github.com/digdir/designsystemet/issues/1895)) ([2f705bb](https://github.com/digdir/designsystemet/commit/2f705bbe063317fd6f21d340ab222e966d912d7e)) - **ErrorMessage:** Remove deprecated `as` prop ([#1899](https://github.com/digdir/designsystemet/issues/1899)) ([b37584a](https://github.com/digdir/designsystemet/commit/b37584af07b2b8a4f536b5931213227fd1fc1c9d)) - **Heading:** Remove deprecated `as` prop ([#1900](https://github.com/digdir/designsystemet/issues/1900)) ([685e438](https://github.com/digdir/designsystemet/commit/685e43897775276c331b2cf548f70fa508806d9e)) - **Ingress:** Remove deprecated `as` prop ([#1903](https://github.com/digdir/designsystemet/issues/1903)) ([1139b31](https://github.com/digdir/designsystemet/commit/1139b312defa3411e9133c949dc19d48011fa74d)) @@ -529,7 +529,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ### Bug Fixes -- **DropdownItem:** add list style none ([#1190](https://github.com/digdir/designsystemet/issues/1190)) ([11bd19b](https://github.com/digdir/designsystemet/commit/11bd19bfb6ac76b2c697a22e876117c4128be3bd)) +- **DropdownMenuItem:** add list style none ([#1190](https://github.com/digdir/designsystemet/issues/1190)) ([11bd19b](https://github.com/digdir/designsystemet/commit/11bd19bfb6ac76b2c697a22e876117c4128be3bd)) - **List:** Wrap in `div` to allow access to `Heading` ([#1217](https://github.com/digdir/designsystemet/issues/1217)) ([afcadb7](https://github.com/digdir/designsystemet/commit/afcadb7c4cb4b368d247af0c41ed8debf53c4b66)) - **Pagination:** Only use needed space for buttons ([#1220](https://github.com/digdir/designsystemet/issues/1220)) ([4bf3d74](https://github.com/digdir/designsystemet/commit/4bf3d745888f500259df5aadf4edee97ec4f95bc)) - **Select:** Select not working properly in Modal ([#1195](https://github.com/digdir/designsystemet/issues/1195)) ([fb8be6a](https://github.com/digdir/designsystemet/commit/fb8be6a647ba0da8b5b23e65813508f34e09c8c1)) From f0ccd07694400cf68f8c25297bc031dc4dd92755 Mon Sep 17 00:00:00 2001 From: barsnes Date: Wed, 18 Sep 2024 12:03:27 +0200 Subject: [PATCH 27/27] misc --- packages/react/src/components/Dropdown/DropdownItem.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/react/src/components/Dropdown/DropdownItem.tsx b/packages/react/src/components/Dropdown/DropdownItem.tsx index 64103040f1..a3b41d6e60 100644 --- a/packages/react/src/components/Dropdown/DropdownItem.tsx +++ b/packages/react/src/components/Dropdown/DropdownItem.tsx @@ -8,7 +8,7 @@ import { DropdownCtx } from './Dropdown'; export type DropdownItemProps = Omit; export const DropdownItem = forwardRef( - function DropdownItem({ children, className, style, ...rest }, ref) { + function DropdownItem({ className, style, ...rest }, ref) { const { size } = useContext(DropdownCtx); return ( @@ -19,9 +19,7 @@ export const DropdownItem = forwardRef( size={size} className='ds-dropdown__item' {...rest} - > - {children} - + /> ); },