Skip to content

Commit

Permalink
feat(app): add questions markdown formatter (#419)
Browse files Browse the repository at this point in the history
* feat(app): add markdown formatter

* Fix bug

* feat(app): add prism theme

* refactor(app): move mdx serializer to page

* fix(app): storybook build error

* refactor(app): disable prism plugin

* refactor(app): change question content tag

Co-authored-by: Michal Miszczyszyn <michal@mmiszy.pl>
  • Loading branch information
AdiPol1359 and typeofweb committed Dec 16, 2022
1 parent 889bcd9 commit 1c97e9e
Show file tree
Hide file tree
Showing 17 changed files with 5,103 additions and 8,951 deletions.
2 changes: 1 addition & 1 deletion apps/api/modules/questions/questions.params.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Prisma } from "@prisma/client";
import { kv } from "../../utils.js";
import { GetQuestionsQuery } from "./questions.schemas";
import { GetQuestionsQuery } from "./questions.schemas.js";

export const getQuestionsPrismaParams = (
{ category, level, status = "accepted", limit, offset, order, orderBy }: GetQuestionsQuery,
Expand Down
2 changes: 2 additions & 0 deletions apps/app/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const nextConfig = {
transpilePackages: [],
esmExternals: true,
fontLoaders: [{ loader: "@next/font/google", options: { subsets: ["latin", "latin-ext"] } }],
serverComponentsExternalPackages: ["remark-gfm", "remark-prism"],
legacyBrowsers: false,
},
images: {
remotePatterns: [
Expand Down
5 changes: 5 additions & 0 deletions apps/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
"@tanstack/react-query-devtools": "4.20.4",
"client-only": "0.0.1",
"next": "13.0.8-canary.0",
"next-mdx-remote": "4.2.0",
"openapi-typescript-fetch": "1.1.3",
"prismjs": "1.29.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"remark-prism": "1.3.6",
"tailwind-merge": "1.8.0"
},
"devDependencies": {
Expand All @@ -33,6 +36,8 @@
"@storybook/react": "^7.0.0-alpha.54",
"@storybook/testing-library": "^0.0.13",
"@svgr/webpack": "6.5.1",
"@types/prismjs": "1.26.0",
"@types/remark-prism": "1.3.4",
"@types/node": "18.11.15",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.9",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { redirect } from "next/navigation";
import { QuestionItem } from "../../../../../components/QuestionsList/QuestionItem/QuestionItem";
import { QuestionsHeader } from "../../../../../components/QuestionsHeader";
import { QuestionsPagination } from "../../../../../components/QuestionsPagination";
import { PAGE_SIZE } from "../../../../../lib/constants";
Expand All @@ -9,6 +8,7 @@ import { technologies } from "../../../../../lib/technologies";
import { getAllQuestions } from "../../../../../services/questions.service";
import { Params, QuestionFilter, SearchParams } from "../../../../../types";
import { QuestionsList } from "../../../../../components/QuestionsList/QuestionsList";
import { serializeQuestionToMarkdown } from "../../../../../lib/question";

export default async function QuestionsPage({
params,
Expand All @@ -34,13 +34,17 @@ export default async function QuestionsPage({
level: levels?.join(","),
};

const { data } = await getAllQuestions(questionFilter);
const {
data: { data, meta },
} = await getAllQuestions(questionFilter);

const questions = await Promise.all(data.map(serializeQuestionToMarkdown));

return (
<div className="flex flex-col gap-y-10">
<QuestionsHeader technology={params.technology} total={data.meta.total} />
<QuestionsList questions={data.data} questionFilter={questionFilter} />
<QuestionsPagination technology={params.technology} total={data.meta.total} />
<QuestionsHeader technology={params.technology} total={meta.total} />
<QuestionsList questions={questions} questionFilter={questionFilter} />
<QuestionsPagination technology={params.technology} total={meta.total} />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { redirect } from "next/navigation";
import { SingleQuestion } from "../../../../../components/SingleQuestion";
import { serializeQuestionToMarkdown } from "../../../../../lib/question";
import { getQuestionById } from "../../../../../services/questions.service";
import { Params } from "../../../../../types";

Expand All @@ -16,5 +17,7 @@ export default async function SingleQuestionPage({ params }: { params: Params<"q
id: questionId,
});

return <SingleQuestion question={data} />;
const question = await serializeQuestionToMarkdown(data);

return <SingleQuestion question={question} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use client";

import { MDXRemote, MDXRemoteSerializeResult } from "next-mdx-remote";

import "prismjs/themes/prism-coy.min.css";

type QuestionContentProps = Readonly<{
source: MDXRemoteSerializeResult;
}>;

export const QuestionContent = ({ source }: QuestionContentProps) => {
return (
<div className="prism-coy grow">
<MDXRemote {...source} />
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const meta: Meta<typeof QuestionItem> = {
title: "QuestionItem",
component: QuestionItem,
args: {
title: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
creationDate: new Date(),
},
};
Expand Down
32 changes: 18 additions & 14 deletions apps/app/src/components/QuestionsList/QuestionItem/QuestionItem.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import Link from "next/link";
import { MDXRemoteSerializeResult } from "next-mdx-remote";
import { format } from "../../../utils/intl";
import { QuestionContent } from "./QuestionContent";
import { QuestionLevel } from "./QuestionLevel";
import { QuestionVoting } from "./QuestionVoting";
import type { Level } from "./QuestionLevel";

type QuestionItemProps = Readonly<{
id: number;
title: string;
mdxContent: MDXRemoteSerializeResult;
level: Level;
creationDate: Date;
votes: number;
Expand All @@ -16,21 +18,23 @@ type QuestionItemProps = Readonly<{

export const QuestionItem = ({
id,
title,
mdxContent,
level,
creationDate,
votes,
voted,
onQuestionVote,
}: QuestionItemProps) => (
<article className="flex bg-white p-5 text-sm text-neutral-500 shadow-md dark:bg-white-dark dark:text-neutral-200">
<QuestionVoting questionId={id} votes={votes} voted={voted} onQuestionVote={onQuestionVote} />
<h3 className="grow">{title}</h3>
<div className="ml-4 flex min-w-max flex-col items-end">
<QuestionLevel level={level} />
<Link href={`/questions/p/${id}`} className="mt-3 text-xs underline">
<time dateTime={creationDate.toISOString()}>{format(creationDate)}</time>
</Link>
</div>
</article>
);
}: QuestionItemProps) => {
return (
<article className="flex bg-white p-5 text-sm text-neutral-500 shadow-md dark:bg-white-dark dark:text-neutral-200">
<QuestionVoting questionId={id} votes={votes} voted={voted} onQuestionVote={onQuestionVote} />
<QuestionContent source={mdxContent} />
<div className="ml-4 flex min-w-max flex-col items-end">
<QuestionLevel level={level} />
<Link href={`/questions/p/${id}`} className="mt-3 text-xs underline">
<time dateTime={creationDate.toISOString()}>{format(creationDate)}</time>
</Link>
</div>
</article>
);
};
4 changes: 2 additions & 2 deletions apps/app/src/components/QuestionsList/QuestionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const QuestionsList = ({ questions, questionFilter }: QuestionsListProps)

return (
<>
{questions.map(({ id, question, _levelId, acceptedAt }) => {
{questions.map(({ id, mdxContent, _levelId, acceptedAt }) => {
const questionVote = questionsVotes?.find((questionVote) => questionVote.id === id);
const [votes, voted] = questionVote
? [questionVote.votesCount, questionVote.currentUserVotedOn]
Expand All @@ -28,7 +28,7 @@ export const QuestionsList = ({ questions, questionFilter }: QuestionsListProps)
<QuestionItem
key={id}
id={id}
title={question}
mdxContent={mdxContent}
level={_levelId}
creationDate={new Date(acceptedAt || "")}
votes={votes}
Expand Down
4 changes: 2 additions & 2 deletions apps/app/src/components/SingleQuestion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ type SingleQuestionProps = Readonly<{
}>;

export const SingleQuestion = ({
question: { id, question, _levelId, acceptedAt },
question: { id, mdxContent, _levelId, acceptedAt },
}: SingleQuestionProps) => {
const { votes, voted, refetch } = useGetQuestionVotesById(id);

return (
<QuestionItem
id={id}
title={question}
mdxContent={mdxContent}
level={_levelId}
creationDate={new Date(acceptedAt || "")}
votes={votes}
Expand Down
9 changes: 9 additions & 0 deletions apps/app/src/lib/markdown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// import remarkPrism from "remark-prism";
import { serialize } from "next-mdx-remote/serialize";

export const serializeSource = (source: string) =>
serialize(source, {
mdxOptions: {
// remarkPlugins: [remarkPrism],
},
});
10 changes: 10 additions & 0 deletions apps/app/src/lib/question.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { paths } from "openapi-types";
import { serializeSource } from "./markdown";

type APIQuestion =
paths["/questions/{id}"]["get"]["responses"][200]["content"]["application/json"]["data"];

export const serializeQuestionToMarkdown = async ({ question, ...rest }: APIQuestion) => {
const mdxContent = await serializeSource(question);
return { mdxContent, ...rest };
};
4 changes: 4 additions & 0 deletions apps/app/src/styles/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,7 @@
--color-main-light: var(--senior-main-color-light);
}
}
.prism-coy pre[class*="language-"]::before,
.prism-coy pre[class*="language-"]::after {
display: none;
}
7 changes: 5 additions & 2 deletions apps/app/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { paths } from "openapi-types";
import { MDXRemoteSerializeResult } from "next-mdx-remote";

type ExcludeUndefined<T> = Exclude<T, undefined>;

export type UserData =
paths["/auth/me"]["get"]["responses"][200]["content"]["application/json"]["data"];

export type Question =
paths["/questions/{id}"]["get"]["responses"][200]["content"]["application/json"]["data"];
export type Question = Omit<
paths["/questions/{id}"]["get"]["responses"][200]["content"]["application/json"]["data"],
"question"
> & { mdxContent: MDXRemoteSerializeResult };

export type QuestionFilter = ExcludeUndefined<
ExcludeUndefined<paths["/questions"]["get"]["parameters"]>["query"]
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"check-types": "turbo run check-types --parallel",
"prepare": "husky install"
},
"resolutions": {
"@mdx-js/mdx": "2.1.5",
"@mdx-js/react": "2.1.5"
},
"devDependencies": {
"eslint-config-devfaq": "workspace:*",
"husky": "8.0.2",
Expand Down
Loading

0 comments on commit 1c97e9e

Please sign in to comment.