Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Download layout #6353

Merged
merged 37 commits into from Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4f4042f
feat: Download layout
canerakdas Feb 18, 2024
faef142
refactor: utils methods moved into the file
canerakdas Feb 18, 2024
317ece0
refactor/docs: deprecated url path
canerakdas Feb 18, 2024
4adac14
refactor/docs: download utils
canerakdas Feb 18, 2024
a2b384c
refactor: markdown formatting
canerakdas Feb 18, 2024
2c18aec
fix: download URL paths
canerakdas Feb 18, 2024
197bd51
feat: exclude option for os dropdown
canerakdas Feb 18, 2024
4d1d45d
refactor: separated type import
canerakdas Feb 18, 2024
de4494c
refactor: styling updates
canerakdas Feb 19, 2024
48fe7b9
refactor: type definitions moved into the own file
canerakdas Feb 19, 2024
68b4f3e
test: unit tests for download utils
canerakdas Feb 19, 2024
cd5202c
fix: icons added into the download/source buttons
canerakdas Feb 19, 2024
d6458a0
fix: LinkWithArrow import path
canerakdas Feb 19, 2024
35ab171
docs/refactor: source button
canerakdas Feb 19, 2024
d7e140d
refactor: review updates
canerakdas Feb 21, 2024
2a64735
chore: code-review and code improvements
ovflowd Feb 24, 2024
98ebdf0
feat: finished download page
ovflowd Feb 24, 2024
c0dfcc1
chore: minor fixes
ovflowd Feb 24, 2024
ad28eae
chore: code-review and bug fixes
ovflowd Feb 24, 2024
8785df3
chore: prevent page reload on tab change
ovflowd Feb 24, 2024
c9eb3af
chore: improve activelink matching
ovflowd Feb 24, 2024
2e0374f
feat: proper download versions and prebuilt binaries
ovflowd Feb 24, 2024
7a0b5c9
feat: added more support text for each respective version
ovflowd Feb 24, 2024
41ef000
chore: minor bitness fixes for macOS
ovflowd Feb 24, 2024
dce64dd
refactor: cleanup of certain logic and added docker package manager
ovflowd Feb 24, 2024
9b3ea7a
chore: fix shiki interop client-server
ovflowd Feb 24, 2024
44583e1
chore: fix unit test
ovflowd Feb 24, 2024
8d275f7
chore: minor text correction
ovflowd Feb 24, 2024
42f3a3e
chore: nvm uses v prefix
ovflowd Feb 24, 2024
10140c3
chore: rename label to ARM64 to keep it easier to understand
ovflowd Feb 24, 2024
51aba24
refactor: reduce layout shift and improve select accessibility
canerakdas Feb 24, 2024
2d595d2
chore: reduce layout shift, simplify codebox and cleanup text
ovflowd Feb 25, 2024
642238f
Apply suggestions from code review
bmuenzenmeyer Feb 25, 2024
009f192
added Docker platform logo, grouped by usage and alphabetized
bmuenzenmeyer Feb 25, 2024
eaa7fc5
chore: minor changes and fixes
ovflowd Feb 25, 2024
dc82ca1
chore: reduce build times by making build of these routes on-demand
ovflowd Feb 25, 2024
1ab41b1
fix: keep same bitness if compatible on OS change, verify OS supports…
ovflowd Feb 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .husky/pre-commit
Expand Up @@ -3,3 +3,6 @@

# lint and format staged files
npx lint-staged

# verify typescript staged files
npx tsc --build .
4 changes: 2 additions & 2 deletions components/Common/ActiveLink/index.tsx
Expand Up @@ -4,7 +4,7 @@ import classNames from 'classnames';
import type { ComponentProps, FC } from 'react';

import Link from '@/components/Link';
import { useClientContext } from '@/hooks';
import { usePathname } from '@/navigation.mjs';

