From 3a944765e0256de7d3f7060280edc1f525705230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Nordstr=C3=B6m?= <totte.nordstrom@gmail.com> Date: Mon, 2 Dec 2024 11:35:02 +0100 Subject: [PATCH 1/3] feat(hooks): add package --- apps/storybook/stories/Hooks.mdx | 9 ---- package-lock.json | 15 +++++++ packages/hooks/CHANGELOG.md | 0 packages/hooks/README.md | 19 ++++++++ packages/hooks/index.ts | 4 ++ packages/hooks/package.json | 43 +++++++++++++++++++ .../useWindowResize}/useWindowResize.test.tsx | 2 +- .../src/useWindowResize}/useWindowResize.tsx | 9 ++-- packages/hooks/stories/Changelog.mdx | 6 +++ packages/hooks/stories/Readme.mdx | 6 +++ packages/modal/src/Modal.tsx | 2 +- 11 files changed, 100 insertions(+), 15 deletions(-) delete mode 100644 apps/storybook/stories/Hooks.mdx create mode 100644 packages/hooks/CHANGELOG.md create mode 100644 packages/hooks/README.md create mode 100644 packages/hooks/index.ts create mode 100644 packages/hooks/package.json rename packages/{modal/src => hooks/src/useWindowResize}/useWindowResize.test.tsx (97%) rename packages/{modal/src => hooks/src/useWindowResize}/useWindowResize.tsx (82%) create mode 100644 packages/hooks/stories/Changelog.mdx create mode 100644 packages/hooks/stories/Readme.mdx diff --git a/apps/storybook/stories/Hooks.mdx b/apps/storybook/stories/Hooks.mdx deleted file mode 100644 index f25405738..000000000 --- a/apps/storybook/stories/Hooks.mdx +++ /dev/null @@ -1,9 +0,0 @@ -import { Meta } from "@storybook/blocks"; - -<Meta title="Utils/Hooks" /> - -# Utils - -## React Hooks - -We use the external library [usehooks-ts](https://usehooks-ts.com/) for Hooks. diff --git a/package-lock.json b/package-lock.json index 242adaf9e..c07dee677 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11318,6 +11318,10 @@ "resolved": "packages/header", "link": true }, + "node_modules/@sikt/sds-hooks": { + "resolved": "packages/hooks", + "link": true + }, "node_modules/@sikt/sds-icons": { "resolved": "packages/icons", "link": true @@ -49602,6 +49606,17 @@ "react-dom": "^18.0.0 || ^19.0.0" } }, + "packages/hooks": { + "name": "@sikt/sds-hooks", + "version": "0.1.0", + "license": "UNLICENSED", + "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, "packages/icons": { "name": "@sikt/sds-icons", "version": "3.0.0", diff --git a/packages/hooks/CHANGELOG.md b/packages/hooks/CHANGELOG.md new file mode 100644 index 000000000..e69de29bb diff --git a/packages/hooks/README.md b/packages/hooks/README.md new file mode 100644 index 000000000..fac5af94e --- /dev/null +++ b/packages/hooks/README.md @@ -0,0 +1,19 @@ +# `@sikt/sds-hooks` + +## Consume + +```sh +npm i -s @sikt/sds-hooks +``` + +### React + +```js +import { use<Hook> } from "@sikt/sds-hooks"; + +use<Hook>(); +``` + +### Custom hooks + +We use [usehooks-ts](https://usehooks-ts.com/) for external hooks. diff --git a/packages/hooks/index.ts b/packages/hooks/index.ts new file mode 100644 index 000000000..f65bcd4b7 --- /dev/null +++ b/packages/hooks/index.ts @@ -0,0 +1,4 @@ +export { + useWindowResize, + type useWindowResizeOptions, +} from "./src/useWindowResize/useWindowResize"; diff --git a/packages/hooks/package.json b/packages/hooks/package.json new file mode 100644 index 000000000..b6d2af153 --- /dev/null +++ b/packages/hooks/package.json @@ -0,0 +1,43 @@ +{ + "name": "@sikt/sds-hooks", + "version": "0.1.0", + "license": "UNLICENSED", + "description": "React Hooks, Sikt component library", + "homepage": "https://designsystem.sikt.no/", + "repository": { + "type": "git", + "url": "git+https://gitlab.sikt.no/designsystem/sds-komponentbibliotek.git" + }, + "type": "commonjs", + "main": "dist/index.js", + "module": "dist/index.mjs", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "./dist/*": "./dist/*" + }, + "types": "dist/index.d.ts", + "files": [ + "CHANGELOG.md", + "dist", + "README.md" + ], + "scripts": { + "build": "tsup" + }, + "dependencies": {}, + "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } +} diff --git a/packages/modal/src/useWindowResize.test.tsx b/packages/hooks/src/useWindowResize/useWindowResize.test.tsx similarity index 97% rename from packages/modal/src/useWindowResize.test.tsx rename to packages/hooks/src/useWindowResize/useWindowResize.test.tsx index aa93e663b..26130ecbc 100644 --- a/packages/modal/src/useWindowResize.test.tsx +++ b/packages/hooks/src/useWindowResize/useWindowResize.test.tsx @@ -1,5 +1,5 @@ import { act, renderHook } from "@testing-library/react"; -import useWindowResize from "./useWindowResize"; +import { useWindowResize } from "./useWindowResize"; describe("useWindowResize", () => { beforeEach(() => { diff --git a/packages/modal/src/useWindowResize.tsx b/packages/hooks/src/useWindowResize/useWindowResize.tsx similarity index 82% rename from packages/modal/src/useWindowResize.tsx rename to packages/hooks/src/useWindowResize/useWindowResize.tsx index cf36c2083..3358980f2 100644 --- a/packages/modal/src/useWindowResize.tsx +++ b/packages/hooks/src/useWindowResize/useWindowResize.tsx @@ -1,10 +1,13 @@ import { useEffect } from "react"; -interface Options { +export interface useWindowResizeOptions { throttleTime?: number; } -const useWindowResize = (callback: () => void, options?: Options) => { +export const useWindowResize = ( + callback: () => void, + options?: useWindowResizeOptions, +) => { const { throttleTime = 200 } = options ?? {}; useEffect(() => { @@ -29,5 +32,3 @@ const useWindowResize = (callback: () => void, options?: Options) => { }; }, [callback, throttleTime]); }; - -export default useWindowResize; diff --git a/packages/hooks/stories/Changelog.mdx b/packages/hooks/stories/Changelog.mdx new file mode 100644 index 000000000..1e4bb1f31 --- /dev/null +++ b/packages/hooks/stories/Changelog.mdx @@ -0,0 +1,6 @@ +import { Markdown, Meta } from "@storybook/blocks"; +import Changelog from "../CHANGELOG.md?raw"; + +<Meta title="Utils/Hooks/Changelog" /> + +<Markdown>{Changelog}</Markdown> diff --git a/packages/hooks/stories/Readme.mdx b/packages/hooks/stories/Readme.mdx new file mode 100644 index 000000000..d6b2f5da6 --- /dev/null +++ b/packages/hooks/stories/Readme.mdx @@ -0,0 +1,6 @@ +import { Markdown, Meta } from "@storybook/blocks"; +import Readme from "../README.md?raw"; + +<Meta title="Utils/Hooks/Readme" /> + +<Markdown>{Readme}</Markdown> diff --git a/packages/modal/src/Modal.tsx b/packages/modal/src/Modal.tsx index a9432bbb5..74b6522b5 100644 --- a/packages/modal/src/Modal.tsx +++ b/packages/modal/src/Modal.tsx @@ -4,8 +4,8 @@ import { XIcon } from "@sikt/sds-icons"; import { clsx } from "clsx/lite"; import { ReactNode, useEffect, useId, useRef, useState } from "react"; import ReactModal from "react-modal"; -import useWindowResize from "./useWindowResize"; import "./modal.pcss"; +import useWindowResize from "./useWindowResize"; export interface ModalProps { /** -- GitLab From 4424d1308de09f4646685f0aa7ba0c9440db4c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Nordstr=C3=B6m?= <totte.nordstrom@gmail.com> Date: Tue, 3 Dec 2024 14:49:41 +0100 Subject: [PATCH 2/3] refactor(modal): use hooks package --- package-lock.json | 1 + packages/modal/package.json | 1 + packages/modal/src/Modal.tsx | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index c07dee677..09fcf4a74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49834,6 +49834,7 @@ "dependencies": { "@sikt/sds-button": "^4.0.1", "@sikt/sds-core": "^4.1.1", + "@sikt/sds-hooks": "^0.1.0", "@sikt/sds-icons": "^2.0.2", "react-modal": "^3.16.1" }, diff --git a/packages/modal/package.json b/packages/modal/package.json index 2dec7d59c..bf54fbe7b 100644 --- a/packages/modal/package.json +++ b/packages/modal/package.json @@ -37,6 +37,7 @@ "dependencies": { "@sikt/sds-button": "^4.0.1", "@sikt/sds-core": "^4.1.1", + "@sikt/sds-hooks": "^0.1.0", "@sikt/sds-icons": "^2.0.2", "react-modal": "^3.16.1" }, diff --git a/packages/modal/src/Modal.tsx b/packages/modal/src/Modal.tsx index 74b6522b5..026ae04ff 100644 --- a/packages/modal/src/Modal.tsx +++ b/packages/modal/src/Modal.tsx @@ -1,11 +1,11 @@ import { Button } from "@sikt/sds-button"; import { Heading1, Paragraph } from "@sikt/sds-core"; +import { useWindowResize } from "@sikt/sds-hooks"; import { XIcon } from "@sikt/sds-icons"; import { clsx } from "clsx/lite"; import { ReactNode, useEffect, useId, useRef, useState } from "react"; import ReactModal from "react-modal"; import "./modal.pcss"; -import useWindowResize from "./useWindowResize"; export interface ModalProps { /** -- GitLab From 11f8ad96422c94f8bafd3b32be336a2f6ec50188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Nordstr=C3=B6m?= <totte.nordstrom@gmail.com> Date: Sun, 1 Dec 2024 13:29:11 +0100 Subject: [PATCH 3/3] fix(popover): anchored target follows trigger on layout shift or window resize --- package-lock.json | 3 ++- packages/popover/package.json | 3 ++- packages/popover/src/Popover.tsx | 27 ++++++++++++++++++++------- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 09fcf4a74..8c24b4bb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49910,7 +49910,8 @@ "version": "1.0.1", "license": "UNLICENSED", "dependencies": { - "@sikt/sds-core": "^4.1.1" + "@sikt/sds-core": "^4.1.1", + "@sikt/sds-hooks": "^0.1.0" }, "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", diff --git a/packages/popover/package.json b/packages/popover/package.json index 6a95829df..a427d647d 100644 --- a/packages/popover/package.json +++ b/packages/popover/package.json @@ -35,7 +35,8 @@ "build": "tsup" }, "dependencies": { - "@sikt/sds-core": "^4.1.1" + "@sikt/sds-core": "^4.1.1", + "@sikt/sds-hooks": "^0.1.0" }, "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", diff --git a/packages/popover/src/Popover.tsx b/packages/popover/src/Popover.tsx index 4c1eb6639..a99095337 100644 --- a/packages/popover/src/Popover.tsx +++ b/packages/popover/src/Popover.tsx @@ -1,5 +1,13 @@ +import { useWindowResize } from "@sikt/sds-hooks"; import { clsx } from "clsx/lite"; -import { HTMLAttributes, MouseEvent, ReactNode, useId, useState } from "react"; +import { + HTMLAttributes, + MouseEvent, + ReactNode, + useId, + useRef, + useState, +} from "react"; import "./popover.pcss"; export interface PopoverProps extends HTMLAttributes<HTMLButtonElement> { @@ -21,28 +29,33 @@ export const Popover = ({ ...rest }: PopoverProps) => { const id = useId(); + const buttonRef = useRef<HTMLButtonElement | null>(null); const [top, setTop] = useState(0); const [left, setLeft] = useState(0); const popovertargetAttr = { popovertarget: id }; const popoverAttr = { popover }; // TODO: Replace with https://developer.mozilla.org/en-US/docs/Web/CSS/position-anchor when good browser support - const setPopoverStylePosition = (event: MouseEvent<HTMLButtonElement>) => { - const target = event.target as HTMLButtonElement; - const bounding = target.getBoundingClientRect(); - setTop(bounding.top + bounding.height + window.scrollY); - setLeft(bounding.left); + const setPopoverStylePosition = () => { + if (buttonRef.current) { + const bounding = buttonRef.current.getBoundingClientRect(); + setTop(bounding.top + bounding.height + window.scrollY); + setLeft(bounding.left); + } }; + anchor && useWindowResize(setPopoverStylePosition, { throttleTime: 10 }); + return ( <> <button + ref={buttonRef} className={clsx("sds-popover", className)} // INFO: This is a hack to solve that React/TypeScript does not support this native attribute {...popovertargetAttr} {...rest} onClick={(event) => { - anchor && setPopoverStylePosition(event); + anchor && setPopoverStylePosition(); if (onClick) { onClick(event); } -- GitLab