Add roadmap rendering
This commit is contained in:
@ -5,6 +5,7 @@ const BlockQuote = styled.blockquote`
|
|||||||
position: relative;
|
position: relative;
|
||||||
background: #e8e8e8;
|
background: #e8e8e8;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
10
components/md-renderer/mdx-components/code.tsx
Normal file
10
components/md-renderer/mdx-components/code.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Code as ChakraCode } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
type CodeType = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Code(props: CodeType) {
|
||||||
|
return <ChakraCode bg='blue.500'>{props.children}</ChakraCode>;
|
||||||
|
}
|
@ -10,6 +10,7 @@ import { BadgeLink } from './badge-link';
|
|||||||
import Ul from './ul';
|
import Ul from './ul';
|
||||||
import Li from './li';
|
import Li from './li';
|
||||||
import PremiumBlock from './premium-block';
|
import PremiumBlock from './premium-block';
|
||||||
|
import { Code } from '@chakra-ui/react';
|
||||||
|
|
||||||
const MdxComponents = {
|
const MdxComponents = {
|
||||||
p: P,
|
p: P,
|
||||||
@ -20,6 +21,7 @@ const MdxComponents = {
|
|||||||
table: Table,
|
table: Table,
|
||||||
iframe: IFrame,
|
iframe: IFrame,
|
||||||
img: Img,
|
img: Img,
|
||||||
|
code: Code,
|
||||||
BadgeLink: BadgeLink,
|
BadgeLink: BadgeLink,
|
||||||
PremiumBlock: PremiumBlock,
|
PremiumBlock: PremiumBlock,
|
||||||
ul: Ul,
|
ul: Ul,
|
||||||
|
@ -16,7 +16,7 @@ export function PageHeader(props: PageHeaderProps) {
|
|||||||
<Heading
|
<Heading
|
||||||
as='h1'
|
as='h1'
|
||||||
color='black'
|
color='black'
|
||||||
fontSize={['25px', '25px', '35px']}
|
fontSize={['25px', '25px', '40px']}
|
||||||
fontWeight={700}
|
fontWeight={700}
|
||||||
mb={['2px', '2px', '5px']}
|
mb={['2px', '2px', '5px']}
|
||||||
>
|
>
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
"featured": true,
|
"featured": true,
|
||||||
"imagePath": "/roadmaps/frontend.png",
|
"imagePath": "/roadmaps/frontend.png",
|
||||||
"resourcesPath": "/roadmaps/1-frontend/resources.md",
|
"resourcesPath": "/roadmaps/1-frontend/resources.md",
|
||||||
|
"id": "frontend",
|
||||||
"url": "/frontend"
|
"url": "/frontend"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -79,6 +80,7 @@
|
|||||||
"name": "Kamran Ahmed",
|
"name": "Kamran Ahmed",
|
||||||
"url": "https://twitter.com/kamranahmedse"
|
"url": "https://twitter.com/kamranahmedse"
|
||||||
},
|
},
|
||||||
|
"id": "backend",
|
||||||
"url": "/backend"
|
"url": "/backend"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -119,6 +121,7 @@
|
|||||||
"name": "Kamran Ahmed",
|
"name": "Kamran Ahmed",
|
||||||
"url": "https://twitter.com/kamranahmedse"
|
"url": "https://twitter.com/kamranahmedse"
|
||||||
},
|
},
|
||||||
|
"id": "devops",
|
||||||
"url": "/devops"
|
"url": "/devops"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -157,6 +160,7 @@
|
|||||||
"name": "Kamran Ahmed",
|
"name": "Kamran Ahmed",
|
||||||
"url": "https://twitter.com/kamranahmedse"
|
"url": "https://twitter.com/kamranahmedse"
|
||||||
},
|
},
|
||||||
|
"id": "react",
|
||||||
"url": "/react"
|
"url": "/react"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -199,6 +203,7 @@
|
|||||||
"featured": true,
|
"featured": true,
|
||||||
"detailed": false,
|
"detailed": false,
|
||||||
"versions": [],
|
"versions": [],
|
||||||
|
"id": "postgresql-dba",
|
||||||
"url": "/postgresql-dba"
|
"url": "/postgresql-dba"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -237,6 +242,7 @@
|
|||||||
"name": "Kamran Ahmed",
|
"name": "Kamran Ahmed",
|
||||||
"url": "https://twitter.com/kamranahmedse"
|
"url": "https://twitter.com/kamranahmedse"
|
||||||
},
|
},
|
||||||
|
"id": "android",
|
||||||
"url": "/android"
|
"url": "/android"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -269,6 +275,7 @@
|
|||||||
"name": "Anas Fitiani",
|
"name": "Anas Fitiani",
|
||||||
"url": "https://github.com/anas-qa"
|
"url": "https://github.com/anas-qa"
|
||||||
},
|
},
|
||||||
|
"id": "qa",
|
||||||
"url": "/qa"
|
"url": "/qa"
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -5,6 +5,3 @@ Also, please note that the list below is exhaustive, and the items are listed in
|
|||||||

|

|
||||||
|
|
||||||
Please note that the list is opinionated, and you might have different opinions than those of the author. Having said that, [we would love to hear your opinions](https://github.com/kamranahmedse/roadmap.sh/issues/new) and incorporate them in the picture if suitable.
|
Please note that the list is opinionated, and you might have different opinions than those of the author. Having said that, [we would love to hear your opinions](https://github.com/kamranahmedse/roadmap.sh/issues/new) and incorporate them in the picture if suitable.
|
||||||
|
|
||||||
<!-- @fixme add padding to the container -->
|
|
||||||
<br /><br /><br />
|
|
||||||
|
@ -1,2 +1,11 @@
|
|||||||
## QA Roadmap
|
> **Roadmap is not ready yet**. Please check back later or [subscribe to get notified](/signup).
|
||||||
Roadmap to becoming a QA Engineer
|
|
||||||
|
While we prepare the roadmap, follow this simple advice to learn anything
|
||||||
|
|
||||||
|
> Just **pick a project and start working on it**, you will learn all that you need along the way.
|
||||||
|
|
||||||
|
**→** [All Roadmaps](/roadmaps) • [Programming guides](/guides) • [Subscribe](/signup)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,14 +20,12 @@ export type RoadmapType = {
|
|||||||
contentPath?: string;
|
contentPath?: string;
|
||||||
resourcesPath: string;
|
resourcesPath: string;
|
||||||
isCommunity: boolean;
|
isCommunity: boolean;
|
||||||
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getRequestedRoadmap(req: NextApiRequest): RoadmapType | undefined {
|
export function getRoadmapById(id: string): RoadmapType | undefined {
|
||||||
// remove trailing slashes
|
return (roadmaps as RoadmapType[]).find(roadmap => roadmap.id === id);
|
||||||
const normalizedUrl = req.url?.replace(/\/$/, '') || '';
|
|
||||||
|
|
||||||
return (roadmaps as RoadmapType[]).find(roadmap => normalizedUrl.startsWith(roadmap.url));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAllRoadmaps(): RoadmapType[] {
|
export function getAllRoadmaps(): RoadmapType[] {
|
||||||
|
5
package-lock.json
generated
5
package-lock.json
generated
@ -6100,6 +6100,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"prism-themes": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prism-themes/-/prism-themes-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-pj7A7C9C8juQRFkZHYeApQHdUiak5nkKYlHyWwh7n/b6WjI9KzGtqaGhxUZAIaV1PZ9Z1epJwe41Px3TUGsNJw=="
|
||||||
|
},
|
||||||
"prismjs": {
|
"prismjs": {
|
||||||
"version": "1.24.1",
|
"version": "1.24.1",
|
||||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz",
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"date-fns": "^2.23.0",
|
"date-fns": "^2.23.0",
|
||||||
"framer-motion": "^4.1.17",
|
"framer-motion": "^4.1.17",
|
||||||
"next": "^11.1.0",
|
"next": "^11.1.0",
|
||||||
|
"prism-themes": "^1.8.0",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
|
@ -1,50 +1,61 @@
|
|||||||
import { Box, Button, Container, SimpleGrid, Stack } from '@chakra-ui/react';
|
import { Box, Button, Container, Stack } from '@chakra-ui/react';
|
||||||
import { DownloadIcon, EmailIcon } from '@chakra-ui/icons';
|
import { DownloadIcon, EmailIcon } from '@chakra-ui/icons';
|
||||||
import styled from 'styled-components';
|
|
||||||
import Image from 'next/image';
|
|
||||||
import { GlobalHeader } from '../../components/global-header';
|
import { GlobalHeader } from '../../components/global-header';
|
||||||
import { OpensourceBanner } from '../../components/opensource-banner';
|
import { OpensourceBanner } from '../../components/opensource-banner';
|
||||||
import { UpdatesBanner } from '../../components/updates-banner';
|
import { UpdatesBanner } from '../../components/updates-banner';
|
||||||
import { Footer } from '../../components/footer';
|
import { Footer } from '../../components/footer';
|
||||||
import { PageHeader } from '../../components/page-header';
|
import { PageHeader } from '../../components/page-header';
|
||||||
|
import { getAllRoadmaps, getRoadmapById, RoadmapType } from '../../lib/roadmap';
|
||||||
|
import MdRenderer from '../../components/md-renderer';
|
||||||
|
|
||||||
const RoadmapBody = styled.div`
|
type RoadmapProps = {
|
||||||
margin-bottom: 30px;
|
roadmap: RoadmapType;
|
||||||
font-size: 15px;
|
};
|
||||||
|
|
||||||
h1 {
|
function ImageRoadmap(props: RoadmapProps) {
|
||||||
font-size: 32px;
|
const { roadmap } = props;
|
||||||
font-weight: 700;
|
if (!roadmap.imagePath) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
return (
|
||||||
line-height: 25px;
|
<Container maxW={'900px'} position='relative'>
|
||||||
margin-bottom: 20px
|
<Box mb='30px'>
|
||||||
|
<img alt='Frontend Roadmap' src={roadmap.imagePath} />
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TextualRoadmap(props: RoadmapProps) {
|
||||||
|
const { roadmap } = props;
|
||||||
|
if (!roadmap.contentPath) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul, ol {
|
// Remove trailing slashes
|
||||||
margin: 0 0 20px 40px;
|
const normalizedPath = roadmap.contentPath.replace(/^\//, '');
|
||||||
|
const RoadmapContent = require(`../../content/${normalizedPath}`).default;
|
||||||
|
|
||||||
li + li {
|
return (
|
||||||
margin-top: 7px;
|
<Container maxW={'container.md'} position='relative'>
|
||||||
}
|
<MdRenderer>
|
||||||
}
|
<RoadmapContent />
|
||||||
|
</MdRenderer>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
img {
|
export default function Roadmap(props: RoadmapProps) {
|
||||||
max-width: 100%;
|
const { roadmap } = props;
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default function Roadmap() {
|
|
||||||
const isImageOnly = true;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box bg='white' minH='100vh'>
|
<Box bg='white' minH='100vh'>
|
||||||
<GlobalHeader />
|
<GlobalHeader />
|
||||||
<Box mb='60px'>
|
<Box mb='60px'>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={'Frontend Developer'}
|
title={ roadmap.title }
|
||||||
subtitle={'Step by step guide to becoming a modern frontend developer'}
|
subtitle={ roadmap.description }
|
||||||
>
|
>
|
||||||
<Stack mt='20px' isInline>
|
<Stack mt='20px' isInline>
|
||||||
<Button size='xs' py='14px' px='10px' leftIcon={<DownloadIcon />} colorScheme='yellow' variant='solid'>
|
<Button size='xs' py='14px' px='10px' leftIcon={<DownloadIcon />} colorScheme='yellow' variant='solid'>
|
||||||
@ -55,11 +66,9 @@ export default function Roadmap() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<Container maxW={ isImageOnly ? '900px': 'container.md'} position='relative'>
|
|
||||||
<RoadmapBody>
|
<ImageRoadmap roadmap={roadmap} />
|
||||||
<img alt='Frontend Roadmap' src='/roadmaps/frontend.png' />
|
<TextualRoadmap roadmap={roadmap} />
|
||||||
</RoadmapBody>
|
|
||||||
</Container>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<OpensourceBanner />
|
<OpensourceBanner />
|
||||||
@ -68,3 +77,37 @@ export default function Roadmap() {
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StaticPathItem = {
|
||||||
|
params: {
|
||||||
|
roadmap: string
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const roadmaps = getAllRoadmaps();
|
||||||
|
const paramsList: StaticPathItem[] = roadmaps.map(roadmap => ({
|
||||||
|
params: { 'roadmap': roadmap.id }
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
paths: paramsList,
|
||||||
|
fallback: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContextType = {
|
||||||
|
params: {
|
||||||
|
roadmap: string
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function getStaticProps(context: ContextType) {
|
||||||
|
const roadmapId: string = context?.params?.roadmap;
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
roadmap: getRoadmapById(roadmapId)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { AppProps } from 'next/app';
|
import type { AppProps } from 'next/app';
|
||||||
import { ChakraProvider } from '@chakra-ui/react';
|
import { ChakraProvider } from '@chakra-ui/react';
|
||||||
import { roadmapTheme } from './theme';
|
import { roadmapTheme } from './theme';
|
||||||
|
import 'prism-themes/themes/prism-shades-of-purple.css';
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }: AppProps) {
|
function MyApp({ Component, pageProps }: AppProps) {
|
||||||
return (
|
return (
|
||||||
|
@ -82,6 +82,7 @@ export default function Home(props: HomeProps) {
|
|||||||
{videos.map(video => (
|
{videos.map(video => (
|
||||||
<LinksListItem
|
<LinksListItem
|
||||||
key={video.url}
|
key={video.url}
|
||||||
|
badgeText={video.isPro ? 'PRO' : ''}
|
||||||
hideSubtitleOnMobile
|
hideSubtitleOnMobile
|
||||||
title={video.title}
|
title={video.title}
|
||||||
subtitle={video.duration}
|
subtitle={video.duration}
|
||||||
|
@ -33,6 +33,7 @@ const roadmapsMeta = roadmapDirs.reduce((metaAcc, roadmapDirName) => {
|
|||||||
...metaAcc,
|
...metaAcc,
|
||||||
{
|
{
|
||||||
...roadmapMeta,
|
...roadmapMeta,
|
||||||
|
id: roadmapSlug,
|
||||||
url: `/${roadmapSlug}`,
|
url: `/${roadmapSlug}`,
|
||||||
contentPath: contentPath,
|
contentPath: contentPath,
|
||||||
resourcesPath: resourcesPath
|
resourcesPath: resourcesPath
|
||||||
|
Reference in New Issue
Block a user