Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions apps/cookbook/src/components/external-link.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { render } from '@testing-library/react';
import React from 'react';
import { expect, test } from 'vitest';
import { ExternalLink } from './external-link';

test(`${ExternalLink.name} renders correctly without UTM parameters`, () => {
const { container } = render(
<ExternalLink href="https://courses.marmicode.io">
Let's cook some tests
</ExternalLink>,
);

const linkEl = container.querySelector('a');

expect
.soft(linkEl)
.toHaveAttribute(
'href',
'https://courses.marmicode.io/?utm_source=cookbook',
);
expect.soft(linkEl).toHaveTextContent(`Let's cook some tests`);
expect.soft(linkEl).toHaveAttribute('target', '_blank');
expect.soft(linkEl).toHaveAttribute('rel', 'noopener noreferrer');
});

test(`${ExternalLink.name} renders correctly with UTM parameters`, () => {
const { container } = render(
<ExternalLink
href="https://courses.marmicode.io"
medium="in-article"
campaign="prep-station-v20"
content="flushing-flusheffects"
>
Let's cook some tests
</ExternalLink>,
);

const linkEl = container.querySelector('a');

expect
.soft(linkEl)
.toHaveAttribute(
'href',
'https://courses.marmicode.io/?utm_source=cookbook&utm_medium=in-article&utm_campaign=prep-station-v20&utm_content=flushing-flusheffects',
);
});
42 changes: 42 additions & 0 deletions apps/cookbook/src/components/external-link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';

interface ExternalLinkProps {
href: string;
children: React.ReactNode;
className?: string;
medium?: 'footer' | 'in-article' | 'toc';
campaign?: string;
content?: string;
}

export function ExternalLink({
href,
children,
className,
medium,
campaign,
content,
}: ExternalLinkProps) {
const url = new URL(href);
url.searchParams.set('utm_source', 'cookbook');
if (medium) {
url.searchParams.set('utm_medium', medium);
}
if (campaign) {
url.searchParams.set('utm_campaign', campaign);
}
if (content) {
url.searchParams.set('utm_content', content);
}

return (
<a
className={className}
href={url.toString()}
target="_blank"
rel="noopener noreferrer"
>
{children}
</a>
);
}
62 changes: 62 additions & 0 deletions apps/cookbook/src/components/special-offers.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.container {
display: flex;
flex-direction: column;
align-items: center;

width: 80%;
max-width: 240px;
margin: 0 auto;
padding: 1rem;

border: 1px solid #e0e0e0;
border-radius: 8px;
background: #fdfcfb;
}

.link {
font-size: 0.8rem;
color: #6f2c91;
text-decoration: underline;
}

.primary {
font-size: 0.875rem;
font-weight: bold;
}

.image {
width: 100%;
border-radius: 4px;
margin-bottom: 0.5rem;
}

.priceInfo {
color: #666;
font-size: 0.82rem;
margin-bottom: 0.5rem;
}

.lifetimeAccess {
color: #333;
}

.description {
color: #333;
font-size: 0.8rem;
line-height: 1.2;
margin: 0;
}

.separator {
margin: 1rem 0;
}

.moreSeasoning {
display: flex;
flex-direction: column;
gap: 0.5rem;
}

.moreSeasoningTitle {
font-size: 0.9rem;
}
72 changes: 72 additions & 0 deletions apps/cookbook/src/components/special-offers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React, { JSX } from 'react';

import styles from './special-offers.module.css';
import { ExternalLink } from './external-link';