type ActiveLocalizedLinkProps = ComponentProps<typeof Link> & {
activeClassName?: string;
Expand All @@ -19,7 +19,7 @@ const ActiveLink: FC<ActiveLocalizedLinkProps> = ({
href = '',
...props
}) => {
const { pathname } = useClientContext();
const pathname = usePathname();

const finalClassName = classNames(className, {
[activeClassName]: allowSubPath
Expand Down
3 changes: 2 additions & 1 deletion components/Common/Breadcrumbs/index.tsx
@@ -1,4 +1,5 @@
import { useMemo, type FC } from 'react';
import type { FC } from 'react';
import { useMemo } from 'react';

import BreadcrumbHomeLink from '@/components/Common/Breadcrumbs/BreadcrumbHomeLink';
import BreadcrumbItem from '@/components/Common/Breadcrumbs/BreadcrumbItem';
Expand Down
8 changes: 5 additions & 3 deletions components/Common/CodeBox/index.tsx
Expand Up @@ -16,7 +16,7 @@ import styles from './index.module.css';

// Transforms a code element with plain text content into a more structured
// format for rendering with line numbers
const transformCode = (code: ReactNode): ReactNode => {
const transformCode = (code: ReactNode, language: string): ReactNode => {
if (!isValidElement(code)) {
// Early return when the `CodeBox` child is not a valid element since the
// type is a ReactNode, and can assume any value
Expand All @@ -35,8 +35,10 @@ const transformCode = (code: ReactNode): ReactNode => {
// being an empty string, so we need to remove it
const lines = content.split('\n');

const extraStyle = language.length === 0 ? { fontFamily: 'monospace' } : {};

return (
<code style={{ fontFamily: 'monospace' }}>
<code style={extraStyle}>
{lines
.flatMap((line, lineIndex) => {
const columns = line.split(' ');
Expand Down Expand Up @@ -97,7 +99,7 @@ const CodeBox: FC<PropsWithChildren<CodeBoxProps>> = ({
return (
<div className={styles.root}>
<pre ref={ref} className={styles.content} tabIndex={0}>
{transformCode(children)}
{transformCode(children, language)}
</pre>

{language && (
Expand Down
15 changes: 10 additions & 5 deletions components/Common/LinkTabs/index.module.css
Expand Up @@ -28,9 +28,14 @@
}
}

.tabsSelect > div {
@apply my-6
hidden
w-full
xs:flex;
.tabsSelect {
@apply sm:visible
md:hidden;

> span {
@apply my-6
hidden
w-full
xs:flex;
}
}
2 changes: 1 addition & 1 deletion components/Common/ProgressionSidebar/index.module.css
Expand Up @@ -20,7 +20,7 @@
xs:hidden;
}

> div {
> span {
@apply hidden
w-full
xs:flex;
Expand Down
12 changes: 6 additions & 6 deletions components/Common/ProgressionSidebar/index.tsx
Expand Up @@ -27,19 +27,19 @@ const ProgressionSidebar: FC<ProgressionSidebarProps> = ({ groups }) => {

return (
<nav className={styles.wrapper}>
<WithRouterSelect
label={t('components.common.sidebar.title')}
values={selectItems}
defaultValue={currentItem?.value}
/>

{groups.map(({ groupName, items }) => (
<ProgressionSidebarGroup
key={groupName.toString()}
groupName={groupName}
items={items}
/>
))}

<WithRouterSelect
label={t('components.common.sidebar.title')}
values={selectItems}
defaultValue={currentItem?.value}
/>
</nav>
);
};
Expand Down
8 changes: 5 additions & 3 deletions components/Common/Select/index.module.css
@@ -1,6 +1,5 @@
.select {
@apply flex
w-fit
@apply inline-flex
flex-col
gap-1.5;

Expand Down Expand Up @@ -47,6 +46,7 @@

.trigger span {
@apply flex
h-5
items-center
gap-2;
}
Expand Down Expand Up @@ -115,9 +115,11 @@
.text {
@apply text-neutral-900
data-[highlighted]:bg-neutral-100
data-[disabled]:text-neutral-600
data-[highlighted]:text-neutral-900
dark:text-white
dark:data-[highlighted]:bg-neutral-900;
dark:data-[highlighted]:bg-neutral-900
dark:data-[disabled]:text-neutral-700;
}

&.dropdown {
Expand Down
19 changes: 15 additions & 4 deletions components/Common/Select/index.tsx
Expand Up @@ -14,6 +14,7 @@ type SelectValue = {
label: FormattedMessage;
value: string;
iconImage?: React.ReactNode;
disabled?: boolean;
};

type SelectGroup = {
Expand All @@ -34,6 +35,7 @@ type SelectProps = {
label?: string;
inline?: boolean;
onChange?: (value: string) => void;
className?: string;
};

const Select: FC<SelectProps> = ({
Expand All @@ -43,6 +45,7 @@ const Select: FC<SelectProps> = ({
label,
inline,
onChange,
className,
}) => {
const id = useId();

Expand All @@ -61,12 +64,19 @@ const Select: FC<SelectProps> = ({
}, [values]);

return (
<div className={classNames(styles.select, { [styles.inline]: inline })}>
{label && (
<span
className={classNames(
styles.select,
{ [styles.inline]: inline },
className
)}
>
{label && !inline && (
<label className={styles.label} htmlFor={id}>
{label}
</label>
)}

<Primitive.Root value={defaultValue} onValueChange={onChange}>
<Primitive.Trigger
className={styles.trigger}
Expand All @@ -92,10 +102,11 @@ const Select: FC<SelectProps> = ({
</Primitive.Label>
)}

{items.map(({ value, label, iconImage }) => (
{items.map(({ value, label, iconImage, disabled }) => (
<Primitive.Item
key={value}
value={value}
disabled={disabled}
className={classNames(styles.item, styles.text)}
>
<Primitive.ItemText>
Expand All @@ -110,7 +121,7 @@ const Select: FC<SelectProps> = ({
</Primitive.Content>
</Primitive.Portal>
</Primitive.Root>
</div>
</span>
);
};

Expand Down
1 change: 1 addition & 0 deletions components/Containers/NavBar/NavItem/index.tsx
Expand Up @@ -27,6 +27,7 @@ const NavItem: FC<PropsWithChildren<NavItemProps>> = ({
allowSubPath={href.startsWith('/')}
>
<span className={styles.label}>{children}</span>

{type === 'nav' && href.startsWith('http') && (
<ArrowUpRightIcon className={styles.icon} />
)}
Expand Down
2 changes: 1 addition & 1 deletion components/Containers/Sidebar/index.module.css
Expand Up @@ -22,7 +22,7 @@
xs:hidden;
}

> div {
> span {
@apply hidden
w-full
xs:flex;
Expand Down
4 changes: 2 additions & 2 deletions components/Downloads/DownloadButton/index.tsx
Expand Up @@ -7,7 +7,7 @@ import type { FC, PropsWithChildren } from 'react';
import Button from '@/components/Common/Button';
import { useDetectOS } from '@/hooks';
import type { NodeRelease } from '@/types';
import { downloadUrlByOS } from '@/util/downloadUrlByOS';
import { getNodeDownloadUrl } from '@/util/getNodeDownloadUrl';

import styles from './index.module.css';

Expand All @@ -18,7 +18,7 @@ const DownloadButton: FC<PropsWithChildren<DownloadButtonProps>> = ({
children,
}) => {
const { os, bitness } = useDetectOS();
const downloadLink = downloadUrlByOS(versionWithPrefix, os, bitness);
const downloadLink = getNodeDownloadUrl(versionWithPrefix, os, bitness);

return (
<>
Expand Down
4 changes: 2 additions & 2 deletions components/Downloads/DownloadLink.tsx
Expand Up @@ -4,7 +4,7 @@ import type { FC, PropsWithChildren } from 'react';

import { useDetectOS } from '@/hooks';
import type { NodeRelease } from '@/types';
import { downloadUrlByOS } from '@/util/downloadUrlByOS';
import { getNodeDownloadUrl } from '@/util/getNodeDownloadUrl';

type DownloadLinkProps = { release: NodeRelease };

Expand All @@ -13,7 +13,7 @@ const DownloadLink: FC<PropsWithChildren<DownloadLinkProps>> = ({
children,
}) => {
const { os, bitness } = useDetectOS();
const downloadLink = downloadUrlByOS(versionWithPrefix, os, bitness);
const downloadLink = getNodeDownloadUrl(versionWithPrefix, os, bitness);

return <a href={downloadLink}>{children}</a>;
};
Expand Down
66 changes: 66 additions & 0 deletions components/Downloads/Release/BitnessDropdown.tsx
@@ -0,0 +1,66 @@
'use client';

import { useTranslations } from 'next-intl';
import type { FC } from 'react';
import { useEffect, useContext, useMemo } from 'react';
import semVer from 'semver';

import Select from '@/components/Common/Select';
import { useDetectOS } from '@/hooks/react-client';
import { ReleaseContext } from '@/providers/releaseProvider';
import { bitnessItems, formatDropdownItems } from '@/util/downloadUtils';

const parseNumericBitness = (bitness: string) =>
/^\d+$/.test(bitness) ? Number(bitness) : bitness;

const BitnessDropdown: FC = () => {
const { bitness: userBitness } = useDetectOS();
const { bitness, os, release, setBitness } = useContext(ReleaseContext);
const t = useTranslations();

// we also reset the bitness when the OS changes, because different OSs have
// different bitnesses available
useEffect(() => setBitness(userBitness), [setBitness, os, userBitness]);

// @TOOD: We should have a proper utility that gives
bmuenzenmeyer marked this conversation as resolved.
Show resolved Hide resolved
// disabled OSs, Platforms, based on specific criteria
// this can be an optimisation for the future
// to remove this logic from this component
const disabledItems = useMemo(() => {
const disabledItems = [];

if (os === 'WIN' && semVer.satisfies(release.version, '< 19.9.0')) {
disabledItems.push('arm64');
}

if (os === 'LINUX' && semVer.satisfies(release.version, '< 4.0.0')) {
disabledItems.push('arm64', 'armv7l');
}

if (os === 'LINUX' && semVer.satisfies(release.version, '< 4.4.0')) {
disabledItems.push('ppc64le');
}

if (os === 'LINUX' && semVer.satisfies(release.version, '< 6.6.0')) {
disabledItems.push('s390x');
}

return disabledItems;
}, [os, release.version]);

return (
<Select
label={t('layouts.download.dropdown.bitness')}
values={formatDropdownItems({
items: bitnessItems[os],
disabledItems,
})}
defaultValue={String(bitness)}
onChange={bitness => setBitness(parseNumericBitness(bitness))}
className="w-28"
inline={true}
/>
);
};

export default BitnessDropdown;
18 changes: 18 additions & 0 deletions components/Downloads/Release/BlogPostLink.tsx
@@ -0,0 +1,18 @@
'use client';

import type { FC, PropsWithChildren } from 'react';
import { useContext } from 'react';

import LinkWithArrow from '@/components/Downloads/Release/LinkWithArrow';
import { ReleaseContext } from '@/providers/releaseProvider';

const BlogPostLink: FC<PropsWithChildren> = ({ children }) => {
const { release } = useContext(ReleaseContext);
const version = release.versionWithPrefix;

return (
<LinkWithArrow href={`/blog/release/${version}`}>{children}</LinkWithArrow>
);
};

export default BlogPostLink;
32 changes: 32 additions & 0 deletions components/Downloads/Release/DownloadButton.tsx
@@ -0,0 +1,32 @@
'use client';

import { CloudArrowDownIcon } from '@heroicons/react/24/outline';
import { useTranslations } from 'next-intl';
import { useContext } from 'react';
import type { FC } from 'react';

import Button from '@/components/Common/Button';
import { ReleaseContext } from '@/providers/releaseProvider';
import { getNodeDownloadUrl } from '@/util/getNodeDownloadUrl';

type DownloadButtonProps = { kind: 'installer' | 'binary' | 'source' };

const DownloadButton: FC<DownloadButtonProps> = ({ kind = 'installer' }) => {
const t = useTranslations();
const { release, os, bitness } = useContext(ReleaseContext);

const version = release.versionWithPrefix;
const url = getNodeDownloadUrl(version, os, bitness, kind);

return (
<div className="mb-2 mt-6">
<Button href={url} disabled={!version}>
<CloudArrowDownIcon />

{t('layouts.download.buttons.prebuilt', { version })}
</Button>
</div>
);
};

export default DownloadButton;