Skip to content

feat: implement level grouping #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 20, 2025
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
8 changes: 7 additions & 1 deletion .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { StorybookConfig } from '@storybook/react-vite';
import { mergeConfig } from 'vite';
import path from 'path';

const config: StorybookConfig = {
stories: [
Expand Down Expand Up @@ -27,7 +28,12 @@ const config: StorybookConfig = {
},
async viteFinal(baseConfig, { configType }) {
return mergeConfig(baseConfig, {
...(configType === 'PRODUCTION' ? { base: '/storybook/' } : {})
...(configType === 'PRODUCTION' ? { base: '/storybook/' } : {}),
resolve: {
alias: {
'@shared': path.resolve(__dirname, '../src', 'shared')
}
}
});
}
};
Expand Down
7 changes: 6 additions & 1 deletion .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ const preview: Preview = {
code: Code
}
},
storySort: ['Introduction', 'Lessons', 'Recipes'],
storySort: [
'Introduction',
'Lessons',
['🥉 Bronze', '🥈 Silver', '🥇 Gold'],
'Recipes'
],
controls: {
matchers: {
color: /(background|color)$/i,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives",
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "npm run build && storybook build -o dist/storybook",
Expand Down
34 changes: 21 additions & 13 deletions src/course/01-introduction/01-Welcome.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,30 @@ I will be going in high detail on each of the lessons so if there are some holes

## Contents

Each lesson is broken down in an exercise file and a final file. The exercise file will have instructions in pseudo format to help guide you through the code snippets. Not to worry though, there will be videos to support you along the way.
Each lesson is broken down in an exercise file and a final file. The exercise file will have instructions in pseudo format to help guide you through the code snippets.

### Lessons

- 01 - [Conditionally rendering pattern](?path=/docs/lessons-01-conditional-rendering-pattern-01-lesson--docs)
- 02 - [Props combination pattern](?path=/docs/lessons-02-props-combination-pattern-01-lesson--docs)
- 03 - [Render props pattern](?path=/docs/lessons-03-render-props-pattern-01-lesson--docs)
- 04 - [Presentational and container components pattern](?path=/docs/lessons-04-presentational-container-pattern-01-lesson--docs)
- 05 - [React Hooks pattern](?path=/docs/lessons-05-hooks-pattern-01-lesson--docs)
- 06 - [Controlled component pattern](?path=/docs/lessons-06-controlled-components-pattern-01-lesson--docs)
- 07 - [Higher order component](?path=/docs/lessons-07-higher-order-components-pattern-01-lesson--docs)
- 08 - [The Provider pattern](?path=/docs/lessons-08-provider-pattern-01-lesson--docs)
- 09 - [The State Reducer pattern](?path=/docs/lessons-09-state-reducer-pattern-01-lesson--docs)
- 10 - [Compound components pattern](?path=/docs/lessons-10-compound-components-pattern-01-lesson--docs)
- 11 - [Slots pattern](?path=/docs/lessons-11-slots-01-lesson--docs)
- 12 - [Portals pattern](?path=/docs/lessons-12-portals-01-lesson--docs)
#### 🥉 Bronze

- [Conditionally rendering pattern](?path=/docs/lessons-🥉-bronze-conditional-rendering-pattern-01-lesson--docs)
- [Props combination pattern](?path=/docs/lessons-🥉-bronze-props-combination-pattern-01-lesson--docs)
- [React Hooks pattern](?path=/docs/lessons-🥉-bronze-hooks-pattern-01-lesson--docs)
- [Presentational and container components pattern](?path=/docs/lessons-🥉-bronze-presentational-container-pattern-01-lesson--docs)
- [Slots pattern](?path=/docs/lessons-🥉-bronze-slots-01-lesson--docs)

#### 🥈 Silver

- [Compound components pattern](?path=/docs/lessons-🥈-sliver-compound-components-pattern-01-lesson--docs)
- [Controlled component pattern](?path=/docs/lessons-🥈-sliver-controlled-components-pattern-01-lesson--docs)
- [Render props pattern](?path=/docs/lessons-🥈-sliver-render-props-pattern-01-lesson--docs)
- [The Provider pattern](?path=/docs/lessons-🥈-sliver-provider-pattern-01-lesson--docs)
- [The State Reducer pattern](?path=/docs/lessons-🥈-sliver-state-reducer-pattern-01-lesson--docs)
- [Portals pattern](?path=/docs/lessons-🥈-sliver-portals-01-lesson--docs)

#### 🥇 Gold

- [Higher order component](?path=/docs/lessons-🥇-gold-higher-order-components-pattern-01-lesson--docs)

## FAQs

Expand Down
2 changes: 1 addition & 1 deletion src/course/01-introduction/02-GettingStarted.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Meta } from '@storybook/blocks';

# Getting Started

> Node version 18 required.
> Node version 18+ required.

## Installation

Expand Down
10 changes: 6 additions & 4 deletions src/course/01-introduction/03-LessonStructure.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { Meta } from '@storybook/blocks';

# Lesson Structure

As you will have already noticed in the sidebar that there is a "Lessons" section. Each lesson will get slightly more and more complex as we go on so it eases us into the course. Each lesson will also contain an "exercise.(tsx)" file and a "final.(tsx)".
As you will have already noticed in the sidebar that there is a "Lessons" section. Each lesson sits within a Bronze/Silver/Gold tier folder which mirrors to the complexity of that pattern & we provide more challenging exercises. Each lesson will also contain an "exercise" folder and a "final" folder.

## Storybook / Folder Structure

As you can see the storybook sidebar mirrors the way the folder structure is within the repo. This is done so you can easily navigate to the files you are changing within the exercises.

## Exercise files

Expand Down Expand Up @@ -60,6 +64,4 @@ const Component = () => {

## Final files

If you get stuck do not worry! Each lesson.mdx file will have a video going through it all and there will be a final.tsx file showing the final solution of each exercise.

[Let's get started](?path=/docs/lessons-01-conditional-rendering-pattern-01-lesson--docs)
If you get stuck do not worry! Each will have a final folder in the lesson showing the final solution of each exercise. Head over to any of the lessons to get started with which patterns you wish to learn.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Meta, StoryObj } from '@storybook/react';

import { userEvent, within, expect } from '@storybook/test';

import { ComponentOne } from './exercise';

const meta: Meta<typeof ComponentOne> = {
title:
'Lessons/🥉 Bronze/Conditional Rendering Pattern/02-Exercise',
component: ComponentOne
};

export default meta;
type Story = StoryObj<typeof ComponentOne>;

const username = 'John Doe';

/*
* See https://storybook.js.org/docs/writing-stories/play-function#working-with-the-canvas
* to learn more about using the canvasElement to query the DOM
*/
export const Default: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

await userEvent.click(
canvas.getByRole('button', { name: 'Login' })
);

await expect(
canvas.getByText(`Welcome ${username}`)
).toBeInTheDocument();
await expect(
canvas.queryByRole('button', { name: 'Login' })
).toBeNull();

await userEvent.click(
canvas.getByRole('button', { name: 'Logout' })
);

await expect(
canvas.queryByText(`Welcome ${username}`)
).toBeNull();
await expect(
canvas.queryByRole('button', { name: 'Logout' })
).toBeNull();
},
args: {
username
}
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button } from '../../../shared/components/Button/Button.component';
import { Button } from '@shared/components/Button/Button.component';

