Skip to content

Commit 4169b0c

Browse files
ahtesham-quraishAhtesham Quraish
andauthored
feat: incorporating the tiptap in articles CRUD operations (#2693)
* feat: adding tiptap integration with article crud operations --------- Co-authored-by: Ahtesham Quraish <ahtesham.quraish@192.168.10.8>
1 parent 2f2ff78 commit 4169b0c

File tree

10 files changed

+305
-183
lines changed

10 files changed

+305
-183
lines changed

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

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22

33
import React from "react"
44
import { useArticleDetail } from "api/hooks/articles"
5-
import { Container, LoadingSpinner, styled, Typography } from "ol-components"
5+
import {
6+
Container,
7+
LoadingSpinner,
8+
styled,
9+
Typography,
10+
TiptapEditorContainer,
11+
} from "ol-components"
612
import { ButtonLink } from "@mitodl/smoot-design"
713
import { notFound } from "next/navigation"
814
import { Permission } from "api/hooks/user"
@@ -24,14 +30,6 @@ const WrapperContainer = styled.div({
2430
paddingBottom: "10px",
2531
})
2632

27-
const PreTag = styled.pre({
28-
background: "#f6f6f6",
29-
padding: "16px",
30-
borderRadius: "8px",
31-
fontSize: "14px",
32-
overflowX: "auto",
33-
})
34-
3533
export const ArticleDetailPage = ({ articleId }: { articleId: number }) => {
3634
const id = Number(articleId)
3735
const { data, isLoading } = useArticleDetail(id)
@@ -58,7 +56,11 @@ export const ArticleDetailPage = ({ articleId }: { articleId: number }) => {
5856
</ButtonLink>
5957
</ControlsContainer>
6058
</WrapperContainer>
61-
<PreTag>{JSON.stringify(data.content, null, 2)}</PreTag>
59+
<TiptapEditorContainer
60+
data-testid="editor"
61+
value={data.content}
62+
readOnly
63+
/>
6264
</Page>
6365
</RestrictedRoute>
6466
)

frontends/main/src/app-pages/Articles/ArticleEditPage.test.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,15 @@ describe("ArticleEditPage", () => {
2424
const article = factories.articles.article({
2525
id: 42,
2626
title: "Existing Title",
27-
content: { id: 1, content: "Existing content" },
27+
content: {
28+
type: "doc",
29+
content: [
30+
{
31+
type: "paragraph",
32+
content: [{ type: "text", text: "Existing Title" }],
33+
},
34+
],
35+
},
2836
})
2937
setMockResponse.get(urls.articles.details(article.id), article)
3038

@@ -45,7 +53,15 @@ describe("ArticleEditPage", () => {
4553
const article = factories.articles.article({
4654
id: 123,
4755
title: "Existing Title",
48-
content: { id: 1, content: "Existing content" },
56+
content: {
57+
type: "doc",
58+
content: [
59+
{
60+
type: "paragraph",
61+
content: [{ type: "text", text: "Existing Title" }],
62+
},
63+
],
64+
},
4965
})
5066
setMockResponse.get(urls.articles.details(article.id), article)
5167

@@ -77,7 +93,15 @@ describe("ArticleEditPage", () => {
7793
const article = factories.articles.article({
7894
id: 7,
7995
title: "Old Title",
80-
content: { id: 1, content: "Bad content" },
96+
content: {
97+
type: "doc",
98+
content: [
99+
{
100+
type: "paragraph",
101+
content: [{ type: "text", text: "Existing Title" }],
102+
},
103+
],
104+
},
81105
})
82106
setMockResponse.get(urls.articles.details(article.id), article)
83107

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

Lines changed: 28 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
"use client"
2-
import React, { useEffect, useState, ChangeEvent } from "react"
2+
import React, { useEffect, useState } from "react"
33
import { Permission } from "api/hooks/user"
44
import { useRouter } from "next-nprogress-bar"
55
import { useArticleDetail, useArticlePartialUpdate } from "api/hooks/articles"
6-
import { Button, Input, Alert } from "@mitodl/smoot-design"
6+
import { Button, Alert } from "@mitodl/smoot-design"
77
import RestrictedRoute from "@/components/RestrictedRoute/RestrictedRoute"
8-
import { Container, Typography, styled, LoadingSpinner } from "ol-components"
8+
import {
9+
Container,
10+
Typography,
11+
styled,
12+
LoadingSpinner,
13+
TiptapEditorContainer,
14+
JSONContent,
15+
} from "ol-components"
16+
917
import { notFound } from "next/navigation"
1018
import { articlesView } from "@/common/urls"
1119

@@ -19,20 +27,17 @@ const ClientContainer = styled.div({
1927
margin: "10px 0",
2028
})
2129

22-
const TitleInput = styled(Input)({
23-
width: "100%",
24-
margin: "10px 0",
25-
})
26-
2730
const ArticleEditPage = ({ articleId }: { articleId: string }) => {
2831
const router = useRouter()
2932

3033
const id = Number(articleId)
3134
const { data: article, isLoading } = useArticleDetail(id)
3235

3336
const [title, setTitle] = useState<string>("")
34-
const [text, setText] = useState("")
35-
const [json, setJson] = useState({})
37+
const [json, setJson] = useState<JSONContent>({
38+
type: "doc",
39+
content: [{ type: "paragraph", content: [] }],
40+
})
3641
const [alertText, setAlertText] = useState("")
3742

3843
const { mutate: updateArticle, isPending } = useArticlePartialUpdate()
@@ -57,7 +62,6 @@ const ArticleEditPage = ({ articleId }: { articleId: string }) => {
5762
useEffect(() => {
5863
if (article && !title) {
5964
setTitle(article.title)
60-
setText(article.content ? JSON.stringify(article.content, null, 2) : "")
6165
setJson(article.content)
6266
}
6367
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -70,20 +74,13 @@ const ArticleEditPage = ({ articleId }: { articleId: string }) => {
7074
return notFound()
7175
}
7276

73-
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
74-
const value = e.target.value
75-
setText(value)
76-
77-
try {
78-
const parsed = JSON.parse(value)
79-
setJson(parsed)
80-
} catch {
81-
setJson({})
82-
}
77+
const handleChange = (json: object) => {
78+
setJson(json)
8379
}
80+
8481
return (
8582
<RestrictedRoute requires={Permission.ArticleEditor}>
86-
<Container className="article-wrapper">
83+
<Container>
8784
<Typography variant="h3" component="h1">
8885
Edit Article
8986
</Typography>
@@ -99,25 +96,17 @@ const ArticleEditPage = ({ articleId }: { articleId: string }) => {
9996
</Typography>
10097
</Alert>
10198
)}
102-
<TitleInput
103-
type="text"
104-
value={title}
105-
onChange={(e) => {
106-
console.log("Title input changed:", e.target.value)
107-
setTitle(e.target.value)
108-
setAlertText("")
109-
}}
110-
placeholder="Enter article title"
111-
className="input-field"
112-
/>
113-
114-
<ClientContainer className="editor-box">
115-
<textarea
99+
100+
<ClientContainer>
101+
<TiptapEditorContainer
116102
data-testid="editor"
117-
value={text}
103+
value={json}
118104
onChange={handleChange}
119-
placeholder="Type or paste JSON here..."
120-
style={{ width: "100%", height: 150 }}
105+
title={title}
106+
setTitle={(e) => {
107+
setTitle(e.target.value)
108+
setAlertText("")
109+
}}
121110
/>
122111
</ClientContainer>
123112

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

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
"use client"
2-
import React, { useState, ChangeEvent } from "react"
2+
import React, { useState } from "react"
33
import { Permission } from "api/hooks/user"
44
import { useRouter } from "next-nprogress-bar"
55
import { useArticleCreate } from "api/hooks/articles"
6-
import { Button, Input, Alert } from "@mitodl/smoot-design"
6+
import { Button, Alert } from "@mitodl/smoot-design"
77
import RestrictedRoute from "@/components/RestrictedRoute/RestrictedRoute"
8-
import { Container, Typography, styled } from "ol-components"
8+
import {
9+
TiptapEditorContainer,
10+
Container,
11+
Typography,
12+
styled,
13+
JSONContent,
14+
} from "ol-components"
915
import { articlesView } from "@/common/urls"
1016

1117
const SaveButton = styled.div({
@@ -18,17 +24,14 @@ const ClientContainer = styled.div({
1824
margin: "10px 0",
1925
})
2026

21-
const TitleInput = styled(Input)({
22-
width: "100%",
23-
margin: "10px 0",
24-
})
25-
2627
const ArticleNewPage: React.FC = () => {
2728
const router = useRouter()
2829

2930
const [title, setTitle] = React.useState<string>("")
30-
const [text, setText] = useState("")
31-
const [json, setJson] = useState({})
31+
const [json, setJson] = useState<JSONContent>({
32+
type: "doc",
33+
content: [{ type: "paragraph", content: [] }],
34+
})
3235
const [alertText, setAlertText] = React.useState("")
3336

3437
const { mutate: createArticle, isPending } = useArticleCreate()
@@ -57,20 +60,13 @@ const ArticleNewPage: React.FC = () => {
5760
},
5861
)
5962
}
60-
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
61-
const value = e.target.value
62-
setText(value)
63-
64-
try {
65-
const parsed = JSON.parse(value)
66-
setJson(parsed)
67-
} catch {
68-
setJson({})
69-
}
63+
const handleChange = (json: object) => {
64+
setJson(json)
7065
}
66+
7167
return (
7268
<RestrictedRoute requires={Permission.ArticleEditor}>
73-
<Container className="article-wrapper">
69+
<Container>
7470
<h1>Write Article</h1>
7571
{alertText && (
7672
<Alert
@@ -84,27 +80,19 @@ const ArticleNewPage: React.FC = () => {
8480
</Typography>
8581
</Alert>
8682
)}
87-
<TitleInput
88-
type="text"
89-
value={title}
90-
onChange={(e) => {
91-
setTitle(e.target.value)
92-
setAlertText("")
93-
}}
94-
placeholder="Enter article title"
95-
className="input-field"
96-
/>
9783

98-
<ClientContainer className="editor-box">
99-
<textarea
84+
<ClientContainer>
85+
<TiptapEditorContainer
10086
data-testid="editor"
101-
value={text}
87+
value={json}
88+
title={title}
89+
setTitle={(e) => {
90+
setTitle(e.target.value)
91+
setAlertText("")
92+
}}
10293
onChange={handleChange}
103-
placeholder="Type or paste JSON here..."
104-
style={{ width: "100%", height: 150 }}
10594
/>
10695
</ClientContainer>
107-
10896
<SaveButton>
10997
<Button
11098
variant="primary"

0 commit comments

Comments
 (0)