From 4f8b407a751512ddc6b8622ef9e74122714e4585 Mon Sep 17 00:00:00 2001 From: Juri Date: Wed, 16 Apr 2025 11:21:07 +0200 Subject: [PATCH] feat(nx-dev): add course preview component --- docs/README.md | 8 ++++ docs/shared/migration/adding-to-monorepo.md | 5 +- nx-dev/nx-dev/styles/main.css | 15 +++--- nx-dev/ui-common/src/index.ts | 1 + nx-dev/ui-common/src/lib/course-video.tsx | 48 +++++++++++++++++++ nx-dev/ui-markdoc/src/index.ts | 6 ++- .../src/lib/tags/course-video.schema.ts | 19 ++++++++ 7 files changed, 91 insertions(+), 11 deletions(-) create mode 100644 nx-dev/ui-common/src/lib/course-video.tsx create mode 100644 nx-dev/ui-markdoc/src/lib/tags/course-video.schema.ts diff --git a/docs/README.md b/docs/README.md index f8954a81f2..59199bc4d9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -299,6 +299,14 @@ Have a more decent button-like widget that you can place below sections of a tut {% video-link link="https://youtu.be/OQ-Zc5tcxJE?t=64" /%} ``` +#### Course video embed + +This is for embedding a video just like with the Youtube component, but in addition to have a link to a Nx Course (nx.dev/courses) video to improve the discoverability of these courses. + +```markdown +{% course-video src="https://youtu.be/3hW53b1IJ84" courseTitle="From PNPM Workspaces to Distributed CI" courseUrl="/courses/pnpm-nx-next/lessons-01-nx-init" title="Initialize Nx in Your Project with nx init" /%} +``` + #### Project Details View Embed a Project Details View that is identical what is shown in Nx Console or `nx show project myproject --web` diff --git a/docs/shared/migration/adding-to-monorepo.md b/docs/shared/migration/adding-to-monorepo.md index e594b8fba3..36f2c78847 100644 --- a/docs/shared/migration/adding-to-monorepo.md +++ b/docs/shared/migration/adding-to-monorepo.md @@ -21,10 +21,7 @@ an existing NPM/Yarn or PNPM-based monorepo setup, you can easily add Nx to get This is a low-impact operation because all that needs to be done is to install the `nx` package at the root level and add an `nx.json` for configuring caching and task pipelines. -{% youtube -src="https://www.youtube.com/embed/ngdoUQBvAjo" -title="Add Nx to a PNPM workspaces monorepo" -width="100%" /%} +{% course-video src="https://youtu.be/3hW53b1IJ84" courseTitle="From PNPM Workspaces to Distributed CI" courseUrl="/courses/pnpm-nx-next/lessons-01-nx-init" title="Initialize Nx in Your Project with nx init" /%} ## Installing Nx diff --git a/nx-dev/nx-dev/styles/main.css b/nx-dev/nx-dev/styles/main.css index 4aef6bd97a..73838d24c3 100644 --- a/nx-dev/nx-dev/styles/main.css +++ b/nx-dev/nx-dev/styles/main.css @@ -52,21 +52,24 @@ pre, iframe[src*='youtube'] { aspect-ratio: 16 / 9; } -.prose iframe, -.prose img { + +.prose iframe:not(.not-prose):not(.not-prose *), +.prose img:not(.not-prose):not(.not-prose *) { display: block; margin: 2rem auto; } -.prose iframe { + +.prose iframe:not(.not-prose):not(.not-prose *) { width: 100%; max-width: 560px; } -.prose img { + +.prose img:not(.not-prose):not(.not-prose *) { width: max-content; } -.prose iframe[src^="https://staging.nx.app"], -.prose iframe[src^="https://nx.app"] +.prose iframe:not(.not-prose):not(.not-prose *)[src^="https://staging.nx.app"], +.prose iframe:not(.not-prose):not(.not-prose *)[src^="https://nx.app"] { height: 80vh; max-width: 100%; diff --git a/nx-dev/ui-common/src/index.ts b/nx-dev/ui-common/src/index.ts index 92bec176c8..42d7787f36 100644 --- a/nx-dev/ui-common/src/index.ts +++ b/nx-dev/ui-common/src/index.ts @@ -17,6 +17,7 @@ export * from './lib/tweet'; export * from './lib/typography'; export * from './lib/github-star-widget'; export * from './lib/youtube.component'; +export * from './lib/course-video'; export * from './lib/x-icon'; export * from './lib/discord-icon'; export * from './lib/trusted-by'; diff --git a/nx-dev/ui-common/src/lib/course-video.tsx b/nx-dev/ui-common/src/lib/course-video.tsx new file mode 100644 index 0000000000..071389433d --- /dev/null +++ b/nx-dev/ui-common/src/lib/course-video.tsx @@ -0,0 +1,48 @@ +import { ButtonLink } from './button'; +import { YouTube } from './youtube.component'; + +export interface CourseVideoProps { + /** + * The YouTube video URL + */ + src: string; + courseTitle: string; + courseUrl: string; +} + +export function CourseVideo({ + src: videoUrl, + courseTitle, + courseUrl, +}: CourseVideoProps): JSX.Element { + return ( +
+
+
+ +
+
+
+

+ {courseTitle} +

+ + Watch full course + +
+
+
+
+ ); +} diff --git a/nx-dev/ui-markdoc/src/index.ts b/nx-dev/ui-markdoc/src/index.ts index 5c27c90bfe..a2546e41d9 100644 --- a/nx-dev/ui-markdoc/src/index.ts +++ b/nx-dev/ui-markdoc/src/index.ts @@ -39,6 +39,8 @@ import { Tab, Tabs } from './lib/tags/tabs.component'; import { tab, tabs } from './lib/tags/tabs.schema'; import { Tweet, tweet } from '@nx/nx-dev/ui-common'; import { YouTube, youtube } from '@nx/nx-dev/ui-common'; +import { CourseVideo } from '@nx/nx-dev/ui-common'; +import { courseVideo } from './lib/tags/course-video.schema'; import { VideoLink, videoLink } from './lib/tags/video-link.component'; // import { SvgAnimation, svgAnimation } from './lib/tags/svg-animation.component'; import { Pill } from './lib/tags/pill.component'; @@ -91,6 +93,7 @@ export const getMarkdocCustomConfig = ( toc: tableOfContents, tweet, youtube, + 'course-video': courseVideo, 'video-link': videoLink, metrics, // 'svg-animation': svgAnimation, @@ -123,6 +126,7 @@ export const getMarkdocCustomConfig = ( Testimonial, Tweet, YouTube, + CourseVideo, VideoLink, VideoPlayer, Metrics, @@ -176,4 +180,4 @@ export const renderMarkdown: ( }; }; -export { Metrics, VideoLink, GithubRepository }; +export { Metrics, VideoLink, GithubRepository, CourseVideo }; diff --git a/nx-dev/ui-markdoc/src/lib/tags/course-video.schema.ts b/nx-dev/ui-markdoc/src/lib/tags/course-video.schema.ts new file mode 100644 index 0000000000..bee55f22bd --- /dev/null +++ b/nx-dev/ui-markdoc/src/lib/tags/course-video.schema.ts @@ -0,0 +1,19 @@ +import { Schema } from '@markdoc/markdoc'; + +export const courseVideo: Schema = { + render: 'CourseVideo', + attributes: { + src: { + type: 'String', + required: true, + }, + courseTitle: { + type: 'String', + required: true, + }, + courseUrl: { + type: 'String', + required: true, + }, + }, +};