export function SpecialOffers(): JSX.Element {
const medium = 'toc';
return (
<section className={styles.container}>
<ExternalLink
href="https://courses.marmicode.io/courses/pragmatic-angular-testing"
medium={medium}
content="course_image"
>
<img
src="/img/course.png"
alt="Pragmatic Angular Testing"
className={styles.image}
/>
</ExternalLink>
<div>
<ExternalLink
className={[styles.primary, styles.link].join(' ')}
href="https://courses.marmicode.io/courses/pragmatic-angular-testing"
medium={medium}
content="course_link"
>
<span role="img" aria-label="cook">
👨🏻‍🍳
</span>
Let's cook some tests →
</ExternalLink>

<p className={styles.priceInfo}>
💰 80€ · <s>170€</s> ·{' '}
<span className={styles.lifetimeAccess}>Lifetime access</span>
</p>

<p className={styles.description}>
Learn how to write reliable tests that survive upgrades and
refactorings.
</p>

<hr className={styles.separator} />

<div className={styles.moreSeasoning}>
<strong className={styles.moreSeasoningTitle}>
<span role="img" aria-label="salt">
🧂
</span>
Need more seasoning?
</strong>
<ExternalLink
className={styles.link}
href="https://courses.marmicode.io/bundles/ginger-review"
medium={medium}
>
See code review/Q&A plans →
</ExternalLink>

<ExternalLink
className={styles.link}
href="https://courses.marmicode.io"
medium={medium}
>
Or let's plan a call →
</ExternalLink>
</div>
</div>
</section>
);
}
6 changes: 3 additions & 3 deletions apps/cookbook/src/components/youtube.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ test(`${Youtube.name} should render iframe`, async () => {
expect
.soft(iframeEl)
.toHaveAttribute('src', 'https://www.youtube.com/embed/aIAKlhrex8c');
expect.soft(iframeEl.style.aspectRatio).toBe('16 / 9');
expect.soft(iframeEl.style.width).toBe('100%');
expect.soft(iframeEl.title).toBe('Nx Implicit Libraries Video');
expect.soft(iframeEl?.style.aspectRatio).toBe('16 / 9');
expect.soft(iframeEl?.style.width).toBe('100%');
expect.soft(iframeEl?.title).toBe('Nx Implicit Libraries Video');
});
4 changes: 4 additions & 0 deletions apps/cookbook/src/css-modules.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '*.module.css' {
const classes: { [key: string]: string };
export default classes;
}
16 changes: 16 additions & 0 deletions apps/cookbook/src/theme/TOCItems/Tree.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { WrapperProps } from '@docusaurus/types';
import { SpecialOffers } from '@site/src/components/special-offers';
import Tree from '@theme-original/TOCItems/Tree';
import type TreeType from '@theme/TOCItems/Tree';
import React, { ReactElement } from 'react';

type Props = WrapperProps<typeof TreeType>;

export default function TreeWrapper(props: Props): ReactElement {
return (
<>
<Tree {...props} />
<SpecialOffers />
</>
);
}
Binary file added apps/cookbook/static/img/course.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions apps/cookbook/tsconfig.app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
// This file is not used in compilation. It is here just for a nice editor experience.
"extends": "./tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"composite": true,
"noEmit": false
},
"exclude": ["src/**/*.spec.ts", "src/**/*.spec.tsx"]
}
11 changes: 10 additions & 1 deletion apps/cookbook/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,14 @@
"extends": "@docusaurus/tsconfig",
"compilerOptions": {
"baseUrl": "."
}
},
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"include": ["src/**/*.ts", "src/**/*.tsx"]
}
9 changes: 9 additions & 0 deletions apps/cookbook/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"composite": true,
"noEmit": false,
"types": ["@testing-library/jest-dom"]
},
"files": ["test-setup.tsx"]
}
16 changes: 0 additions & 16 deletions apps/cookbook/vite.config.mts
Original file line number Diff line number Diff line change
@@ -1,24 +1,8 @@
/// <reference types='vitest' />
import { defineConfig } from 'vite';

import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';

export default defineConfig({
root: __dirname,
cacheDir: '../../node_modules/.vite/cookbook',

plugins: [nxViteTsPaths()],

test: {
globals: true,
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],

reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/cookbook',
provider: 'v8',
},
setupFiles: ['./test-setup.tsx'],
},
});
15 changes: 15 additions & 0 deletions apps/cookbook/vitest.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { mergeConfig } from 'vitest/config';
import viteConfig from './vite.config.mts';

export default mergeConfig(viteConfig, {
test: {
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/cookbook',
provider: 'v8',
},
setupFiles: ['./test-setup.tsx'],
},
});
Binary file modified bun.lockb
Binary file not shown.