Skip to content

Commit 3414934

Browse files
authored
account for header notifications in Copilot Search UI (#55298)
1 parent a6e0364 commit 3414934

File tree

4 files changed

+71
-2
lines changed

4 files changed

+71
-2
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// For places where we might need to know the "state" of one component from another component
2+
// Useful for when the inheritance between the two components is too complicated for passing down props, or they aren't in the same tree
3+
4+
import React, { createContext, useContext, useState } from 'react'
5+
6+
type SharedUIContextType = {
7+
hasOpenHeaderNotifications: boolean
8+
setHasOpenHeaderNotifications: (value: boolean) => void
9+
}
10+
11+
const SharedUIContext = createContext<SharedUIContextType | undefined>(undefined)
12+
13+
export const useSharedUIContext = (): SharedUIContextType => {
14+
const context = useContext(SharedUIContext)
15+
if (!context) {
16+
throw new Error('useSharedUIContext must be used within a SharedUIContextProvider')
17+
}
18+
return context
19+
}
20+
21+
export const SharedUIContextProvider = ({ children }: { children: React.ReactNode }) => {
22+
const [hasOpenHeaderNotifications, setHasOpenHeaderNotifications] = useState(false)
23+
24+
return (
25+
<SharedUIContext.Provider
26+
value={{
27+
hasOpenHeaderNotifications: hasOpenHeaderNotifications,
28+
setHasOpenHeaderNotifications: setHasOpenHeaderNotifications,
29+
}}
30+
>
31+
{children}
32+
</SharedUIContext.Provider>
33+
)
34+
}

src/frame/components/page-header/HeaderNotifications.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useEffect } from 'react'
12
import { useRouter } from 'next/router'
23
import cx from 'classnames'
34
import { XIcon } from '@primer/octicons-react'
@@ -9,6 +10,7 @@ import { ExcludesNull } from 'src/frame/components/lib/ExcludesNull'
910
import { useVersion } from 'src/versions/components/useVersion'
1011
import { useUserLanguage } from 'src/languages/components/useUserLanguage'
1112
import styles from './HeaderNotifications.module.scss'
13+
import { useSharedUIContext } from 'src/frame/components/context/SharedUIContext'
1214

1315
enum NotificationType {
1416
RELEASE = 'RELEASE',
@@ -30,6 +32,7 @@ export const HeaderNotifications = () => {
3032
const page = mainContext.page!
3133
const { userLanguage, setUserLanguageCookie } = useUserLanguage()
3234
const { languages } = useLanguages()
35+
const { setHasOpenHeaderNotifications } = useSharedUIContext()
3336

3437
const { t } = useTranslation('header')
3538

@@ -78,6 +81,10 @@ export const HeaderNotifications = () => {
7881
: null,
7982
].filter(ExcludesNull)
8083

84+
useEffect(() => {
85+
setHasOpenHeaderNotifications(allNotifications.length > 0)
86+
}, [allNotifications, setHasOpenHeaderNotifications])
87+
8188
return (
8289
<div data-container="notifications">
8390
{allNotifications.map(({ type, content, onClose }, i) => {

src/frame/pages/app.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
LanguageItem,
1717
} from 'src/languages/components/LanguagesContext'
1818
import { useTheme } from 'src/color-schemes/components/useTheme'
19+
import { SharedUIContextProvider } from 'src/frame/components/context/SharedUIContext'
1920

2021
type MyAppProps = AppProps & {
2122
isDotComAuthenticated: boolean
@@ -138,7 +139,9 @@ const MyApp = ({ Component, pageProps, languagesContext, stagingName }: MyAppPro
138139
preventSSRMismatch
139140
>
140141
<LanguagesContext.Provider value={languagesContext}>
141-
<Component {...pageProps} />
142+
<SharedUIContextProvider>
143+
<Component {...pageProps} />
144+
</SharedUIContextProvider>
142145
</LanguagesContext.Provider>
143146
</ThemeProvider>
144147
</>

src/search/components/input/SearchOverlay.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
ArrowRightIcon,
2424
ArrowLeftIcon,
2525
} from '@primer/octicons-react'
26+
import { focusTrap } from '@primer/behaviors'
2627

2728
import { useTranslation } from 'src/languages/components/useTranslation'
2829
import { useVersion } from 'src/versions/components/useVersion'
@@ -42,7 +43,7 @@ import { EventType } from '@/events/types'
4243
import { ASK_AI_EVENT_GROUP, SEARCH_OVERLAY_EVENT_GROUP } from '@/events/components/event-groups'
4344
import type { AIReference } from '../types'
4445
import type { AutocompleteSearchHit, GeneralSearchHit } from '@/search/types'
45-
import { focusTrap } from '@primer/behaviors'
46+
import { useSharedUIContext } from '@/frame/components/context/SharedUIContext'
4647

4748
type Props = {
4849
searchOverlayOpen: boolean
@@ -89,6 +90,9 @@ export function SearchOverlay({
8990
const [aiReferences, setAIReferences] = useState<AIReference[]>([] as AIReference[])
9091
const [aiCouldNotAnswer, setAICouldNotAnswer] = useState<boolean>(false)
9192
const [showSpinner, setShowSpinner] = useState(false)
93+
const [scrollPos, setScrollPos] = useState(0)
94+
95+
const { hasOpenHeaderNotifications } = useSharedUIContext()
9296

9397
// Group all events between open / close of the overlay together
9498
const searchEventGroupId = useRef<string>('')
@@ -106,6 +110,19 @@ export function SearchOverlay({
106110
// Group all events within an "Ask AI" session together
107111
const askAIEventGroupId = useRef<string>('')
108112

113+
// When there is a notification above the header, we need to adjust the top position of the overlay to account for it
114+
useEffect(() => {
115+
if (hasOpenHeaderNotifications) {
116+
const handleScroll = () => {
117+
setScrollPos(window.scrollY)
118+
}
119+
120+
window.addEventListener('scroll', handleScroll)
121+
return () => window.removeEventListener('scroll', handleScroll)
122+
}
123+
}, [hasOpenHeaderNotifications])
124+
const overlayTopValue = scrollPos > 72 ? '0px' : `${88 - scrollPos}px !important`
125+
109126
const {
110127
autoCompleteOptions,
111128
searchLoading,
@@ -653,6 +670,14 @@ export function SearchOverlay({
653670
onClickOutside={onClose}
654671
anchorSide="inside-center"
655672
className={cx(styles.overlayContainer, 'position-fixed')}
673+
// We need to override the top value of the overlay when there are header notifications
674+
sx={
675+
hasOpenHeaderNotifications
676+
? {
677+
top: overlayTopValue,
678+
}
679+
: undefined
680+
}
656681
role="dialog"
657682
aria-modal="true"
658683
aria-labelledby={overlayHeadingId}

0 commit comments

Comments
 (0)