interface IComponentProps {
username: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { Meta, StoryObj } from '@storybook/react';

import { userEvent, within, expect } from '@storybook/test';

import { ComponentOne } from './final';

const meta: Meta<typeof ComponentOne> = {
title: 'Lessons/01 - Conditional Rendering Pattern/03-Final',
title: 'Lessons/🥉 Bronze/Conditional Rendering Pattern/03-Final',
component: ComponentOne
};

Expand All @@ -22,15 +21,27 @@ export const Default: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);

await userEvent.click(canvas.getByRole('button', { name: 'Login' }));

await expect(canvas.getByText(`Welcome ${username}`)).toBeInTheDocument();
await expect(canvas.queryByRole('button', { name: 'Login' })).toBeNull();

await userEvent.click(canvas.getByRole('button', { name: 'Logout' }));

await expect(canvas.queryByText(`Welcome ${username}`)).toBeNull();
await expect(canvas.queryByRole('button', { name: 'Logout' })).toBeNull();
await userEvent.click(
canvas.getByRole('button', { name: 'Login' })
);

await expect(
canvas.getByText(`Welcome ${username}`)
).toBeInTheDocument();
await expect(
canvas.queryByRole('button', { name: 'Login' })
).toBeNull();

await userEvent.click(
canvas.getByRole('button', { name: 'Logout' })
);

