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

Commit49de44c

Browse files
feat: Add LicenseBanner (#3568)
* Extract reusable Pill component* Make icon optional* Get pills in place* Rough styling* Extract Expander component* Fix alignment* Put it in action - type error* Hide banner by default* Use generated type* Move PaletteIndex type* Tweak colors* Format, another color tweak* Add stories* Add tests* Update site/src/components/Pill/Pill.tsxCo-authored-by: Kira Pilot <kira@coder.com>* Update site/src/components/Pill/Pill.tsxCo-authored-by: Kira Pilot <kira@coder.com>* Comments* Remove empty story, improve empty test* LintCo-authored-by: Kira Pilot <kira@coder.com>
1 parentf7ccfa2 commit49de44c

File tree

18 files changed

+509
-97
lines changed

18 files changed

+509
-97
lines changed

‎site/src/api/api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,3 +378,8 @@ export const putWorkspaceExtension = async (
378378
):Promise<void>=>{
379379
awaitaxios.put(`/api/v2/workspaces/${workspaceId}/extend`,{deadline:newDeadline})
380380
}
381+
382+
exportconstgetEntitlements=async():Promise<TypesGen.Entitlements>=>{
383+
constresponse=awaitaxios.get("/api/v2/entitlements")
384+
returnresponse.data
385+
}

‎site/src/app.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
importCssBaselinefrom"@material-ui/core/CssBaseline"
22
importThemeProviderfrom"@material-ui/styles/ThemeProvider"
3+
import{LicenseBanner}from"components/LicenseBanner/LicenseBanner"
34
import{FC}from"react"
45
import{HelmetProvider}from"react-helmet-async"
56
import{BrowserRouterasRouter}from"react-router-dom"
@@ -18,6 +19,7 @@ export const App: FC = () => {
1819
<CssBaseline/>
1920
<ErrorBoundary>
2021
<XServiceProvider>
22+
<LicenseBanner/>
2123
<AppRouter/>
2224
<GlobalSnackbar/>
2325
</XServiceProvider>

‎site/src/components/DropdownArrows/DropdownArrows.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import KeyboardArrowDown from "@material-ui/icons/KeyboardArrowDown"
33
importKeyboardArrowUpfrom"@material-ui/icons/KeyboardArrowUp"
44
import{FC}from"react"
55

6-
constuseStyles=makeStyles((theme:Theme)=>({
6+
constuseStyles=makeStyles<Theme,ArrowProps>((theme:Theme)=>({
77
arrowIcon:{
88
color:fade(theme.palette.primary.contrastText,0.7),
9-
marginLeft:theme.spacing(1),
9+
marginLeft:({ margin})=>(margin ?theme.spacing(1) :0),
1010
width:16,
1111
height:16,
1212
},
@@ -15,12 +15,16 @@ const useStyles = makeStyles((theme: Theme) => ({
1515
},
1616
}))
1717

18-
exportconstOpenDropdown:FC=()=>{
19-
conststyles=useStyles()
18+
interfaceArrowProps{
19+
margin?:boolean
20+
}
21+
22+
exportconstOpenDropdown:FC<ArrowProps>=({ margin=true})=>{
23+
conststyles=useStyles({ margin})
2024
return<KeyboardArrowDownclassName={styles.arrowIcon}/>
2125
}
2226

23-
exportconstCloseDropdown:FC=()=>{
24-
conststyles=useStyles()
27+
exportconstCloseDropdown:FC<ArrowProps>=({ margin=true})=>{
28+
conststyles=useStyles({ margin})
2529
return<KeyboardArrowUpclassName={`${styles.arrowIcon}${styles.arrowIconUp}`}/>
2630
}

‎site/src/components/ErrorSummary/ErrorSummary.tsx

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
importButtonfrom"@material-ui/core/Button"
22
importCollapsefrom"@material-ui/core/Collapse"
33
importIconButtonfrom"@material-ui/core/IconButton"
4-
importLinkfrom"@material-ui/core/Link"
54
import{darken,lighten,makeStyles,Theme}from"@material-ui/core/styles"
65
importCloseIconfrom"@material-ui/icons/Close"
76
importRefreshIconfrom"@material-ui/icons/Refresh"
87
import{ApiError,getErrorDetail,getErrorMessage}from"api/errors"
8+
import{Expander}from"components/Expander/Expander"
99
import{Stack}from"components/Stack/Stack"
1010
import{FC,useState}from"react"
1111

@@ -36,10 +36,6 @@ export const ErrorSummary: FC<React.PropsWithChildren<ErrorSummaryProps>> = ({
3636

3737
conststyles=useStyles({ showDetails})
3838

39-
consttoggleShowDetails=()=>{
40-
setShowDetails(!showDetails)
41-
}
42-
4339
constcloseError=()=>{
4440
setOpen(false)
4541
}
@@ -51,19 +47,10 @@ export const ErrorSummary: FC<React.PropsWithChildren<ErrorSummaryProps>> = ({
5147
return(
5248
<StackclassName={styles.root}>
5349
<Stackdirection="row"alignItems="center"className={styles.messageBox}>
54-
<div>
50+
<Stackdirection="row"spacing={0}>
5551
<spanclassName={styles.errorMessage}>{message}</span>
56-
{!!detail&&(
57-
<Link
58-
aria-expanded={showDetails}
59-
onClick={toggleShowDetails}
60-
className={styles.detailsLink}
61-
tabIndex={0}
62-
>
63-
{showDetails ?Language.lessDetails :Language.moreDetails}
64-
</Link>
65-
)}
66-
</div>
52+
{!!detail&&<Expanderexpanded={showDetails}setExpanded={setShowDetails}/>}
53+
</Stack>
6754
{dismissible&&(
6855
<IconButtononClick={closeError}className={styles.iconButton}>
6956
<CloseIconclassName={styles.closeIcon}/>
@@ -101,6 +88,9 @@ const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
10188
borderRadius:theme.shape.borderRadius,
10289
gap:0,
10390
},
91+
flex:{
92+
display:"flex",
93+
},
10494
messageBox:{
10595
justifyContent:"space-between",
10696
},
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import{Story}from"@storybook/react"
2+
import{Expander,ExpanderProps}from"./Expander"
3+
4+
exportdefault{
5+
title:"components/Expander",
6+
component:Expander,
7+
argTypes:{
8+
setExpanded:{action:"setExpanded"},
9+
},
10+
}
11+
12+
constTemplate:Story<ExpanderProps>=(args)=><Expander{...args}/>
13+
14+
exportconstExpanded=Template.bind({})
15+
Expanded.args={
16+
expanded:true,
17+
}
18+
19+
exportconstCollapsed=Template.bind({})
20+
Collapsed.args={
21+
expanded:false,
22+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
importLinkfrom"@material-ui/core/Link"
2+
importmakeStylesfrom"@material-ui/core/styles/makeStyles"
3+
import{CloseDropdown,OpenDropdown}from"components/DropdownArrows/DropdownArrows"
4+
5+
constLanguage={
6+
expand:"More",
7+
collapse:"Less",
8+
}
9+
10+
exportinterfaceExpanderProps{
11+
expanded:boolean
12+
setExpanded:(val:boolean)=>void
13+
}
14+
15+
exportconstExpander:React.FC<ExpanderProps>=({ expanded, setExpanded})=>{
16+
consttoggleExpanded=()=>setExpanded(!expanded)
17+
conststyles=useStyles()
18+
return(
19+
<Linkaria-expanded={expanded}onClick={toggleExpanded}className={styles.expandLink}>
20+
{expanded ?(
21+
<spanclassName={styles.text}>
22+
{Language.collapse}
23+
<CloseDropdownmargin={false}/>{" "}
24+
</span>
25+
) :(
26+
<spanclassName={styles.text}>
27+
{Language.expand}
28+
<OpenDropdownmargin={false}/>
29+
</span>
30+
)}
31+
</Link>
32+
)
33+
}
34+
35+
constuseStyles=makeStyles((theme)=>({
36+
expandLink:{
37+
cursor:"pointer",
38+
color:theme.palette.text.primary,
39+
display:"flex",
40+
},
41+
text:{
42+
display:"flex",
43+
alignItems:"center",
44+
},
45+
}))
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import{screen}from"@testing-library/react"
2+
import{rest}from"msw"
3+
import{MockEntitlementsWithWarnings}from"testHelpers/entities"
4+
import{render}from"testHelpers/renderHelpers"
5+
import{server}from"testHelpers/server"
6+
import{LicenseBanner}from"./LicenseBanner"
7+
import{Language}from"./LicenseBannerView"
8+
9+
describe("LicenseBanner",()=>{
10+
it("does not show when there are no warnings",async()=>{
11+
render(<LicenseBanner/>)
12+
constbannerPillSingular=awaitscreen.queryByText(Language.licenseIssue)
13+
constbannerPillPlural=awaitscreen.queryByText(Language.licenseIssues(2))
14+
expect(bannerPillSingular).toBe(null)
15+
expect(bannerPillPlural).toBe(null)
16+
})
17+
it("shows when there are warnings",async()=>{
18+
server.use(
19+
rest.get("/api/v2/entitlements",(req,res,ctx)=>{
20+
returnres(ctx.status(200),ctx.json(MockEntitlementsWithWarnings))
21+
}),
22+
)
23+
render(<LicenseBanner/>)
24+
constbannerPill=awaitscreen.findByText(Language.licenseIssues(2))
25+
expect(bannerPill).toBeDefined()
26+
})
27+
})
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import{useActor}from"@xstate/react"
2+
import{useContext,useEffect}from"react"
3+
import{XServiceContext}from"xServices/StateContext"
4+
import{LicenseBannerView}from"./LicenseBannerView"
5+
6+
exportconstLicenseBanner:React.FC=()=>{
7+
constxServices=useContext(XServiceContext)
8+
const[entitlementsState,entitlementsSend]=useActor(xServices.entitlementsXService)
9+
const{ warnings}=entitlementsState.context.entitlements
10+
11+
/** Gets license data on app mount because LicenseBanner is mounted in App */
12+
useEffect(()=>{
13+
entitlementsSend("GET_ENTITLEMENTS")
14+
},[entitlementsSend])
15+
16+
if(warnings.length){
17+
return<LicenseBannerViewwarnings={warnings}/>
18+
}else{
19+
returnnull
20+
}
21+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import{Story}from"@storybook/react"
2+
import{LicenseBannerView,LicenseBannerViewProps}from"./LicenseBannerView"
3+
4+
exportdefault{
5+
title:"components/LicenseBannerView",
6+
component:LicenseBannerView,
7+
}
8+
9+
constTemplate:Story<LicenseBannerViewProps>=(args)=><LicenseBannerView{...args}/>
10+
11+
exportconstOneWarning=Template.bind({})
12+
OneWarning.args={
13+
warnings:["You have exceeded the number of seats in your license."],
14+
}
15+
16+
exportconstTwoWarnings=Template.bind({})
17+
TwoWarnings.args={
18+
warnings:[
19+
"You have exceeded the number of seats in your license.",
20+
"You are flying too close to the sun.",
21+
],
22+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
importCollapsefrom"@material-ui/core/Collapse"
2+
import{makeStyles}from"@material-ui/core/styles"
3+
import{Expander}from"components/Expander/Expander"
4+
import{Pill}from"components/Pill/Pill"
5+
import{useState}from"react"
6+
7+
exportconstLanguage={
8+
licenseIssue:"License Issue",
9+
licenseIssues:(num:number):string=>`${num} License Issues`,
10+
upgrade:"Contact us to upgrade your license.",
11+
exceeded:"It looks like you've exceeded some limits of your license.",
12+
lessDetails:"Less",
13+
moreDetails:"More",
14+
}
15+
16+
exportinterfaceLicenseBannerViewProps{
17+
warnings:string[]
18+
}
19+
20+
exportconstLicenseBannerView:React.FC<LicenseBannerViewProps>=({ warnings})=>{
21+
conststyles=useStyles()
22+
const[showDetails,setShowDetails]=useState(false)
23+
if(warnings.length===1){
24+
return(
25+
<divclassName={styles.container}>
26+
<Pilltext={Language.licenseIssue}type="warning"lightBorder/>
27+
<spanclassName={styles.text}>{warnings[0]}</span>
28+
&nbsp;
29+
<ahref="mailto:sales@coder.com"className={styles.link}>
30+
{Language.upgrade}
31+
</a>
32+
</div>
33+
)
34+
}else{
35+
return(
36+
<divclassName={styles.container}>
37+
<divclassName={styles.flex}>
38+
<divclassName={styles.leftContent}>
39+
<Pilltext={Language.licenseIssues(warnings.length)}type="warning"lightBorder/>
40+
<spanclassName={styles.text}>{Language.exceeded}</span>
41+
&nbsp;
42+
<ahref="mailto:sales@coder.com"className={styles.link}>
43+
{Language.upgrade}
44+
</a>
45+
</div>
46+
<Expanderexpanded={showDetails}setExpanded={setShowDetails}/>
47+
</div>
48+
<Collapsein={showDetails}>
49+
<ulclassName={styles.list}>
50+
{warnings.map((warning)=>(
51+
<liclassName={styles.listItem}key={`${warning}`}>
52+
{warning}
53+
</li>
54+
))}
55+
</ul>
56+
</Collapse>
57+
</div>
58+
)
59+
}
60+
}
61+
62+
constuseStyles=makeStyles((theme)=>({
63+
container:{
64+
padding:theme.spacing(1.5),
65+
backgroundColor:theme.palette.warning.main,
66+
},
67+
flex:{
68+
display:"flex",
69+
},
70+
leftContent:{
71+
marginRight:theme.spacing(1),
72+
},
73+
text:{
74+
marginLeft:theme.spacing(1),
75+
},
76+
link:{
77+
color:"inherit",
78+
textDecoration:"none",
79+
fontWeight:"bold",
80+
},
81+
list:{
82+
margin:theme.spacing(1.5),
83+
},
84+
listItem:{
85+
margin:theme.spacing(1),
86+
},
87+
}))

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp