Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit12f87cb

Browse files
refactor(site): Show update notification as snackbar (#7546)
1 parenta7f14f8 commit12f87cb

File tree

7 files changed

+114
-103
lines changed

7 files changed

+114
-103
lines changed

‎docs/contributing/frontend.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,31 @@ user.click(screen.getByRole("button"))
166166
const form=screen.getByTestId("form")
167167
user.click(within(form).getByRole("button"))
168168
```
169+
170+
####`jest.spyOn` with the API is not working
171+
172+
For some unknown reason, we figured out the`jest.spyOn` is not able to mock the API function when they are passed directly into the services XState machine configuration.
173+
174+
❌ Does not work
175+
176+
```ts
177+
import {getUpdateCheck }from"api/api"
178+
179+
createMachine({... }, {
180+
services: {
181+
getUpdateCheck,
182+
},
183+
})
184+
```
185+
186+
✅ It works
187+
188+
```ts
189+
import {getUpdateCheck }from"api/api"
190+
191+
createMachine({... }, {
192+
services: {
193+
getUpdateCheck: ()=>getUpdateCheck(),
194+
},
195+
})
196+
```
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import{Route,Routes}from"react-router-dom"
2+
import{renderWithAuth}from"testHelpers/renderHelpers"
3+
import{DashboardLayout}from"./DashboardLayout"
4+
import*asAPIfrom"api/api"
5+
import{screen}from"@testing-library/react"
6+
7+
test("Show the new Coder version notification",async()=>{
8+
jest.spyOn(API,"getUpdateCheck").mockResolvedValue({
9+
current:false,
10+
version:"v0.12.9",
11+
url:"https://github.com/coder/coder/releases/tag/v0.12.9",
12+
})
13+
renderWithAuth(
14+
<Routes>
15+
<Routeelement={<DashboardLayout/>}>
16+
<Routeelement={<h1>Test page</h1>}/>
17+
</Route>
18+
</Routes>,
19+
)
20+
awaitscreen.findByTestId("update-check-snackbar")
21+
})
Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import{makeStyles}from"@mui/styles"
22
import{useMachine}from"@xstate/react"
3-
import{UpdateCheckResponse}from"api/typesGenerated"
43
import{DeploymentBanner}from"components/DeploymentBanner/DeploymentBanner"
54
import{LicenseBanner}from"components/LicenseBanner/LicenseBanner"
65
import{Loader}from"components/Loader/Loader"
7-
import{Margins}from"components/Margins/Margins"
86
import{ServiceBanner}from"components/ServiceBanner/ServiceBanner"
9-
import{UpdateCheckBanner}from"components/UpdateCheckBanner/UpdateCheckBanner"
107
import{usePermissions}from"hooks/usePermissions"
118
import{FC,Suspense}from"react"
129
import{Outlet}from"react-router-dom"
1310
import{dashboardContentBottomPadding}from"theme/constants"
1411
import{updateCheckMachine}from"xServices/updateCheck/updateCheckXService"
1512
import{Navbar}from"../Navbar/Navbar"
13+
importSnackbarfrom"@mui/material/Snackbar"
14+
importLinkfrom"@mui/material/Link"
15+
importBoxfrom"@mui/material/Box"
16+
importInfoOutlinedfrom"@mui/icons-material/InfoOutlined"
17+
importButtonfrom"@mui/material/Button"
1618

1719
exportconstDashboardLayout:FC=()=>{
1820
conststyles=useStyles()
@@ -22,8 +24,7 @@ export const DashboardLayout: FC = () => {
2224
permissions,
2325
},
2426
})
25-
const{error:updateCheckError, updateCheck}=updateCheckState.context
26-
27+
const{ updateCheck}=updateCheckState.context
2728
constcanViewDeployment=Boolean(permissions.viewDeploymentValues)
2829

2930
return(
@@ -34,48 +35,80 @@ export const DashboardLayout: FC = () => {
3435
<divclassName={styles.site}>
3536
<Navbar/>
3637

37-
{updateCheckState.matches("show")&&(
38-
<divclassName={styles.updateCheckBanner}>
39-
<Margins>
40-
<UpdateCheckBanner
41-
// We can trust when it is show, the update check is filled
42-
// unfortunately, XState does not has typed state - context yet
43-
updateCheck={updateCheckasUpdateCheckResponse}
44-
error={updateCheckError}
45-
onDismiss={()=>updateCheckSend("DISMISS")}
46-
/>
47-
</Margins>
48-
</div>
49-
)}
50-
5138
<divclassName={styles.siteContent}>
5239
<Suspensefallback={<Loader/>}>
5340
<Outlet/>
5441
</Suspense>
5542
</div>
5643

5744
<DeploymentBanner/>
45+
46+
<Snackbar
47+
data-testid="update-check-snackbar"
48+
open={updateCheckState.matches("show")}
49+
anchorOrigin={{
50+
vertical:"bottom",
51+
horizontal:"right",
52+
}}
53+
ContentProps={{
54+
sx:(theme)=>({
55+
background:theme.palette.background.paper,
56+
color:theme.palette.text.primary,
57+
maxWidth:theme.spacing(55),
58+
flexDirection:"row",
59+
borderColor:theme.palette.info.light,
60+
61+
"& .MuiSnackbarContent-message":{
62+
flex:1,
63+
},
64+
65+
"& .MuiSnackbarContent-action":{
66+
marginRight:0,
67+
},
68+
}),
69+
}}
70+
message={
71+
<Boxdisplay="flex"gap={2}>
72+
<InfoOutlined
73+
sx={(theme)=>({
74+
fontSize:16,
75+
height:20,// 20 is the height of the text line so we can align them
76+
color:theme.palette.info.light,
77+
})}
78+
/>
79+
<Box>
80+
Coder{updateCheck?.version} is now available. View the{" "}
81+
<Linkhref={updateCheck?.url}>release notes</Link> and{" "}
82+
<Linkhref="https://coder.com/docs/coder-oss/latest/admin/upgrade">
83+
upgrade instructions
84+
</Link>{" "}
85+
for more information.
86+
</Box>
87+
</Box>
88+
}
89+
action={
90+
<Button
91+
variant="text"
92+
size="small"
93+
onClick={()=>updateCheckSend("DISMISS")}
94+
>
95+
Dismiss
96+
</Button>
97+
}
98+
/>
5899
</div>
59100
</>
60101
)
61102
}
62103

63-
constuseStyles=makeStyles((theme)=>({
104+
constuseStyles=makeStyles({
64105
site:{
65106
display:"flex",
66107
minHeight:"100vh",
67108
flexDirection:"column",
68109
},
69-
updateCheckBanner:{
70-
// Add spacing at the top and remove some from the bottom. Removal
71-
// is necessary to avoid a visual jerk when the banner is dismissed.
72-
// It also give a more pleasant distance to the site content when
73-
// the banner is visible.
74-
marginTop:theme.spacing(2),
75-
marginBottom:theme.spacing(-2),
76-
},
77110
siteContent:{
78111
flex:1,
79112
paddingBottom:dashboardContentBottomPadding,// Add bottom space since we don't use a footer
80113
},
81-
}))
114+
})

‎site/src/components/UpdateCheckBanner/UpdateCheckBanner.stories.tsx

Lines changed: 0 additions & 25 deletions
This file was deleted.

‎site/src/components/UpdateCheckBanner/UpdateCheckBanner.tsx

Lines changed: 0 additions & 47 deletions
This file was deleted.

‎site/src/theme/theme.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export let dark = createTheme({
4848
dark:colors.green[15],
4949
},
5050
info:{
51+
light:colors.blue[9],
5152
main:colors.blue[11],
5253
dark:colors.blue[15],
5354
contrastText:colors.gray[4],

‎site/src/xServices/updateCheck/updateCheckXService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ export const updateCheckMachine = createMachine(
7878
},
7979
{
8080
services:{
81-
getUpdateCheck,
81+
// For some reason, when passing values directly, jest.spy does not work.
82+
getUpdateCheck:()=>getUpdateCheck(),
8283
},
8384
actions:{
8485
assignUpdateCheck:assign({
@@ -101,7 +102,6 @@ export const updateCheckMachine = createMachine(
101102
shouldShowUpdateCheck:(_,{ data})=>{
102103
constisNotDismissed=getDismissedVersionOnLocal()!==data.version
103104
constisOutdated=!data.current
104-
105105
returnisNotDismissed&&isOutdated
106106
},
107107
},

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp