diff --git a/apps/site/components/Common/CodeBox.tsx b/apps/site/components/Common/CodeBox.tsx index d44884de67880..5ef26c06342b5 100644 --- a/apps/site/components/Common/CodeBox.tsx +++ b/apps/site/components/Common/CodeBox.tsx @@ -1,14 +1,9 @@ 'use client'; -import { - DocumentDuplicateIcon, - CodeBracketIcon, -} from '@heroicons/react/24/outline'; import BaseCodeBox from '@node-core/ui-components/Common/BaseCodeBox'; import { useTranslations } from 'next-intl'; import Link from '#site/components/Link'; -import { useCopyToClipboard } from '#site/hooks'; import type { FC, PropsWithChildren } from 'react'; @@ -18,25 +13,13 @@ type CodeBoxProps = { }; const CodeBox: FC> = props => { - const [copied, copyToClipboard] = useCopyToClipboard(); const t = useTranslations(); - const ButtonIcon = copied ? DocumentDuplicateIcon : CodeBracketIcon; - return ( - - {t( - copied - ? 'components.common.codebox.copied' - : 'components.common.codebox.copy' - )} - - } + copyButtonLabel={t('components.common.codebox.copy')} + copiedButtonLabel={t('components.common.codebox.copied')} {...props} /> ); diff --git a/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx b/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx index 7b174221912d9..ea3f9024243e8 100644 --- a/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx +++ b/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx @@ -4,7 +4,7 @@ import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import { useContext, useEffect, useMemo } from 'react'; -import { useClientContext } from '#site/hooks'; +import { useClientContext } from '#site/hooks/client'; import { ReleaseContext } from '#site/providers/releaseProvider'; import { nextItem, OPERATING_SYSTEMS, parseCompat } from '#site/util/download'; diff --git a/apps/site/components/Downloads/Release/PlatformDropdown.tsx b/apps/site/components/Downloads/Release/PlatformDropdown.tsx index 7a4578d5d60e7..3d0a7639b69fc 100644 --- a/apps/site/components/Downloads/Release/PlatformDropdown.tsx +++ b/apps/site/components/Downloads/Release/PlatformDropdown.tsx @@ -4,7 +4,7 @@ import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import { useEffect, useContext, useMemo } from 'react'; -import { useClientContext } from '#site/hooks'; +import { useClientContext } from '#site/hooks/client'; import { ReleaseContext } from '#site/providers/releaseProvider'; import { PLATFORMS, nextItem, parseCompat } from '#site/util/download'; import { getUserPlatform } from '#site/util/userAgent'; diff --git a/apps/site/components/EOL/EOLReleaseTable/TableBody.tsx b/apps/site/components/EOL/EOLReleaseTable/TableBody.tsx index e7a1a83a740c2..8e01264bbc7eb 100644 --- a/apps/site/components/EOL/EOLReleaseTable/TableBody.tsx +++ b/apps/site/components/EOL/EOLReleaseTable/TableBody.tsx @@ -8,8 +8,7 @@ import LinkWithArrow from '#site/components/Common/LinkWithArrow'; import EOLModal from '#site/components/EOL/EOLModal'; import VulnerabilityChips from '#site/components/EOL/VulnerabilityChips'; -import type { NodeRelease } from '#site/types/releases.js'; -import type { GroupedVulnerabilities } from '#site/types/vulnerabilities.js'; +import type { GroupedVulnerabilities, NodeRelease } from '#site/types'; import type { FC } from 'react'; type EOLReleaseTableBodyProps = { diff --git a/apps/site/components/Releases/PreviousReleasesTable/TableBody.tsx b/apps/site/components/Releases/PreviousReleasesTable/TableBody.tsx index 69f388fbd54ac..0d91d28b00339 100644 --- a/apps/site/components/Releases/PreviousReleasesTable/TableBody.tsx +++ b/apps/site/components/Releases/PreviousReleasesTable/TableBody.tsx @@ -8,7 +8,7 @@ import FormattedTime from '#site/components/Common/FormattedTime'; import LinkWithArrow from '#site/components/Common/LinkWithArrow'; import Link from '#site/components/Link'; -import type { NodeRelease } from '#site/types/releases.js'; +import type { NodeRelease } from '#site/types'; import type { FC } from 'react'; import ReleaseModal from '../ReleaseModal'; diff --git a/apps/site/components/withBreadcrumbs.tsx b/apps/site/components/withBreadcrumbs.tsx index 7611dc2058c0d..3091cd90cd277 100644 --- a/apps/site/components/withBreadcrumbs.tsx +++ b/apps/site/components/withBreadcrumbs.tsx @@ -4,11 +4,8 @@ import Breadcrumbs from '@node-core/ui-components/Common/Breadcrumbs'; import { useTranslations } from 'next-intl'; import Link from '#site/components/Link'; -import { - useClientContext, - useMediaQuery, - useSiteNavigation, -} from '#site/hooks'; +import { useClientContext, useMediaQuery } from '#site/hooks/client'; +import { useSiteNavigation } from '#site/hooks/generic'; import { dashToCamelCase } from '#site/util/string'; import type { NavigationKeys } from '#site/types'; diff --git a/apps/site/components/withMetaBar.tsx b/apps/site/components/withMetaBar.tsx index a22e3a30a5d65..7467753567b8c 100644 --- a/apps/site/components/withMetaBar.tsx +++ b/apps/site/components/withMetaBar.tsx @@ -7,8 +7,7 @@ import { useFormatter, useLocale, useTranslations } from 'next-intl'; import Link from '#site/components/Link'; import WithAvatarGroup from '#site/components/withAvatarGroup'; -import { useClientContext } from '#site/hooks/react-client'; -import useMediaQuery from '#site/hooks/react-client/useMediaQuery'; +import { useClientContext, useMediaQuery } from '#site/hooks/client'; import { DEFAULT_DATE_FORMAT } from '#site/next.calendar.constants.mjs'; import { TRANSLATION_URL } from '#site/next.constants.mjs'; import { getGitHubBlobUrl } from '#site/util/github'; diff --git a/apps/site/components/withNavBar.tsx b/apps/site/components/withNavBar.tsx index f62aaca997899..973ce87901216 100644 --- a/apps/site/components/withNavBar.tsx +++ b/apps/site/components/withNavBar.tsx @@ -15,7 +15,7 @@ import SearchButton from '#site/components/Common/Searchbox'; import Link from '#site/components/Link'; import WithBanner from '#site/components/withBanner'; import WithNodejsLogo from '#site/components/withNodejsLogo'; -import { useSiteNavigation } from '#site/hooks'; +import { useSiteNavigation } from '#site/hooks/generic'; import { useRouter, usePathname } from '#site/navigation.mjs'; import type { SimpleLocaleConfig } from '@node-core/ui-components/types'; diff --git a/apps/site/components/withSidebar.tsx b/apps/site/components/withSidebar.tsx index caa4f95ecbd6c..6d5ab83b6605b 100644 --- a/apps/site/components/withSidebar.tsx +++ b/apps/site/components/withSidebar.tsx @@ -5,8 +5,8 @@ import { usePathname } from 'next/navigation'; import { useLocale, useTranslations } from 'next-intl'; import Link from '#site/components/Link'; -import { useClientContext } from '#site/hooks'; -import { useSiteNavigation } from '#site/hooks/server'; +import { useClientContext } from '#site/hooks/client'; +import { useSiteNavigation } from '#site/hooks/generic'; import { useRouter } from '#site/navigation.mjs'; import type { NavigationKeys } from '#site/types'; diff --git a/apps/site/components/withSidebarCrossLinks.tsx b/apps/site/components/withSidebarCrossLinks.tsx index 3385e12d39185..8b9d4c73f6514 100644 --- a/apps/site/components/withSidebarCrossLinks.tsx +++ b/apps/site/components/withSidebarCrossLinks.tsx @@ -1,5 +1,6 @@ import CrossLink from '#site/components/Common/CrossLink'; -import { useClientContext, useSiteNavigation } from '#site/hooks/server'; +import { useSiteNavigation } from '#site/hooks/generic'; +import { useClientContext } from '#site/hooks/server'; import type { NavigationKeys } from '#site/types'; import type { FC } from 'react'; diff --git a/apps/site/hooks/react-client/__tests__/useClientContext.test.jsx b/apps/site/hooks/client/__tests__/useClientContext.test.jsx similarity index 91% rename from apps/site/hooks/react-client/__tests__/useClientContext.test.jsx rename to apps/site/hooks/client/__tests__/useClientContext.test.jsx index 7d43d41caafa3..2843d4ed3c63c 100644 --- a/apps/site/hooks/react-client/__tests__/useClientContext.test.jsx +++ b/apps/site/hooks/client/__tests__/useClientContext.test.jsx @@ -3,7 +3,7 @@ import { renderHook } from '@testing-library/react'; import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; -import useClientContext from '#site/hooks/react-client/useClientContext'; +import useClientContext from '#site/hooks/client/useClientContext'; import { MatterContext } from '#site/providers/matterProvider'; describe('useClientContext', () => { diff --git a/apps/site/hooks/react-client/__tests__/useDetectOS.test.mjs b/apps/site/hooks/client/__tests__/useDetectOS.test.mjs similarity index 96% rename from apps/site/hooks/react-client/__tests__/useDetectOS.test.mjs rename to apps/site/hooks/client/__tests__/useDetectOS.test.mjs index f1b644c6af2f9..9a904ca30b46c 100644 --- a/apps/site/hooks/react-client/__tests__/useDetectOS.test.mjs +++ b/apps/site/hooks/client/__tests__/useDetectOS.test.mjs @@ -3,7 +3,7 @@ import { describe, it, afterEach } from 'node:test'; import { renderHook, waitFor } from '@testing-library/react'; -import useDetectOS from '#site/hooks/react-client/useDetectOS'; +import useDetectOS from '#site/hooks/client/useDetectOS'; const windowsUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'; diff --git a/apps/site/hooks/react-client/__tests__/useMediaQuery.test.mjs b/apps/site/hooks/client/__tests__/useMediaQuery.test.mjs similarity index 96% rename from apps/site/hooks/react-client/__tests__/useMediaQuery.test.mjs rename to apps/site/hooks/client/__tests__/useMediaQuery.test.mjs index 5bc2c382204f5..468c8dc8b3116 100644 --- a/apps/site/hooks/react-client/__tests__/useMediaQuery.test.mjs +++ b/apps/site/hooks/client/__tests__/useMediaQuery.test.mjs @@ -3,7 +3,7 @@ import { describe, it } from 'node:test'; import { renderHook } from '@testing-library/react'; -import useMediaQuery from '#site/hooks/react-client/useMediaQuery'; +import useMediaQuery from '#site/hooks/client/useMediaQuery'; const noop = () => {}; diff --git a/apps/site/hooks/react-server/index.ts b/apps/site/hooks/client/index.ts similarity index 72% rename from apps/site/hooks/react-server/index.ts rename to apps/site/hooks/client/index.ts index e4c16cec3c466..70d3ac90d9116 100644 --- a/apps/site/hooks/react-server/index.ts +++ b/apps/site/hooks/client/index.ts @@ -1,4 +1,3 @@ -export { default as useCopyToClipboard } from './useCopyToClipboard'; export { default as useDetectOS } from './useDetectOS'; export { default as useMediaQuery } from './useMediaQuery'; export { default as useClientContext } from './useClientContext'; diff --git a/apps/site/hooks/client/useClientContext.ts b/apps/site/hooks/client/useClientContext.ts new file mode 100644 index 0000000000000..5145fa7af08fb --- /dev/null +++ b/apps/site/hooks/client/useClientContext.ts @@ -0,0 +1,9 @@ +'use client'; + +import { useContext } from 'react'; + +import { MatterContext } from '#site/providers/matterProvider'; + +import type { ClientSharedServerContext } from '#site/types'; + +export default () => useContext(MatterContext) as ClientSharedServerContext; diff --git a/apps/site/hooks/react-client/useDetectOS.ts b/apps/site/hooks/client/useDetectOS.ts similarity index 100% rename from apps/site/hooks/react-client/useDetectOS.ts rename to apps/site/hooks/client/useDetectOS.ts diff --git a/apps/site/hooks/react-client/useMediaQuery.ts b/apps/site/hooks/client/useMediaQuery.ts similarity index 100% rename from apps/site/hooks/react-client/useMediaQuery.ts rename to apps/site/hooks/client/useMediaQuery.ts diff --git a/apps/site/hooks/react-generic/index.ts b/apps/site/hooks/generic/index.ts similarity index 100% rename from apps/site/hooks/react-generic/index.ts rename to apps/site/hooks/generic/index.ts diff --git a/apps/site/hooks/react-generic/useSiteNavigation.ts b/apps/site/hooks/generic/useSiteNavigation.ts similarity index 100% rename from apps/site/hooks/react-generic/useSiteNavigation.ts rename to apps/site/hooks/generic/useSiteNavigation.ts diff --git a/apps/site/hooks/index.ts b/apps/site/hooks/index.ts deleted file mode 100644 index 08588a58f88b3..0000000000000 --- a/apps/site/hooks/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './react-client'; -export * from './react-generic'; diff --git a/apps/site/hooks/react-client/index.ts b/apps/site/hooks/react-client/index.ts deleted file mode 100644 index 30d4e71478cea..0000000000000 --- a/apps/site/hooks/react-client/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default as useCopyToClipboard } from './useCopyToClipboard'; -export { default as useDetectOS } from './useDetectOS'; -export { default as useMediaQuery } from './useMediaQuery'; -export { default as useClientContext } from './useClientContext'; -export { default as useNavigationState } from './useNavigationState'; diff --git a/apps/site/hooks/react-client/useClientContext.ts b/apps/site/hooks/react-client/useClientContext.ts deleted file mode 100644 index 44d9c3d992e49..0000000000000 --- a/apps/site/hooks/react-client/useClientContext.ts +++ /dev/null @@ -1,33 +0,0 @@ -'use client'; - -import { useContext } from 'react'; - -import { MatterContext } from '#site/providers/matterProvider'; - -import type { ClientSharedServerContext } from '#site/types'; - -const useClientContext = (): ClientSharedServerContext => { - const { - frontmatter, - pathname, - headings, - readingTime, - filename, - os, - architecture, - bitness, - } = useContext(MatterContext); - - return { - pathname, - frontmatter, - headings, - readingTime, - filename, - os, - architecture, - bitness, - }; -}; - -export default useClientContext; diff --git a/apps/site/hooks/react-client/useCopyToClipboard.ts b/apps/site/hooks/react-client/useCopyToClipboard.ts deleted file mode 100644 index 78ac256c9fdaa..0000000000000 --- a/apps/site/hooks/react-client/useCopyToClipboard.ts +++ /dev/null @@ -1,35 +0,0 @@ -'use client'; - -import { useEffect, useState } from 'react'; - -const copyToClipboard = (value: string | undefined) => { - if (!value || typeof navigator === 'undefined') { - return Promise.resolve(false); - } - - return navigator.clipboard - .writeText(value) - .then(() => true) - .catch(() => false); -}; - -const useCopyToClipboard = () => { - const [copied, setCopied] = useState(false); - - const copyText = (text: string | undefined) => - copyToClipboard(text).then(setCopied); - - useEffect(() => { - if (copied) { - const timerId = setTimeout(() => setCopied(false), 3000); - - return () => clearTimeout(timerId); - } - - return undefined; - }, [copied]); - - return [copied, copyText] as const; -}; - -export default useCopyToClipboard; diff --git a/apps/site/hooks/react-client/useNavigationState.ts b/apps/site/hooks/react-client/useNavigationState.ts deleted file mode 100644 index 961854280fe12..0000000000000 --- a/apps/site/hooks/react-client/useNavigationState.ts +++ /dev/null @@ -1,42 +0,0 @@ -'use client'; - -import { useContext, useEffect } from 'react'; - -import { NavigationStateContext } from '#site/providers/navigationStateProvider'; -import { debounce } from '#site/util/objects'; - -import type { RefObject } from 'react'; - -const useNavigationState = ( - id: string, - ref: RefObject, - debounceTime = 300 -) => { - const navigationState = useContext(NavigationStateContext); - - const handleScroll = debounce(() => { - if (ref.current) { - navigationState[id] = { - x: ref.current.scrollLeft, - y: ref.current.scrollTop, - }; - } - }, debounceTime); - - useEffect(() => { - const element = ref.current; - if (element) { - if (navigationState[id] && navigationState[id].y !== element.scrollTop) { - element.scroll({ top: navigationState[id].y, behavior: 'instant' }); - } - - element.addEventListener('scroll', handleScroll, { passive: true }); - - return () => element.removeEventListener('scroll', handleScroll); - } - // We need this effect to run only on mount - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); -}; - -export default useNavigationState; diff --git a/apps/site/hooks/react-server/useCopyToClipboard.ts b/apps/site/hooks/react-server/useCopyToClipboard.ts deleted file mode 100644 index 053a5ac87b24f..0000000000000 --- a/apps/site/hooks/react-server/useCopyToClipboard.ts +++ /dev/null @@ -1,5 +0,0 @@ -const useCopyToClipboard = () => { - throw new Error('Attempted to call useCopyToClipboard from RSC'); -}; - -export default useCopyToClipboard; diff --git a/apps/site/hooks/react-server/useDetectOS.ts b/apps/site/hooks/react-server/useDetectOS.ts deleted file mode 100644 index 6ed3ced76bce2..0000000000000 --- a/apps/site/hooks/react-server/useDetectOS.ts +++ /dev/null @@ -1,5 +0,0 @@ -const useDetectOS = () => { - throw new Error('Attempted to call useDetectOS from RSC'); -}; - -export default useDetectOS; diff --git a/apps/site/hooks/react-server/useMediaQuery.ts b/apps/site/hooks/react-server/useMediaQuery.ts deleted file mode 100644 index f42db993f50ea..0000000000000 --- a/apps/site/hooks/react-server/useMediaQuery.ts +++ /dev/null @@ -1,5 +0,0 @@ -const useMediaQuery = () => { - throw new Error('Attempted to call useMediaQuery from RSC'); -}; - -export default useMediaQuery; diff --git a/apps/site/hooks/server.ts b/apps/site/hooks/server.ts deleted file mode 100644 index cd08e225ee0bf..0000000000000 --- a/apps/site/hooks/server.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './react-server'; -export * from './react-generic'; diff --git a/apps/site/hooks/server/index.ts b/apps/site/hooks/server/index.ts new file mode 100644 index 0000000000000..85c173af25f29 --- /dev/null +++ b/apps/site/hooks/server/index.ts @@ -0,0 +1 @@ +export { default as useClientContext } from './useClientContext'; diff --git a/apps/site/hooks/react-server/useClientContext.ts b/apps/site/hooks/server/useClientContext.ts similarity index 100% rename from apps/site/hooks/react-server/useClientContext.ts rename to apps/site/hooks/server/useClientContext.ts diff --git a/apps/site/layouts/Post.tsx b/apps/site/layouts/Post.tsx index b638b1ef8c5d5..406dcf01c4acb 100644 --- a/apps/site/layouts/Post.tsx +++ b/apps/site/layouts/Post.tsx @@ -6,7 +6,7 @@ import WithBlogCrossLinks from '#site/components/withBlogCrossLinks'; import WithFooter from '#site/components/withFooter'; import WithMetaBar from '#site/components/withMetaBar'; import WithNavBar from '#site/components/withNavBar'; -import { useClientContext } from '#site/hooks/react-server'; +import { useClientContext } from '#site/hooks/server'; import { mapAuthorToCardAuthors } from '#site/util/author'; import { mapBlogCategoryToPreviewType } from '#site/util/blog'; diff --git a/apps/site/providers/matterProvider.tsx b/apps/site/providers/matterProvider.tsx index 6c0dcd8a510b3..28376bd55d097 100644 --- a/apps/site/providers/matterProvider.tsx +++ b/apps/site/providers/matterProvider.tsx @@ -2,7 +2,7 @@ import { createContext } from 'react'; -import { useDetectOS } from '#site/hooks'; +import { useDetectOS } from '#site/hooks/client'; import { assignClientContext } from '#site/util/context'; import type { ClientSharedServerContext } from '#site/types'; diff --git a/apps/site/types/server.ts b/apps/site/types/server.ts index 11f02ee74a8b6..4af58c6cf1969 100644 --- a/apps/site/types/server.ts +++ b/apps/site/types/server.ts @@ -1,4 +1,4 @@ -import type { useDetectOS } from '#site/hooks'; +import type { useDetectOS } from '#site/hooks/client'; import type { Frontmatter } from '#site/types/frontmatter'; import type { Heading } from '@vcarl/remark-headings'; import type { ReadTimeResults } from 'reading-time'; diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 815779e762dd1..03522769bab71 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@node-core/ui-components", - "version": "1.5.0", + "version": "1.5.1", "type": "module", "exports": { "./*": [ diff --git a/packages/ui-components/src/Common/BaseCodeBox/index.tsx b/packages/ui-components/src/Common/BaseCodeBox/index.tsx index 7a6c9ed70e010..c5ee36c1773b8 100644 --- a/packages/ui-components/src/Common/BaseCodeBox/index.tsx +++ b/packages/ui-components/src/Common/BaseCodeBox/index.tsx @@ -1,9 +1,14 @@ 'use client'; +import { + DocumentDuplicateIcon, + CodeBracketIcon, +} from '@heroicons/react/24/outline'; import classNames from 'classnames'; import { Fragment, isValidElement, useRef } from 'react'; import BaseButton from '#ui/Common/BaseButton'; +import useCopy from '#ui/hooks/useCopy'; import type { LinkLike } from '#ui/types'; import type { FC, PropsWithChildren, ReactElement, ReactNode } from 'react'; @@ -66,27 +71,25 @@ const transformCode = >( type CodeBoxProps = { language: string; className?: string; - onCopy: (text: string) => void; as?: LinkLike; - buttonContent: ReactNode; + copyButtonLabel: ReactNode; + copiedButtonLabel: ReactNode; }; const BaseCodeBox: FC> = ({ children, language, className, - onCopy, - buttonContent, + copyButtonLabel, + copiedButtonLabel, as = 'a', }: PropsWithChildren) => { + const [copied, copy] = useCopy(); + const containerRef = useRef(null); - const handleCopy = (): void => { - const text = containerRef.current?.textContent; - if (text) { - onCopy(text); - } - }; + const handleCopy = () => copy(containerRef.current?.textContent); + const ButtonIcon = copied ? DocumentDuplicateIcon : CodeBracketIcon; return (
@@ -106,7 +109,8 @@ const BaseCodeBox: FC> = ({ kind="neutral" onClick={handleCopy} > - {buttonContent} + + {copied ? copiedButtonLabel : copyButtonLabel}
)} diff --git a/packages/ui-components/src/Common/Select/StatelessSelect/index.tsx b/packages/ui-components/src/Common/Select/StatelessSelect/index.tsx index 62a0e9388927a..063cb1033b7e2 100644 --- a/packages/ui-components/src/Common/Select/StatelessSelect/index.tsx +++ b/packages/ui-components/src/Common/Select/StatelessSelect/index.tsx @@ -2,11 +2,11 @@ import { ChevronDownIcon } from '@heroicons/react/24/solid'; import classNames from 'classnames'; import { useId, useMemo } from 'react'; -import { isStringArray, isValuesArray } from '#ui/util/array'; - import type { SelectGroup, SelectProps } from '#ui/Common/Select'; import type { LinkLike } from '#ui/types'; +import { mapValues } from '../utils'; + import styles from '../index.module.css'; type StatelessSelectConfig = { @@ -29,23 +29,9 @@ const StatelessSelect = ({ }: StatelessSelectProps) => { const id = useId(); - const mappedValues = useMemo(() => { - let mappedValues = values; - - if (isStringArray(mappedValues)) { - mappedValues = mappedValues.map(value => ({ - label: value, - value, - })); - } - - if (isValuesArray(mappedValues)) { - return [{ items: mappedValues }]; - } - - return mappedValues as Array>; - }, [values]) as Array>; - + const mappedValues = useMemo(() => mapValues(values), [values]) as Array< + SelectGroup + >; // Find the current/default item to display in summary const currentItem = useMemo( () => diff --git a/packages/ui-components/src/Common/Select/index.tsx b/packages/ui-components/src/Common/Select/index.tsx index ea25eeeb65399..760b54b2e9733 100644 --- a/packages/ui-components/src/Common/Select/index.tsx +++ b/packages/ui-components/src/Common/Select/index.tsx @@ -6,11 +6,12 @@ import classNames from 'classnames'; import { useEffect, useId, useMemo, useState } from 'react'; import Skeleton from '#ui/Common/Skeleton'; -import { isStringArray, isValuesArray } from '#ui/util/array'; import type { FormattedMessage, LinkLike } from '#ui/types'; import type { ReactElement, ReactNode } from 'react'; +import { mapValues } from './utils'; + import styles from './index.module.css'; export type SelectValue = { @@ -65,22 +66,9 @@ const Select = ({ useEffect(() => setValue(defaultValue), [defaultValue]); - const mappedValues = useMemo(() => { - let mappedValues = values; - - if (isStringArray(mappedValues)) { - mappedValues = mappedValues.map(value => ({ - label: value, - value, - })); - } - - if (isValuesArray(mappedValues)) { - return [{ items: mappedValues }]; - } - - return mappedValues; - }, [values]) as Array>; + const mappedValues = useMemo(() => mapValues(values), [values]) as Array< + SelectGroup + >; // We render the actual item slotted to fix/prevent the issue // of the tirgger flashing on the initial render diff --git a/packages/ui-components/src/Common/Select/utils.ts b/packages/ui-components/src/Common/Select/utils.ts new file mode 100644 index 0000000000000..2b753daaa5131 --- /dev/null +++ b/packages/ui-components/src/Common/Select/utils.ts @@ -0,0 +1,24 @@ +export const isStringArray = ( + values: Array +): values is Array => + Boolean(values[0] && typeof values[0] === 'string'); + +export const isValuesArray = (values: Array): values is Array => + Boolean(values[0] && typeof values[0] === 'object' && 'value' in values[0]); + +export const mapValues = (values: Array) => { + let mappedValues = values; + + if (isStringArray(mappedValues)) { + mappedValues = mappedValues.map(value => ({ + label: value, + value, + })); + } + + if (isValuesArray(mappedValues)) { + return [{ items: mappedValues }]; + } + + return mappedValues; +}; diff --git a/apps/site/hooks/react-client/__tests__/useCopyToClipboard.test.jsx b/packages/ui-components/src/hooks/__test__/useCopy.test.jsx similarity index 90% rename from apps/site/hooks/react-client/__tests__/useCopyToClipboard.test.jsx rename to packages/ui-components/src/hooks/__test__/useCopy.test.jsx index 5bb64c54c7592..06ac3f406110f 100644 --- a/apps/site/hooks/react-client/__tests__/useCopyToClipboard.test.jsx +++ b/packages/ui-components/src/hooks/__test__/useCopy.test.jsx @@ -4,7 +4,7 @@ import { setTimeout } from 'node:timers/promises'; import { render, fireEvent, screen } from '@testing-library/react'; -import useCopyToClipboard from '#site/hooks/react-client/useCopyToClipboard'; +import useCopy from '../useCopy'; navigator.clipboard = { writeText: () => {} }; @@ -13,7 +13,7 @@ await describe('useCopyToClipboard', async () => { t.mock.method(navigator.clipboard, 'writeText', () => Promise.resolve()); const TestComponent = ({ textToCopy }) => { - const [copied, copyText] = useCopyToClipboard(); + const [copied, copyText] = useCopy(); return (