diff --git a/.github/workflows/lint-and-tests.yml b/.github/workflows/lint-and-tests.yml index 4f1dacb2c68ad..86995c912fe6b 100644 --- a/.github/workflows/lint-and-tests.yml +++ b/.github/workflows/lint-and-tests.yml @@ -204,7 +204,7 @@ jobs: # sha reference has no stable git tag reference or URL. see https://github.com/chromaui/chromatic-cli/issues/797 uses: chromaui/action@30b6228aa809059d46219e0f556752e8672a7e26 with: - workingDir: apps/site + workingDir: packages/ui-components buildScriptName: storybook:build projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} exitOnceUploaded: true @@ -220,6 +220,9 @@ jobs: uses: MishaKav/jest-coverage-comment@d74238813c33e6ea20530ff91b5ea37953d11c91 # v1.0.27 with: title: 'Unit Test Coverage Report' - junitxml-path: ./apps/site/junit.xml - junitxml-title: Unit Test Report - coverage-summary-path: ./apps/site/coverage/coverage-summary.json + multiple-junitxml-files: | + @node-core/ui-components, ./packages/ui-components/junit.xml + @nodejs/website, ./apps/site/junit.xml + multiple-files: | + @node-core/ui-components, ./packages/ui-components/coverage/coverage-summary.json + @nodejs/website, ./apps/site/coverage/coverage-summary.json diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index 00b0939cf28fd..00316821f8541 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -102,10 +102,10 @@ The Website also uses several other Open Source libraries (not limited to) liste Locations are subject to change. (If you are someone updating these paths, please document those changes here.) -- React Components are defined on `apps/site/components` +- React Components are defined on `apps/site/components` and `packages/ui-components` - React Templates are defined on `apps/site/layouts` -- Global Stylesheets are declared on `apps/site/styles` - - Styles are done with [PostCSS][] +- Global Stylesheets are declared on `packages/ui-components/styles` + - Styles are done with [PostCSS][] and [Tailwind][] - Public files are stored on `apps/site/public` - Static Images, JavaScript files, and others are stored within `apps/site/public/static` - Internationalisation is done on `apps/site/i18n` @@ -126,7 +126,7 @@ please document those changes here.) - Generation of build-time indexes such as blog data - Multi-Purpose Scripts are stored within `apps/site/scripts` - Such as Node.js Release Blog Post generation -- Storybook Configuration is done within `apps/site/.storybook` +- Storybook Configuration is done within `packages/ui-components/.storybook` - We use an almost out-of-the-box Storybook Experience with a few extra customisations ### Adding new Pages @@ -197,8 +197,6 @@ Finally, if you're unfamiliar with how to use Tailwind or how to use Tailwind wi - We discourage the usage of any plain CSS styles and tokens, when in doubt ask for help - We require that you define one Tailwind Token per line, just as shown on the example above, since this improves readability - Only write CSS within CSS Modules, avoid writing CSS within JavaScript files -- We recommend creating mixins for reusable animations, effects and more - - You can create Mixins within the `apps/site/styles/mixins` folder > \[!NOTE]\ > Tailwind is already configured for this repository. You don't need to import any Tailwind module within your CSS module.\ @@ -211,26 +209,76 @@ Finally, if you're unfamiliar with how to use Tailwind or how to use Tailwind wi ### Best practices when creating a Component -- All React Components should be placed within the `apps/site/components` folder. -- Each Component should be placed, whenever possible, within a sub-folder, which we call the "Domain" of the Component - - The domain represents where these Components belong to or where they will be used. - - For example, Components used within Article Pages or that are part of the structure of an Article or the Article Layouts, - should be placed within `apps/site/components/Article` -- Each component should have its folder with the name of the Component -- The structure of each component folder follows the following template: +- **All React components** should be placed within either `@node-core/ui-components` (for reusable components) or `apps/site/components` (for website-specific components). +- **Generic UI components** that are not tied to the website should be placed in the `@node-core/ui-components` package. + - These components should be **framework-agnostic** and must not rely on Next.js-specific features such as `usePathname()` or `useTranslations()`. + - If a component previously relied on Next.js, it should now accept these values as **props** instead. +- **Website-specific components** that rely on Next.js or are tied to the website should remain in `apps/site/components`. + - These components can use Next.js-specific hooks, API calls, or configurations. + - When using a generic UI component that requires Next.js functionality, pass it as a **prop** instead of modifying the component. +- **Each component** should be placed within a sub-folder, which we call the **"Domain"** of the component. + - The domain represents where the component belongs or where it will be used. + - For example, components used within article pages or related to the structure of an article should be placed within `@node-core/ui-components/Common/Article`. +- **Each component should have its own folder** with the name of the component. +- The structure of each component folder follows this template: + ```text - ComponentName - - index.tsx // the component itself - - index.module.css // all styles of the component are placed there - - index.stories.tsx // component Storybook stories - - __tests__ // component tests (such as unit tests, etc) - - index.test.mjs // unit tests should be done in ESM and not TypeScript + - index.tsx // The component itself + - index.module.css // Component-specific styles + - index.stories.tsx // Storybook stories (only for @node-core/ui-components) + - __tests__/ // Component tests (such as unit tests, etc.) + - index.test.mjs // Unit tests should be done in ESM, not TypeScript ``` -- React Hooks belonging to a single Component should be placed within the Component's folder - - If the Hook as a wider usability or can be used by other components, it should be placed in the root `hooks` folder. -- If the Component has "sub-components" they should follow the same philosophy as the Component itself. - - For example, if the Component `ComponentName` has a sub-component called `SubComponentName`, - then it should be placed within `ComponentName/SubComponentName` + +- **If a component requires Next.js features, it should be wrapped within `apps/site`** rather than being modified directly in `@node-core/ui-components`. + + - Example: A component that requires `usePathname()` should **not** call it directly inside `@node-core/ui-components`. Instead: + - The **base component** should accept `pathname` as a prop. + - The **wrapper component** in `apps/site` should call `usePathname()` and pass it to the base component. + + Example structure: + + - **Base Component (`@node-core/ui-components`)** + + ```tsx + const BaseComponent: FC<...> = ({ pathname, ariaLabel }) => { + return <... ariaLabel={ariaLabel}>; + }; + ``` + + - **Wrapper Component (`apps/site/components`)** + + ```tsx + const Component: FC<...> = (...) => { + const pathname = usePathname(); + const t = useTranslations(); + + return ; + }; + ``` + + - **Importing Components:** + - **For website-specific functionality**, import the wrapper from `apps/site/components`. + - **For direct UI use cases**, import from `@node-core/ui-components`. + +- **Storybook is now a dependency of `@node-core/ui-components`** and should not be included in `apps/site`. + + - Storybook stories should be written only for components in `@node-core/ui-components`. + +- **React Hooks that belong to a single component should be placed within that component’s folder.** + + - If the hook has a **wider usability** or can be used by multiple components, it should be placed in the `apps/site/hooks` folder. + - These hooks should only exist in `apps/site`. + +- **If a component has sub-components, they should follow the same structure as the main component.** + - Example: If `ComponentName` has a sub-component called `SubComponentName`, it should be placed within: + ```text + - ComponentName/ + - index.tsx + - SubComponentName/ + - index.tsx + ``` #### How a new Component should look like when freshly created @@ -272,7 +320,7 @@ To add a new download installation method, follow these steps: - Add a new entry to the `INSTALL_METHODS` array. - Each entry should have the following properties: - - `iconImage`: The React component of the icon image for the installation method. This should be an SVG component stored within `apps/site/components/Icons/InstallationMethod` and must follow the other icon component references (being a `FC` supporting `SVGSVGElement` props). + - `iconImage`: The React component of the icon image for the installation method. This should be an SVG component stored within `@node-core/ui-components/Icons/InstallationMethod` and must follow the other icon component references (being a `FC` supporting `SVGSVGElement` props). - Don't forget to add it on the `index.tsx` file from the `InstallationMethod` folder. - `recommended`: A boolean indicating if this method is recommended. This property is available only for official installation methods. - `url`: The URL for the installation method. @@ -379,7 +427,7 @@ Each new feature or bug fix should be accompanied by a unit test (when deemed va We use [Jest][] as our test runner and [React Testing Library][] for our React unit tests. We also use [Storybook][] to document our components. -Each component should have a storybook story that documents the component's usage. +Components within `packages/ui-components` should have a storybook story that documents the component's usage. Visual Regression Testing is automatically done via [Chromatic](https://www.chromatic.com/) to ensure that Components are rendered correctly. @@ -407,7 +455,7 @@ They also allow Developers to preview Components and be able to test them manual ```tsx import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import NameOfComponent from '@components/PathTo/YourComponent'; +import NameOfComponent from '@node-core/ui-components/PathTo/YourComponent'; type Story = StoryObj; type Meta = MetaObj; @@ -548,7 +596,7 @@ The Node.js Website uses Tailwind as a CSS Framework for crafting our React Comp #### Font Families on the Website We use `next/fonts` Open Sans as the default font for the Node.js Website. -The font is configured as a CSS variable and then configured on `tailwind.config.js` as the default font for the Website. +The font is configured as a CSS variable and then configured on `packages/ui-components/tailwind.config.ts` as the default font for the Website. #### Why we use RadixUI? diff --git a/apps/site/.storybook/main.ts b/apps/site/.storybook/main.ts deleted file mode 100644 index f6b01fc299b9a..0000000000000 --- a/apps/site/.storybook/main.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { join } from 'node:path'; - -import type { StorybookConfig } from '@storybook/react-webpack5'; - -const mocksFolder = join(__dirname, '../components/__mocks__'); - -const config: StorybookConfig = { - stories: ['../components/**/*.stories.tsx'], - logLevel: 'error', - staticDirs: ['../public'], - typescript: { reactDocgen: false, check: false }, - core: { disableTelemetry: true, disableWhatsNewNotifications: true }, - framework: '@storybook/react-webpack5', - swc: () => ({ - jsc: { - parser: { - syntax: 'typescript', - tsx: true, - }, - transform: { - react: { - runtime: 'automatic', - }, - }, - }, - }), - addons: [ - '@storybook/addon-webpack5-compiler-swc', - '@storybook/addon-controls', - '@storybook/addon-interactions', - '@storybook/addon-themes', - '@storybook/addon-viewport', - { - name: '@storybook/addon-styling-webpack', - options: { - rules: [ - { - test: /\.css$/, - use: [ - 'style-loader', - { loader: 'css-loader', options: { url: false } }, - 'postcss-loader', - ], - }, - ], - }, - }, - ], - webpack: async config => ({ - ...config, - // We want to conform as much as possible with our target settings - target: 'browserslist:development', - // Performance Hints do not make sense on Storybook as it is bloated by design - performance: { hints: false }, - // `nodevu` is a Node.js-specific package that requires Node.js modules - // this is incompatible with Storybook. So we just mock the module - resolve: { - ...config.resolve, - alias: { - 'next/image': join(mocksFolder, './next-image.mjs'), - 'next-intl/navigation': join(mocksFolder, './next-intl.mjs'), - '@/client-context': join(mocksFolder, './client-context.mjs'), - '@': join(__dirname, '../'), - }, - }, - // Removes Pesky Critical Dependency Warnings due to `next/font` - ignoreWarnings: [ - e => - e.message.includes('was not found in') || - e.message.includes('generated code contains'), - ], - }), -}; - -export default config; diff --git a/apps/site/.stylelintignore b/apps/site/.stylelintignore index cb71469e033ce..a8600c22f4b77 100644 --- a/apps/site/.stylelintignore +++ b/apps/site/.stylelintignore @@ -10,8 +10,5 @@ public # Jest coverage -# Storybook -storybook-static - # Old Styles styles/old diff --git a/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx b/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx index 844c9fa6753f5..effcd1719afc8 100644 --- a/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx +++ b/apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx @@ -1,7 +1,7 @@ +import HexagonGrid from '@node-core/ui-components/Icons/HexagonGrid'; +import JsWhiteIcon from '@node-core/ui-components/Icons/Logos/JsWhite'; import { ImageResponse } from 'next/og'; -import HexagonGrid from '@/components/Icons/HexagonGrid'; -import JsIconWhite from '@/components/Icons/Logos/JsIconWhite'; import { DEFAULT_CATEGORY_OG_TYPE } from '@/next.constants.mjs'; import { defaultLocale } from '@/next.locales.mjs'; import tailwindConfig from '@/tailwind.config'; @@ -37,7 +37,7 @@ export const GET = async (_: Request, props: StaticParams) => {
- +

