From 90b2cfbfeb8720853c8467f6b7c10be9abd39bf5 Mon Sep 17 00:00:00 2001
From: Dario-DC <105294544+Dario-DC@users.noreply.github.com>
Date: Tue, 13 Aug 2024 16:17:26 +0200
Subject: [PATCH] feat(curriculum): add book inventory lab (#55678)
---
client/i18n/locales/english/intro.json | 10 +-
.../lab-book-inventory-app/index.md | 9 +
.../_meta/lab-book-inventory-app/meta.json | 11 +
.../66a207974c806a19d6607073.md | 635 ++++++++++++++++++
4 files changed, 660 insertions(+), 5 deletions(-)
create mode 100644 client/src/pages/learn/front-end-development/lab-book-inventory-app/index.md
create mode 100644 curriculum/challenges/_meta/lab-book-inventory-app/meta.json
create mode 100644 curriculum/challenges/english/25-front-end-development/lab-book-inventory-app/66a207974c806a19d6607073.md
diff --git a/client/i18n/locales/english/intro.json b/client/i18n/locales/english/intro.json
index 244efafc7f7..6d8f7bf9827 100644
--- a/client/i18n/locales/english/intro.json
+++ b/client/i18n/locales/english/intro.json
@@ -2087,9 +2087,9 @@
"title": "94",
"intro": []
},
- "95": {
- "title": "95",
- "intro": []
+ "lab-book-inventory-app": {
+ "title": "Build a Book Inventory App",
+ "intro": ["For this lab, you will create a book inventory app."]
},
"96": {
"title": "96",
@@ -2634,8 +2634,8 @@
"title": "230",
"intro": []
},
- "230": {
- "title": "230",
+ "231": {
+ "title": "231",
"intro": []
},
"232": {
diff --git a/client/src/pages/learn/front-end-development/lab-book-inventory-app/index.md b/client/src/pages/learn/front-end-development/lab-book-inventory-app/index.md
new file mode 100644
index 00000000000..c7690bd17c1
--- /dev/null
+++ b/client/src/pages/learn/front-end-development/lab-book-inventory-app/index.md
@@ -0,0 +1,9 @@
+---
+title: Introduction to the Build a Book Inventory App
+block: lab-book-inventory-app
+superBlock: front-end-development
+---
+
+## Introduction to the Build a Book Inventory App
+
+For this lab, you will create a book inventory app.
diff --git a/curriculum/challenges/_meta/lab-book-inventory-app/meta.json b/curriculum/challenges/_meta/lab-book-inventory-app/meta.json
new file mode 100644
index 00000000000..03f7bed9e65
--- /dev/null
+++ b/curriculum/challenges/_meta/lab-book-inventory-app/meta.json
@@ -0,0 +1,11 @@
+{
+ "name": "Build a Book Inventory App",
+ "blockType": "lab",
+ "isUpcomingChange": true,
+ "usesMultifileEditor": true,
+ "dashedName": "lab-book-inventory-app",
+ "order": 95,
+ "superBlock": "front-end-development",
+ "challengeOrder": [{ "id": "66a207974c806a19d6607073", "title": "Build a Book Inventory App" }],
+ "helpCategory": "HTML-CSS"
+}
diff --git a/curriculum/challenges/english/25-front-end-development/lab-book-inventory-app/66a207974c806a19d6607073.md b/curriculum/challenges/english/25-front-end-development/lab-book-inventory-app/66a207974c806a19d6607073.md
new file mode 100644
index 00000000000..7439d3fb6ad
--- /dev/null
+++ b/curriculum/challenges/english/25-front-end-development/lab-book-inventory-app/66a207974c806a19d6607073.md
@@ -0,0 +1,635 @@
+---
+id: 66a207974c806a19d6607073
+title: Build a Book Inventory App
+challengeType: 14
+dashedName: build-a-book-inventory-app
+---
+
+# --description--
+
+
+**Objective:** Fulfill the user stories below and get all the tests to pass to complete the lab.
+
+**User Stories:**
+
+1. You should have an `h1` element with the text `Book Inventory`.
+1. You should have a `table` element with columns named `Title`, `Author`, `Category`, `Status`, and `Rate`.
+1. Your table should have at least four rows, the first for the column headings and the rest filled with book information.
+1. Each table row inside the table body should have either the class `read`, `to-read`, or `in-progress`.
+1. `td` elements of the `Status` column should contain a `span` element with the `class` of `status` surrounding the text `Read`, `To Read`, or `In Progress`, depending on the class of that row.
+1. `td` elements of the `Rate` column should contain a `span` element with the `class` of `rate` wrapping three empty `span` elements.
+1. `.rate` elements placed inside `.read` rows should have an additional class with the value of either `one`, `two`, or `three`, depending on the personal rate. This value should come after `rate`.
+1. You should create three attribute selectors to target the elements with the class of `read`, `to-read`, and `in-progress`, and set their `background-image` property to use a `linear-gradient` of your choice.
+1. You should set the `display` property of each `span` element to `inline-block`.
+1. You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `to-read` and set their `border` and `background-image` properties.
+1. You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `read` and set their `border` and `background-image` properties.
+1. You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `in-progress` and set their `border` and `background-image` properties.
+1. You should use an attribute selector to target the `span` elements with the class of `status` and the `span` elements with the class value starting with `rate` and set their `height`, `width`, and `padding` properties.
+1. You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `border`, `border-radius`, `margin`, `height`, `width`, and `background-color` properties.
+1. You should use an attribute selector to target the first descendant of `span` elements that have `one` as a part of their `class` value and set its `background-image` property to use a `linear-gradient`.
+1. You should use an attribute selector to target the first two descendants of `span` elements that have `two` as a part of their `class` value and set their `background-image` property to use a `linear-gradient`.
+1. You should use an attribute selector to target the three `span` elements that are descendants of `span` elements that have `three` as a part of their `class` value and set their `background-image` property to use a `linear-gradient`.
+
+# --hints--
+
+You should have an `h1` element with the text `Book Inventory`.
+
+```js
+assert.equal(document.querySelector('h1')?.innerText, 'Book Inventory');
+```
+
+You should have only one `h1` element.
+
+```js
+assert.equal(document.querySelectorAll('h1')?.length, 1);
+```
+
+You should have a `table` element after your `h1` element.
+
+```js
+assert.equal(document.querySelector('table')?.previousElementSibling?.tagName, 'H1')
+```
+
+Your `table` element should have five columns.
+
+```js
+assert.equal(document.querySelectorAll('th')?.length, 5);
+```
+
+Your first column should have the text `Title` as the heading.
+
+```js
+assert.equal(document.querySelectorAll('th')[0]?.innerText, 'Title');
+```
+
+Your second column should have the text `Author` as the heading.
+
+```js
+assert.equal(document.querySelectorAll('th')[1]?.innerText, 'Author');
+```
+
+Your third column should have the text `Category` as the heading.
+
+```js
+assert.equal(document.querySelectorAll('th')[2]?.innerText, 'Category');
+```
+
+Your fourth column should have the text `Status` as the heading.
+
+```js
+assert.equal(document.querySelectorAll('th')[3]?.innerText, 'Status');
+```
+
+Your fifth column should have the text `Rate` as the heading.
+
+```js
+assert.equal(document.querySelectorAll('th')[4]?.innerText, 'Rate');
+```
+
+Your table should have at least four rows.
+
+```js
+const rows = document.querySelectorAll('tr');
+assert.isAtLeast(rows.length, 4);
+```
+
+Each table row except the heading row should have either the class `read`, `to-read`, or `in-progress`.
+
+```js
+const rows = document.querySelectorAll('tr');
+assert.isAtLeast(rows.length, 4);
+for (let i = 1; i < rows.length; i++) {
+ assert(
+ (
+ rows[i].classList).contains('read')
+ || (rows[i].classList).contains('to-read')
+ || (rows[i].classList).contains('in-progress')
+ );
+}
+```
+
+`td` elements of the `Status` column should contain a `span` element.
+
+```js
+const statusColumnData = document.querySelectorAll('td:nth-child(4)');
+assert.isAbove(statusColumnData.length, 0);
+for (let e of statusColumnData) {
+ assert.equal(e?.children[0]?.tagName, 'SPAN');
+}
+```
+
+Each `span` element of the `Status` column should have the class of `status`.
+
+```js
+const statusSpans = document.querySelectorAll('tr td:nth-child(4) :first-child');
+assert.isAbove(statusSpans.length, 0);
+for (let e of statusSpans) {
+ assert(e.classList.contains('status'));
+}
+```
+
+Each `.status` element should have the text `Read`, `To Read`, or `In Progress`, depending on the class of its row.
+
+```js
+const statusSpans = document.querySelectorAll('tr td:nth-child(4) :first-child');
+const rows = Array.from(document.querySelectorAll('tr')).slice(1);
+assert.isAbove(statusSpans.length, 0);
+for (let i = 0; i < rows.length; i++) {
+ switch (statusSpans[i]?.innerText) {
+ case 'Read':
+ assert(rows[i].classList.contains('read'));
+ break;
+ case 'To Read':
+ assert(rows[i].classList.contains('to-read'));
+ break;
+ case 'In Progress':
+ assert(rows[i].classList.contains('in-progress'));
+ break;
+ default:
+ assert(false);
+ }
+}
+```
+
+`td` elements of the `Rate` column should contain a `span` element.
+
+```js
+const rateColumnData = document.querySelectorAll('tr td:last-child');
+assert.isAbove(rateColumnData.length, 0);
+for (let e of rateColumnData) {
+ assert.equal(e.children[0]?.tagName, 'SPAN')
+}
+```
+
+Each `span` element which is a direct child of a `td` element of the `Rate` column should have the class of `rate` as the first class.
+
+```js
+const rateSpans = document.querySelectorAll('tr td:last-child > span:first-child');
+assert.isAbove(rateSpans.length, 0);
+for (let e of rateSpans) {
+ assert.equal(e.classList[0], 'rate');
+}
+```
+
+Each `.rate` element should contain three empty `span` elements.
+
+```js
+const rateSpans = document.getElementsByClassName('rate');
+assert.isAbove(rateSpans.length, 0);
+for (let e of rateSpans) {
+ assert.equal(e.children.length, 3);
+ for (let child of e.children) {
+ assert.equal(child.tagName, 'SPAN');
+ assert.equal(child.innerText.length, 0);
+ }
+}
+```
+
+`.rate` elements placed inside `.read` rows should have an additional class after the `rate` class with the value of either `one`, `two`, or `three`, depending on the personal rate.
+
+```js
+const readBooksRates = document.querySelectorAll('.read .rate');
+assert.isAbove(readBooksRates.length, 0);
+for (let e of readBooksRates) {
+ assert(['one', 'two', 'three'].includes(e.classList[1]));
+}
+```
+
+You should have an attribute selector to target rows that have the class of `read`.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="read"]'));
+```
+
+You should use an attribute selector to target rows that have the class of `read` and set their `background-image` property to a linear gradient of your choice.
+
+```js
+assert.include(new __helpers.CSSHelp(document).getStyle('tr[class="read"]')?.backgroundImage, 'linear-gradient(');
+```
+
+You should have an attribute selector to target rows that have the class of `to-read`.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="to-read"]'));
+```
+
+You should use an attribute selector to target rows that have the class of `to-read` and set their `background-image` property to a linear gradient of your choice.
+
+```js
+assert.include(new __helpers.CSSHelp(document).getStyle('tr[class="to-read"]')?.backgroundImage, 'linear-gradient(');
+```
+
+You should have an attribute selector to target rows that have the class of `in-progress`.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="in-progress"]'));
+```
+
+You should use an attribute selector to target rows that have the class of `in-progress` and set their `background-image` property to a linear gradient of your choice.
+
+```js
+assert.include(new __helpers.CSSHelp(document).getStyle('tr[class="in-progress"]')?.backgroundImage, 'linear-gradient(');
+```
+
+You should set the `display` property of each `span` element to `inline-block`.
+
+```js
+assert.equal(new __helpers.CSSHelp(document).getStyle('span')?.getPropVal('display'), 'inline-block');
+```
+
+You should have an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `to-read`.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="to-read"] span[class="status"]'));
+```
+
+You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `to-read` and set their `border` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('tr[class="to-read"] span[class="status"]')?.border);
+```
+
+You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `to-read` and set their `background-image` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('tr[class="to-read"] span[class="status"]')?.backgroundImage);
+```
+
+You should have an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `read`.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="read"] span[class="status"]'));
+```
+
+You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `read` and set their `border` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('tr[class="read"] span[class="status"]')?.border);
+```
+
+You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `read` and set their `background-image` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('tr[class="read"] span[class="status"]')?.backgroundImage);
+```
+
+You should have an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `in-progress`.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('tr[class="in-progress"] span[class="status"]'));
+```
+
+You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `in-progress` and set their `border` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('tr[class="in-progress"] span[class="status"]')?.border);
+```
+
+You should use an attribute selector to target the `span` elements with the class of `status` that are descendants of `tr` elements with the class of `in-progress` and set their `background-image` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('tr[class="in-progress"] span[class="status"]')?.backgroundImage);
+```
+
+You should have an attribute selector to target the `span` elements with the class of `status` and the `span` elements with the class value starting with `rate`.
+
+```js
+const selector1 = new __helpers.CSSHelp(document).getStyle('span[class="status"], span[class^="rate"]');
+const selector2 = new __helpers.CSSHelp(document).getStyle('span[class^="rate"], span[class="status"]');
+assert.exists(selector1 || selector2);
+```
+
+You should use an attribute selector to target the `span` elements with the class of `status` and the `span` elements with the class value starting with `rate` and set their `height` property.
+
+```js
+const selector1 = new __helpers.CSSHelp(document).getStyle('span[class="status"], span[class^="rate"]');
+const selector2 = new __helpers.CSSHelp(document).getStyle('span[class^="rate"], span[class="status"]');
+assert(selector1?.height || selector2?.height);
+```
+
+You should use an attribute selector to target the `span` elements with the class of `status` and the `span` elements with the class value starting with `rate` and set their `width` property.
+
+```js
+const selector1 = new __helpers.CSSHelp(document).getStyle('span[class="status"], span[class^="rate"]');
+const selector2 = new __helpers.CSSHelp(document).getStyle('span[class^="rate"], span[class="status"]');
+assert(selector1?.width || selector2?.width);
+```
+
+You should use an attribute selector to target the `span` elements with the class of `status` and the `span` elements with the class value starting with `rate` and set their `padding` property.
+
+```js
+const selector1 = new __helpers.CSSHelp(document).getStyle('span[class="status"], span[class^="rate"]');
+const selector2 = new __helpers.CSSHelp(document).getStyle('span[class^="rate"], span[class="status"]');
+assert(selector1?.padding || selector2?.padding);
+```
+
+You should have an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate`.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span'));
+```
+
+You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `border` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('border'));
+```
+
+You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `border-radius` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('border-radius'));
+```
+
+You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `margin` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('margin'));
+```
+
+You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `height` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('height'));
+```
+
+You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `width` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('width'));
+```
+
+You should use an attribute selector to target the `span` elements which are direct children of `span` elements with the `class` value starting with `rate` and set their `background-color` property.
+
+```js
+assert(new __helpers.CSSHelp(document).getStyle('span[class^="rate"] > span')?.getPropVal('background-color'));
+```
+
+You should have an attribute selector to target the first descendant of `span` elements that have `one` as a part of their `class` value.
+
+```js
+const selector1 = new __helpers.CSSHelp(document).getStyle('span[class~="one"] :first-child');
+const selector2 = new __helpers.CSSHelp(document).getStyle('span[class~="one"] :nth-child(1)');
+assert.exists(selector1 || selector2);
+```
+
+You should use an attribute selector to target the first descendant of `span` elements that have `one` as a part of their `class` value and set its `background-image` property to use a `linear-gradient`.
+
+```js
+const selector1 = new __helpers.CSSHelp(document).getStyle('span[class~="one"] :first-child');
+const selector2 = new __helpers.CSSHelp(document).getStyle('span[class~="one"] :nth-child(1)');
+assert(selector1?.getPropVal('background-image').includes('linear-gradient(') || selector2?.getPropVal('background-image').includes('linear-gradient('));
+```
+
+You should have an attribute selector to target the first two descendants of `span` elements that have `two` as a part of their `class` value.
+
+```js
+const selector1 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(1), span[class~="two"] :nth-child(2)');
+const selector2 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(2), span[class~="two"] :nth-child(1)');
+const selector3 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :first-child, span[class~="two"] :nth-child(2)');
+const selector4 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(2), span[class~="two"] :first-child');
+assert.exists(selector1 || selector2 || selector3 || selector4);
+```
+
+You should use an attribute selector to target the first two descendants of `span` elements that have `two` as a part of their `class` value and set their `background-image` property to use a `linear-gradient`.
+
+```js
+const selector1 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(1), span[class~="two"] :nth-child(2)');
+const selector2 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(2), span[class~="two"] :nth-child(1)');
+const selector3 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :first-child, span[class~="two"] :nth-child(2)');
+const selector4 = new __helpers.CSSHelp(document).getStyle('span[class~="two"] :nth-child(2), span[class~="two"] :first-child');
+const selectors = [selector1, selector2, selector3, selector4];
+let isTrue = false;
+for (let selector of selectors) {
+ if (selector?.backgroundImage.includes('linear-gradient(')) {
+ isTrue = true;
+ }
+}
+assert(isTrue);
+```
+
+You should have an attribute selector to target the `span` elements that are descendants of `span` elements that have `three` as a part of their `class` value.
+
+```js
+assert.exists(new __helpers.CSSHelp(document).getStyle('span[class~="three"] span'));
+```
+
+You should use an attribute selector to target the `span` elements that are descendants of `span` elements that have `three` as a part of their `class` value and set their `background-image` property to use a `linear-gradient`.
+
+```js
+assert.include(new __helpers.CSSHelp(document).getStyle('span[class~="three"] span')?.getPropVal('background-image'), 'linear-gradient(');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+