| | import React, { useEffect, useState } from 'react' |
| | import Cookies from '@/frame/components/lib/cookies' |
| | import { UnderlineNav } from '@primer/react' |
| | import { sendEvent } from '@/events/components/events' |
| | import { EventType } from '@/events/types' |
| | import { useRouter } from 'next/router' |
| |
|
| | import styles from './InArticlePicker.module.scss' |
| |
|
| | type Option = { |
| | value: string |
| | label: string |
| | } |
| | type Props = { |
| | |
| | defaultValue?: string |
| | |
| | fallbackValue: string |
| | cookieKey: string |
| | queryStringKey: string |
| | onValue: (value: string) => void |
| | preferenceName: string |
| | options: Option[] |
| | ariaLabel: string |
| | } |
| | export const InArticlePicker = ({ |
| | defaultValue, |
| | fallbackValue, |
| | cookieKey, |
| | queryStringKey, |
| | onValue, |
| | preferenceName, |
| | options, |
| | ariaLabel, |
| | }: Props) => { |
| | const router = useRouter() |
| | const { query, locale } = router |
| | const [currentValue, setCurrentValue] = useState('') |
| |
|
| | |
| | useEffect(() => { |
| | const raw = query[queryStringKey] |
| | let value = '' |
| | if (raw) { |
| | if (Array.isArray(raw)) value = raw[0] |
| | else value = raw |
| | } |
| | |
| | |
| | const possibleValues = options.map((option) => option.value) |
| | if (!value || !possibleValues.includes(value)) { |
| | const cookieValue = Cookies.get(cookieKey) |
| | if (defaultValue) { |
| | value = defaultValue |
| | } else if (cookieValue && possibleValues.includes(cookieValue)) { |
| | value = cookieValue |
| | } else { |
| | value = fallbackValue |
| | } |
| | } |
| | setCurrentValue(value) |
| | }, [query, fallbackValue, defaultValue, options]) |
| |
|
| | const [asPathRoot, asPathQuery = ''] = router.asPath.split('#')[0].split('?') |
| |
|
| | useEffect(() => { |
| | |
| | |
| | |
| | |
| | |
| | if (currentValue) { |
| | onValue(currentValue) |
| | } |
| | }, [ |
| | currentValue, |
| | |
| | |
| | |
| | |
| | |
| | asPathRoot, |
| | ]) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | useEffect(() => { |
| | let mounted = true |
| | const toggleVisibility = () => { |
| | if (document.visibilityState === 'visible') { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | setTimeout(() => { |
| | if (mounted) { |
| | onValue(currentValue) |
| | } |
| | }, 100) |
| | } |
| | } |
| | if (process.env.NODE_ENV === 'development') { |
| | document.addEventListener('visibilitychange', toggleVisibility) |
| | } |
| |
|
| | return () => { |
| | mounted = false |
| | if (process.env.NODE_ENV === 'development') { |
| | document.removeEventListener('visibilitychange', toggleVisibility) |
| | } |
| | } |
| | }, [currentValue]) |
| |
|
| | function onClickChoice(value: string) { |
| | const params = new URLSearchParams(asPathQuery) |
| | params.set(queryStringKey, value) |
| | const newPath = `/${locale}${asPathRoot}?${params}` |
| | router.push(newPath, undefined, { shallow: true, locale }) |
| |
|
| | sendEvent({ |
| | type: EventType.preference, |
| | preference_name: preferenceName, |
| | preference_value: value, |
| | }) |
| |
|
| | Cookies.set(cookieKey, value) |
| | } |
| |
|
| | const sharedContainerProps = { |
| | 'aria-label': ariaLabel, |
| | } |
| |
|
| | const params = new URLSearchParams(asPathQuery) |
| |
|
| | return ( |
| | <div data-testid={`${queryStringKey}-picker`} className={styles.container}> |
| | {/* The key attribute is required for a bug in UnderlineNav that doesn't render the component when there are changes to the items. */} |
| | <UnderlineNav key={router.asPath} {...sharedContainerProps}> |
| | {options.map((option) => { |
| | params.set(queryStringKey, option.value) |
| | const linkProps = { |
| | [`data-${queryStringKey}`]: option.value, |
| | } |
| | return ( |
| | <UnderlineNav.Item |
| | href={`?${params}`} |
| | key={option.value} |
| | aria-current={option.value === currentValue ? 'page' : undefined} |
| | onSelect={(event: React.MouseEvent | React.KeyboardEvent) => { |
| | event.preventDefault() |
| | onClickChoice(option.value) |
| | }} |
| | {...linkProps} |
| | > |
| | {option.label} |
| | </UnderlineNav.Item> |
| | ) |
| | })} |
| | </UnderlineNav> |
| | </div> |
| | ) |
| | } |
| |
|