{params.title.slice(0, 100)}

diff --git a/apps/site/components/Blog/BlogHeader/index.stories.tsx b/apps/site/components/Blog/BlogHeader/index.stories.tsx deleted file mode 100644 index 0e29b475119bc..0000000000000 --- a/apps/site/components/Blog/BlogHeader/index.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import BlogHeader from '@/components/Blog/BlogHeader'; - -type Story = StoryObj; -type Meta = MetaObj; - -export const Default: Story = { - args: { - // See `@/site.json` for the `rssFeeds` object - category: 'all', - }, - decorators: [ - // We need to wrap to allow global styles to be applied (markdown styles) - Story => ( -
- -
- ), - ], -}; - -export default { component: BlogHeader } as Meta; diff --git a/apps/site/components/Common/BlogPostCard/__tests__/index.test.mjs b/apps/site/components/Blog/BlogPostCard/__tests__/index.test.mjs similarity index 97% rename from apps/site/components/Common/BlogPostCard/__tests__/index.test.mjs rename to apps/site/components/Blog/BlogPostCard/__tests__/index.test.mjs index e4752d4b09dc9..3094ba35e9c03 100644 --- a/apps/site/components/Common/BlogPostCard/__tests__/index.test.mjs +++ b/apps/site/components/Blog/BlogPostCard/__tests__/index.test.mjs @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react'; -import BlogPostCard from '@/components/Common/BlogPostCard'; +import BlogPostCard from '@/components/Blog/BlogPostCard'; function renderBlogPostCard({ title = 'Blog post title', diff --git a/apps/site/components/Common/BlogPostCard/index.module.css b/apps/site/components/Blog/BlogPostCard/index.module.css similarity index 100% rename from apps/site/components/Common/BlogPostCard/index.module.css rename to apps/site/components/Blog/BlogPostCard/index.module.css diff --git a/apps/site/components/Common/BlogPostCard/index.tsx b/apps/site/components/Blog/BlogPostCard/index.tsx similarity index 96% rename from apps/site/components/Common/BlogPostCard/index.tsx rename to apps/site/components/Blog/BlogPostCard/index.tsx index 30815ad9ed62c..b99c1ac13a465 100644 --- a/apps/site/components/Common/BlogPostCard/index.tsx +++ b/apps/site/components/Blog/BlogPostCard/index.tsx @@ -1,8 +1,8 @@ +import Preview from '@node-core/ui-components/Common/Preview'; import { useTranslations } from 'next-intl'; import type { FC } from 'react'; import FormattedTime from '@/components/Common/FormattedTime'; -import Preview from '@/components/Common/Preview'; import Link from '@/components/Link'; import WithAvatarGroup from '@/components/withAvatarGroup'; import type { BlogCategory } from '@/types'; diff --git a/apps/site/components/Common/ActiveLink.tsx b/apps/site/components/Common/ActiveLink.tsx new file mode 100644 index 0000000000000..63f6b2f2e16eb --- /dev/null +++ b/apps/site/components/Common/ActiveLink.tsx @@ -0,0 +1,14 @@ +'use client'; + +import type { ActiveLocalizedLinkProps } from '@node-core/ui-components/Common/BaseActiveLink'; +import BaseActiveLink from '@node-core/ui-components/Common/BaseActiveLink'; +import type { FC } from 'react'; + +import Link from '@/components/Link'; +import { usePathname } from '@/navigation.mjs'; + +const ActiveLink: FC< + Omit +> = props => ; + +export default ActiveLink; diff --git a/apps/site/components/Common/AvatarGroup/Avatar/index.tsx b/apps/site/components/Common/AvatarGroup/Avatar/index.tsx deleted file mode 100644 index 3e7f98724cb80..0000000000000 --- a/apps/site/components/Common/AvatarGroup/Avatar/index.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import classNames from 'classnames'; -import Image from 'next/image'; -import type { HTMLAttributes } from 'react'; -import { forwardRef } from 'react'; - -import Link from '@/components/Link'; - -import styles from './index.module.css'; - -export type AvatarProps = { - image?: string; - name?: string; - nickname: string; - fallback?: string; - size?: 'small' | 'medium'; - url?: string; -}; - -// @TODO: We temporarily removed the Avatar Radix UI primitive, since it was causing flashing -// during initial load and not being able to render nicely when images are already cached. -// @see https://github.com/radix-ui/primitives/pull/3008 -const Avatar = forwardRef< - HTMLSpanElement, - HTMLAttributes & AvatarProps ->(({ image, nickname, name, fallback, url, size = 'small', ...props }, ref) => { - const Wrapper = url ? Link : 'div'; - - return ( - - - {image && ( - {name - )} - - {!image && ( - - {fallback} - - )} - - - ); -}); - -export default Avatar; diff --git a/apps/site/components/Common/AvatarGroup/Overlay/index.stories.tsx b/apps/site/components/Common/AvatarGroup/Overlay/index.stories.tsx deleted file mode 100644 index 1c462999ccd77..0000000000000 --- a/apps/site/components/Common/AvatarGroup/Overlay/index.stories.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import AvatarOverlay from '@/components/Common/AvatarGroup/Overlay'; -import { getAuthorWithId, getAuthorWithName } from '@/util/authorUtils'; - -type Story = StoryObj; -type Meta = MetaObj; - -export const Default: Story = { - args: getAuthorWithId(['nodejs'], true)[0], -}; - -export const FallBack: Story = { - args: getAuthorWithName(['Node.js'], true)[0], -}; - -export const WithoutName: Story = { - args: getAuthorWithId(['canerakdas'], true)[0], -}; - -export default { component: AvatarOverlay } as Meta; diff --git a/apps/site/components/Common/AvatarGroup/index.tsx b/apps/site/components/Common/AvatarGroup/index.tsx deleted file mode 100644 index 2bacc71b8ab7b..0000000000000 --- a/apps/site/components/Common/AvatarGroup/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -'use client'; - -import classNames from 'classnames'; -import type { FC } from 'react'; -import { useState, useMemo, Fragment } from 'react'; - -import type { AvatarProps } from '@/components/Common/AvatarGroup/Avatar'; -import Avatar from '@/components/Common/AvatarGroup/Avatar'; -import avatarstyles from '@/components/Common/AvatarGroup/Avatar/index.module.css'; -import AvatarOverlay from '@/components/Common/AvatarGroup/Overlay'; -import Tooltip from '@/components/Common/Tooltip'; - -import styles from './index.module.css'; - -type AvatarGroupProps = { - avatars: Array; - limit?: number; - isExpandable?: boolean; - size?: AvatarProps['size']; - container?: HTMLElement; -}; - -const AvatarGroup: FC = ({ - avatars, - limit = 10, - isExpandable = true, - size = 'small', - container, -}) => { - const [showMore, setShowMore] = useState(false); - - const renderAvatars = useMemo( - () => avatars.slice(0, showMore ? avatars.length : limit), - [showMore, avatars, limit] - ); - - return ( -
limit, - })} - > - {renderAvatars.map(({ ...avatar }) => ( - - } - > - - - - ))} - - {avatars.length > limit && ( - setShowMore(prev => !prev) : undefined} - className={classNames( - avatarstyles.avatar, - avatarstyles[size], - 'cursor-pointer' - )} - > - - {`${showMore ? '-' : '+'}${avatars.length - limit}`} - - - )} -
- ); -}; - -export default AvatarGroup; diff --git a/apps/site/components/Common/Blockquote/index.module.css b/apps/site/components/Common/Blockquote/index.module.css deleted file mode 100644 index 410802f9cb90f..0000000000000 --- a/apps/site/components/Common/Blockquote/index.module.css +++ /dev/null @@ -1,27 +0,0 @@ -.wrapper { - @apply flex - max-w-2xl - flex-col - items-start - gap-4 - self-stretch - border-l-2 - border-green-600 - py-2 - pl-5 - text-lg - font-semibold - text-neutral-900 - dark:border-green-400 - dark:text-white; - - & cite { - @apply font-regular - text-base - not-italic; - - &::before { - @apply content-['—_']; - } - } -} diff --git a/apps/site/components/Common/BlogPostCard/index.stories.tsx b/apps/site/components/Common/BlogPostCard/index.stories.tsx deleted file mode 100644 index dca5f00ccaa1c..0000000000000 --- a/apps/site/components/Common/BlogPostCard/index.stories.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import BlogPostCard from '@/components/Common/BlogPostCard'; - -type Story = StoryObj; -type Meta = MetaObj; - -export const Default: Story = { - args: { - title: 'Node.js March 17th Infrastructure Incident Post-mortem', - category: 'vulnerability', - description: - 'Starting on March 15th and going through to March 17th (with much of the issue being mitigated on the 16th), users were receiving intermittent 404 responses when trying to download Node.js from nodejs.org, or even accessing parts of the website.', - authors: ['Claudio Wunder'], - slug: '/blog/vulnerability/something', - date: new Date('17 October 2023'), - }, - decorators: [ - Story => ( -
- -
- ), - ], -}; - -export const MoreThanOneAuthor: Story = { - ...Default, - args: { - ...Default.args, - authors: [...(Default.args?.authors ?? []), 'Brian Muenzenmeyer'], - }, -}; - -export default { component: BlogPostCard } as Meta; diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbItem/index.module.css b/apps/site/components/Common/Breadcrumbs/BreadcrumbItem/index.module.css deleted file mode 100644 index 713fad32dac39..0000000000000 --- a/apps/site/components/Common/Breadcrumbs/BreadcrumbItem/index.module.css +++ /dev/null @@ -1,39 +0,0 @@ -.item { - @apply flex - max-w-fit - items-center - gap-5 - truncate - text-sm - font-medium; - - &:not(:last-child) { - @apply shrink-0; - } - - a { - @apply shrink - grow; - } - - &, - > a, - > a:hover { - @apply text-neutral-800 - motion-safe:transition-colors - dark:text-neutral-200; - } - - &.visuallyHidden { - @apply hidden; - } - - .separator { - @apply size-4 - max-w-fit - shrink-0 - grow - text-neutral-600 - dark:text-neutral-400; - } -} diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbLink/index.module.css b/apps/site/components/Common/Breadcrumbs/BreadcrumbLink/index.module.css deleted file mode 100644 index ea0d6b9db96b5..0000000000000 --- a/apps/site/components/Common/Breadcrumbs/BreadcrumbLink/index.module.css +++ /dev/null @@ -1,20 +0,0 @@ -.link { - @apply max-w-fit - truncate; - - &.active { - @apply rounded - bg-green-600 - px-2 - py-1 - font-semibold - text-white - motion-safe:transition-colors - dark:text-white; - - &:hover { - @apply bg-green-700 - text-white; - } - } -} diff --git a/apps/site/components/Common/Breadcrumbs/BreadcrumbRoot/index.module.css b/apps/site/components/Common/Breadcrumbs/BreadcrumbRoot/index.module.css deleted file mode 100644 index 51043398d4657..0000000000000 --- a/apps/site/components/Common/Breadcrumbs/BreadcrumbRoot/index.module.css +++ /dev/null @@ -1,7 +0,0 @@ -.list { - @apply xs:w-full - flex - w-screen - gap-5 - px-6; -} diff --git a/apps/site/components/Common/Button.tsx b/apps/site/components/Common/Button.tsx new file mode 100644 index 0000000000000..87d98febce2d1 --- /dev/null +++ b/apps/site/components/Common/Button.tsx @@ -0,0 +1,12 @@ +import BaseButton, { + type ButtonProps, +} from '@node-core/ui-components/Common/BaseButton'; +import type { FC, ComponentProps } from 'react'; + +import Link from '@/components/Link'; + +const Button: FC< + Omit & Omit, 'as' | 'size'> +> = props => ; + +export default Button; diff --git a/apps/site/components/Common/CodeBox.tsx b/apps/site/components/Common/CodeBox.tsx new file mode 100644 index 0000000000000..b95304a11a08e --- /dev/null +++ b/apps/site/components/Common/CodeBox.tsx @@ -0,0 +1,36 @@ +'use client'; + +import BaseCodeBox from '@node-core/ui-components/Common/BaseCodeBox'; +import { useTranslations } from 'next-intl'; +import type { FC, PropsWithChildren, ReactNode } from 'react'; + +import Link from '@/components/Link'; +import { useCopyToClipboard, useNotification } from '@/hooks'; + +type CodeBoxProps = { + language: string; + className?: string; + showCopyButton?: boolean; +}; + +const CodeBox: FC> = props => { + const [, copyToClipboard] = useCopyToClipboard(); + const notify = useNotification(); + const t = useTranslations(); + + const onCopy = (text: string, message: ReactNode) => { + copyToClipboard(text); + notify({ duration: 300, message }); + }; + return ( + + ); +}; + +export default CodeBox; diff --git a/apps/site/components/Common/CodeTabs/index.tsx b/apps/site/components/Common/CodeTabs/index.tsx deleted file mode 100644 index 614db0017079b..0000000000000 --- a/apps/site/components/Common/CodeTabs/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { ArrowUpRightIcon } from '@heroicons/react/24/solid'; -import type { ComponentProps, FC, PropsWithChildren } from 'react'; - -import Tabs from '@/components/Common/Tabs'; -import { Link } from '@/navigation.mjs'; - -import styles from './index.module.css'; - -type CodeTabsProps = Pick< - ComponentProps, - 'tabs' | 'defaultValue' -> & { - linkUrl?: string; - linkText?: string; -}; - -const CodeTabs: FC> = ({ - children, - linkUrl, - linkText, - ...props -}) => ( - - {linkText} - - - ) - } - > - {children} - -); - -export default CodeTabs; diff --git a/apps/site/components/Common/CrossLink.tsx b/apps/site/components/Common/CrossLink.tsx new file mode 100644 index 0000000000000..85ef3fc0c8809 --- /dev/null +++ b/apps/site/components/Common/CrossLink.tsx @@ -0,0 +1,19 @@ +import BaseCrossLink from '@node-core/ui-components/Common/BaseCrossLink'; +import type { CrossLinkProps } from '@node-core/ui-components/Common/BaseCrossLink'; +import { useTranslations } from 'next-intl'; +import type { FC } from 'react'; + +import Link from '@/components/Link'; + +const CrossLink: FC> = props => { + const t = useTranslations(); + return ( + + ); +}; + +export default CrossLink; diff --git a/apps/site/components/Common/LinkTabs.tsx b/apps/site/components/Common/LinkTabs.tsx new file mode 100644 index 0000000000000..7f45ef41b8994 --- /dev/null +++ b/apps/site/components/Common/LinkTabs.tsx @@ -0,0 +1,15 @@ +'use client'; + +import BaseLinkTabs from '@node-core/ui-components/Common/BaseLinkTabs'; +import type { LinkTabsProps } from '@node-core/ui-components/Common/BaseLinkTabs'; +import type { FC } from 'react'; + +import Link from '@/components/Link'; +import { useRouter } from '@/navigation.mjs'; + +const LinkTabs: FC> = props => { + const { push } = useRouter(); + return push(value)} as={Link} {...props} />; +}; + +export default LinkTabs; diff --git a/apps/site/components/Common/NodejsLogo/index.tsx b/apps/site/components/Common/NodejsLogo/index.tsx deleted file mode 100644 index 7ca383e10c505..0000000000000 --- a/apps/site/components/Common/NodejsLogo/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { FC } from 'react'; - -import Nodejs from '@/components/Icons/Logos/Nodejs'; -import type { LogoVariant } from '@/types'; - -import style from './index.module.css'; - -type NodejsLogoProps = { - variant?: LogoVariant; -}; - -const NodejsLogo: FC = ({ variant = 'default' }) => ( - -); - -export default NodejsLogo; diff --git a/apps/site/components/Common/Pagination.tsx b/apps/site/components/Common/Pagination.tsx new file mode 100644 index 0000000000000..05a759cba1d31 --- /dev/null +++ b/apps/site/components/Common/Pagination.tsx @@ -0,0 +1,30 @@ +import BasePagination from '@node-core/ui-components/Common/BasePagination'; +import type { PaginationProps } from '@node-core/ui-components/Common/BasePagination'; +import { useTranslations } from 'next-intl'; +import type { FC } from 'react'; + +import Link from '@/components/Link'; + +const Pagination: FC< + Omit +> = props => { + const t = useTranslations(); + return ( + + t('components.common.pagination.pageLabel', { pageNumber }) + } + {...props} + /> + ); +}; + +export default Pagination; diff --git a/apps/site/components/Containers/Footer/index.stories.tsx b/apps/site/components/Containers/Footer/index.stories.tsx deleted file mode 100644 index ec44bb46b083b..0000000000000 --- a/apps/site/components/Containers/Footer/index.stories.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import Footer from '@/components/Containers/Footer'; - -type Story = StoryObj; -type Meta = MetaObj; - -export const Default: Story = {}; - -export default { component: Footer } as Meta; diff --git a/apps/site/components/Containers/Footer/index.tsx b/apps/site/components/Containers/Footer/index.tsx deleted file mode 100644 index 3723b3c284f96..0000000000000 --- a/apps/site/components/Containers/Footer/index.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { useTranslations } from 'next-intl'; -import type { FC, SVGProps } from 'react'; - -import NavItem from '@/components/Containers/NavBar/NavItem'; -import Bluesky from '@/components/Icons/Social/Bluesky'; -import Discord from '@/components/Icons/Social/Discord'; -import GitHub from '@/components/Icons/Social/GitHub'; -import LinkedIn from '@/components/Icons/Social/LinkedIn'; -import Mastodon from '@/components/Icons/Social/Mastodon'; -import Slack from '@/components/Icons/Social/Slack'; -import Twitter from '@/components/Icons/Social/Twitter'; -import { siteNavigation } from '@/next.json.mjs'; - -import styles from './index.module.css'; - -const footerSocialIcons: Record>> = { - github: GitHub, - mastodon: Mastodon, - twitter: Twitter, - slack: Slack, - linkedin: LinkedIn, - bluesky: Bluesky, - discord: Discord, -}; - -const Footer: FC = () => { - const t = useTranslations(); - - const openJSlink = siteNavigation.footerLinks.at(-1)!; - - return ( -
-
- {siteNavigation.footerLinks.slice(0, -1).map(item => ( - - {t(item.text)} - - ))} -
- -
- - © {openJSlink.text} - - -
- {siteNavigation.socialLinks.map(link => { - const SocialIcon = footerSocialIcons[link.icon]; - - return ( - - - - ); - })} -
-
-
- ); -}; - -export default Footer; diff --git a/apps/site/components/Containers/NavBar/index.tsx b/apps/site/components/Containers/NavBar/index.tsx deleted file mode 100644 index 840ef6d242a35..0000000000000 --- a/apps/site/components/Containers/NavBar/index.tsx +++ /dev/null @@ -1,109 +0,0 @@ -'use client'; - -import Hamburger from '@heroicons/react/24/solid/Bars3Icon'; -import XMark from '@heroicons/react/24/solid/XMarkIcon'; -import * as Label from '@radix-ui/react-label'; -import classNames from 'classnames'; -import dynamic from 'next/dynamic'; -import { useTranslations } from 'next-intl'; -import { useState } from 'react'; -import type { FC, ComponentProps, HTMLAttributeAnchorTarget } from 'react'; - -import LanguageDropdown from '@/components/Common/LanguageDropDown'; -import Skeleton from '@/components/Common/Skeleton'; -import ThemeToggle from '@/components/Common/ThemeToggle'; -import NavItem from '@/components/Containers/NavBar/NavItem'; -import GitHub from '@/components/Icons/Social/GitHub'; -import Link from '@/components/Link'; -import WithNodejsLogo from '@/components/withNodejsLogo'; -import type { FormattedMessage } from '@/types'; - -import style from './index.module.css'; - -const SearchButton = dynamic(() => import('@/components/Common/Search'), { - ssr: false, - loading: () => ( - - ), -}); - -const navInteractionIcons = { - show: , - close: , -}; - -type NavbarProps = { - navItems: Array<{ - text: FormattedMessage; - link: string; - target?: HTMLAttributeAnchorTarget | undefined; - }>; - languages: ComponentProps; - onThemeTogglerClick: () => void; -}; - -const NavBar: FC = ({ - navItems, - languages, - onThemeTogglerClick, -}) => { - const [isMenuOpen, setIsMenuOpen] = useState(false); - const t = useTranslations(); - - return ( - - ); -}; - -export default NavBar; diff --git a/apps/site/components/Containers/Sidebar/SidebarGroup/index.tsx b/apps/site/components/Containers/Sidebar/SidebarGroup/index.tsx deleted file mode 100644 index f22de23fa5408..0000000000000 --- a/apps/site/components/Containers/Sidebar/SidebarGroup/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { ComponentProps, FC } from 'react'; - -import SidebarItem from '@/components/Containers/Sidebar/SidebarItem'; -import type { FormattedMessage } from '@/types'; - -import styles from './index.module.css'; - -type SidebarGroupProps = { - groupName: FormattedMessage; - items: Array>; -}; - -const SidebarGroup: FC = ({ groupName, items }) => ( -
- -
    - {items.map(({ label, link }) => ( - - ))} -
-
-); - -export default SidebarGroup; diff --git a/apps/site/components/Containers/Sidebar/SidebarItem/index.module.css b/apps/site/components/Containers/Sidebar/SidebarItem/index.module.css deleted file mode 100644 index 38c89b9fd6028..0000000000000 --- a/apps/site/components/Containers/Sidebar/SidebarItem/index.module.css +++ /dev/null @@ -1,33 +0,0 @@ -.sideBarItem { - @apply flex - w-full - list-none - text-neutral-800 - dark:text-neutral-200; - - a { - @apply inline-flex - items-center - gap-2 - p-2; - } - - .label { - @apply font-regular - w-full - text-sm; - } - - .icon { - @apply size-3 - text-neutral-500 - dark:text-neutral-200; - } -} - -.active { - @apply rounded - bg-green-600 - text-white - dark:text-white; -} diff --git a/apps/site/components/Downloads/DownloadButton/index.stories.tsx b/apps/site/components/Downloads/DownloadButton/index.stories.tsx deleted file mode 100644 index 84c68376a49a9..0000000000000 --- a/apps/site/components/Downloads/DownloadButton/index.stories.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import DownloadButton from '@/components/Downloads/DownloadButton'; - -type Story = StoryObj; -type Meta = MetaObj; - -export const Default: Story = { - args: { - release: { - currentStart: '2023-04-18', - ltsStart: '2023-10-24', - maintenanceStart: '2024-10-22', - endOfLife: '2026-04-30', - status: 'LTS', - major: 20, - version: '20.11.0', - versionWithPrefix: 'v20.11.0', - codename: 'Iron', - isLts: true, - npm: '10.2.4', - v8: '11.3.244.8', - releaseDate: '2024-01-09', - modules: '115', - }, - children: 'Download Node.js', - }, -}; - -export default { component: DownloadButton } as Meta; diff --git a/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx b/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx index a5d4a9307db52..c857c9b0ddf87 100644 --- a/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx +++ b/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx @@ -1,10 +1,10 @@ 'use client'; +import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import { useContext, useEffect, useMemo } from 'react'; import type { FC } from 'react'; -import Select from '@/components/Common/Select'; import { ReleaseContext } from '@/providers/releaseProvider'; import type { InstallationMethod } from '@/types/release'; import { nextItem, INSTALL_METHODS, parseCompat } from '@/util/downloadUtils'; diff --git a/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx b/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx index 536707ad2c506..578c0e0e0be15 100644 --- a/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx +++ b/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx @@ -1,10 +1,10 @@ 'use client'; +import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import { useContext, useEffect, useMemo } from 'react'; import type { FC } from 'react'; -import Select from '@/components/Common/Select'; import { useClientContext } from '@/hooks'; import { ReleaseContext } from '@/providers/releaseProvider'; import type { UserOS } from '@/types/userOS'; diff --git a/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx b/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx index 3faf61f0a1e9e..ff58252e08420 100644 --- a/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx +++ b/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx @@ -1,10 +1,10 @@ 'use client'; +import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import { useContext, useEffect, useMemo } from 'react'; import type { FC } from 'react'; -import Select from '@/components/Common/Select'; import { ReleaseContext } from '@/providers/releaseProvider'; import type { PackageManager } from '@/types/release'; import { nextItem, PACKAGE_MANAGERS, parseCompat } from '@/util/downloadUtils'; diff --git a/apps/site/components/Downloads/Release/PlatformDropdown.tsx b/apps/site/components/Downloads/Release/PlatformDropdown.tsx index de595264ab5d9..a6a30bbaef6a7 100644 --- a/apps/site/components/Downloads/Release/PlatformDropdown.tsx +++ b/apps/site/components/Downloads/Release/PlatformDropdown.tsx @@ -1,10 +1,10 @@ 'use client'; +import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import type { FC } from 'react'; import { useEffect, useContext, useMemo } from 'react'; -import Select from '@/components/Common/Select'; import { useClientContext } from '@/hooks'; import { ReleaseContext } from '@/providers/releaseProvider'; import type { UserPlatform } from '@/types/userOS'; diff --git a/apps/site/components/Downloads/Release/PrebuiltDownloadButtons.tsx b/apps/site/components/Downloads/Release/PrebuiltDownloadButtons.tsx index 5a943b0472a8b..b968e68651c2b 100644 --- a/apps/site/components/Downloads/Release/PrebuiltDownloadButtons.tsx +++ b/apps/site/components/Downloads/Release/PrebuiltDownloadButtons.tsx @@ -1,12 +1,12 @@ 'use client'; import { CloudArrowDownIcon } from '@heroicons/react/24/outline'; +import Skeleton from '@node-core/ui-components/Common/Skeleton'; import { useTranslations } from 'next-intl'; import { useContext } from 'react'; import type { FC } from 'react'; import Button from '@/components/Common/Button'; -import Skeleton from '@/components/Common/Skeleton'; import { ReleaseContext } from '@/providers/releaseProvider'; import { OperatingSystemLabel, diff --git a/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx b/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx index 70de6d02fc989..7678f7ac66f16 100644 --- a/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx +++ b/apps/site/components/Downloads/Release/ReleaseCodeBox.tsx @@ -1,12 +1,12 @@ 'use client'; +import AlertBox from '@node-core/ui-components/Common/AlertBox'; +import Skeleton from '@node-core/ui-components/Common/Skeleton'; import { useTranslations } from 'next-intl'; import type { FC } from 'react'; import { useContext, useMemo } from 'react'; -import AlertBox from '@/components/Common/AlertBox'; import CodeBox from '@/components/Common/CodeBox'; -import Skeleton from '@/components/Common/Skeleton'; import Link from '@/components/Link'; import LinkWithArrow from '@/components/LinkWithArrow'; import { createSval } from '@/next.jsx.compiler.mjs'; diff --git a/apps/site/components/Downloads/Release/VersionDropdown.tsx b/apps/site/components/Downloads/Release/VersionDropdown.tsx index 9cdff66faab24..2f8b1b015c358 100644 --- a/apps/site/components/Downloads/Release/VersionDropdown.tsx +++ b/apps/site/components/Downloads/Release/VersionDropdown.tsx @@ -1,10 +1,10 @@ 'use client'; +import Select from '@node-core/ui-components/Common/Select'; import { useTranslations } from 'next-intl'; import type { FC } from 'react'; import { useContext } from 'react'; -import Select from '@/components/Common/Select'; import { ReleaseContext, ReleasesContext } from '@/providers/releaseProvider'; const getDropDownStatus = (version: string, status: string) => { diff --git a/apps/site/components/Icons/InstallationMethod/FNM.tsx b/apps/site/components/Icons/InstallationMethod/FNM.tsx deleted file mode 100644 index 90407358527b3..0000000000000 --- a/apps/site/components/Icons/InstallationMethod/FNM.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import type { FC, SVGProps } from 'react'; - -// @todo: replace with original vector (https://github.com/Schniz/fnm/issues/798#issuecomment-2068220441) -// this is a rough tracing of the available raster image -const FNM: FC> = props => ( - - - - - - - - - - - - - - - - - - - - - - - - - - -); - -export default FNM; diff --git a/apps/site/components/Icons/InstallationMethod/index.ts b/apps/site/components/Icons/InstallationMethod/index.ts deleted file mode 100644 index 41c7897e2172f..0000000000000 --- a/apps/site/components/Icons/InstallationMethod/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Choco from '@/components/Icons/InstallationMethod/Choco'; -import Devbox from '@/components/Icons/InstallationMethod/Devbox'; -import Docker from '@/components/Icons/InstallationMethod/Docker'; -import FNM from '@/components/Icons/InstallationMethod/FNM'; -import Homebrew from '@/components/Icons/InstallationMethod/Homebrew'; -import NVM from '@/components/Icons/InstallationMethod/NVM'; -import Volta from '@/components/Icons/InstallationMethod/Volta'; - -export default { Choco, Devbox, Docker, FNM, Homebrew, NVM, Volta }; diff --git a/apps/site/components/Icons/OperatingSystem/index.ts b/apps/site/components/Icons/OperatingSystem/index.ts deleted file mode 100644 index 167e970f87135..0000000000000 --- a/apps/site/components/Icons/OperatingSystem/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import AIX from '@/components/Icons/OperatingSystem/AIX'; -import Apple from '@/components/Icons/OperatingSystem/Apple'; -import Linux from '@/components/Icons/OperatingSystem/Linux'; -import Microsoft from '@/components/Icons/OperatingSystem/Microsoft'; - -export default { AIX, Apple, Linux, Microsoft }; diff --git a/apps/site/components/Icons/PackageManager/index.ts b/apps/site/components/Icons/PackageManager/index.ts deleted file mode 100644 index 4ba989918ca4c..0000000000000 --- a/apps/site/components/Icons/PackageManager/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import NPM from '@/components/Icons/PackageManager/Npm'; -import PNPM from '@/components/Icons/PackageManager/Pnpm'; -import YARN from '@/components/Icons/PackageManager/Yarn'; - -export default { NPM, PNPM, YARN }; diff --git a/apps/site/components/MDX/Calendar/Event/index.stories.tsx b/apps/site/components/MDX/Calendar/Event/index.stories.tsx deleted file mode 100644 index d35c692533176..0000000000000 --- a/apps/site/components/MDX/Calendar/Event/index.stories.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import Event from '@/components/MDX/Calendar/Event'; - -type Story = StoryObj; -type Meta = MetaObj; - -export const Default: Story = { - args: { - start: { date: '2024-02-19T12:30:00.000Z' }, - end: { date: '2024-02-19T16:00:00.000Z' }, - summary: 'Example Event', - location: 'Event Location', - description: 'This is an example event description.', - }, -}; - -export default { component: Event } as Meta; diff --git a/apps/site/components/MDX/CodeBox/index.stories.tsx b/apps/site/components/MDX/CodeBox/index.stories.tsx deleted file mode 100644 index 8982fe9ef4e0b..0000000000000 --- a/apps/site/components/MDX/CodeBox/index.stories.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import { VFile } from 'vfile'; - -import { compile } from '@/next.mdx.compiler.mjs'; -import { MDX_COMPONENTS } from '@/next.mdx.components.mjs'; - -type Props = { children: string }; - -type Story = StoryObj; -type Meta = MetaObj; - -export const Default: Story = { - args: { - children: `\`\`\`javascript -const http = require('http'); - -const hostname = '127.0.0.1'; -const port = 3000; - -const server = http.createServer((req, res) => { - res.statusCode = 200; - res.setHeader('Content-Type', 'text/plain'); - res.end('Hello World'); -}); - -server.listen(port, hostname, () => { - console.log(\`Server running at http://\${hostname}:\${port}/\`); -}); -\`\`\``, - }, -}; - -export default { - title: 'MDX/CodeBox', - render: (_, { loaded: { Content } }) => Content, - loaders: [ - async ({ args }) => { - const { content } = await compile( - new VFile(args.children), - 'mdx', - MDX_COMPONENTS - ); - - return { Content: content }; - }, - ], -} as Meta; diff --git a/apps/site/components/MDX/CodeTabs/index.stories.tsx b/apps/site/components/MDX/CodeTabs/index.stories.tsx deleted file mode 100644 index dd6474be252f1..0000000000000 --- a/apps/site/components/MDX/CodeTabs/index.stories.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; -import { VFile } from 'vfile'; - -import { compile } from '@/next.mdx.compiler.mjs'; -import { MDX_COMPONENTS } from '@/next.mdx.components.mjs'; - -type Props = { children: string }; - -type Story = StoryObj; -type Meta = MetaObj; - -export const Default: Story = { - args: { - children: `\`\`\`mjs -const { createHmac } = await import('node:crypto'); - -const secret = 'abcdefg'; -const hash = createHmac('sha256', secret) - .update('I love cupcakes') - .digest('hex'); - -console.log(hash); -// Prints: -// c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e -\`\`\` - -\`\`\`cjs displayName="CommonJS" showCopyButton="true" -const { createHmac } = require('node:crypto'); - -const secret = 'abcdefg'; -const hash = createHmac('sha256', secret) - .update('I love cupcakes') - .digest('hex'); - -console.log(hash); -// Prints: -// c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e -\`\`\``, - }, -}; - -export default { - title: 'MDX/CodeTabs', - render: (_, { loaded: { Content } }) => Content, - loaders: [ - async ({ args }) => { - const { content } = await compile( - new VFile(args.children), - 'mdx', - MDX_COMPONENTS - ); - - return { Content: content }; - }, - ], -} as Meta; diff --git a/apps/site/components/MDX/CodeTabs/index.tsx b/apps/site/components/MDX/CodeTabs/index.tsx index 45adc0f5cf751..cebf603d2b37a 100644 --- a/apps/site/components/MDX/CodeTabs/index.tsx +++ b/apps/site/components/MDX/CodeTabs/index.tsx @@ -1,12 +1,8 @@ +import CodeTabs from '@node-core/ui-components/Common/CodeTabs'; import * as TabsPrimitive from '@radix-ui/react-tabs'; -import type { ComponentProps, FC, ReactElement } from 'react'; +import type { FC, ReactElement } from 'react'; -import CodeTabs from '@/components/Common/CodeTabs'; - -type MDXCodeTabsProps = Pick< - ComponentProps, - 'linkText' | 'linkUrl' -> & { +type MDXCodeTabsProps = { children: Array>; languages: string; displayNames?: string; diff --git a/apps/site/components/__design__/node-logos.stories.tsx b/apps/site/components/__design__/node-logos.stories.tsx deleted file mode 100644 index 157a5cd3df709..0000000000000 --- a/apps/site/components/__design__/node-logos.stories.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import JsIconGreen from '@/components/Icons/Logos/JsIconGreen'; -import JsIconWhite from '@/components/Icons/Logos/JsIconWhite'; -import NodejsLogo from '@/components/Icons/Logos/Nodejs'; -import NodejsStackedBlack from '@/components/Icons/Logos/NodejsStackedBlack'; -import NodejsStackedDark from '@/components/Icons/Logos/NodejsStackedDark'; -import NodejsStackedLight from '@/components/Icons/Logos/NodejsStackedLight'; -import NodejsStackedWhite from '@/components/Icons/Logos/NodejsStackedWhite'; - -export const HorizontalLogo: StoryObj = { - render: () => , -}; - -export const PrideLogo: StoryObj = { - render: () => , -}; - -export const StackedLogos: StoryObj = { - render: () => ( -
-
- - - - - - - -
-
- ), -}; - -export const JSSymbols: StoryObj = { - render: () => ( -
- - -
- ), -}; - -export default { title: 'Design System' } as MetaObj; diff --git a/apps/site/components/__design__/package-manager.stories.tsx b/apps/site/components/__design__/package-manager.stories.tsx deleted file mode 100644 index 76ecbe338b8eb..0000000000000 --- a/apps/site/components/__design__/package-manager.stories.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import PackageManagerIcons from '@/components/Icons/PackageManager'; - -export const PackageManager: StoryObj = { - render: () => ( -
- - - -
- ), -}; - -export default { title: 'Design System' } as MetaObj; diff --git a/apps/site/components/__design__/platform-logos.stories.tsx b/apps/site/components/__design__/platform-logos.stories.tsx deleted file mode 100644 index 39d6b20c7534f..0000000000000 --- a/apps/site/components/__design__/platform-logos.stories.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import InstallMethodIcons from '@/components/Icons/InstallationMethod'; -import OSIcons from '@/components/Icons/OperatingSystem'; - -export const PlatformLogos: StoryObj = { - render: () => ( -
-
- - - - -
-
- - - - - -
-
- -
-
- ), -}; - -export default { title: 'Design System' } as MetaObj; diff --git a/apps/site/components/__design__/social-logos.stories.tsx b/apps/site/components/__design__/social-logos.stories.tsx deleted file mode 100644 index 0fc12674a0ddc..0000000000000 --- a/apps/site/components/__design__/social-logos.stories.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -import Bluesky from '@/components/Icons/Social/Bluesky'; -import Discord from '@/components/Icons/Social/Discord'; -import GitHub from '@/components/Icons/Social/GitHub'; -import LinkedIn from '@/components/Icons/Social/LinkedIn'; -import Mastodon from '@/components/Icons/Social/Mastodon'; -import Slack from '@/components/Icons/Social/Slack'; -import Twitter from '@/components/Icons/Social/Twitter'; - -export const SocialMediaLogos: StoryObj = { - render: () => ( -
-
- - - -
-
- - - -
-
- -
-
- ), -}; - -export default { title: 'Design System' } as MetaObj; diff --git a/apps/site/components/__design__/table.stories.tsx b/apps/site/components/__design__/table.stories.tsx deleted file mode 100644 index fb5be4b91ecd4..0000000000000 --- a/apps/site/components/__design__/table.stories.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import type { Meta as MetaObj, StoryObj } from '@storybook/react'; - -export const Table: StoryObj = { - render: () => ( -
- - - - - - - - - - - - - - - - - - - - - - - - - -
Column 1Column 2Column 3
Data 1Data 2Data 3
Data 1Data 2Data 3
Data 1Data 2Data 3
-
- ), -}; - -export const HeadlessTable: StoryObj = { - render: () => ( -
- - - - - - - - - - - - - - - - - - -
Data 1Data 2Data 3
Data 1Data 2Data 3
Data 1Data 2Data 3
-
- ), -}; - -export default { title: 'Design System' } as MetaObj; diff --git a/apps/site/components/withAvatarGroup.tsx b/apps/site/components/withAvatarGroup.tsx index dbd8dee55e530..10bbcfc8732fb 100644 --- a/apps/site/components/withAvatarGroup.tsx +++ b/apps/site/components/withAvatarGroup.tsx @@ -1,12 +1,16 @@ +'use client'; + +import AvatarGroup from '@node-core/ui-components/Common/AvatarGroup'; +import Image from 'next/image'; import type { ComponentProps, FC } from 'react'; -import AvatarGroup from '@/components/Common/AvatarGroup'; +import Link from '@/components/Link'; import type { AuthorProps } from '@/types'; import { getAuthors } from '@/util/authorUtils'; type WithAvatarGroupProps = Omit< ComponentProps, - 'avatars' + 'avatars' | 'as' > & AuthorProps; @@ -22,6 +26,8 @@ const WithAvatarGroup: FC = ({ names: names, clickable: clickable, })} + as={Link} + img={Image} {...props} /> ); diff --git a/apps/site/components/withBadge.tsx b/apps/site/components/withBadge.tsx index b927605692ab1..7b91c84c373c5 100644 --- a/apps/site/components/withBadge.tsx +++ b/apps/site/components/withBadge.tsx @@ -1,6 +1,7 @@ +import Badge from '@node-core/ui-components/Common/Badge'; import type { FC } from 'react'; -import Badge from '@/components/Common/Badge'; +import Link from '@/components/Link'; import { siteConfig } from '@/next.json.mjs'; import { dateIsBetween } from '@/util/dateUtils'; @@ -9,7 +10,12 @@ const WithBadge: FC<{ section: string }> = ({ section }) => { if (badge && dateIsBetween(badge.startDate, badge.endDate)) { return ( - + {badge.text} ); diff --git a/apps/site/components/withBanner.tsx b/apps/site/components/withBanner.tsx index af571479ad8f4..32061a10c7284 100644 --- a/apps/site/components/withBanner.tsx +++ b/apps/site/components/withBanner.tsx @@ -1,6 +1,8 @@ +import { ArrowUpRightIcon } from '@heroicons/react/24/outline'; +import Banner from '@node-core/ui-components/Common/Banner'; import type { FC } from 'react'; -import Banner from '@/components/Common/Banner'; +import Link from '@/components/Link'; import { siteConfig } from '@/next.json.mjs'; import { dateIsBetween } from '@/util/dateUtils'; @@ -9,8 +11,13 @@ const WithBanner: FC<{ section: string }> = ({ section }) => { if (banner && dateIsBetween(banner.startDate, banner.endDate)) { return ( - - {banner.text} + + {banner.link ? ( + {banner.text} + ) : ( + banner.text + )} + {banner.link && } ); } diff --git a/apps/site/components/withBlogCategories.tsx b/apps/site/components/withBlogCategories.tsx index f061e93013187..3c4c4cbcb8d5b 100644 --- a/apps/site/components/withBlogCategories.tsx +++ b/apps/site/components/withBlogCategories.tsx @@ -1,7 +1,7 @@ import { useTranslations } from 'next-intl'; import type { ComponentProps, FC } from 'react'; -import BlogPostCard from '@/components/Common/BlogPostCard'; +import BlogPostCard from '@/components/Blog/BlogPostCard'; import LinkTabs from '@/components/Common/LinkTabs'; import Pagination from '@/components/Common/Pagination'; import type { BlogPostsRSC } from '@/types'; diff --git a/apps/site/components/withBreadcrumbs.tsx b/apps/site/components/withBreadcrumbs.tsx index 7d4c166c11350..2a740173061ae 100644 --- a/apps/site/components/withBreadcrumbs.tsx +++ b/apps/site/components/withBreadcrumbs.tsx @@ -1,9 +1,11 @@ 'use client'; +import type { BreadcrumbLink } from '@node-core/ui-components/Common/Breadcrumbs'; +import Breadcrumbs from '@node-core/ui-components/Common/Breadcrumbs'; +import { useTranslations } from 'next-intl'; import type { FC } from 'react'; -import type { BreadcrumbLink } from '@/components/Common/Breadcrumbs'; -import Breadcrumbs from '@/components/Common/Breadcrumbs'; +import Link from '@/components/Link'; import { useClientContext, useMediaQuery, useSiteNavigation } from '@/hooks'; import type { NavigationKeys } from '@/types'; import { dashToCamelCase } from '@/util/stringUtils'; @@ -14,6 +16,7 @@ type WithBreadcrumbsProps = { const WithBreadcrumbs: FC = ({ navKeys = [] }) => { const { getSideNavigation } = useSiteNavigation(); + const t = useTranslations(); const { pathname } = useClientContext(); const isMobileScreen = useMediaQuery('(max-width: 639px)'); @@ -57,7 +60,14 @@ const WithBreadcrumbs: FC = ({ navKeys = [] }) => { }, [] as Array); }; - return ; + return ( + + ); }; export default WithBreadcrumbs; diff --git a/apps/site/components/withFooter.tsx b/apps/site/components/withFooter.tsx index abdc64b295f53..53b9c96592b54 100644 --- a/apps/site/components/withFooter.tsx +++ b/apps/site/components/withFooter.tsx @@ -1,7 +1,28 @@ +'use client'; + +import Footer from '@node-core/ui-components/Containers/Footer'; +import { useTranslations } from 'next-intl'; import type { FC } from 'react'; -import Footer from '@/components/Containers/Footer'; +import Link from '@/components/Link'; +import { usePathname } from '@/navigation.mjs'; +import { siteNavigation } from '@/next.json.mjs'; + +const WithFooter: FC = () => { + const t = useTranslations(); + const pathname = usePathname(); + + const { socialLinks, footerLinks } = siteNavigation; + const updatedFooterLinks = footerLinks + .slice(0, -1) + .map(link => ({ ...link, text: t(link.text) })); + + // Add OpenJS link + updatedFooterLinks.push(footerLinks.at(-1)!); + + const navigation = { socialLinks, footerLinks: updatedFooterLinks }; -const WithFooter: FC = () =>