- Notifications
You must be signed in to change notification settings - Fork928
feat: Add LicenseBanner#3568
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
f9566ba
7a6dd98
0b6e336
dc62449
5c208ce
0cf73fd
3cf35aa
c0cdf8a
0aebe44
ac2b5ae
c55cba6
77dd18b
94f8b51
69f89ef
5d00122
a8e6341
4d55a33
66e75bc
60e310b
95c5c2d
62d090b
da041eb
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -3,10 +3,10 @@ import KeyboardArrowDown from "@material-ui/icons/KeyboardArrowDown" | ||
import KeyboardArrowUp from "@material-ui/icons/KeyboardArrowUp" | ||
import { FC } from "react" | ||
const useStyles = makeStyles<Theme, ArrowProps>((theme: Theme) => ({ | ||
arrowIcon: { | ||
color: fade(theme.palette.primary.contrastText, 0.7), | ||
marginLeft:({ margin }) => (margin ?theme.spacing(1) : 0), | ||
width: 16, | ||
height: 16, | ||
}, | ||
@@ -15,12 +15,16 @@ const useStyles = makeStyles((theme: Theme) => ({ | ||
}, | ||
})) | ||
interface ArrowProps { | ||
margin?: boolean | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. We could share this typing with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I also wonder if, instead of a ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more.
(This comment ended up in the wrong place, sorry for the confusion!) @Kira-Pilot Yeah, so I changed this because I thought the color combination was bad and it was also going to pose a contrast issue, but I have been wondering if it's obvious enough. I think the chevron and placement helps. But I also think I'm having trouble making it blue or underlined because it's a not a navigation link. It's really a button in functionality. What do you think is the best way to style that? I guess Zenhub styles "Show 4 more" on this page like a link (bold and blue). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Going to wait on a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. @presleyp That makes sense! I agree with your logic and I don't think it's something we need to change right now. Let's keep it back of mind and look out for link inspo in the future. | ||
export const OpenDropdown: FC<ArrowProps> = ({ margin = true }) => { | ||
const styles = useStyles({ margin }) | ||
return <KeyboardArrowDown className={styles.arrowIcon} /> | ||
} | ||
export const CloseDropdown: FC<ArrowProps> = ({ margin = true }) => { | ||
const styles = useStyles({ margin }) | ||
return <KeyboardArrowUp className={`${styles.arrowIcon} ${styles.arrowIconUp}`} /> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { Story } from "@storybook/react" | ||
import { Expander, ExpanderProps } from "./Expander" | ||
export default { | ||
title: "components/Expander", | ||
component: Expander, | ||
argTypes: { | ||
setExpanded: { action: "setExpanded" }, | ||
}, | ||
} | ||
const Template: Story<ExpanderProps> = (args) => <Expander {...args} /> | ||
export const Expanded = Template.bind({}) | ||
Expanded.args = { | ||
expanded: true, | ||
} | ||
export const Collapsed = Template.bind({}) | ||
Collapsed.args = { | ||
expanded: false, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import Link from "@material-ui/core/Link" | ||
import makeStyles from "@material-ui/core/styles/makeStyles" | ||
import { CloseDropdown, OpenDropdown } from "components/DropdownArrows/DropdownArrows" | ||
const Language = { | ||
expand: "More", | ||
collapse: "Less", | ||
} | ||
export interface ExpanderProps { | ||
expanded: boolean | ||
setExpanded: (val: boolean) => void | ||
} | ||
export const Expander: React.FC<ExpanderProps> = ({ expanded, setExpanded }) => { | ||
const toggleExpanded = () => setExpanded(!expanded) | ||
const styles = useStyles() | ||
return ( | ||
<Link aria-expanded={expanded} onClick={toggleExpanded} className={styles.expandLink}> | ||
{expanded ? ( | ||
<span className={styles.text}> | ||
{Language.collapse} | ||
<CloseDropdown margin={false} />{" "} | ||
</span> | ||
) : ( | ||
<span className={styles.text}> | ||
{Language.expand} | ||
<OpenDropdown margin={false} /> | ||
</span> | ||
)} | ||
</Link> | ||
) | ||
} | ||
const useStyles = makeStyles((theme) => ({ | ||
expandLink: { | ||
cursor: "pointer", | ||
color: theme.palette.text.primary, | ||
display: "flex", | ||
}, | ||
text: { | ||
display: "flex", | ||
alignItems: "center", | ||
}, | ||
})) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { screen } from "@testing-library/react" | ||
import { rest } from "msw" | ||
import { MockEntitlementsWithWarnings } from "testHelpers/entities" | ||
import { render } from "testHelpers/renderHelpers" | ||
import { server } from "testHelpers/server" | ||
import { LicenseBanner } from "./LicenseBanner" | ||
import { Language } from "./LicenseBannerView" | ||
describe("LicenseBanner", () => { | ||
it("does not show when there are no warnings", async () => { | ||
render(<LicenseBanner />) | ||
const bannerPillSingular = await screen.queryByText(Language.licenseIssue) | ||
const bannerPillPlural = await screen.queryByText(Language.licenseIssues(2)) | ||
expect(bannerPillSingular).toBe(null) | ||
expect(bannerPillPlural).toBe(null) | ||
}) | ||
it("shows when there are warnings", async () => { | ||
server.use( | ||
rest.get("/api/v2/entitlements", (req, res, ctx) => { | ||
return res(ctx.status(200), ctx.json(MockEntitlementsWithWarnings)) | ||
}), | ||
) | ||
render(<LicenseBanner />) | ||
const bannerPill = await screen.findByText(Language.licenseIssues(2)) | ||
expect(bannerPill).toBeDefined() | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { useActor } from "@xstate/react" | ||
import { useContext, useEffect } from "react" | ||
import { XServiceContext } from "xServices/StateContext" | ||
import { LicenseBannerView } from "./LicenseBannerView" | ||
export const LicenseBanner: React.FC = () => { | ||
const xServices = useContext(XServiceContext) | ||
const [entitlementsState, entitlementsSend] = useActor(xServices.entitlementsXService) | ||
const { warnings } = entitlementsState.context.entitlements | ||
/** Gets license data on app mount because LicenseBanner is mounted in App */ | ||
useEffect(() => { | ||
entitlementsSend("GET_ENTITLEMENTS") | ||
}, [entitlementsSend]) | ||
if (warnings.length) { | ||
return <LicenseBannerView warnings={warnings} /> | ||
} else { | ||
return null | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { Story } from "@storybook/react" | ||
import { LicenseBannerView, LicenseBannerViewProps } from "./LicenseBannerView" | ||
export default { | ||
title: "components/LicenseBannerView", | ||
component: LicenseBannerView, | ||
} | ||
const Template: Story<LicenseBannerViewProps> = (args) => <LicenseBannerView {...args} /> | ||
export const OneWarning = Template.bind({}) | ||
OneWarning.args = { | ||
warnings: ["You have exceeded the number of seats in your license."], | ||
} | ||
export const TwoWarnings = Template.bind({}) | ||
TwoWarnings.args = { | ||
warnings: [ | ||
"You have exceeded the number of seats in your license.", | ||
"You are flying too close to the sun.", | ||
], | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import Collapse from "@material-ui/core/Collapse" | ||
import { makeStyles } from "@material-ui/core/styles" | ||
import { Expander } from "components/Expander/Expander" | ||
import { Pill } from "components/Pill/Pill" | ||
import { useState } from "react" | ||
export const Language = { | ||
licenseIssue: "License Issue", | ||
licenseIssues: (num: number): string => `${num} License Issues`, | ||
upgrade: "Contact us to upgrade your license.", | ||
exceeded: "It looks like you've exceeded some limits of your license.", | ||
lessDetails: "Less", | ||
moreDetails: "More", | ||
} | ||
export interface LicenseBannerViewProps { | ||
warnings: string[] | ||
} | ||
export const LicenseBannerView: React.FC<LicenseBannerViewProps> = ({ warnings }) => { | ||
const styles = useStyles() | ||
const [showDetails, setShowDetails] = useState(false) | ||
if (warnings.length === 1) { | ||
return ( | ||
<div className={styles.container}> | ||
<Pill text={Language.licenseIssue} type="warning" lightBorder /> | ||
<span className={styles.text}>{warnings[0]}</span> | ||
| ||
<a href="mailto:sales@coder.com" className={styles.link}> | ||
{Language.upgrade} | ||
</a> | ||
</div> | ||
) | ||
} else { | ||
return ( | ||
<div className={styles.container}> | ||
<div className={styles.flex}> | ||
<div className={styles.leftContent}> | ||
<Pill text={Language.licenseIssues(warnings.length)} type="warning" lightBorder /> | ||
<span className={styles.text}>{Language.exceeded}</span> | ||
| ||
<a href="mailto:sales@coder.com" className={styles.link}> | ||
{Language.upgrade} | ||
</a> | ||
</div> | ||
<Expander expanded={showDetails} setExpanded={setShowDetails} /> | ||
</div> | ||
<Collapse in={showDetails}> | ||
<ul className={styles.list}> | ||
{warnings.map((warning) => ( | ||
<li className={styles.listItem} key={`${warning}`}> | ||
{warning} | ||
</li> | ||
))} | ||
</ul> | ||
</Collapse> | ||
</div> | ||
) | ||
} | ||
} | ||
const useStyles = makeStyles((theme) => ({ | ||
container: { | ||
padding: theme.spacing(1.5), | ||
backgroundColor: theme.palette.warning.main, | ||
}, | ||
flex: { | ||
display: "flex", | ||
}, | ||
leftContent: { | ||
marginRight: theme.spacing(1), | ||
}, | ||
text: { | ||
marginLeft: theme.spacing(1), | ||
}, | ||
link: { | ||
color: "inherit", | ||
textDecoration: "none", | ||
fontWeight: "bold", | ||
}, | ||
list: { | ||
margin: theme.spacing(1.5), | ||
}, | ||
listItem: { | ||
margin: theme.spacing(1), | ||
}, | ||
})) |
Uh oh!
There was an error while loading.Please reload this page.