-
Notifications
You must be signed in to change notification settings - Fork 146
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
modify advanced component design lessons
- Loading branch information
1 parent
54fd925
commit 2c81db2
Showing
38 changed files
with
387 additions
and
58 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
26 changes: 26 additions & 0 deletions
26
react/advanced-component-design/02-specialization/lecture/App.final.tsx
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,26 @@ | ||
import { useState } from 'react' | ||
import { DialogConfirm } from './Dialog.final' | ||
|
||
export function App() { | ||
const [isOpen, setIsOpen] = useState(false) | ||
return ( | ||
<div> | ||
<button className="button" onClick={() => setIsOpen(true)}> | ||
Open dialog | ||
</button> | ||
|
||
<DialogConfirm | ||
title="Remove User" | ||
isOpen={isOpen} | ||
onConfirm={() => { | ||
setIsOpen(false) | ||
// other stuff | ||
}} | ||
onCancel={() => setIsOpen(false)} | ||
> | ||
Are you sure you want to deactivate your account? All of your data will be permanently | ||
removed. | ||
</DialogConfirm> | ||
</div> | ||
) | ||
} |
32 changes: 32 additions & 0 deletions
32
react/advanced-component-design/02-specialization/lecture/App.tsx
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,32 @@ | ||
import { Dialog, DialogPanel, DialogTitle } from '@headlessui/react' | ||
import { useState } from 'react' | ||
|
||
export function App() { | ||
const [isOpen, setIsOpen] = useState(false) | ||
return ( | ||
<div> | ||
<button className="button" onClick={() => setIsOpen(true)}> | ||
Open dialog | ||
</button> | ||
<Dialog open={isOpen} onClose={() => setIsOpen(false)} className="relative z-50"> | ||
<div className="fixed inset-0 flex w-screen items-center justify-center p-4 bg-black/20"> | ||
<DialogPanel className="max-w-lg space-y-4 bg-white p-12 rounded"> | ||
<DialogTitle className="font-bold">Deactivate account</DialogTitle> | ||
<p> | ||
Are you sure you want to deactivate your account? All of your data will be permanently | ||
removed. | ||
</p> | ||
<div className="flex gap-4"> | ||
<button className="button" onClick={() => setIsOpen(false)}> | ||
Yes | ||
</button> | ||
<button className="button" onClick={() => setIsOpen(false)}> | ||
Cancel | ||
</button> | ||
</div> | ||
</DialogPanel> | ||
</div> | ||
</Dialog> | ||
</div> | ||
) | ||
} |
48 changes: 48 additions & 0 deletions
48
react/advanced-component-design/02-specialization/lecture/Dialog.final.tsx
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,48 @@ | ||
import { Dialog as HeadlessDialog, DialogPanel, DialogTitle } from '@headlessui/react' | ||
|
||
type DialogProps = { | ||
open: boolean | ||
onClose(): void | ||
children: React.ReactNode | ||
} | ||
|
||
export function Dialog({ children, open, onClose }: DialogProps) { | ||
return ( | ||
<HeadlessDialog open={open} onClose={onClose} className="relative z-50"> | ||
<div className="fixed inset-0 flex w-screen items-center justify-center p-4 bg-black/20"> | ||
<DialogPanel className="max-w-lg space-y-4 bg-white p-12 rounded">{children}</DialogPanel> | ||
</div> | ||
</HeadlessDialog> | ||
) | ||
} | ||
|
||
type DialogConfirmProps = { | ||
title: string | ||
children: string | ||
onConfirm: () => void | ||
onCancel: () => void | ||
isOpen: boolean | ||
} | ||
|
||
export function DialogConfirm({ | ||
title, | ||
children, | ||
onConfirm, | ||
onCancel, | ||
isOpen, | ||
}: DialogConfirmProps) { | ||
return ( | ||
<Dialog open={isOpen} onClose={onCancel}> | ||
<DialogTitle className="font-bold">{title}</DialogTitle> | ||
<p>{children}</p> | ||
<div className="flex gap-4"> | ||
<button className="button" onClick={onConfirm}> | ||
Yes | ||
</button> | ||
<button className="button" onClick={onCancel}> | ||
Cancel | ||
</button> | ||
</div> | ||
</Dialog> | ||
) | ||
} |
3 changes: 3 additions & 0 deletions
3
react/advanced-component-design/02-specialization/lecture/Dialog.tsx
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,3 @@ | ||
// import { Dialog, DialogPanel, DialogTitle } from '@headlessui/react' | ||
|
||
export function Dialog() {} |
11 changes: 11 additions & 0 deletions
11
react/advanced-component-design/02-specialization/lecture/index.tsx
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,11 @@ | ||
import * as ReactDOM from 'react-dom/client' | ||
import { App } from './App' | ||
import { LessonBody, LessonCard } from '~/Lesson' | ||
|
||
ReactDOM.createRoot(document.getElementById('root')!).render( | ||
<LessonBody> | ||
<LessonCard className="w-96"> | ||
<App /> | ||
</LessonCard> | ||
</LessonBody> | ||
) |
70 changes: 70 additions & 0 deletions
70
react/advanced-component-design/02-specialization/practice/App.final.tsx
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,70 @@ | ||
import { Heading } from '~/Heading' | ||
import { CartProvider, useCart } from './Cart' | ||
import classnames from 'classnames' | ||
import { DialogConfirm } from './Dialog' | ||
import { useState } from 'react' | ||
|
||
export function App() { | ||
return ( | ||
<CartProvider> | ||
<ProductDetails productId={1} /> | ||
</CartProvider> | ||
) | ||
} | ||
|
||
/**************************************** | ||
Start Here: | ||
*****************************************/ | ||
|
||
type Props = { | ||
productId: number | ||
} | ||
|
||
function ProductDetails({ productId }: Props) { | ||
return ( | ||
<div className="space-y-3"> | ||
<Heading>iPhone Pro Max</Heading> | ||
<div>Price: 1,199.00</div> | ||
<AddToCartButton productId={productId} /> | ||
</div> | ||
) | ||
} | ||
|
||
/**************************************** | ||
Specialization (Task Two) Here: | ||
*****************************************/ | ||
|
||
function AddToCartButton({ productId }: { productId: number }) { | ||
const { cart, addToCart, removeFromCart } = useCart() | ||
const [confirmOpen, setConfirmOpen] = useState(false) | ||
const inCart = cart.includes(productId) | ||
|
||
function onClick() { | ||
if (!inCart) { | ||
addToCart(productId) | ||
} else { | ||
setConfirmOpen(true) | ||
} | ||
} | ||
|
||
function remove() { | ||
removeFromCart(productId) | ||
setConfirmOpen(false) | ||
} | ||
|
||
return ( | ||
<> | ||
<button className={classnames('button', { 'bg-red-600': inCart })} onClick={onClick}> | ||
{!inCart ? 'Add To Cart' : 'Remove From Cart'} | ||
</button> | ||
<DialogConfirm | ||
title="Remove from Cart" | ||
onConfirm={remove} | ||
onCancel={() => setConfirmOpen(false)} | ||
isOpen={confirmOpen} | ||
> | ||
Are you sure you want to remove this item from the cart? | ||
</DialogConfirm> | ||
</> | ||
) | ||
} |
64 changes: 64 additions & 0 deletions
64
react/advanced-component-design/02-specialization/practice/App.tsx
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,64 @@ | ||
import { Heading } from '~/Heading' | ||
import { CartProvider, useCart } from './Cart' | ||
import classnames from 'classnames' | ||
import { DialogConfirm } from './Dialog' | ||
import { useState } from 'react' | ||
|
||
export function App() { | ||
return ( | ||
<CartProvider> | ||
<ProductDetails productId={1} /> | ||
</CartProvider> | ||
) | ||
} | ||
|
||
/**************************************** | ||
Start Here: | ||
*****************************************/ | ||
|
||
type Props = { | ||
productId: number | ||
} | ||
|
||
function ProductDetails({ productId }: Props) { | ||
const { cart, addToCart, removeFromCart } = useCart() | ||
const inCart = cart.includes(productId) | ||
|
||
function onClick() { | ||
if (!inCart) { | ||
addToCart(productId) | ||
} else { | ||
removeFromCart(productId) | ||
} | ||
} | ||
|
||
return ( | ||
<div className="space-y-3"> | ||
<Heading>iPhone Pro Max</Heading> | ||
<div>Price: 1,199.00</div> | ||
<button className={classnames('button', { 'bg-red-600': inCart })} onClick={onClick}> | ||
{!inCart ? 'Add To Cart' : 'Remove From Cart'} | ||
</button> | ||
{/* <DialogConfirm | ||
title="Remove from Cart" | ||
onConfirm={() => { | ||
// Your code here | ||
}} | ||
onCancel={() => { | ||
// Your code here | ||
}} | ||
isOpen={false} | ||
> | ||
Are you sure you want to remove this item from the cart? | ||
</DialogConfirm> */} | ||
</div> | ||
) | ||
} | ||
|
||
/**************************************** | ||
Specialization (Task Two) Here: | ||
*****************************************/ | ||
|
||
function AddToCartButton({ productId }: { productId: number }) { | ||
// | ||
} |
43 changes: 43 additions & 0 deletions
43
react/advanced-component-design/02-specialization/practice/Cart.tsx
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,43 @@ | ||
import { createContext, use, useState } from 'react' | ||
|
||
/**************************************** | ||
YOU DON'T NEED TO TOUCH THIS FILE | ||
*****************************************/ | ||
|
||
type ContextType = { | ||
cart: number[] | ||
addToCart(id: number): void | ||
removeFromCart(id: number): void | ||
} | ||
|
||
const Context = createContext<ContextType>(null!) | ||
|
||
export function CartProvider({ children }: { children: React.ReactNode }) { | ||
const [cart, setCart] = useState<number[]>([]) | ||
|
||
function addToCart(id: number) { | ||
if (!cart.includes(id)) { | ||
setCart(cart.concat(id)) | ||
} | ||
} | ||
|
||
function removeFromCart(id: number) { | ||
setCart(cart.filter((cartId) => cartId !== id)) | ||
} | ||
|
||
const context = { | ||
cart, | ||
addToCart, | ||
removeFromCart, | ||
} | ||
|
||
return <Context value={context}>{children}</Context> | ||
} | ||
|
||
export function useCart() { | ||
const context = use(Context) | ||
if (!context) { | ||
console.error('You are trying to consume Cart context without a provider') | ||
} | ||
return context || {} | ||
} |
52 changes: 52 additions & 0 deletions
52
react/advanced-component-design/02-specialization/practice/Dialog.tsx
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,52 @@ | ||
import { Dialog as HeadlessDialog, DialogPanel, DialogTitle } from '@headlessui/react' | ||
|
||
/**************************************** | ||
YOU DON'T NEED TO TOUCH THIS FILE | ||
*****************************************/ | ||
|
||
type DialogProps = { | ||
open: boolean | ||
onClose(): void | ||
children: React.ReactNode | ||
} | ||
|
||
export function Dialog({ children, open, onClose }: DialogProps) { | ||
return ( | ||
<HeadlessDialog open={open} onClose={onClose} className="relative z-50"> | ||
<div className="fixed inset-0 flex w-screen items-center justify-center p-4 bg-black/20"> | ||
<DialogPanel className="max-w-lg space-y-4 bg-white p-12 rounded">{children}</DialogPanel> | ||
</div> | ||
</HeadlessDialog> | ||
) | ||
} | ||
|
||
type DialogConfirmProps = { | ||
title: string | ||
children: string | ||
onConfirm: () => void | ||
onCancel: () => void | ||
isOpen: boolean | ||
} | ||
|
||
export function DialogConfirm({ | ||
title, | ||
children, | ||
onConfirm, | ||
onCancel, | ||
isOpen, | ||
}: DialogConfirmProps) { | ||
return ( | ||
<Dialog open={isOpen} onClose={onCancel}> | ||
<DialogTitle className="font-bold">{title}</DialogTitle> | ||
<p>{children}</p> | ||
<div className="flex gap-4"> | ||
<button className="button" onClick={onConfirm}> | ||
Yes | ||
</button> | ||
<button className="button" onClick={onCancel}> | ||
Cancel | ||
</button> | ||
</div> | ||
</Dialog> | ||
) | ||
} |
25 changes: 25 additions & 0 deletions
25
react/advanced-component-design/02-specialization/practice/GUIDE.md
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,25 @@ | ||
# Specialization | ||
|
||
## Goals | ||
|
||
Implement a confirmation step on the `ProductDetails` component when the user removes an item from the shopping cart. Then make the logic for add/remove from cart with the confirmation more re-usable with specialization. | ||
|
||
## Task 1 | ||
|
||
The only file you'll need to work on is `App.tsx`. Currently there is a working shopping cart button for a product. Implement the `DialogConfirm` component (template code provided in JSX already) so that it asks the user to confirm when they want to remove the item from the cart. | ||
|
||
## Task 2 | ||
|
||
The amount of logic in the `ProductDetails` component to make the button work would have to be repeated in other places where we want another shopping cart button. Let's make a special `AddToCartButton` component that specializes in handling all this logic so the end result is the profile looking like this with an easy-to-use `AddToCartButton` button: | ||
|
||
```tsx | ||
function ProductDetails({ productId }: Props) { | ||
return ( | ||
<div className="space-y-3"> | ||
<Heading>iPhone Pro Max</Heading> | ||
<div>Price: 1,199.00</div> | ||
<AddToCartButton productId={productId} /> | ||
</div> | ||
) | ||
} | ||
``` |
Oops, something went wrong.