diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index d89a0ccd2..d169dac6b 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -3,9 +3,11 @@ on:
push:
branches:
- 'main'
+ - 'next'
pull_request:
branches:
- 'main'
+ - 'next'
jobs:
setup:
# ignore all-contributors PRs
diff --git a/config/tsconfig.exercise.json b/config/tsconfig.exercise.json
new file mode 100644
index 000000000..9ccbb3c97
--- /dev/null
+++ b/config/tsconfig.exercise.json
@@ -0,0 +1,7 @@
+{
+ "extends": "./tsconfig.shared.json",
+ "include": ["../src/exercise/"],
+ "compilerOptions": {
+ "strict": false
+ }
+}
diff --git a/config/tsconfig.final.json b/config/tsconfig.final.json
new file mode 100644
index 000000000..97df133bc
--- /dev/null
+++ b/config/tsconfig.final.json
@@ -0,0 +1,9 @@
+{
+ "extends": "./tsconfig.shared.json",
+ "exclude": ["../src/exercise/"],
+ "include": ["../src"],
+ "compilerOptions": {
+ "strict": true,
+ "noUncheckedIndexedAccess": true
+ }
+}
diff --git a/config/tsconfig.shared.json b/config/tsconfig.shared.json
new file mode 100644
index 000000000..3122ef39e
--- /dev/null
+++ b/config/tsconfig.shared.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": false,
+ "forceConsistentCasingInFileNames": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "noFallthroughCasesInSwitch": true
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index bc8c98bc8..9facb4c10 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18196,8 +18196,7 @@
"typescript": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz",
- "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==",
- "dev": true
+ "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw=="
},
"typographic-apostrophes": {
"version": "1.1.1",
diff --git a/package.json b/package.json
index b603206c5..8ca75ac79 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"version": "1.0.0",
"private": true,
"keywords": [],
- "homepage": "http://react-fundamentals.netlify.app/",
+ "homepage": "http://react-fundamentals-next.netlify.app/",
"license": "GPL-3.0-only",
"main": "src/index.js",
"engines": {
@@ -17,12 +17,15 @@
"@kentcdodds/react-workshop-app": "^4.0.0",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.8.1",
+ "@types/jest": "^26.0.20",
+ "@types/node": "^14.14.31",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"chalk": "^4.1.0",
"codegen.macro": "^4.1.0",
"react": "^17.0.1",
- "react-dom": "^17.0.1"
+ "react-dom": "^17.0.1",
+ "typescript": "^4.1.3"
},
"devDependencies": {
"husky": "^4.3.8",
@@ -40,7 +43,8 @@
"setup": "node setup",
"lint": "eslint .",
"format": "prettier --write \"./src\"",
- "validate": "npm-run-all --parallel build test:coverage lint"
+ "typecheck": "tsc -b",
+ "validate": "npm-run-all --parallel build test:coverage lint typecheck"
},
"husky": {
"hooks": {
diff --git a/scripts/remove-ts b/scripts/remove-ts
new file mode 100755
index 000000000..ebfeedaf8
--- /dev/null
+++ b/scripts/remove-ts
@@ -0,0 +1,2 @@
+npx https://gist.github.com/kentcdodds/e49b9b8da0dd467149d8d67258251585
+./scripts/fix-links
diff --git a/src/__tests__/05.js b/src/__tests__/06.tsx
similarity index 95%
rename from src/__tests__/05.js
rename to src/__tests__/06.tsx
index dde4875d8..14d116575 100644
--- a/src/__tests__/05.js
+++ b/src/__tests__/06.tsx
@@ -1,8 +1,8 @@
import * as React from 'react'
import chalk from 'chalk'
import {render, screen, prettyDOM} from '@testing-library/react'
-import App from '../final/05'
-// import App from '../exercise/05'
+import {App} from '../final/06'
+// import {App} from '../exercise/06'
test('renders the correct styles new', () => {
const {container} = render()
diff --git a/src/__tests__/06.extra-2.js b/src/__tests__/07.extra-2.tsx
similarity index 79%
rename from src/__tests__/06.extra-2.js
rename to src/__tests__/07.extra-2.tsx
index 4bc2c8c48..77edd01c9 100644
--- a/src/__tests__/06.extra-2.js
+++ b/src/__tests__/07.extra-2.tsx
@@ -2,20 +2,21 @@ import * as React from 'react'
import {alfredTip} from '@kentcdodds/react-workshop-app/test-utils'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import App from '../final/06.extra-2'
-// import App from '../exercise/06'
+import {App} from '../final/07.extra-2'
+// import {App} from '../exercise/07'
+let alert = jest.spyOn(global, 'alert')
beforeAll(() => {
- jest.spyOn(global, 'alert').mockImplementation(() => {})
+ alert.mockImplementation(() => {})
})
beforeEach(() => {
- global.alert.mockClear()
+ alert.mockClear()
})
test('calls the onSubmitUsername handler when the submit is fired', () => {
render()
- const input = screen.getByLabelText(/username/i)
+ const input = screen.getByLabelText(/username/i) as HTMLInputElement
const submit = screen.getByText(/submit/i)
let value = 'A'
diff --git a/src/__tests__/06.extra-3.js b/src/__tests__/07.extra-3.tsx
similarity index 69%
rename from src/__tests__/06.extra-3.js
rename to src/__tests__/07.extra-3.tsx
index e09413535..f33709ca8 100644
--- a/src/__tests__/06.extra-3.js
+++ b/src/__tests__/07.extra-3.tsx
@@ -1,20 +1,21 @@
import * as React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import App from '../final/06.extra-3'
-// import App from '../exercise/06'
+import {App} from '../final/07.extra-3'
+// import {App} from '../exercise/07'
+let alert = jest.spyOn(global, 'alert')
beforeAll(() => {
- jest.spyOn(global, 'alert').mockImplementation(() => {})
+ alert.mockImplementation(() => {})
})
beforeEach(() => {
- global.alert.mockClear()
+ alert.mockClear()
})
test('calls the onSubmitUsername handler when the submit is fired', () => {
render()
- const input = screen.getByLabelText(/username/i)
+ const input = screen.getByLabelText(/username/i) as HTMLInputElement
const submit = screen.getByText(/submit/i)
const value = 'A'
diff --git a/src/__tests__/06.js b/src/__tests__/07.tsx
similarity index 69%
rename from src/__tests__/06.js
rename to src/__tests__/07.tsx
index 74f66515e..dbd271e8a 100644
--- a/src/__tests__/06.js
+++ b/src/__tests__/07.tsx
@@ -1,20 +1,21 @@
import * as React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import App from '../final/06'
-// import App from '../exercise/06'
+import {App} from '../final/07'
+// import {App} from '../exercise/07'
+let alert = jest.spyOn(global, 'alert')
beforeAll(() => {
- jest.spyOn(global, 'alert').mockImplementation(() => {})
+ alert.mockImplementation(() => {})
})
beforeEach(() => {
- global.alert.mockClear()
+ alert.mockClear()
})
test('calls the onSubmitUsername handler when the submit is fired', () => {
render()
- const input = screen.getByLabelText(/username/i)
+ const input = screen.getByLabelText(/username/i) as HTMLInputElement
const submit = screen.getByText(/submit/i)
const username = 'jenny'
diff --git a/src/__tests__/07.js b/src/__tests__/08.tsx
similarity index 72%
rename from src/__tests__/07.js
rename to src/__tests__/08.tsx
index 7bc83340a..9040b2159 100644
--- a/src/__tests__/07.js
+++ b/src/__tests__/08.tsx
@@ -1,8 +1,8 @@
import * as React from 'react'
import {render, screen, within} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import App from '../final/07'
-// import App from '../exercise/07'
+import {App} from '../final/08'
+// import {App} from '../exercise/08'
test('renders', () => {
const {container} = render()
@@ -14,6 +14,9 @@ test('renders', () => {
const orangeInput = screen.getByLabelText(/orange/i)
const orangeContainer = screen.getByText(/orange/i).closest('li')
+ if (!orangeContainer) {
+ throw new Error(`🚨 Can't find the li for "orange"`)
+ }
const inOrange = within(orangeContainer)
userEvent.type(orangeInput, 'sup dawg')
userEvent.click(inOrange.getByText('remove'))
@@ -22,6 +25,11 @@ test('renders', () => {
Array.from(allLis).forEach(li => {
const label = li.querySelector('label')
const input = li.querySelector('input')
+ if (!label || !input) {
+ throw new Error(
+ '🚨 Please make sure to not remove the label and input in the li elements',
+ )
+ }
expect(label.textContent).toBe(input.value)
})
})
diff --git a/src/exercise/01.html b/src/exercise/01.html
index 834757f37..31aca293e 100644
--- a/src/exercise/01.html
+++ b/src/exercise/01.html
@@ -1,16 +1,19 @@
-
+
-
-
+
+
+
-
-
+
+
+
+
+
-
diff --git a/src/exercise/01.md b/src/exercise/01.md
index f9f4ab1da..d3528bc3a 100644
--- a/src/exercise/01.md
+++ b/src/exercise/01.md
@@ -56,6 +56,8 @@ 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.
+Now, open `exercise/01.html` and follow the instructions in there.
+
## Extra Credit
### 1. 💯 generate the root node
@@ -63,7 +65,11 @@ document.
[Production deploy](http://react-fundamentals.netlify.app/isolated/final/01.extra-1.html)
Rather than having the `root` node in the HTML, see if you can create that one
-using JavaScript as well.
+using JavaScript as well. Remove the `
` from the HTML and
+instead of trying to find it with `document.getElementById('root')`, create it
+and append it to the `document.body`.
+
+Do this in `exercise/01.html`, building on top of the work you've already done.
## 🦉 Feedback
diff --git a/src/exercise/02.html b/src/exercise/02.html
index 31ff2bea3..4ac8a3c31 100644
--- a/src/exercise/02.html
+++ b/src/exercise/02.html
@@ -15,14 +15,15 @@
// 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 React.createElement
+ // 📜 https://reactjs.org/docs/react-api.html#createelement
const element = document.createElement('div')
- element.textContent = 'Hello World' // 💰 in React, you set this with the "children" prop
element.className = 'container' // 💰 in React, this is also called the "className" prop
+ element.append('Hello World') // 💰 in React, you set this with the "children" prop
// This is similar to ReactDOM.render
+ // 📜 https://reactjs.org/docs/react-dom.html#render
rootElement.append(element)
- // 🐨 Please re-implement the regular document.createElement code above
- // with these React API calls
+ // 🐨 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.
-
-