Skip to content

Commit

Permalink
bring back first four exercises
Browse files Browse the repository at this point in the history
  • Loading branch information
kentcdodds committed Aug 23, 2024
1 parent 8cff5a9 commit d0699c6
Show file tree
Hide file tree
Showing 253 changed files with 5,204 additions and 16 deletions.
17 changes: 13 additions & 4 deletions epicshop/post-set-playground.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import fs from 'node:fs'
import path from 'node:path'

fs.writeFileSync(
path.join(process.env.EPICSHOP_PLAYGROUND_DEST_DIR, 'tsconfig.json'),
JSON.stringify({ extends: '../tsconfig' }, null, 2),
)
const exclude = [
'exercises/01.',
'exercises/02.',
'exercises/03.',
'exercises/04.',
]

if (exclude.every(e => !process.env.EPICSHOP_PLAYGROUND_SRC_DIR.includes(e))) {
fs.writeFileSync(
path.join(process.env.EPICSHOP_PLAYGROUND_DEST_DIR, 'tsconfig.json'),
JSON.stringify({ extends: '../tsconfig' }, null, 2),
)
}
20 changes: 20 additions & 0 deletions exercises/01.js-hello-world/01.problem.hello/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Hello World in JS

πŸ‘¨β€πŸ’Ό It's important to have a basic understanding of how to generate and interact
with DOM nodes using JavaScript because it will help you understand how React
works under the hood a little better. So in this exercise we're actually not
going to use React at all. Instead we're going to use JavaScript to create a
`div` DOM node with the text "Hello World" and insert that DOM node into the
document.

🐨 We'll be in <InlineFile file="index.html" /> to help guide you through making
this work! See you there!

