11import { useRouter } from 'next/router'
2+ import { useState } from 'react'
3+ import { ActionMenu , ActionList } from '@primer/react'
24import { ArrowRightIcon , InfoIcon } from '@primer/octicons-react'
5+ import cx from 'classnames'
36
7+ import Cookies from '@/frame/components/lib/cookies'
8+ import { USER_VERSION_COOKIE_NAME } from '@/frame/lib/constants'
49import { useMainContext } from '@/frame/components/context/MainContext'
510import { DEFAULT_VERSION , useVersion } from '@/versions/components/useVersion'
611import { useTranslation } from '@/languages/components/useTranslation'
7- import { Picker } from '@/tools/components/Picker'
812
913import styles from './VersionPicker.module.scss'
1014
@@ -16,8 +20,9 @@ export const VersionPicker = ({ xs }: Props) => {
1620 const router = useRouter ( )
1721 const { currentVersion } = useVersion ( )
1822 const mainContext = useMainContext ( )
19- // Use TypeScript's "not null assertion" because mainContext.page` should
20- // will present in mainContext if it's gotten to the stage of React
23+ const [ open , setOpen ] = useState ( false )
24+ // Use TypeScript's "not null assertion" because mainContext.page should
25+ // be present in mainContext if it's gotten to the stage of React
2126 // rendering.
2227 const page = mainContext . page !
2328 const { allVersions, enterpriseServerVersions } = mainContext
@@ -32,13 +37,26 @@ export const VersionPicker = ({ xs }: Props) => {
3237 return prefix + router . asPath . replace ( `/${ currentVersion } ` , '' )
3338 }
3439
35- const allLinks = ( page . applicableVersions || [ ] ) . map ( ( pageVersion ) => ( {
40+ type VersionPickerLink = {
41+ text : string
42+ selected : boolean
43+ href : string
44+ extra : {
45+ arrow : boolean
46+ info : boolean
47+ version ?: string
48+ }
49+ divider : boolean
50+ }
51+
52+ const allLinks : VersionPickerLink [ ] = ( page . applicableVersions || [ ] ) . map ( ( pageVersion ) => ( {
3653 text : allVersions [ pageVersion ] . versionTitle ,
3754 selected : currentVersion === pageVersion ,
3855 href : versionToHref ( pageVersion ) ,
3956 extra : {
4057 arrow : false ,
4158 info : false ,
59+ version : pageVersion ,
4260 } ,
4361 divider : false ,
4462 } ) )
@@ -54,6 +72,7 @@ export const VersionPicker = ({ xs }: Props) => {
5472 extra : {
5573 arrow : false ,
5674 info : false ,
75+ version : undefined ,
5776 } ,
5877 divider : true ,
5978 } )
@@ -66,6 +85,7 @@ export const VersionPicker = ({ xs }: Props) => {
6685 extra : {
6786 arrow : true ,
6887 info : false ,
88+ version : undefined ,
6989 } ,
7090 divider : false ,
7191 } )
@@ -81,32 +101,73 @@ export const VersionPicker = ({ xs }: Props) => {
81101 extra : {
82102 arrow : false ,
83103 info : true ,
104+ version : undefined ,
84105 } ,
85106 divider : false ,
86107 } )
87108 }
88109
110+ const selectedOption = allLinks . find ( ( item ) => item . selected )
111+
112+ const handleVersionSelect = ( item : VersionPickerLink ) => {
113+ // Save the user's version preference when they actively select one
114+ if ( item . extra ?. version ) {
115+ try {
116+ Cookies . set ( USER_VERSION_COOKIE_NAME , item . extra . version )
117+ } catch ( err ) {
118+ console . warn ( 'Unable to set preferred version cookie' , err )
119+ }
120+ }
121+ setOpen ( false )
122+ // Navigate after setting cookie
123+ if ( item . href ) {
124+ router . push ( item . href )
125+ }
126+ }
127+
89128 return (
90129 < div data-testid = "version-picker" className = { xs ? 'd-flex' : '' } >
91- < Picker
92- defaultText = { t ( 'version_picker_default_text' ) }
93- items = { allLinks }
94- alignment = "end"
95- pickerLabel = { xs ? `Version\n` : `Version: ` }
96- dataTestId = "field"
97- descriptionFontSize = { xs ? 6 : 5 }
98- renderItem = { ( item ) => {
99- return (
100- < div data-testid = "version-picker-item" className = { styles . itemsWidth } >
101- { item . text }
102- { item . extra ?. arrow && (
103- < ArrowRightIcon verticalAlign = "middle" size = { 15 } className = "ml-1" />
104- ) }
105- { item . extra ?. info && < InfoIcon verticalAlign = "middle" size = { 15 } className = "ml-1" /> }
106- </ div >
107- )
108- } }
109- />
130+ < ActionMenu open = { open } onOpenChange = { setOpen } >
131+ < ActionMenu . Button
132+ variant = "invisible"
133+ className = { `color-fg-default width-full p-1 pl-2 pr-2` }
134+ >
135+ < span className = { styles . pickerLabel } > { xs ? `Version\n` : `Version: ` } </ span >
136+ < span className = { `f${ xs ? 6 : 5 } color-fg-muted text-normal` } data-testid = "field" >
137+ { selectedOption ?. text || t ( 'version_picker_default_text' ) }
138+ </ span >
139+ </ ActionMenu . Button >
140+ < ActionMenu . Overlay width = "auto" align = "end" >
141+ < ActionList selectionVariant = "single" role = "menu" >
142+ { allLinks . map ( ( item , i ) =>
143+ item . divider ? (
144+ < ActionList . Divider key = { `divider${ i } ` } />
145+ ) : (
146+ < ActionList . Item
147+ key = { item . text }
148+ active = { item . selected }
149+ onSelect = { ( e ) => {
150+ e . preventDefault ( )
151+ handleVersionSelect ( item )
152+ } }
153+ className = { cx ( ( item . extra ?. arrow || item . extra ?. info ) && styles . extrasDisplay ) }
154+ role = { item . extra ?. arrow || item . extra ?. info ? 'menuitem' : 'menuitemradio' }
155+ >
156+ < div data-testid = "version-picker-item" className = { styles . itemsWidth } >
157+ { item . text }
158+ { item . extra ?. arrow && (
159+ < ArrowRightIcon verticalAlign = "middle" size = { 15 } className = "ml-1" />
160+ ) }
161+ { item . extra ?. info && (
162+ < InfoIcon verticalAlign = "middle" size = { 15 } className = "ml-1" />
163+ ) }
164+ </ div >
165+ </ ActionList . Item >
166+ ) ,
167+ ) }
168+ </ ActionList >
169+ </ ActionMenu . Overlay >
170+ </ ActionMenu >
110171 </ div >
111172 )
112173}
0 commit comments