-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8cff5a9
commit d0699c6
Showing
253 changed files
with
5,204 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
13
exercises/01.js-hello-world/01.solution.hello/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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', | ||
) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
24
exercises/01.js-hello-world/02.solution.root/index.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! |
Oops, something went wrong.