Skip to content

Commit

Permalink
Ability to close your language banner (#39223)
Browse files Browse the repository at this point in the history
  • Loading branch information
peterbe authored Jul 25, 2023
1 parent 99df899 commit 0251a4f
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 31 deletions.
9 changes: 7 additions & 2 deletions components/hooks/useUserLanguage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { USER_LANGUAGE_COOKIE_NAME } from '../../lib/constants.js'

export function useUserLanguage() {
const { locale } = useRouter()
const [userLanguage, setUserLanguage] = useState<string>('en')
const [userLanguage, setUserLanguage] = useState('en')
const { languages } = useLanguages()

useEffect(() => {
Expand All @@ -28,5 +28,10 @@ export function useUserLanguage() {
}
}, [locale])

return { userLanguage }
function setUserLanguageCookie(language: string) {
Cookies.set(USER_LANGUAGE_COOKIE_NAME, language)
setUserLanguage(language)
}

return { userLanguage, setUserLanguageCookie }
}
35 changes: 31 additions & 4 deletions components/page-header/HeaderNotifications.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useRouter } from 'next/router'
import cx from 'classnames'
import { XIcon } from '@primer/octicons-react'

import { useLanguages } from 'components/context/LanguagesContext'
import { useMainContext } from 'components/context/MainContext'
Expand All @@ -18,12 +19,14 @@ enum NotificationType {
type Notif = {
content: string
type?: NotificationType
onClose?: () => void
}

export const HeaderNotifications = () => {
const router = useRouter()
const { currentVersion } = useVersion()
const { relativePath, allVersions, data, currentPathWithoutLanguage, page } = useMainContext()
const { userLanguage } = useUserLanguage()
const { userLanguage, setUserLanguageCookie } = useUserLanguage()
const { languages } = useLanguages()

const { t } = useTranslation('header')
Expand All @@ -38,6 +41,18 @@ export const HeaderNotifications = () => {
translationNotices.push({
type: NotificationType.TRANSLATION,
content: `This article is also available in <a href="${href}">${languages[userLanguage]?.name}</a>.`,
onClose: () => {
try {
setUserLanguageCookie('en')
} catch (err) {
// You can never be too careful because setting a cookie
// can fail. For example, some browser
// extensions disallow all setting of cookies and attempts
// at the `document.cookie` setter could throw. Just swallow
// and move on.
console.warn('Unable to set cookie', err)
}
},
})
}
} else {
Expand Down Expand Up @@ -75,8 +90,9 @@ export const HeaderNotifications = () => {

return (
<div>
{allNotifications.map(({ type, content }, i) => {
{allNotifications.map(({ type, content, onClose }, i) => {
const isLast = i === allNotifications.length - 1

return (
<div
key={content}
Expand All @@ -91,8 +107,19 @@ export const HeaderNotifications = () => {
type === NotificationType.EARLY_ACCESS && 'color-bg-danger',
!isLast && 'border-bottom color-border-default',
)}
dangerouslySetInnerHTML={{ __html: content }}
/>
>
{onClose && (
<button
className="flash-close js-flash-close"
type="button"
aria-label="Close"
onClick={() => onClose()}
>
<XIcon size="small" className="octicon mr-1" />
</button>
)}
<p dangerouslySetInnerHTML={{ __html: content }} />
</div>
)
})}
</div>
Expand Down
39 changes: 14 additions & 25 deletions components/page-header/LanguagePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,19 @@
import { useRouter } from 'next/router'
import Cookies from 'components/lib/cookies'
import { GlobeIcon } from '@primer/octicons-react'

import { useLanguages } from 'components/context/LanguagesContext'
import { useTranslation } from 'components/hooks/useTranslation'
import { useUserLanguage } from 'components/hooks/useUserLanguage'
import { Picker } from 'components/ui/Picker'

import { USER_LANGUAGE_COOKIE_NAME } from '../../lib/constants.js'

function rememberPreferredLanguage(value: string) {
try {
// The reason we use a cookie and not local storage is because
// this cookie value is used and needed by the server. For
// example, when doing `GET /some/page` we need the cookie
// to redirect to `Location: /ja/some/page`.
// It's important it's *not* an HttpOnly cookie because we
// need this in the client-side which is used to determine
// the UI about displaying notifications about preferred
// language if your cookie doesn't match the current URL.
Cookies.set(USER_LANGUAGE_COOKIE_NAME, value)
} catch (err) {
// You can never be too careful because setting a cookie
// can fail. For example, some browser
// extensions disallow all setting of cookies and attempts
// at the `document.cookie` setter could throw. Just swallow
// and move on.
console.warn('Unable to set preferred language cookie', err)
}
}

type Props = {
mediumOrLower?: boolean
}

export const LanguagePicker = ({ mediumOrLower }: Props) => {
const router = useRouter()
const { languages } = useLanguages()
const { setUserLanguageCookie } = useUserLanguage()

const locale = router.locale || 'en'

Expand Down Expand Up @@ -74,7 +52,18 @@ export const LanguagePicker = ({ mediumOrLower }: Props) => {
pickerLabel={mediumOrLower ? 'Language' : ''}
iconButton={mediumOrLower ? undefined : GlobeIcon}
onSelect={(item) => {
if (item.extra?.locale) rememberPreferredLanguage(item.extra.locale)
if (item.extra?.locale) {
try {
setUserLanguageCookie(item.extra.locale)
} catch (err) {
// You can never be too careful because setting a cookie
// can fail. For example, some browser
// extensions disallow all setting of cookies and attempts
// at the `document.cookie` setter could throw. Just swallow
// and move on.
console.warn('Unable to set preferred language cookie', err)
}
}
}}
buttonBorder={mediumOrLower}
dataTestId="default-language"
Expand Down

0 comments on commit 0251a4f

Please sign in to comment.