diff --git a/client/i18n/locales/english/intro.json b/client/i18n/locales/english/intro.json index 1c6a2caf5d0..5510d03d162 100644 --- a/client/i18n/locales/english/intro.json +++ b/client/i18n/locales/english/intro.json @@ -3342,7 +3342,12 @@ "In these lecture videos, you will learn about working with data in React." ] }, - "cdfr": { "title": "263", "intro": [] }, + "workshop-reusable-profile-card-component": { + "title": "Build a Reusable Profile Card Component", + "intro": [ + "In this workshop, you will learn how to work with props by building a reusable profile card component." + ] + }, "lab-mood-board": { "title": "Build a Mood Board", "intro": [ diff --git a/client/src/pages/learn/full-stack-developer/workshop-reusable-profile-card-component/index.md b/client/src/pages/learn/full-stack-developer/workshop-reusable-profile-card-component/index.md new file mode 100644 index 00000000000..e39fc4587d4 --- /dev/null +++ b/client/src/pages/learn/full-stack-developer/workshop-reusable-profile-card-component/index.md @@ -0,0 +1,9 @@ +--- +title: Introduction to the Reusable Profile Card Component +block: workshop-reusable-profile-card-component +superBlock: full-stack-developer +--- + +## Introduction to the Reusable Profile Card Component + +This is a test for the new project-based curriculum. diff --git a/curriculum/challenges/_meta/workshop-reusable-profile-card-component/meta.json b/curriculum/challenges/_meta/workshop-reusable-profile-card-component/meta.json new file mode 100644 index 00000000000..a71a5043110 --- /dev/null +++ b/curriculum/challenges/_meta/workshop-reusable-profile-card-component/meta.json @@ -0,0 +1,41 @@ +{ + "name": "Build a Reusable Profile Card Component", + "isUpcomingChange": true, + "blockType": "workshop", + "blockLayout": "challenge-grid", + "usesMultifileEditor": true, + "hasEditableBoundaries": true, + "dashedName": "workshop-reusable-profile-card-component", + "superBlock": "full-stack-developer", + "challengeOrder": [ + { + "id": "674ef11f75254548672d998c", + "title": "Step 1" + }, + { + "id": "674ef2d357676e50e4691658", + "title": "Step 2" + }, + { + "id": "674ef2d357676e50e4691659", + "title": "Step 3" + }, + { + "id": "674ef2d357676e50e469165a", + "title": "Step 4" + }, + { + "id": "674ef2d357676e50e469165b", + "title": "Step 5" + }, + { + "id": "674ef2d357676e50e469165c", + "title": "Step 6" + }, + { + "id": "674ef2d357676e50e469165d", + "title": "Step 7" + } + ], + "helpCategory": "JavaScript" +} \ No newline at end of file diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef11f75254548672d998c.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef11f75254548672d998c.md new file mode 100644 index 00000000000..274e512a793 --- /dev/null +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef11f75254548672d998c.md @@ -0,0 +1,125 @@ +--- +id: 674ef11f75254548672d998c +title: Step 1 +challengeType: 0 +dashedName: step-1 +demoType: onLoad +--- + +# --description-- + +In this project, you will learn how to work with props by building a reusable profile card component. + +Start by using the `export` keyword to create a `Card` functional component with `name`, `title`, and `bio` as the props. Don't forget to make sure they're all wrapped in a curly brace. That way, you destructure them instead of accessing them from `props`. + +Also, return a pair of parentheses with an empty string inside of them for now. + +# --hints-- + +You should create and export a `Card` functional component. Don't forget to return an empty string inside it. + +```js +const mockedComponent = Enzyme.mount( + React.createElement(window.index.Card) +); + +assert.lengthOf(mockedComponent.find('Card'), 1); +``` + +Your `Card` component should have a `name`, `title`, and `bio` props. + +```js +const mockedComponent = Enzyme.mount( + React.createElement(window.index.Card) +); + +const componentFunction = mockedComponent.type(); +const propsSource = componentFunction.toString(); + +assert.match(propsSource, /\bname\b/); +assert.match(propsSource, /\btitle\b/); +assert.match(propsSource, /\bbio\b/); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + Reusable Card component + + + + + + + + +
+ + + +``` + +```css +:root { + --dark-grey: #1b1b32; + --light-grey: #f5f6f7; + --dark-orange: #f89808; +} + +body { + background-color: var(--dark-grey); +} + +.flex-container { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + align-items: center; +} + +.card { + border: 5px solid var(--dark-orange); + border-radius: 10px; + width: 100%; + padding: 20px; + margin: 10px 0; + background-color: var(--light-grey); +} + +.card-title { + border-bottom: 4px solid var(--dark-orange); + width: fit-content; +} + +@media (min-width: 768px) { + .card { + width: 300px; + } +} +``` + +```jsx +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691658.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691658.md new file mode 100644 index 00000000000..cd896b45aaf --- /dev/null +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691658.md @@ -0,0 +1,116 @@ +--- +id: 674ef2d357676e50e4691658 +title: Step 2 +challengeType: 0 +dashedName: step-2 +--- + +# --description-- + +Inside the return, remove the empty string, then create a `div` element with a `className` of `card`. + +# --hints-- + +You should not have an empty string in your Card component. + +```js +assert.notMatch(code, /""/) +``` + +You should create a `div` element with the `className` `card` at the top level of your `Card` component. + +```js +const mockedComponent = Enzyme.mount( + React.createElement(window.index.Card) +); + +assert.equal(mockedComponent.childAt(0).type(), 'div'); +assert.isTrue(mockedComponent.childAt(0).hasClass('card')); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + Reusable Card component + + + + + + + + +
+ + + +``` + +```css +:root { + --dark-grey: #1b1b32; + --light-grey: #f5f6f7; + --dark-orange: #f89808; +} + +body { + background-color: var(--dark-grey); +} + +.flex-container { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + align-items: center; +} + +.card { + border: 5px solid var(--dark-orange); + border-radius: 10px; + width: 100%; + padding: 20px; + margin: 10px 0; + background-color: var(--light-grey); +} + +.card-title { + border-bottom: 4px solid var(--dark-orange); + width: fit-content; +} + +@media (min-width: 768px) { + .card { + width: 300px; + } +} +``` + +```jsx +export function Card({ name, title, bio }) { + return ( + --fcc-editable-region-- + "" + --fcc-editable-region-- + ) +} +``` diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691659.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691659.md new file mode 100644 index 00000000000..e2dd5790f06 --- /dev/null +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e4691659.md @@ -0,0 +1,166 @@ +--- +id: 674ef2d357676e50e4691659 +title: Step 3 +challengeType: 0 +dashedName: step-3 +--- + +# --description-- + +Inside the `div` element, create an `h2` element and interpolate the `name` prop as its text. + +Also, inside the `div`, create a paragraph with the `className` of `card-title` to interpolate the `title` prop, and another paragraph to interpolate the `bio` prop. + +# --hints-- + +You should create an `h2` element inside your `div` element. + +```js +const mockedComponent = Enzyme.mount( + React.createElement(window.index.Card) +); + +const h2Elem = mockedComponent.find('h2'); + +assert.equal(h2Elem.parent().type(), 'div'); +``` + +Your `h2` element should have `{name}` as its text content. + +```js +const mockedComponent = Enzyme.mount( + React.createElement(window.index.Card, { name: 'nameVal' }) +); + +const h2Elem = mockedComponent.find('h2'); + +assert.equal(h2Elem.text(), 'nameVal'); +``` + +You should create a `p` tag with the `className` of `card-title` inside your `div` element. + +```js +const mockedComponent = Enzyme.mount( + React.createElement(window.index.Card) +); + +const pElem = mockedComponent.find('p.card-title'); + +assert.equal(pElem.parent().type(), 'div'); +``` + +Your `p` element with `className` of `card-title` should have `{title}` as its text content. + +```js +const mockedComponent = Enzyme.mount( + React.createElement(window.index.Card, { title: 'titleVal' }) +); + +const pElem = mockedComponent.find('p.card-title'); + +assert.equal(pElem.text(), 'titleVal'); +assert.equal(pElem.parent().type(), 'div'); +``` + +You should create another `p` element with `{bio}` as its text. + +```js +const mockedComponent = Enzyme.mount( + React.createElement(window.index.Card, { bio: 'bioVal' }) +); + +const secondPElem = mockedComponent.children().childAt(2) + +assert.equal(secondPElem.type(), 'p') +assert.equal(secondPElem.text(), 'bioVal'); +assert.equal(secondPElem.parent().type(), 'div'); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + Reusable Card component + + + + + + + + +
+ + + +``` + +```css +:root { + --dark-grey: #1b1b32; + --light-grey: #f5f6f7; + --dark-orange: #f89808; +} + +body { + background-color: var(--dark-grey); +} + +.flex-container { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + align-items: center; +} + +.card { + border: 5px solid var(--dark-orange); + border-radius: 10px; + width: 100%; + padding: 20px; + margin: 10px 0; + background-color: var(--light-grey); +} + +.card-title { + border-bottom: 4px solid var(--dark-orange); + width: fit-content; +} + +@media (min-width: 768px) { + .card { + width: 300px; + } +} +``` + +```jsx +export function Card({ name, title, bio }) { + return ( + --fcc-editable-region-- +
+ +
+ --fcc-editable-region-- + ) +} +``` diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165a.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165a.md new file mode 100644 index 00000000000..7e92584a83c --- /dev/null +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165a.md @@ -0,0 +1,117 @@ +--- +id: 674ef2d357676e50e469165a +title: Step 4 +challengeType: 0 +dashedName: step-4 +--- + +# --description-- + +To start using the `Card` component, use the `export` keyword to create an `App` functional component. + +Inside the `App` component, return a pair of parentheses containing an empty string. + +# --hints-- + +You should export an `App` functional component. + +```js +const mockedComponent = Enzyme.mount( + React.createElement(window.index.App) +); + +assert.lengthOf(mockedComponent.find('App'), 1); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + Reusable Card component + + + + + + + + +
+ + + +``` + +```css +:root { + --dark-grey: #1b1b32; + --light-grey: #f5f6f7; + --dark-orange: #f89808; +} + +body { + background-color: var(--dark-grey); +} + +.flex-container { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + align-items: center; +} + +.card { + border: 5px solid var(--dark-orange); + border-radius: 10px; + width: 100%; + padding: 20px; + margin: 10px 0; + background-color: var(--light-grey); +} + +.card-title { + border-bottom: 4px solid var(--dark-orange); + width: fit-content; +} + +@media (min-width: 768px) { + .card { + width: 300px; + } +} +``` + +```jsx +export function Card({ name, title, bio }) { + return ( +
+

{name}

+

{title}

+

{bio}

+
+ ) +} + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165b.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165b.md new file mode 100644 index 00000000000..db3fbd1070c --- /dev/null +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165b.md @@ -0,0 +1,126 @@ +--- +id: 674ef2d357676e50e469165b +title: Step 5 +challengeType: 0 +dashedName: step-5 +--- + +# --description-- + +Inside your return statement, replace the empty string with a `div` element with a `className` property of `flex-container`. + +# --hints-- + +You should not have an empty string in your Card component. + +```js +assert.notMatch(code, /""/) +``` + +You should create a `div` element with the class `flex-container`. + +```js +const mockedComponent = Enzyme.mount( + React.createElement(window.index.App) +); + +assert.equal(mockedComponent.childAt(0).type(), 'div'); +assert.isTrue(mockedComponent.childAt(0).hasClass('flex-container')); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + Reusable Card component + + + + + + + + +
+ + + +``` + +```css +:root { + --dark-grey: #1b1b32; + --light-grey: #f5f6f7; + --dark-orange: #f89808; +} + +body { + background-color: var(--dark-grey); +} + +.flex-container { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + align-items: center; +} + +.card { + border: 5px solid var(--dark-orange); + border-radius: 10px; + width: 100%; + padding: 20px; + margin: 10px 0; + background-color: var(--light-grey); +} + +.card-title { + border-bottom: 4px solid var(--dark-orange); + width: fit-content; +} + +@media (min-width: 768px) { + .card { + width: 300px; + } +} +``` + +```jsx +export function Card({ name, title, bio }) { + return ( +
+

{name}

+

{title}

+

{bio}

+
+ ) +} + +export function App() { + return ( + --fcc-editable-region-- + "" + --fcc-editable-region-- + ); +} +``` diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165c.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165c.md new file mode 100644 index 00000000000..ebe73d4c75b --- /dev/null +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165c.md @@ -0,0 +1,144 @@ +--- +id: 674ef2d357676e50e469165c +title: Step 6 +challengeType: 0 +dashedName: step-6 +--- + +# --description-- + +Now, use the `Card` component with the `name` prop set to `"Mark"`, the `title` prop set to `"Frontend developer"`, and the `bio` set to `"I like to work with different frontend technologies and play video games."` + +# --hints-- + +You should use the `Card` component as an element with a `name` prop set to `"Mark"`. + +```js +const mockedComponent = Enzyme.mount(React.createElement(window.index.App)); +const cardItem = mockedComponent.find(window.index.Card); + +const cardItemsProps = cardItem.props() + +assert.equal(cardItem.props().name, "Mark") +``` + +You should use the `Card` component as an element with a `title` prop set to `"Frontend developer"`. + +```js +const mockedComponent = Enzyme.mount(React.createElement(window.index.App)); +const cardItem = mockedComponent.find(window.index.Card); + +const cardItemsProps = cardItem.props() + +assert.equal(cardItem.props().title, "Frontend developer") +``` + +You should use the `Card` component as an element with a `bio` prop set to `"I like to work with different frontend technologies and play video games."`. + +```js +const mockedComponent = Enzyme.mount(React.createElement(window.index.App)); +const cardItem = mockedComponent.find(window.index.Card); + +const cardItemsProps = cardItem.props() + +assert.equal(cardItem.props().bio, "I like to work with different frontend technologies and play video games.") +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + Reusable Card component + + + + + + + + +
+ + + +``` + +```css +:root { + --dark-grey: #1b1b32; + --light-grey: #f5f6f7; + --dark-orange: #f89808; +} + +body { + background-color: var(--dark-grey); +} + +.flex-container { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + align-items: center; +} + +.card { + border: 5px solid var(--dark-orange); + border-radius: 10px; + width: 100%; + padding: 20px; + margin: 10px 0; + background-color: var(--light-grey); +} + +.card-title { + border-bottom: 4px solid var(--dark-orange); + width: fit-content; +} + +@media (min-width: 768px) { + .card { + width: 300px; + } +} +``` + +```jsx +export function Card({ name, title, bio }) { + return ( +
+

{name}

+

{title}

+

{bio}

+
+ ) +} + +export function App() { + return ( +
+ --fcc-editable-region-- + + --fcc-editable-region-- +
+ ); +} +``` diff --git a/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165d.md b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165d.md new file mode 100644 index 00000000000..7b1d60ed468 --- /dev/null +++ b/curriculum/challenges/english/25-front-end-development/workshop-reusable-profile-card-component/674ef2d357676e50e469165d.md @@ -0,0 +1,265 @@ +--- +id: 674ef2d357676e50e469165d +title: Step 7 +challengeType: 0 +dashedName: step-7 +--- + +# --description-- + +Again, use the `Card` component two more times with the following: + +| Props | First Card Values | Second Card Values | +|-------|------------------------------------------------------------|-----------------------------------------------------| +| `name` | `"Tiffany"` | `"Doug"`| +| `title` | `"Engineering manager"` | `"Backend developer"` | +| `bio` | `"I have worked in tech for 15 years and love to help people grow in this industry."` | `"I have been a software developer for over 20 years and I love working with Go and Rust."`| + +With that, your reusable profile card component project is complete! + +# --hints-- + +You should use the `Card` component as an element with the appropriate props and first card values. + +```js +const mockedComponent = Enzyme.mount(React.createElement(window.index.App)); +const cardItems = mockedComponent.find(window.index.Card); + +const propsList = cardItems.map(item => item.props()); + +assert.deepEqual(propsList[1], { + name: 'Tiffany', + title: 'Engineering manager', + bio: 'I have worked in tech for 15 years and love to help people grow in this industry.' +}); +``` + +You should use the `Card` component as an element with the appropriate props and second card values. + +```js +const mockedComponent = Enzyme.mount(React.createElement(window.index.App)); +const cardItems = mockedComponent.find(window.index.Card); + +const propsList = cardItems.map(item => item.props()); + +assert.deepEqual(propsList[2], { + name: 'Doug', + title: 'Backend developer', + bio: 'I have been a software developer for over 20 years and I love working with Go and Rust.' +}); +``` + +# --seed-- + +## --seed-contents-- + +```html + + + + + Reusable Card component + + + + + + + + +
+ + + +``` + +```css +:root { + --dark-grey: #1b1b32; + --light-grey: #f5f6f7; + --dark-orange: #f89808; +} + +body { + background-color: var(--dark-grey); +} + +.flex-container { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + align-items: center; +} + +.card { + border: 5px solid var(--dark-orange); + border-radius: 10px; + width: 100%; + padding: 20px; + margin: 10px 0; + background-color: var(--light-grey); +} + +.card-title { + border-bottom: 4px solid var(--dark-orange); + width: fit-content; +} + +@media (min-width: 768px) { + .card { + width: 300px; + } +} +``` + +```jsx +export function Card({ name, title, bio }) { + return ( +
+

{name}

+

{title}

+

{bio}

+
+ ) +} + +export function App() { + return ( +
+ + --fcc-editable-region-- + + --fcc-editable-region-- +
+ ); +} +``` + +# --solutions-- + +```html + + + + + Reusable Card component + + + + + + + + +
+ + + +``` + +```css +:root { + --dark-grey: #1b1b32; + --light-grey: #f5f6f7; + --dark-orange: #f89808; +} + +body { + background-color: var(--dark-grey); +} + +.flex-container { + display: flex; + flex-wrap: wrap; + justify-content: space-around; + align-items: center; +} + +.card { + border: 5px solid var(--dark-orange); + border-radius: 10px; + width: 100%; + padding: 20px; + margin: 10px 0; + background-color: var(--light-grey); +} + +.card-title { + border-bottom: 4px solid var(--dark-orange); + width: fit-content; +} + +@media (min-width: 768px) { + .card { + width: 300px; + } +} +``` + +```jsx +export function Card({ name, title, bio }) { + return ( +
+

{name}

+

{title}

+

{bio}

+
+ ) +} + +export function App() { + return ( +
+ + + + + +
+ ); +} +``` diff --git a/curriculum/superblock-structure/full-stack.json b/curriculum/superblock-structure/full-stack.json index cb6f74ec50d..f2ec2e466dc 100644 --- a/curriculum/superblock-structure/full-stack.json +++ b/curriculum/superblock-structure/full-stack.json @@ -556,6 +556,7 @@ { "dashedName": "workshop-reusable-mega-navbar" }, { "dashedName": "lab-reusable-footer" }, { "dashedName": "lecture-working-with-data-in-react" }, + { "dashedName": "workshop-reusable-profile-card-component" }, { "dashedName": "lab-mood-board" }, { "dashedName": "review-react-basics" }, { "dashedName": "quiz-react-basics" }