diff --git a/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx b/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx index 4cc89b2149d2a..671114fa084d6 100644 --- a/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx +++ b/apps/site/components/Downloads/Release/InstallationMethodDropdown.tsx @@ -5,7 +5,12 @@ import { useTranslations } from 'next-intl'; import { use, useEffect, useMemo } from 'react'; import { ReleaseContext } from '#site/providers/releaseProvider'; -import { nextItem, INSTALL_METHODS, parseCompat } from '#site/util/download'; +import { + availableItems, + nextItem, + INSTALL_METHODS, + parseCompat, +} from '#site/util/download'; import type { InstallationMethod } from '#site/types/release'; import type { FC } from 'react'; @@ -16,7 +21,7 @@ const InstallationMethodDropdown: FC = () => { // We parse the compatibility of the dropdown items const parsedInstallMethods = useMemo( - () => parseCompat(INSTALL_METHODS, release), + () => availableItems(parseCompat(INSTALL_METHODS, release)), // We only want to react on the change of the OS and Version // eslint-disable-next-line @eslint-react/exhaustive-deps [release.os, release.version] @@ -25,16 +30,17 @@ const InstallationMethodDropdown: FC = () => { // We group Platforms on the Platform Dropdown to provide the User // understanding of what is recommended/official and what is not. const grouppedMethods = useMemo( - () => [ - { - label: t('layouts.download.dropdown.platformGroups.official'), - items: parsedInstallMethods.filter(({ recommended }) => recommended), - }, - { - label: t('layouts.download.dropdown.platformGroups.unofficial'), - items: parsedInstallMethods.filter(({ recommended }) => !recommended), - }, - ], + () => + [ + { + label: t('layouts.download.dropdown.platformGroups.official'), + items: parsedInstallMethods.filter(({ recommended }) => recommended), + }, + { + label: t('layouts.download.dropdown.platformGroups.unofficial'), + items: parsedInstallMethods.filter(({ recommended }) => !recommended), + }, + ].filter(({ items }) => items.length > 0), // We only want to react on the change of the parsedPlatforms // eslint-disable-next-line @eslint-react/exhaustive-deps [parsedInstallMethods] @@ -46,10 +52,11 @@ const InstallationMethodDropdown: FC = () => { // `detectOS` has finished running and decided what platform we are running on. if (release.os !== 'LOADING' && release.installMethod === '') { const installationMethod = - // Sets either the utmost recommended platform or the first non-disabled one - // Note that the first item of groupped platforms is always the recommended one - nextItem('', grouppedMethods[0].items) || - nextItem('', parsedInstallMethods); + // Sets either the utmost recommended method or the first available one + nextItem( + '', + grouppedMethods[0]?.items ?? [] + ) || nextItem('', parsedInstallMethods); // This will never return an empty string as there should always be an item // when the OS has finished loading for a given installation method diff --git a/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx b/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx index b559a7b046373..3ae2cab5726ee 100644 --- a/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx +++ b/apps/site/components/Downloads/Release/OperatingSystemDropdown.tsx @@ -6,7 +6,12 @@ import { use, useEffect, useMemo } from 'react'; import useClientContext from '#site/hooks/useClientContext'; import { ReleaseContext } from '#site/providers/releaseProvider'; -import { nextItem, OPERATING_SYSTEMS, parseCompat } from '#site/util/download'; +import { + availableItems, + nextItem, + OPERATING_SYSTEMS, + parseCompat, +} from '#site/util/download'; import type { OperatingSystem } from '#site/types/userAgent'; import type { FC } from 'react'; @@ -30,7 +35,7 @@ const OperatingSystemDropdown: FC = () => { // We parse the compatibility of the dropdown items const parsedOperatingSystems = useMemo( - () => parseCompat(OPERATING_SYSTEMS, release), + () => availableItems(parseCompat(OPERATING_SYSTEMS, release)), // We only want to react on the change of the Install Method and Version // eslint-disable-next-line @eslint-react/exhaustive-deps [release.installMethod, release.version] diff --git a/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx b/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx index 8915c29d0dbb8..5a169577b0dea 100644 --- a/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx +++ b/apps/site/components/Downloads/Release/PackageManagerDropdown.tsx @@ -5,7 +5,12 @@ import { useTranslations } from 'next-intl'; import { use, useEffect, useMemo } from 'react'; import { ReleaseContext } from '#site/providers/releaseProvider'; -import { nextItem, PACKAGE_MANAGERS, parseCompat } from '#site/util/download'; +import { + availableItems, + nextItem, + PACKAGE_MANAGERS, + parseCompat, +} from '#site/util/download'; import type { PackageManager } from '#site/types/release'; import type { FC } from 'react'; @@ -16,7 +21,7 @@ const PackageManagerDropdown: FC = () => { // We parse the compatibility of the dropdown items const parsedPackageManagers = useMemo( - () => parseCompat(PACKAGE_MANAGERS, release), + () => availableItems(parseCompat(PACKAGE_MANAGERS, release)), // We only want to react on the change of the Version // eslint-disable-next-line @eslint-react/exhaustive-deps [release.version] diff --git a/apps/site/components/Downloads/Release/PlatformDropdown.tsx b/apps/site/components/Downloads/Release/PlatformDropdown.tsx index 3285d32fdb21c..0a2b547482dde 100644 --- a/apps/site/components/Downloads/Release/PlatformDropdown.tsx +++ b/apps/site/components/Downloads/Release/PlatformDropdown.tsx @@ -6,7 +6,12 @@ import { useEffect, use, useMemo, useState } from 'react'; import useClientContext from '#site/hooks/useClientContext'; import { ReleaseContext } from '#site/providers/releaseProvider'; -import { PLATFORMS, nextItem, parseCompat } from '#site/util/download'; +import { + availableItems, + PLATFORMS, + nextItem, + parseCompat, +} from '#site/util/download'; import { getUserPlatform } from '#site/util/userAgent'; import type { Platform } from '#site/types/userAgent'; @@ -48,7 +53,7 @@ const PlatformDropdown: FC = () => { // We only want to parse the compatibility when the OS has finished loading // Otherwise, we would be parsing the compatibility of an empty array release.os !== 'LOADING' - ? parseCompat(PLATFORMS[release.os], release) + ? availableItems(parseCompat(PLATFORMS[release.os], release)) : [], // We only want to react on the change of the OS, Platform, and Version // eslint-disable-next-line @eslint-react/exhaustive-deps diff --git a/apps/site/util/__tests__/download.test.mjs b/apps/site/util/__tests__/download.test.mjs index c1aa3ffd1c655..06c39c81d3ebc 100644 --- a/apps/site/util/__tests__/download.test.mjs +++ b/apps/site/util/__tests__/download.test.mjs @@ -3,6 +3,7 @@ import { describe, it } from 'node:test'; import { parseCompat, + availableItems, nextItem, OPERATING_SYSTEMS, INSTALL_METHODS, @@ -145,6 +146,19 @@ describe('nextItem', () => { }); }); +describe('availableItems', () => { + it('should filter disabled items out of dropdown options', () => { + const items = [ + { value: 'invalid', disabled: true }, + { value: 'valid', disabled: false }, + ]; + + assert.deepEqual(availableItems(items), [ + { value: 'valid', disabled: false }, + ]); + }); +}); + describe('INSTALL_METHODS', () => { it('should create icons only when an icon is configured', () => { const nvmMethod = INSTALL_METHODS.find(method => method.value === 'NVM'); @@ -153,4 +167,18 @@ describe('INSTALL_METHODS', () => { assert.ok(nvmMethod?.iconImage); assert.equal(asdfMethod?.iconImage, undefined); }); + + it('should recommend nvm on AIX', () => { + const aixMethods = availableItems( + parseCompat(INSTALL_METHODS, { + os: 'AIX', + installMethod: '', + platform: 'ppc64', + version: 'v24.0.0', + release: { status: 'LTS' }, + }) + ); + + assert.ok(aixMethods.some(method => method.value === 'NVM')); + }); }); diff --git a/apps/site/util/download/constants.json b/apps/site/util/download/constants.json index 1a349025d003c..51e8089a0cb1e 100644 --- a/apps/site/util/download/constants.json +++ b/apps/site/util/download/constants.json @@ -129,7 +129,7 @@ "icon": "NVM", "name": "nvm", "compatibility": { - "os": ["MAC", "LINUX", "OTHER"] + "os": ["MAC", "LINUX", "AIX", "OTHER"] }, "recommended": true, "url": "https://github.com/nvm-sh/nvm", diff --git a/apps/site/util/download/index.tsx b/apps/site/util/download/index.tsx index 69713f4fb005a..d865eeb73fd54 100644 --- a/apps/site/util/download/index.tsx +++ b/apps/site/util/download/index.tsx @@ -73,6 +73,13 @@ export const parseCompat = < })); }; +export const availableItems = < + K extends string, + T extends DownloadDropdownItem, +>( + items: Array +): Array => items.filter(({ disabled }) => !disabled); + /** * Creates an icon element for a component */