πŸ‘¨β€πŸ’Ό Once you're finished, open up the
[browser devtools](https://developer.chrome.com/docs/devtools) so
you can check the DOM is what you expect it to be.

{/* prettier-ignore */}
<callout-info class="aside">
πŸ’‘ Tip: you may find it useful to{' '}<a target="_blank" href="/app/playground">open the playground in a separate tab</a>{'.'}
</callout-info>
28 changes: 28 additions & 0 deletions exercises/01.js-hello-world/01.problem.hello/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!-- 🐨 Create (in HTML) an <html> with a <body> with a <div> with the id of "root" -->

<!-- 🐨 Then create a <script type="module"> for your JavaScript -->
<!-- πŸ“œ https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attributes -->
<!-- πŸ“œ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules -->
<!-- πŸ“œ https://github.com/mdn/js-examples/tree/master/module-examples -->

<!-- These next instructions are to be written in JavaScript in the <script> tag -->

<!-- 🐨 Get the root div using the JavaScript DOM API -->
<!-- πŸ’° Hint, you'll use: document.getElementById('root') -->
<!-- πŸ“œ https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById -->

<!-- 🐨 Create a div with the JavaScript DOM API: -->
<!-- πŸ’° Hint, you'll use: document.createElement('div') -->
<!-- πŸ“œ https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement -->

<!-- 🐨 Set the div's className property to 'container' -->
<!-- πŸ’° element.className = '...' -->
<!-- πŸ“œ https://developer.mozilla.org/en-US/docs/Web/API/Element/className -->

<!-- 🐨 Set the div's textContent to 'Hello World' -->
<!-- πŸ’° element.textContent = '...' -->
<!-- πŸ“œ https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent -->

<!-- 🐨 Append the div to the root div using `append` -->
<!-- πŸ’° root.append(div) -->
<!-- πŸ“œ https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append -->
6 changes: 6 additions & 0 deletions exercises/01.js-hello-world/01.solution.hello/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Hello World in JS

πŸ‘¨β€πŸ’Ό Awesome job. Now you know how to create DOM nodes using the regular JS APIs.

But you know what, we could probably do even more in JS... Let's look at that
next.
15 changes: 15 additions & 0 deletions exercises/01.js-hello-world/01.solution.hello/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<html>
<body>
<div id="root"></div>

<script type="module">
const rootElement = document.getElementById('root')
const element = document.createElement('div')

element.className = 'container'
element.textContent = 'Hello World'

rootElement.append(element)
</script>
</body>
</html>
13 changes: 13 additions & 0 deletions exercises/01.js-hello-world/01.solution.hello/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { expect, testStep } from '@epic-web/workshop-utils/test'

await testStep('"Hello World" is rendered to the DOM', () => {
const rootElement = document.getElementById('root')
expect(rootElement, 'root element not found').to.be.instanceOf(HTMLElement)

const element = rootElement!.querySelector('.container')
expect(element, 'container element not found').to.be.instanceOf(HTMLElement)

expect(element!.textContent, 'element text is not correct').to.equal(
'Hello World',
)
})
6 changes: 6 additions & 0 deletions exercises/01.js-hello-world/02.problem.root/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Generate the Root Node

Rather than having the `root` node in the HTML, see if you can create that one
using JavaScript as well. Remove the `<div id="root"></div>` from the HTML and
instead of trying to find it with `document.getElementById('root')`, create it
and append it to the `document.body`.
23 changes: 23 additions & 0 deletions exercises/01.js-hello-world/02.problem.root/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<html>
<body>
<!-- πŸ’£ delete this element (we'll make it with JS instead) -->
<div id="root"></div>

<script type="module">
// 🐨 rather than getting the element from the DOM created by the HTML
// create the root element like you do the hello world element
const rootElement = document.getElementById('root')
// 🐨 you can even add the id to the root element if you like

const element = document.createElement('div')
element.className = 'container'
element.textContent = 'Hello World'

rootElement.append(element)
// 🐨 add the rootElement to the document.body
</script>

<!-- this is here to add automatic browser reloading as you save your work -->
<script type="module" src="epic_ws.js"></script>
</body>
</html>
7 changes: 7 additions & 0 deletions exercises/01.js-hello-world/02.solution.root/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Generate the Root Node

πŸ‘¨β€πŸ’Ό Great! Now we can create DOM nodes dynamically using JavaScript. This is only
the beginning of our journey. Creating DOM nodes ourselves is not typically the
best way to build a full fledged application, but it's important for you to
understand that what libraries like React are doing is not magic. They're just
creating and modifying DOM nodes using JavaScript.
18 changes: 18 additions & 0 deletions exercises/01.js-hello-world/02.solution.root/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<html>
<body>
<script type="module">
const rootElement = document.createElement('div')
rootElement.id = 'root'

const element = document.createElement('div')
element.className = 'container'
element.textContent = 'Hello World'

rootElement.append(element)
document.body.append(rootElement)
</script>

<!-- this is here to add automatic browser reloading as you save your work -->
<script type="module" src="epic_ws.js"></script>
</body>
</html>
24 changes: 24 additions & 0 deletions exercises/01.js-hello-world/02.solution.root/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect, testStep } from '@epic-web/workshop-utils/test'

await testStep('"Hello World" is rendered to the DOM', () => {
const rootElement = document.getElementById('root')
expect(rootElement, 'root element not found').to.be.instanceOf(HTMLElement)

const element = rootElement!.querySelector('.container')
expect(element, 'container element not found').to.be.instanceOf(HTMLElement)

expect(element!.textContent, 'element text is not correct').to.equal(
'Hello World',
)
})

await testStep('root element is not in the HTML', async () => {
const response = await fetch(location.href)
const text = await response.text()
const node = document.createElement('div')
node.innerHTML = text
expect(
node.querySelector('#root'),
'root element found in the HTML when it should not be',
).to.be.null
})
4 changes: 4 additions & 0 deletions exercises/01.js-hello-world/FINISHED.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Hello World in JS

πŸ‘¨β€πŸ’Ό Great job! You did it! Now's your opportunity to review what you learned.
Writing down what you've learned helps you to remember it better.
74 changes: 74 additions & 0 deletions exercises/01.js-hello-world/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Hello World in JS

It doesn't take long to learn how to make "Hello World" appear on the page with
HTML:

```html
<html>
<body>
<div>Hello World</div>
</body>
</html>
```

The browser takes this HTML code and generates
[the DOM (the Document Object Model)](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction)
out of it. The browser then exposes the DOM to JavaScript so you can interact
with it to add a layer of interactivity to your web-page.

```html
<html>
<body>
<div>Hello World</div>
<script type="module">
// your JavaScript here
</script>
</body>
</html>
```

Years ago, people were generating HTML on the server and then adding JavaScript
on top of that generated HTML for interactivity. However, as requirements for
that interactivity became more challenging, this approach produced applications
that were difficult to maintain and had performance issues.

<callout-info class="aside">
If you'd like to learn more about the history, read [The Web's Next
Transition](https://www.epicweb.dev/the-webs-next-transition).
</callout-info>

So modern JavaScript frameworks were created to address some of the challenges
by programmatically creating the DOM rather than defining it in hand-written
HTML.

```html
<html>
<body>
<script type="module">
const element = document.createElement('div')
element.textContent = 'Hello World'
document.body.append(element)
</script>
</body>
</html>
```

This approach is more flexible, but comes at the cost of making the browser do
a little extra work. The browser has to parse the JavaScript code, execute it,
and then generate the DOM from the JavaScript code before displaying things to
the user.

This is why hybrid approaches are popular. You can generate the DOM with HTML
and then add interactivity with JavaScript. In the world of React, you can use
React code to generate the HTML on the server and then use the same React code
to add interactivity on the client. Doing this isn't an enormous amount of
effort, however there are a lot of considerations to take into account so you'll
want to use [Remix](https://remix.run) (which is built on top of React) to make
it easier (and you get a lot of other critical pieces to the puzzle as well).

We're going to focus on the client-side of things in this workshop, but you can
apply the same principles to frameworks like Remix. But let's just start with
the total fundamentals of creating and appending our own DOM nodes before we
get into the React side of things. I call this "going down to level up." You'll
find yourself much more efficient with React if you understand the fundamentals
of the DOM and JavaScript. So let's get started!
73 changes: 73 additions & 0 deletions exercises/02.raw-react/01.problem.elements/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Create React Elements

πŸ‘¨β€πŸ’Ό Let's convert this to use React! But don't worry, we won't be doing any JSX just
yet... You're going to use raw React APIs here.

In modern applications you'll get React and React DOM files from a "package
registry" like [npm](https://npmjs.com) ([react](https://npm.im/react) and
[react-dom](https://npm.im/react-dom)).

To keep things as simple as possible in these first exercises, we'll be
importing the file `public/react.js` at `/react.js` and
`public/react-dom/client.js` at `/react-dom/client.js`. Those files re-export
the packages from [esm.sh](https://esm.sh).

Here's a simple example of the API:

```typescript
import { createElement } from '/react.js'
import { createRoot } from '/react-dom/client.js'

const elementProps = { id: 'element-id', children: 'Hello world!' }
const elementType = 'h1'
const reactElement = createElement(elementType, elementProps)

const root = createRoot(rootElement)
root.render(reactElement)
```

<callout-info>

πŸ¦‰ As a reminder, in a typical application, you're import will be something
like

```ts nonumber nolang
import { createRoot } from 'react-dom/client'
```

With that, a build tool or `importmap` will handle resolving that to the
correct path.

</callout-info>

The "props" in `elementProps` above is short for "properties" and they're a
foundational part of React elements. You can think of the element type as a
blueprint for the kind of React component to create and the props are the inputs
for when React actually creates that element.

`children` is a special prop. You can pass a single element like above, or an
array of children (if there's more than one). You can also pass the children as
any number of additional arguments:

```typescript
const elementProps = { id: 'element-id' }
const child1 = 'Hello'
const child2 = ' '
const child3 = 'world!'
const elementType = 'h1'
const reactElement = createElement(
elementType,
elementProps,
child1,
child2,
child3,
)
createRoot(rootElement).render(reactElement)
```

Alright! Let's do this!

<callout-info class="aside">
πŸ’° Tip: `console.log` the `reactElement` to see what it looks like. You might
find it kinda interesting!
</callout-info>
29 changes: 29 additions & 0 deletions exercises/02.raw-react/01.problem.elements/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<html>
<body>
<div id="root"></div>

<script type="module">
// 🐨 add imports for react and react-dom/client here
// πŸ’° import { createElement } from '/react.js'
// πŸ’° import { createRoot } from '/react-dom/client.js'

const rootElement = document.getElementById('root')

// You're going to re-implement this code using React!
// πŸ’£ So go ahead and delete this implementation (or comment it out for now)
// These three lines are similar to createElement
// πŸ“œ https://react.dev/reference/react/createElement
const element = document.createElement('div')
element.className = 'container' // πŸ’° in React, this is also called the "className" prop
element.textContent = 'Hello World' // πŸ’° in React, this is the "children" prop or a third argument
// This is similar to createRoot().render()
rootElement.append(element)

// 🐨 Please replace all the DOM-related code above with React API calls
// πŸ’° The example in the markdown file should be a good hint for you.
</script>

<!-- this is here to add automatic browser reloading as you save your work -->
<script type="module" src="epic_ws.js"></script>
</body>
</html>
5 changes: 5 additions & 0 deletions exercises/02.raw-react/01.solution.elements/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Create React Elements

πŸ‘¨β€πŸ’Ό Great work! We're well on our way to using React for building UIs on the web!
But most apps are a little more complicated than a single element. Let's go
deeper!
Loading

0 comments on commit d0699c6

Please sign in to comment.