Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/lib/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
)
Expand Down
20 changes: 20 additions & 0 deletions cli/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions content/nav.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
68 changes: 46 additions & 22 deletions src/components/variant-select.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
<p id={labelId} className={styles.Box}>
Select CLI Version:
</p>
<ActionMenu anchorRef={anchorRef} open={open} onOpenChange={setOpen}>
<ActionMenu anchorRef={anchorRef} open={open} onOpenChange={handleOpenChange}>
<ActionMenu.Button aria-describedby={labelId} className={styles.menuButton}>
{title}
</ActionMenu.Button>
<StyledOverlay width="auto" onEscape={() => setOpen(false)}>
<ActionList aria-labelledby={labelId}>
<ActionList.Group>
<ActionList.GroupHeading>Current</ActionList.GroupHeading>
<VariantItem {...latest} />
{current && <VariantItem {...current} />}
{prerelease && <VariantItem {...prerelease} />}
</ActionList.Group>
<StyledOverlay width="auto" onEscape={closeMenu}>
<ActionList aria-labelledby={labelId} onClickCapture={trackSelection} onKeyDownCapture={trackSelection}>
{latest || current || prerelease ? (
<ActionList.Group>
<ActionList.GroupHeading>Current</ActionList.GroupHeading>
{latest && <VariantItem {...latest} />}
{current && <VariantItem {...current} />}
{prerelease && <VariantItem {...prerelease} />}
</ActionList.Group>
) : null}
<ActionList.Group>
<ActionList.GroupHeading>Legacy</ActionList.GroupHeading>
{legacy.map(item => (
Expand Down
Loading