From 7458c0b34cacbb8220264630e9bb7279d8c701f5 Mon Sep 17 00:00:00 2001 From: Ken Date: Thu, 11 Jan 2024 15:58:15 +0100 Subject: [PATCH 01/15] :lipstick: Synket prettier --- .../v2.0.0/update-js-tokens/update-js-tokens.ts | 8 ++++---- @navikt/aksel/src/codemod/utils/check.ts | 8 ++++---- .../core/react/src/accordion/AccordionItem.tsx | 4 ++-- @navikt/core/react/src/dropdown/Toggle.tsx | 2 +- @navikt/core/react/src/tooltip/Tooltip.tsx | 16 ++++++++-------- .../core/react/src/util/hooks/useCallbackRef.ts | 2 +- .../react/src/util/hooks/useControllableState.ts | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/@navikt/aksel/src/codemod/transforms/v2.0.0/update-js-tokens/update-js-tokens.ts b/@navikt/aksel/src/codemod/transforms/v2.0.0/update-js-tokens/update-js-tokens.ts index 4953796ac5b..b6fb03f1bd1 100644 --- a/@navikt/aksel/src/codemod/transforms/v2.0.0/update-js-tokens/update-js-tokens.ts +++ b/@navikt/aksel/src/codemod/transforms/v2.0.0/update-js-tokens/update-js-tokens.ts @@ -21,7 +21,7 @@ export default function transformer(file: JSCodeshift, api) { const jsImport = root.find(j.ImportDeclaration).filter((x) => { return ["@navikt/ds-tokens/dist/tokens"].includes( - x.node.source.value as string + x.node.source.value as string, ); }); @@ -36,7 +36,7 @@ export default function transformer(file: JSCodeshift, api) { let foundName: string = ""; getImportSpecifier(j, root, name, "@navikt/ds-tokens/dist/tokens").forEach( - (x) => (foundName = x.node.imported.name) + (x) => (foundName = x.node.imported.name), ); if (name === foundName) { @@ -45,7 +45,7 @@ export default function transformer(file: JSCodeshift, api) { j, root, name, - "@navikt/ds-tokens/dist/tokens" + "@navikt/ds-tokens/dist/tokens", ) || name; renameImportSpecifier( @@ -53,7 +53,7 @@ export default function transformer(file: JSCodeshift, api) { root, name, out, - "@navikt/ds-tokens/dist/tokens" + "@navikt/ds-tokens/dist/tokens", ); let code = root.toSource(getLineTerminator(file.source)); diff --git a/@navikt/aksel/src/codemod/utils/check.ts b/@navikt/aksel/src/codemod/utils/check.ts index 4149f2b8f57..213988fcac6 100644 --- a/@navikt/aksel/src/codemod/utils/check.ts +++ b/@navikt/aksel/src/codemod/utils/check.ts @@ -12,7 +12,7 @@ interface TestT { export function check( dirName: string, - { fixture, migration, extension = "js", options = {} }: TestT + { fixture, migration, extension = "js", options = {} }: TestT, ) { describe(migration, () => { it(fixture, async () => { @@ -22,7 +22,7 @@ export function check( const source = fs.readFileSync(inputPath, "utf8"); const expected = fs.readFileSync( path.join(fixtureDir, `${fixture}.output.${extension}`), - "utf8" + "utf8", ); // Assumes transform is one level up from tests directory const module = await import(path.join(dirName, "..", migration)); @@ -34,11 +34,11 @@ export function check( expect( await prettier.format(output, { parser: parser === "js" ? "typescript" : parser, - }) + }), ).toBe( await prettier.format(expected, { parser: parser === "js" ? "typescript" : parser, - }) + }), ); }); }); diff --git a/@navikt/core/react/src/accordion/AccordionItem.tsx b/@navikt/core/react/src/accordion/AccordionItem.tsx index 1d613097b49..e049a9fa32e 100644 --- a/@navikt/core/react/src/accordion/AccordionItem.tsx +++ b/@navikt/core/react/src/accordion/AccordionItem.tsx @@ -38,7 +38,7 @@ export const AccordionItemContext = const AccordionItem = forwardRef( ( { children, className, open, defaultOpen = false, onOpenChange, ...rest }, - ref + ref, ) => { const [_open, _setOpen] = useControllableState({ defaultValue: defaultOpen, @@ -78,7 +78,7 @@ const AccordionItem = forwardRef( ); - } + }, ); export default AccordionItem; diff --git a/@navikt/core/react/src/dropdown/Toggle.tsx b/@navikt/core/react/src/dropdown/Toggle.tsx index effdcea9c69..9ab8c3c2243 100644 --- a/@navikt/core/react/src/dropdown/Toggle.tsx +++ b/@navikt/core/react/src/dropdown/Toggle.tsx @@ -42,7 +42,7 @@ export const Toggle = forwardRef( className={cl("navds-dropdown__toggle", className)} /> ); - } + }, ); export default Toggle; diff --git a/@navikt/core/react/src/tooltip/Tooltip.tsx b/@navikt/core/react/src/tooltip/Tooltip.tsx index 737315b7674..19034b7d161 100644 --- a/@navikt/core/react/src/tooltip/Tooltip.tsx +++ b/@navikt/core/react/src/tooltip/Tooltip.tsx @@ -113,7 +113,7 @@ export const Tooltip = forwardRef( maxChar = 80, ...rest }, - ref + ref, ) => { const [_open, _setOpen] = useControllableState({ defaultValue: defaultOpen, @@ -168,11 +168,11 @@ export const Tooltip = forwardRef( const mergedRef = useMemo( () => mergeRefs([ref, refs.setFloating]), - [refs.setFloating, ref] + [refs.setFloating, ref], ); const childMergedRef = useMemo( () => mergeRefs([(children as any).ref, refs.setReference]), - [children, refs.setReference] + [children, refs.setReference], ); if ( @@ -181,7 +181,7 @@ export const Tooltip = forwardRef( (children as any) === React.Fragment ) { console.error( - " children needs to be a single ReactElement and not: | <>" + " children needs to be a single ReactElement and not: | <>", ); return null; } @@ -189,7 +189,7 @@ export const Tooltip = forwardRef( if (content?.length > maxChar) { _open && console.warn( - `Because of strict accessibility concers we encourage all Tooltips to have less than 80 characters. Can be overwritten with the maxChar-prop\n\nLength:${content.length}\nTooltip-content: ${content}` + `Because of strict accessibility concers we encourage all Tooltips to have less than 80 characters. Can be overwritten with the maxChar-prop\n\nLength:${content.length}\nTooltip-content: ${content}`, ); } @@ -203,7 +203,7 @@ export const Tooltip = forwardRef( "aria-describedby": _open ? cl(ariaId, children?.props["aria-describedby"]) : children?.props["aria-describedby"], - }) + }), )} {_open && ( @@ -222,7 +222,7 @@ export const Tooltip = forwardRef( className: cl( "navds-tooltip", "navds-detail navds-detail--small", - className + className, ), })} data-side={placement} @@ -267,7 +267,7 @@ export const Tooltip = forwardRef( ); - } + }, ); export default Tooltip; diff --git a/@navikt/core/react/src/util/hooks/useCallbackRef.ts b/@navikt/core/react/src/util/hooks/useCallbackRef.ts index 09c878a05f9..29f9aaff879 100644 --- a/@navikt/core/react/src/util/hooks/useCallbackRef.ts +++ b/@navikt/core/react/src/util/hooks/useCallbackRef.ts @@ -7,7 +7,7 @@ import { useCallback, useEffect, useRef } from "react"; */ export function useCallbackRef any>( callback: T | undefined, - deps: React.DependencyList = [] + deps: React.DependencyList = [], ) { const callbackRef = useRef(callback); diff --git a/@navikt/core/react/src/util/hooks/useControllableState.ts b/@navikt/core/react/src/util/hooks/useControllableState.ts index e4903e04464..41dc5fbd18a 100644 --- a/@navikt/core/react/src/util/hooks/useControllableState.ts +++ b/@navikt/core/react/src/util/hooks/useControllableState.ts @@ -33,7 +33,7 @@ export function useControllableState({ onChangeProp(nextValue); }, - [controlled, onChangeProp, value] + [controlled, onChangeProp, value], ); return [value, setValue] as const; From 2268009ac410ce1a08097c5dfbbbacb287bdf241 Mon Sep 17 00:00:00 2001 From: Ken Date: Thu, 11 Jan 2024 16:09:01 +0100 Subject: [PATCH 02/15] :sparkles: ComposeEventHandlers --- .../react/src/accordion/AccordionHeader.tsx | 6 ++--- .../react/src/util/composeEventHandlers.ts | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 @navikt/core/react/src/util/composeEventHandlers.ts diff --git a/@navikt/core/react/src/accordion/AccordionHeader.tsx b/@navikt/core/react/src/accordion/AccordionHeader.tsx index dff62d7ebff..08f91d6d911 100644 --- a/@navikt/core/react/src/accordion/AccordionHeader.tsx +++ b/@navikt/core/react/src/accordion/AccordionHeader.tsx @@ -2,6 +2,7 @@ import cl from "clsx"; import React, { forwardRef, useContext } from "react"; import { ChevronDownIcon } from "@navikt/aksel-icons"; import { Heading } from "../typography"; +import { composeEventHandlers } from "../util/composeEventHandlers"; import { AccordionContext } from "./AccordionContext"; import { AccordionItemContext } from "./AccordionItem"; @@ -30,10 +31,7 @@ const AccordionHeader = forwardRef( ref={ref} {...rest} className={cl("navds-accordion__header", className)} - onClick={(e: React.MouseEvent) => { - itemContext.toggleOpen(); - onClick && onClick(e); - }} + onClick={composeEventHandlers(onClick, itemContext.toggleOpen)} aria-expanded={itemContext.open} type="button" > diff --git a/@navikt/core/react/src/util/composeEventHandlers.ts b/@navikt/core/react/src/util/composeEventHandlers.ts new file mode 100644 index 00000000000..2eae43af53e --- /dev/null +++ b/@navikt/core/react/src/util/composeEventHandlers.ts @@ -0,0 +1,22 @@ +/* https://github.com/radix-ui/primitives/blob/main/packages/core/primitive/src/primitive.tsx#L1 */ + +/** + * Utility to consistently call original eventhandler, often from props and internal eventhandler + * @internal + */ +export function composeEventHandlers( + originalEventHandler?: (event: T) => void, + ourEventHandler?: (event: T) => void, + { checkForDefaultPrevented = true } = {}, +) { + return function handleEvent(event: T) { + originalEventHandler?.(event); + + if ( + checkForDefaultPrevented === false || + !(event as unknown as Event).defaultPrevented + ) { + return ourEventHandler?.(event); + } + }; +} From 2c14b8f805be44ccc446a8eddefb2436893deefd Mon Sep 17 00:00:00 2001 From: Ken Date: Thu, 11 Jan 2024 16:10:56 +0100 Subject: [PATCH 03/15] :recycle: Button composeEvent --- @navikt/core/react/src/button/Button.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/@navikt/core/react/src/button/Button.tsx b/@navikt/core/react/src/button/Button.tsx index 9d964f44f66..1a7eb52f26a 100644 --- a/@navikt/core/react/src/button/Button.tsx +++ b/@navikt/core/react/src/button/Button.tsx @@ -8,6 +8,7 @@ import { omit, useClientLayoutEffect, } from "../util"; +import { composeEventHandlers } from "../util/composeEventHandlers"; export interface ButtonProps extends React.ButtonHTMLAttributes { @@ -103,22 +104,18 @@ export const Button: OverridableComponent = const filterProps: React.ButtonHTMLAttributes = disabled ?? widthOverride ? omit(rest, ["href"]) : rest; + const handleKeyUp = (e: React.KeyboardEvent) => { + if (e.key === " " && !disabled && !widthOverride) { + e.currentTarget.click(); + } + }; + return ( ) => { - filterProps.onKeyUp?.(e); - if ( - e.key === " " && - !disabled && - !widthOverride && - !e.isDefaultPrevented() - ) { - e.currentTarget.click(); - } - }} + onKeyUp={composeEventHandlers(filterProps.onKeyUp, handleKeyUp)} className={cl( className, "navds-button", From 54718221d6a9349928858edd98943c499f6e561e Mon Sep 17 00:00:00 2001 From: Ken Date: Thu, 11 Jan 2024 16:12:20 +0100 Subject: [PATCH 04/15] :recycle: chips composeEvent --- @navikt/core/react/src/chips/Removable.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/@navikt/core/react/src/chips/Removable.tsx b/@navikt/core/react/src/chips/Removable.tsx index 1221f1d76ea..19cbf99a157 100644 --- a/@navikt/core/react/src/chips/Removable.tsx +++ b/@navikt/core/react/src/chips/Removable.tsx @@ -1,6 +1,7 @@ import cl from "clsx"; import React, { forwardRef } from "react"; import { XMarkIcon } from "@navikt/aksel-icons"; +import { composeEventHandlers } from "../util/composeEventHandlers"; export interface RemovableChipsProps extends React.ButtonHTMLAttributes { @@ -33,6 +34,7 @@ export const RemovableChips = forwardRef< removeLabel = "slett", onDelete, type = "button", + onClick, ...rest }, ref, @@ -48,10 +50,7 @@ export const RemovableChips = forwardRef< `navds-chips__removable--${variant}`, )} aria-label={`${children} ${removeLabel}`} - onClick={(e) => { - onDelete?.(); - rest?.onClick?.(e); - }} + onClick={composeEventHandlers(onClick, onDelete)} > {children} From fc08729d25cf857cc8d103e5c5feb069d8f5ecc9 Mon Sep 17 00:00:00 2001 From: Ken Date: Thu, 11 Jan 2024 16:13:24 +0100 Subject: [PATCH 05/15] :recycle: copybutton composeEvent --- @navikt/core/react/src/copybutton/CopyButton.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/@navikt/core/react/src/copybutton/CopyButton.tsx b/@navikt/core/react/src/copybutton/CopyButton.tsx index a4fe983bf4a..a82b11b4593 100644 --- a/@navikt/core/react/src/copybutton/CopyButton.tsx +++ b/@navikt/core/react/src/copybutton/CopyButton.tsx @@ -8,6 +8,7 @@ import React, { } from "react"; import { CheckmarkIcon, FilesIcon } from "@navikt/aksel-icons"; import { Label } from "../typography"; +import { composeEventHandlers } from "../util/composeEventHandlers"; import copy from "../util/copy"; export interface CopyButtonProps @@ -97,6 +98,7 @@ export const CopyButton = forwardRef( title = "Kopier", activeTitle = "Kopiert", iconPosition = "left", + onClick, ...rest }, ref, @@ -110,14 +112,11 @@ export const CopyButton = forwardRef( }; }, []); - const handleClick = ( - event: React.MouseEvent, - ) => { + const handleClick = () => { timeoutRef.current && clearTimeout(timeoutRef.current); copy(copyText); setActive(true); onActiveChange?.(true); - rest.onClick?.(event); timeoutRef.current = window.setTimeout(() => { setActive(false); @@ -160,7 +159,7 @@ export const CopyButton = forwardRef( "navds-copybutton--active": active, }, )} - onClick={handleClick} + onClick={composeEventHandlers(onClick, handleClick)} > {iconPosition === "left" && copyIcon} From 58062a3d431cbb25fcdb84bc74439544021c08a3 Mon Sep 17 00:00:00 2001 From: Ken Date: Thu, 11 Jan 2024 16:32:04 +0100 Subject: [PATCH 06/15] :recycle: composeEventhandlers dropdown --- .../dropdown/Menu/GroupedList/GroupedItem.tsx | 44 +++++++++---------- .../react/src/dropdown/Menu/List/Item.tsx | 44 +++++++++---------- @navikt/core/react/src/dropdown/Toggle.tsx | 14 +++--- 3 files changed, 53 insertions(+), 49 deletions(-) diff --git a/@navikt/core/react/src/dropdown/Menu/GroupedList/GroupedItem.tsx b/@navikt/core/react/src/dropdown/Menu/GroupedList/GroupedItem.tsx index 5969a6fcab3..ff3bdf6f02c 100644 --- a/@navikt/core/react/src/dropdown/Menu/GroupedList/GroupedItem.tsx +++ b/@navikt/core/react/src/dropdown/Menu/GroupedList/GroupedItem.tsx @@ -1,6 +1,7 @@ import cl from "clsx"; import React, { forwardRef, useContext } from "react"; import { OverridableComponent } from "../../../util"; +import { composeEventHandlers } from "../../../util/composeEventHandlers"; import { DropdownContext } from "../../context"; export interface GroupedItemProps @@ -14,28 +15,27 @@ export interface GroupedItemProps export const GroupedItem: OverridableComponent< GroupedItemProps, HTMLButtonElement -> = forwardRef(({ as: Component = "button", className, ...rest }, ref) => { - const context = useContext(DropdownContext); +> = forwardRef( + ({ as: Component = "button", className, onClick, ...rest }, ref) => { + const context = useContext(DropdownContext); - return ( -
- ) => { - context?.onSelect?.(event); - rest?.onClick?.(event); - }} - ref={ref} - className={cl( - "navds-dropdown__item", - "navds-body-short", - "navds-body-short--small", - className, - )} - /> -
- ); -}); + return ( +
+ +
+ ); + }, +); export default GroupedItem; diff --git a/@navikt/core/react/src/dropdown/Menu/List/Item.tsx b/@navikt/core/react/src/dropdown/Menu/List/Item.tsx index 9b4c61bbec0..5139ad293c1 100644 --- a/@navikt/core/react/src/dropdown/Menu/List/Item.tsx +++ b/@navikt/core/react/src/dropdown/Menu/List/Item.tsx @@ -1,6 +1,7 @@ import cl from "clsx"; import React, { forwardRef, useContext } from "react"; import { OverridableComponent } from "../../../util"; +import { composeEventHandlers } from "../../../util/composeEventHandlers"; import { DropdownContext } from "../../context"; export interface ListItemProps extends React.ButtonHTMLAttributes { @@ -11,28 +12,27 @@ export interface ListItemProps extends React.ButtonHTMLAttributes { } export const ListItem: OverridableComponent = - forwardRef(({ as: Component = "button", className, ...rest }, ref) => { - const context = useContext(DropdownContext); + forwardRef( + ({ as: Component = "button", className, onClick, ...rest }, ref) => { + const context = useContext(DropdownContext); - return ( -
  • - ) => { - context?.onSelect?.(event); - rest?.onClick?.(event); - }} - ref={ref} - className={cl( - "navds-dropdown__item", - "navds-body-short", - "navds-body-short--small", - className, - )} - /> -
  • - ); - }); + return ( +
  • + +
  • + ); + }, + ); export default ListItem; diff --git a/@navikt/core/react/src/dropdown/Toggle.tsx b/@navikt/core/react/src/dropdown/Toggle.tsx index 9ab8c3c2243..fcf73b0a3e1 100644 --- a/@navikt/core/react/src/dropdown/Toggle.tsx +++ b/@navikt/core/react/src/dropdown/Toggle.tsx @@ -1,5 +1,6 @@ import cl from "clsx"; import React, { forwardRef, useContext } from "react"; +import { composeEventHandlers } from "../util/composeEventHandlers"; import { DropdownContext } from "./context"; export interface ToggleProps @@ -21,6 +22,13 @@ export const Toggle = forwardRef( const { setAnchorEl, handleToggle, isOpen } = context; + const handleClick = ( + e: React.MouseEvent, + ) => { + setAnchorEl(e.currentTarget); + handleToggle(!isOpen); + }; + return (