diff --git a/cli/lib/build.js b/cli/lib/build.js index 320765443ed..46b31a74afb 100644 --- a/cli/lib/build.js +++ b/cli/lib/build.js @@ -83,7 +83,7 @@ const main = async ({loglevel, releases: rawReleases, useCurrent, navPath, conte ...release, version: version.toString(), // the default release is always controlled by the latest dist-tag - default: semver.eq(version, pack.latest), + default: semver.major(version) === semver.major(pack.latest), prerelease: version.prerelease.length > 0, } ) diff --git a/cli/test/index.js b/cli/test/index.js index dbfae1d05c1..1c659786b80 100644 --- a/cli/test/index.js +++ b/cli/test/index.js @@ -219,6 +219,26 @@ test('earlier release is latest', async t => { await build() }) +test('major containing the latest dist-tag stays latest when a newer patch is not tagged yet', async t => { + const {build, testdir, releases} = await mockBuild(t, { + packument: {versions: ['6.14.18', '7.24.2', '8.19.3', '9.0.0', '9.0.1'], latest: '9.0.0'}, + }) + + await build() + + const nav = yaml.parse(await fs.readFile(join(testdir, 'nav.yml'), 'utf-8')) + const builtUrls = releases.map(r => `/cli/${r.id}`) + const variants = nav.find(c => c.url === '/cli').variants.filter(v => builtUrls.includes(v.url)) + const v9 = variants.find(v => v.url === '/cli/v9') + assert.equal(v9.title, 'Version 9.0.1') + assert.equal(v9.type, 'latest') + assert.equal(v9.default, true) + assert.deepEqual( + variants.filter(v => v.type === 'latest').map(v => v.url), + ['/cli/v9'], + ) +}) + test('can skip fetching latest', async t => { const {build} = await mockBuild(t) diff --git a/content/nav.yml b/content/nav.yml index a6789c6e25e..c0ccdd60183 100644 --- a/content/nav.yml +++ b/content/nav.yml @@ -1897,8 +1897,8 @@ - title: Version 12.2.1 shortName: v12 url: /cli/v12 - default: false - type: legacy + default: true + type: latest children: - title: CLI Commands shortName: Commands diff --git a/src/components/variant-select.js b/src/components/variant-select.js index 30f9a5db95b..6dfcfd1623e 100644 --- a/src/components/variant-select.js +++ b/src/components/variant-select.js @@ -31,49 +31,73 @@ const VariantItem = ({title, shortName, url, active}) => { ) } -const useVariantFocus = () => { - const locationChange = useLocationChange() - const anchorRef = React.useRef(null) - - React.useEffect(() => { - if (locationChange.change && getNav.didVariantChange(locationChange.previous, locationChange.current)) { - anchorRef.current.focus() - } - }, [locationChange]) - - return anchorRef -} +const isPlainActivation = event => + !event.metaKey && + !event.ctrlKey && + !event.shiftKey && + !event.altKey && + (event.type === 'click' ? event.button === 0 : event.key === 'Enter') const VariantMenu = ({title, latest, current, prerelease, legacy}) => { const [open, setOpen] = React.useState(false) - const anchorRef = useVariantFocus() + const anchorRef = React.useRef(null) + const selectingRef = React.useRef(false) + const focusAnchorRef = React.useRef(false) const labelId = 'label-versions-list-item' const locationChange = useLocationChange() React.useEffect(() => { + selectingRef.current = false if (locationChange.change && getNav.didVariantChange(locationChange.previous, locationChange.current)) { + focusAnchorRef.current = true setOpen(false) } // eslint-disable-next-line react-hooks/exhaustive-deps -- granular deps are intentional; locationChange.current is a URL string, not a ref }, [locationChange.change, locationChange.current, locationChange.previous]) + React.useEffect(() => { + if (!open && focusAnchorRef.current) { + focusAnchorRef.current = false + anchorRef.current?.focus() + } + }, [open, locationChange]) + + const handleOpenChange = React.useCallback(nextOpen => { + if (!nextOpen && selectingRef.current) { + return + } + setOpen(nextOpen) + }, []) + + const trackSelection = React.useCallback(event => { + const item = event.target.closest?.('[role="menuitem"]') + selectingRef.current = Boolean(item && !item.hasAttribute('aria-current') && isPlainActivation(event)) + }, []) + + const closeMenu = React.useCallback(() => { + selectingRef.current = false + setOpen(false) + }, []) + return ( <>

Select CLI Version:

- + {title} - setOpen(false)}> - - - Current - - {current && } - {prerelease && } - + + + {latest || current || prerelease ? ( + + Current + {latest && } + {current && } + {prerelease && } + + ) : null} Legacy {legacy.map(item => (