await expect(
canvas.queryByText(`Welcome ${username}`)
).toBeNull();
await expect(
canvas.queryByRole('button', { name: 'Logout' })
).toBeNull();
},
args: {
username
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { Button } from '../../../shared/components/Button/Button.component';
import { Button } from '@shared/components/Button/Button.component';

interface IComponentProps {
username: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Meta } from '@storybook/blocks';

<Meta title="Lessons/01 - Conditional Rendering Pattern/01-Lesson" />
<Meta title="Lessons/🥉 Bronze/Conditional Rendering Pattern/01-Lesson" />

# Conditional Rendering Pattern

Expand Down Expand Up @@ -86,7 +86,7 @@ const Component = () => {

## Exercise

In the first exercise we are going to look into building a login and logout toggle which will render a username when they have logged in. Go to the exercise.tsx inside the 01-ConditionalRendering folder and start the exercise. Once completed, the Tests will show as passed in the storybook "Interactions" addon section.
In the first exercise we are going to look into building a login and logout toggle which will render a username when they have logged in. Go to the exercise.tsx inside the ConditionalRendering folder and start the exercise. Once completed, the Tests will show as passed in the storybook "Interactions" addon section.

## Feedback

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Input } from '../../../shared/components/Input/Input.component';
import { Label } from '../../../shared/components/Label/Label.component';
import { ErrorMessage } from '../../../shared/components/ErrorMessage/ErrorMessage.component';
import { Input } from '@shared/components/Input/Input.component';
import { Label } from '@shared/components/Label/Label.component';
import { ErrorMessage } from '@shared/components/ErrorMessage/ErrorMessage.component';
import { HTMLAttributes } from 'react';

export interface ITextFieldProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { Exercise } from './exercise';

const meta: Meta<typeof Exercise> = {
title: 'Lessons/08 - Provider Pattern/02-Exercise',
title: 'Lessons/🥉 Bronze/Hooks Pattern/02-Exercise',
component: Exercise
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ChangeEvent, useState } from 'react';
import { ITextFieldProps, TextFieldComponent } from './components';
import { ITextFieldProps, TextFieldComponent } from '../components';

/*
* Observations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { Final } from './final';

const meta: Meta<typeof Final> = {
title: 'Lessons/03 - Render Props Pattern/03-Final',
title: 'Lessons/🥉 Bronze/Hooks Pattern/03-Final',
component: Final
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ChangeEvent, useState } from 'react';
import { TextFieldComponent } from './components';
import { TextFieldComponent } from '../components';

interface IFieldProps {
name: string;
Expand All @@ -10,7 +10,7 @@
const validateTextString = (value: string) =>
value.trim().length === 0;

export const useField = ({

Check warning on line 13 in src/course/02- lessons/01-Bronze/Hooks/final/final.tsx

View workflow job for this annotation

GitHub Actions / Quality checks (1)

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
name,
required,
validate
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Meta } from '@storybook/blocks';

<Meta title="Lessons/05 - Hooks Pattern/01-Lesson" />
<Meta title="Lessons/🥉 Bronze/Hooks Pattern/01-Lesson" />

# Hooks Pattern

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BrandPageOne, BrandPageTwo } from './exercise';

const meta: Meta<typeof BrandPageOne> = {
title:
'Lessons/04 - Presentational & Container Pattern/02-Exercise',
'Lessons/🥉 Bronze/Presentational & Container Pattern/02-Exercise',
component: BrandPageOne
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useEffect, useState } from 'react';
import { ErrorMessage } from '../../../shared/components/ErrorMessage/ErrorMessage.component';
import { ErrorMessage } from '@shared/components/ErrorMessage/ErrorMessage.component';
import {
ICheckoutData,
useBrandOnePayment,
useCheckout
} from './mocks';
import { Skeleton } from '../../../shared/components/Skeleton/Skeleton.component';
import { Button } from '../../../shared/components/Button/Button.component';
} from '../mocks';
import { Skeleton } from '@shared/components/Skeleton/Skeleton.component';
import { Button } from '@shared/components/Button/Button.component';

interface IPaymentTemplate {
hasPaymentFailed: boolean;
Expand Down Expand Up @@ -45,7 +45,7 @@

useEffect(() => {
getCheckoutInfo();
}, []);

Check warning on line 48 in src/course/02- lessons/01-Bronze/PresentationalAndContainer/exercise/exercise.tsx

View workflow job for this annotation

GitHub Actions / Quality checks (1)

React Hook useEffect has a missing dependency: 'getCheckoutInfo'. Either include it or remove the dependency array

useEffect(() => {
if (hasPaymentSucceeded) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import type { Meta, StoryObj } from '@storybook/react';
import { BrandPageOne, BrandPageTwo } from './final';

const meta: Meta<typeof BrandPageOne> = {
title: 'Lessons/04 - Presentational & Container Pattern/03-Final',
title:
'Lessons/🥉 Bronze/Presentational & Container Pattern/03-Final',
component: BrandPageOne
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useEffect, useState } from 'react';
import { ErrorMessage } from '../../../shared/components/ErrorMessage/ErrorMessage.component';
import { ErrorMessage } from '@shared/components/ErrorMessage/ErrorMessage.component';
import {
useBrandOnePayment,
useCheckout,
useBrandTwoPayment,
ICheckoutData
} from './mocks';
import { Button } from '../../../shared/components/Button/Button.component';
import { Skeleton } from '../../../shared/components/Skeleton/Skeleton.component';
} from '../mocks';
import { Button } from '@shared/components/Button/Button.component';
import { Skeleton } from '@shared/components/Skeleton/Skeleton.component';

interface IPaymentTemplate {
hasPaymentFailed: boolean;
Expand Down Expand Up @@ -111,7 +111,7 @@

useEffect(() => {
getCheckoutInfo();
}, []);

Check warning on line 114 in src/course/02- lessons/01-Bronze/PresentationalAndContainer/final/final.tsx

View workflow job for this annotation

GitHub Actions / Quality checks (1)

React Hook useEffect has a missing dependency: 'getCheckoutInfo'. Either include it or remove the dependency array

useEffect(() => {
if (hasPaymentSucceeded) {
Expand Down Expand Up @@ -146,7 +146,7 @@

useEffect(() => {
getCheckoutInfo();
}, []);

Check warning on line 149 in src/course/02- lessons/01-Bronze/PresentationalAndContainer/final/final.tsx

View workflow job for this annotation

GitHub Actions / Quality checks (1)

React Hook useEffect has a missing dependency: 'getCheckoutInfo'. Either include it or remove the dependency array

useEffect(() => {
if (hasPaymentSucceeded) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Meta } from '@storybook/blocks';

<Meta title="Lessons/04 - Presentational & Container Pattern/01-Lesson" />
<Meta title="Lessons/🥉 Bronze/Presentational & Container Pattern/01-Lesson" />

# Presentational & Container Pattern

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { Exercise } from './exercise';

const meta: Meta<typeof Exercise> = {
title: 'Lessons/02 - Props Combination Pattern/02-Exercise',
title: 'Lessons/🥉 Bronze/Props Combination Pattern/02-Exercise',
component: Exercise
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { Final } from './final';

const meta: Meta<typeof Final> = {
title: 'Lessons/02 - Props Combination Pattern/03-Final',
title: 'Lessons/🥉 Bronze/Props Combination Pattern/03-Final',
component: Final
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Meta } from '@storybook/blocks';

<Meta title="Lessons/02 - Props Combination Pattern/01-Lesson" />
<Meta title="Lessons/🥉 Bronze/Props Combination Pattern/01-Lesson" />

# Props Combination Pattern

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { Exercise } from './exercise';

const meta: Meta<typeof Exercise> = {
title: 'Lessons/11 - Slots/02-Exercise',
title: 'Lessons/🥉 Bronze/Slots/02-Exercise',
component: Exercise
};

Expand Down
Loading