Skip to content

Commit 0f2fc14

Browse files
ahtesham-quraishAhtesham Quraish
andauthored
fix: add link to the creation page of article and news (#3430)
* fix: add link to the creation page of article and news --------- Co-authored-by: Ahtesham Quraish <ahtesham.quraish@192.168.1.6>
1 parent 0959f03 commit 0f2fc14

5 files changed

Lines changed: 123 additions & 22 deletions

File tree

frontends/main/src/app-pages/Articles/ArticleListingPage.tsx

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ import type { WebsiteContent } from "api/v1"
2222
import { LocalDate } from "ol-utilities"
2323
import { useWebsiteContentList } from "api/hooks/website_content"
2424
import { extractArticleContent } from "@/common/websiteContentUtils"
25-
import { articleView } from "@/common/urls"
25+
import { articleView, websiteContentCreateView } from "@/common/urls"
26+
import { Permission, useUserHasPermission } from "api/hooks/user"
27+
import { ButtonLink } from "@mitodl/smoot-design"
2628

2729
const PAGE_SIZE = 10
2830
const MAX_PAGE = 50
@@ -223,6 +225,10 @@ const BannerGridContainer = styled.div`
223225
max-width: 842px;
224226
margin: 0 auto;
225227
padding: 64px 0;
228+
display: flex;
229+
flex-direction: row;
230+
justify-content: space-between;
231+
align-items: center;
226232
227233
${theme.breakpoints.down("sm")} {
228234
max-width: 100%;
@@ -299,6 +305,10 @@ const BannerSection = styled.div`
299305
background: ${theme.custom.colors.white};
300306
border-bottom: 1px solid ${theme.custom.colors.lightGray2};
301307
`
308+
const NewArticleLink = styled(ButtonLink)`
309+
display: flex;
310+
justify-content: end;
311+
`
302312

303313
const EmptyState = styled.div`
304314
display: flex;
@@ -319,7 +329,6 @@ const EmptyState = styled.div`
319329

320330
const BannerTitle = styled(Typography)`
321331
color: ${theme.custom.colors.black};
322-
margin-top: 8px;
323332
${theme.breakpoints.down("md")} {
324333
${{ ...theme.typography.h2 }}
325334
}
@@ -329,16 +338,6 @@ const BannerTitle = styled(Typography)`
329338
}
330339
` as typeof Typography
331340

332-
const BannerDescription = styled(Typography)`
333-
color: ${theme.custom.colors.black};
334-
margin-top: 16px;
335-
font-size: 20px;
336-
line-height: 32px;
337-
${theme.breakpoints.down("sm")} {
338-
${{ ...theme.typography.body2 }}
339-
}
340-
`
341-
342341
const BreadcrumbBar = styled.div(({ theme }) => ({
343342
width: "100%",
344343
padding: "18px 0 2px 0",
@@ -418,6 +417,8 @@ const ArticleListingPage: React.FC = () => {
418417
const parsedPage = Number.parseInt(searchParams.get("page") ?? "1", 10)
419418
const page = Number.isFinite(parsedPage) && parsedPage > 0 ? parsedPage : 1
420419

420+
const isArticleEditor = useUserHasPermission(Permission.ArticleEditor)
421+
421422
const { data: articles, isLoading } = useWebsiteContentList({
422423
limit: PAGE_SIZE,
423424
offset: (page - 1) * PAGE_SIZE,
@@ -454,11 +455,16 @@ const ArticleListingPage: React.FC = () => {
454455
<BannerTitle component="h1" variant="h1">
455456
Articles
456457
</BannerTitle>
457-
<BannerDescription variant="body1">
458-
Dive into articles covering emerging technologies, global
459-
challenges, and creative thinking. Stay connected with the latest
460-
insights and discoveries from MIT.
461-
</BannerDescription>
458+
{isArticleEditor && (
459+
<NewArticleLink
460+
variant="primary"
461+
href={websiteContentCreateView("article")}
462+
>
463+
<Typography variant="body1" color="white">
464+
New Article
465+
</Typography>
466+
</NewArticleLink>
467+
)}
462468
</BannerGridContainer>
463469
</Container>
464470
</BannerSection>

frontends/main/src/app-pages/News/NewsBanner.tsx

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import {
77
Breadcrumbs,
88
BannerBackground,
99
} from "ol-components"
10+
import { ButtonLink } from "@mitodl/smoot-design"
11+
import { Permission, useUserHasPermission } from "api/hooks/user"
12+
import { websiteContentCreateView } from "@/common/urls"
1013

1114
export const DEFAULT_BACKGROUND_IMAGE_URL =
1215
"/images/backgrounds/backgroung_steps.jpg"
@@ -30,7 +33,17 @@ const BannerSection = styled(BannerBackground)`
3033
z-index: 2;
3134
}
3235
`
33-
36+
const NewArticleLink = styled(ButtonLink)`
37+
display: flex;
38+
justify-content: end;
39+
`
40+
const InfoContainer = styled.div`
41+
display: flex;
42+
flex-direction: row;
43+
align-items: center;
44+
justify-content: space-between;
45+
gap: 16px;
46+
`
3447
const BannerTitle = styled(Typography)`
3548
color: ${theme.custom.colors.white};
3649
margin-top: 8px;
@@ -65,6 +78,7 @@ const NewsBanner: React.FC<NewsBannerProps> = ({
6578
backgroundUrl = DEFAULT_BACKGROUND_IMAGE_URL,
6679
className,
6780
}) => {
81+
const isArticleEditor = useUserHasPermission(Permission.ArticleEditor)
6882
return (
6983
<BannerSection
7084
className={className}
@@ -78,10 +92,22 @@ const NewsBanner: React.FC<NewsBannerProps> = ({
7892
ancestors={[{ href: "/", label: "Home" }]}
7993
current={currentBreadcrumb}
8094
/>
81-
<BannerTitle component="h1" variant="h1">
82-
{title}
83-
</BannerTitle>
84-
<BannerDescription variant="body1">{description}</BannerDescription>
95+
<InfoContainer>
96+
<div>
97+
<BannerTitle component="h1" variant="h1">
98+
{title}
99+
</BannerTitle>
100+
<BannerDescription variant="body1">{description}</BannerDescription>
101+
</div>
102+
{isArticleEditor && (
103+
<NewArticleLink
104+
variant="tertiary"
105+
href={websiteContentCreateView("news")}
106+
>
107+
<Typography variant="body1">Add news</Typography>
108+
</NewArticleLink>
109+
)}
110+
</InfoContainer>
85111
</Container>
86112
</BannerSection>
87113
)

frontends/main/src/app-pages/News/NewsListingPage.test.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ describe("NewsListingPage", () => {
2323
beforeEach(() => {
2424
mockedUseFeatureFlagEnabled.mockReturnValue(true)
2525
mockedUseFeatureFlagsLoaded.mockReturnValue(true)
26+
setMockResponse.get(urls.userMe.get(), { is_authenticated: false })
2627
})
2728

2829
afterEach(() => {
@@ -82,6 +83,34 @@ describe("NewsListingPage", () => {
8283
})
8384
})
8485

86+
test("shows 'Add news' button in banner when user is an article editor", async () => {
87+
setMockResponse.get(urls.userMe.get(), {
88+
is_authenticated: true,
89+
is_article_editor: true,
90+
})
91+
setupAPI(21)
92+
renderWithProviders(<NewsListingPage />)
93+
94+
await waitFor(() => {
95+
expect(
96+
screen.getByRole("link", { name: /add news/i }),
97+
).toBeInTheDocument()
98+
})
99+
})
100+
101+
test("hides 'Add news' button in banner when user is not an article editor", async () => {
102+
setupAPI(21)
103+
renderWithProviders(<NewsListingPage />)
104+
105+
await waitFor(() => {
106+
expect(screen.queryByRole("progressbar")).not.toBeInTheDocument()
107+
})
108+
109+
expect(
110+
screen.queryByRole("link", { name: /add news/i }),
111+
).not.toBeInTheDocument()
112+
})
113+
85114
test("displays News images when available", async () => {
86115
const news = setupAPI(21)
87116
renderWithProviders(<NewsListingPage />)

frontends/main/src/page-components/Header/Header.test.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,31 @@ describe("UserMenu", () => {
148148
})
149149
expect(link).toBe(null)
150150
})
151+
152+
test("Article editors see 'Article' and 'News' links in the user menu", async () => {
153+
setMockResponse.get(urls.userMe.get(), {
154+
is_authenticated: true,
155+
is_article_editor: true,
156+
})
157+
renderWithProviders(<Header />)
158+
const menu = await findUserMenu()
159+
160+
const articleLink = within(menu).getByRole("menuitem", { name: "Article" })
161+
expect(articleLink).toHaveAttribute("href", "/website_content/article/new")
162+
163+
const newsLink = within(menu).getByRole("menuitem", { name: "News" })
164+
expect(newsLink).toHaveAttribute("href", "/website_content/news/new")
165+
})
166+
167+
test("Users WITHOUT ArticleEditor permission do not see 'Article' or 'News' links", async () => {
168+
setMockResponse.get(urls.userMe.get(), {
169+
is_authenticated: true,
170+
is_article_editor: false,
171+
})
172+
renderWithProviders(<Header />)
173+
const menu = await findUserMenu()
174+
175+
expect(within(menu).queryByRole("menuitem", { name: "Article" })).toBe(null)
176+
expect(within(menu).queryByRole("menuitem", { name: "News" })).toBe(null)
177+
})
151178
})

frontends/main/src/page-components/Header/UserMenu.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from "@remixicon/react"
1414
import { useUserMe, User } from "api/hooks/user"
1515
import MITLogoLink from "@/components/MITLogoLink/MITLogoLink"
16+
import { websiteContentCreateView } from "@/common/urls"
1617

1718
const FlexContainer = styled.div({
1819
display: "flex",
@@ -158,6 +159,18 @@ const UserMenu: React.FC<UserMenuProps> = ({ variant }) => {
158159
allow: !!user?.is_learning_path_editor,
159160
href: urls.LEARNINGPATH_LISTING,
160161
},
162+
{
163+
label: "Article",
164+
key: "articles",
165+
allow: !!user?.is_article_editor,
166+
href: websiteContentCreateView("article"),
167+
},
168+
{
169+
label: "News",
170+
key: "news",
171+
allow: !!user?.is_article_editor,
172+
href: websiteContentCreateView("news"),
173+
},
161174
{
162175
label: "Log Out",
163176
key: "logout",

0 commit comments

Comments
 (0)