diff --git a/packages/react/package.json b/packages/react/package.json index f924d4d472..cdf1abb847 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -47,6 +47,7 @@ "@navikt/aksel-icons": "^6.14.0", "@radix-ui/react-slot": "^1.1.0", "@tanstack/react-virtual": "^3.10.7", + "@u-elements/u-datalist": "^0.0.9", "@u-elements/u-details": "^0.1.0", "clsx": "^2.1.1" }, diff --git a/packages/react/src/components/Suggestion/Suggestion.mdx b/packages/react/src/components/Suggestion/Suggestion.mdx new file mode 100644 index 0000000000..3b6ffb8814 --- /dev/null +++ b/packages/react/src/components/Suggestion/Suggestion.mdx @@ -0,0 +1,11 @@ +import { Meta, Canvas, Controls, Primary } from '@storybook/blocks'; +import * as SuggestionStories from './Suggestion.stories'; + + + +# ToggleGroup + +Med `ToggleGroup` kan brukerne velge alternativer som påvirker innholdet på en side. Komponenten består av en gruppe knapper som henger sammen, der kun én knapp er mulig å velge om gangen. + + + diff --git a/packages/react/src/components/Suggestion/Suggestion.stories.tsx b/packages/react/src/components/Suggestion/Suggestion.stories.tsx new file mode 100644 index 0000000000..dbced23abd --- /dev/null +++ b/packages/react/src/components/Suggestion/Suggestion.stories.tsx @@ -0,0 +1,10 @@ +import type { Meta, StoryFn } from '@storybook/react'; +import { Suggestion } from './Suggestion'; +export default { + title: 'Komponenter/Suggestion', + component: Suggestion, +} as Meta; + +export const Preview: StoryFn = (args) => { + return ; +}; diff --git a/packages/react/src/components/Suggestion/Suggestion.tsx b/packages/react/src/components/Suggestion/Suggestion.tsx new file mode 100644 index 0000000000..9433179437 --- /dev/null +++ b/packages/react/src/components/Suggestion/Suggestion.tsx @@ -0,0 +1,32 @@ +import { useMergeRefs } from '@floating-ui/react'; +import cl from 'clsx/lite'; +import { createContext, forwardRef, useId, useRef, useState } from 'react'; +import type { DefaultProps } from '../../types'; + +type SuggestionContextType = { + listId?: string; + setListId?: (id: string) => void; +}; + +export const SuggestionContext = createContext({}); + +export type SuggestionProps = + DefaultProps & {} & React.HTMLAttributes; + +export const Suggestion = forwardRef( + function Suggestion({ className, ...rest }, ref) { + const [listId, setListId] = useState(useId()); + const innerRef = useRef(null); + const mergedRefs = useMergeRefs([innerRef, ref]); + + return ( + +
+ + ); + }, +); diff --git a/packages/react/src/components/Suggestion/SuggestionEmpty.tsx b/packages/react/src/components/Suggestion/SuggestionEmpty.tsx new file mode 100644 index 0000000000..697401d9db --- /dev/null +++ b/packages/react/src/components/Suggestion/SuggestionEmpty.tsx @@ -0,0 +1,19 @@ +import type { HTMLAttributes } from 'react'; +import { forwardRef } from 'react'; +import type { DefaultProps } from '../../types'; + +export type SuggestionEmptyProps = HTMLAttributes & + DefaultProps; +export const SuggestionEmpty = forwardRef( + function SuggestionEmpty(rest, ref) { + return ( +
+ ); + }, +); diff --git a/packages/react/src/components/Suggestion/SuggestionInput.tsx b/packages/react/src/components/Suggestion/SuggestionInput.tsx new file mode 100644 index 0000000000..a9d6612c86 --- /dev/null +++ b/packages/react/src/components/Suggestion/SuggestionInput.tsx @@ -0,0 +1,15 @@ +import { forwardRef, useContext } from 'react'; +import { Input, type InputProps } from '../Input'; +import { SuggestionContext } from './Suggestion'; + +export type SuggestionInputProps = InputProps; + +export const SuggestionInput = forwardRef< + HTMLInputElement, + SuggestionInputProps +>(function SuggestionList(rest, ref) { + const { listId } = useContext(SuggestionContext); + + /* We need an empty placeholder for the clear button to be able to show/hide */ + return ; +}); diff --git a/packages/react/src/components/Suggestion/SuggestionList.tsx b/packages/react/src/components/Suggestion/SuggestionList.tsx new file mode 100644 index 0000000000..d6211f8be6 --- /dev/null +++ b/packages/react/src/components/Suggestion/SuggestionList.tsx @@ -0,0 +1,29 @@ +import type { HTMLAttributes } from 'react'; +import { forwardRef, useContext, useEffect } from 'react'; +import '@u-elements/u-datalist'; + +import type { DefaultProps } from '../../types'; +import { SuggestionContext } from './Suggestion'; + +export type SuggestionListProps = HTMLAttributes & + DefaultProps; + +export const SuggestionList = forwardRef< + HTMLDataListElement, + SuggestionListProps +>(function SuggestionList({ className, id, ...rest }, ref) { + const { listId, setListId } = useContext(SuggestionContext); + + useEffect(() => { + if (id && listId !== id) setListId?.(id); + }, [listId, id, setListId]); + + return ( + + ); +}); diff --git a/packages/react/src/components/Suggestion/index.ts b/packages/react/src/components/Suggestion/index.ts new file mode 100644 index 0000000000..c7ead3fdeb --- /dev/null +++ b/packages/react/src/components/Suggestion/index.ts @@ -0,0 +1,15 @@ +import { Suggestion as SuggestionRoot } from './Suggestion'; +import { SuggestionEmpty } from './SuggestionEmpty'; +import { SuggestionInput } from './SuggestionInput'; +import { SuggestionList } from './SuggestionList'; + +const Suggestion = Object.assign(SuggestionRoot, { + List: SuggestionList, + Input: SuggestionInput, + Empty: SuggestionEmpty, +}); + +export { Suggestion, SuggestionList, SuggestionInput, SuggestionEmpty }; +export type { SuggestionProps } from './Suggestion'; +export type { SuggestionListProps } from './SuggestionList'; +export type { SuggestionInputProps } from './SuggestionInput'; diff --git a/yarn.lock b/yarn.lock index 03459f5752..d17843ef67 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1720,6 +1720,7 @@ __metadata: "@testing-library/jest-dom": "npm:^6.4.8" "@testing-library/react": "npm:^16.0.0" "@testing-library/user-event": "npm:^14.5.2" + "@u-elements/u-datalist": "npm:^0.0.9" "@u-elements/u-details": "npm:^0.1.0" clsx: "npm:^2.1.1" copyfiles: "npm:^2.4.1" @@ -5525,6 +5526,13 @@ __metadata: languageName: node linkType: hard +"@u-elements/u-datalist@npm:^0.0.9": + version: 0.0.9 + resolution: "@u-elements/u-datalist@npm:0.0.9" + checksum: 10/35e517271bec2c67aaee5806fa90d6f8755ed86cbdc339bb47d1584d8f424668ebe78052828a5a353b1143cff5f91bfb5b3105b5408949468fe33d363d12c439 + languageName: node + linkType: hard + "@u-elements/u-details@npm:^0.1.0": version: 0.1.0 resolution: "@u-elements/u-details@npm:0.1.0"