Back to Freecodecamp

Build a Music Shopping Cart Page

curriculum/challenges/english/blocks/lab-music-shopping-cart-page/685dbd19200ad656d48b77ff.md

latest22.4 KB
Original Source

--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 element with an id called shopping-cart-container.
  2. Your #shopping-cart-container element should use the correct utility class for setting the element to a Flexbox layout.
  3. Your #shopping-cart-container element should set the direction of flex items to column for smaller devices and row for larger devices. Remember that Tailwind CSS utilizes the mobile first approach and you will use the lg: breakpoint prefix to target larger devices.
  4. Inside your #shopping-cart-container element, you should have an element with an id called products-container.
  5. Your #products-container element should have at least two child elements each with a class called card.
  6. Each .card element should have the following elements nested inside:
    • An h2 element with text representing the product name, and a utility class that sets the font size using only predefined size classes such as text-sm, text-md, text-lg, text-xl, text-2xl, etc.
    • An element with a class called quantity and text for the number of cart items for that product.
    • An element with a class called price and text for the price.
    • A button with a class called remove-button and text Remove. Your button should have utility classes for a predefined red background color of your choosing and different predefined red background color for the hover state. Examples of predefined red background colors include bg-red-500, bg-red-600, etc. Examples of predefined hover red background colors include hover:bg-red-500, hover:bg-red-600, etc.
  7. Inside your #shopping-cart-container element, you should have an element with an id called order-summary-container.
  8. Your #order-summary-container element should have the following styles:
    • A utility class of your choosing for predefined rounded corners. Examples include rounded, rounded-lg, rounded-full, etc.
    • A utility class of your choosing for setting the border width on all sides.
  9. Your #order-summary-container element should have the following elements nested inside:
    • An h2 element with the text Order Summary and a utility class of your choosing that sets the font size.
    • An element with the text Total: and id set to total. This element should also use utility classes to set the font weight to a predefined Tailwind CSS font weight and font size of your choosing for the element.
    • An element with an id set to total-amount to display the total for all items in the cart.
    • A link with the text Checkout and the correct utility class for centering the text. The href value should be set to #. Your link should also have a utility class for setting the background color to a predefined Tailwind CSS blue color of your choosing and a different blue background color for the hover state.

--hints--

You should have an element with an id set to shopping-cart-container.

js
assert.exists(document.getElementById("shopping-cart-container"));

Your #shopping-cart-container element should use the flex utility class to set the element to a Flexbox layout.

js
const shoppingCartContainer = document.getElementById("shopping-cart-container");
assert.isTrue(shoppingCartContainer.classList.contains("flex"));

Your #shopping-cart-container element should use the correct utility class to set the direction of flex items to column.

js
const shoppingCartContainer = document.getElementById("shopping-cart-container");
assert.isTrue(shoppingCartContainer.classList.contains("flex-col"));

Your #shopping-cart-container element should use the lg: breakpoint prefix followed by the correct utility class to set the direction of flex items to row for larger devices.

js
const shoppingCartContainer = document.getElementById("shopping-cart-container");
assert.isTrue(shoppingCartContainer.classList.contains("lg:flex-row"));

You should have an element with an id set to products-container inside your #shopping-cart-container element.

js
const shoppingCartContainer = document.getElementById("shopping-cart-container");
assert.exists(shoppingCartContainer);
const productsContainer = document.getElementById("products-container");
assert.exists(productsContainer);
assert.isTrue(shoppingCartContainer.contains(productsContainer));

Your #products-container element should have at least two child elements each with a class called card.

js
const productsContainer = document.getElementById("products-container");
const cards = productsContainer.querySelectorAll("#products-container .card");
assert.isAtLeast(cards.length, 2);

Each .card element should have an h2 element with text representing the product name.

js
const productsContainer = document.getElementById("products-container");
const cards = productsContainer.querySelectorAll("#products-container .card");

cards?.forEach((card) => {
  const productName = card.querySelector("h2");
  assert.exists(productName);
  assert.isNotEmpty(productName.textContent);
});

Each h2 element inside of the .card element should use a utility class that sets the font size using only predefined size classes such as text-sm, text-base, text-lg, text-xl, text-2xl, etc.

js
const productsContainer = document.getElementById("products-container");
const cards = productsContainer.querySelectorAll("#products-container .card");
const predefinedSizes = [
  "text-xs",
  "text-sm",
  "text-base",
  "text-lg",
  "text-xl",
  "text-2xl",
  "text-3xl",
  "text-4xl",
  "text-5xl",
  "text-6xl",
  "text-7xl",
  "text-8xl",
  "text-9xl"
];

cards?.forEach((card) => {
  const productName = card.querySelector("h2");
  assert.exists(productName);
  const hasPredefinedSize = predefinedSizes.some(size => productName.classList.contains(size));
  assert.isTrue(hasPredefinedSize);
});

Each .card element should have an element with a class called quantity.

js
const productsContainer = document.getElementById("products-container");
const cards = productsContainer.querySelectorAll("#products-container .card");

cards?.forEach((card) => {
  const quantity = card.querySelector(".quantity");
  assert.exists(quantity);
});

Each .quantity element should have text representing the number of cart items for that product.

js
const productsContainer = document.getElementById("products-container");
const cards = productsContainer.querySelectorAll("#products-container .card");

cards?.forEach((card) => {
  const quantity = card.querySelector(".quantity");
  assert.exists(quantity);
  assert.isNotEmpty(quantity.textContent.trim());
});

Each .card element should have an element with a class called price.

js
const productsContainer = document.getElementById("products-container");
const cards = productsContainer.querySelectorAll("#products-container .card");  

cards?.forEach((card) => {
  const price = card.querySelector(".price");
  assert.exists(price);
});

Each .price element should have text representing the price.

js
const productsContainer = document.getElementById("products-container");
const cards = productsContainer.querySelectorAll("#products-container .card");

cards?.forEach((card) => {
  const price = card.querySelector(".price");
  assert.exists(price);
  assert.isNotEmpty(price.textContent.trim());
});

Each .card element should have a button element with a class called remove-button and text Remove.

js
const productsContainer = document.getElementById("products-container");
const cards = productsContainer.querySelectorAll("#products-container .card");

cards?.forEach((card) => {
  const removeButton = card.querySelector("button.remove-button");
  assert.exists(removeButton);
  assert.equal(removeButton.textContent.trim(), "Remove");
});

Each button element should have a utility class for a red background color of your choosing.

js
const productsContainer = document.getElementById("products-container");
const cards = productsContainer.querySelectorAll("#products-container .card");
const predefinedRedColors = [
  "bg-red-50",
  "bg-red-100",
  "bg-red-200",
  "bg-red-300",
  "bg-red-400",
  "bg-red-500",
  "bg-red-600",
  "bg-red-700",
  "bg-red-800",
  "bg-red-900",
  "bg-red-950"
];

cards?.forEach((card) => {
  const removeButton = card.querySelector("button.remove-button");
  assert.exists(removeButton);
  const hasRedBackground = predefinedRedColors.some(color => removeButton.classList.contains(color));
  assert.isTrue(hasRedBackground);
});

Each button element should have different red background color for the hover state.

js
const productsContainer = document.getElementById("products-container");
const cards = productsContainer.querySelectorAll("#products-container .card");

const predefinedRedHoverColors = [
  "hover:bg-red-50",
  "hover:bg-red-100",
  "hover:bg-red-200",
  "hover:bg-red-300",
  "hover:bg-red-400",
  "hover:bg-red-500",
  "hover:bg-red-600",
  "hover:bg-red-700",
  "hover:bg-red-800",
  "hover:bg-red-900",
  "hover:bg-red-950"
];

cards?.forEach((card) => {
  const removeButton = card.querySelector("button.remove-button");
  assert.exists(removeButton);

  const classList = removeButton.classList;

  const hoverBgClass = [...classList].find(cls => cls.startsWith("hover:bg-red-"));
  const bgClass = [...classList].find(cls => cls.startsWith("bg-red-") && !cls.startsWith("hover:"));

  assert.notEqual(bgClass, hoverBgClass.replace("hover:", ""));
});

You should have an element with an id called order-summary-container inside your #shopping-cart-container element.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
assert.exists(orderSummaryContainer);

Your #order-summary-container element should have a utility class of your choosing for setting the rounded corners.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
const predefinedRoundedCorners = [
  "rounded-none",
  "rounded-sm",
  "rounded",
  "rounded-md",
  "rounded-lg",
  "rounded-xl",
  "rounded-2xl",
  "rounded-3xl",
  "rounded-full"
];

const hasRoundedCorners = predefinedRoundedCorners.some(corner => orderSummaryContainer.classList.contains(corner));
assert.isTrue(hasRoundedCorners);

Your #order-summary-container element should have a utility class of your choosing for setting the border width on all sides.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");

// border classes can vary in width, so this check is for the presence of any border class
const hasBorder = [...orderSummaryContainer.classList].some(className => className.startsWith("border-") || className === "border");
assert.isTrue(hasBorder);

Your #order-summary-container element should have an h2 element with the text Order Summary.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
const orderSummaryTitle = orderSummaryContainer.querySelector("h2");
assert.exists(orderSummaryTitle);
assert.equal(orderSummaryTitle.textContent.trim(), "Order Summary");

Your h2 inside of the #order-summary-container element should use a utility class of your choosing that sets the font size.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
const predefinedSizes = [
  "text-xs",
  "text-sm",
  "text-base",
  "text-lg",
  "text-xl",
  "text-2xl",
  "text-3xl",
  "text-4xl",
  "text-5xl",
  "text-6xl",
  "text-7xl",
  "text-8xl",
  "text-9xl"
];

const orderSummaryTitle = orderSummaryContainer.querySelector("h2");
assert.exists(orderSummaryTitle);
const hasPredefinedSize = predefinedSizes.some(size => orderSummaryTitle.classList.contains(size));
assert.isTrue(hasPredefinedSize);

Your #order-summary-container element should have an element with the text Total: and id set to total.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
const totalElement = orderSummaryContainer.querySelector("#total");
assert.exists(totalElement);
assert.equal(totalElement.textContent.trim(), "Total:");

Your #total element should use a utility class to set the font weight.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
const totalElement = orderSummaryContainer.querySelector("#total");
const predefinedFontWeights = [
  "font-thin",
  "font-extralight",
  "font-light",
  "font-normal",
  "font-medium",
  "font-semibold",
  "font-bold",
  "font-extrabold",
  "font-black"
];

const hasFontWeight = predefinedFontWeights.some(weight => totalElement.classList.contains(weight));
assert.isTrue(hasFontWeight);

Your #total element should use a utility class to set the font size.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
const totalElement = orderSummaryContainer.querySelector("#total");
const predefinedSizes = [
  "text-xs",
  "text-sm",
  "text-base",
  "text-lg",
  "text-xl",
  "text-2xl",
  "text-3xl",
  "text-4xl",
  "text-5xl",
  "text-6xl",
  "text-7xl",
  "text-8xl",
  "text-9xl"
];

const hasPredefinedSize = predefinedSizes.some(size => totalElement.classList.contains(size));
assert.isTrue(hasPredefinedSize);

Your #order-summary-container element should have an element with the id set to total-amount to display the total for all items in the cart.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
const totalElement = orderSummaryContainer.querySelector("#total-amount");
assert.exists(totalElement);
assert.isNotEmpty(totalElement.textContent.trim());

Your #order-summary-container element should have a link with the text Checkout and a # for the href value.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
const checkoutLink = orderSummaryContainer.querySelector("a");
assert.exists(checkoutLink);
assert.equal(checkoutLink.textContent.trim(), "Checkout");
assert.equal(checkoutLink.getAttribute("href"), "#");

Your Checkout link should have a utility class for centering the text.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
const checkoutLink = orderSummaryContainer.querySelector("a");
const hasTextCentering = checkoutLink.classList.contains("text-center");
assert.isTrue(hasTextCentering);

Your Checkout link should have a utility class for setting the background color to a predefined Tailwind CSS blue color of your choosing.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
const checkoutLink = orderSummaryContainer.querySelector("a");
const predefinedBlueColors = [
  "bg-blue-50",
  "bg-blue-100",
  "bg-blue-200",
  "bg-blue-300",
  "bg-blue-400",
  "bg-blue-500",
  "bg-blue-600",
  "bg-blue-700",
  "bg-blue-800",
  "bg-blue-900",
  "bg-blue-950"
];

const hasBlueBackground = predefinedBlueColors.some(color => checkoutLink.classList.contains(color));
assert.isTrue(hasBlueBackground);

Your Checkout link should have a different blue background color for the hover state.

js
const orderSummaryContainer = document.querySelector("#shopping-cart-container #order-summary-container");
const checkoutLink = orderSummaryContainer.querySelector("a");
const predefinedBlueHoverColors = [
  "hover:bg-blue-50",
  "hover:bg-blue-100",
  "hover:bg-blue-200",
  "hover:bg-blue-300",
  "hover:bg-blue-400",
  "hover:bg-blue-500",
  "hover:bg-blue-600",
  "hover:bg-blue-700",
  "hover:bg-blue-800",
  "hover:bg-blue-900",
  "hover:bg-blue-950"
];

const classList = checkoutLink.classList;
const hoverBgClass = [...classList].find(cls => cls.startsWith("hover:bg-blue-"));
const bgClass = [...classList].find(cls => cls.startsWith("bg-blue-") && !cls.startsWith("hover:"));

assert.notEqual(bgClass, hoverBgClass.replace("hover:", ""));

--seed--

--seed-contents--

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Music Shopping Cart Page</title>
    <script src="https://cdn.tailwindcss.com"></script>
  </head>
  <body>

  </body>
</html>

--solutions--

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Music Shopping Cart Page</title>
    <script src="https://cdn.tailwindcss.com"></script>
  </head>
  <body class="bg-gray-900 text-white">
    <h1 class="text-center text-3xl my-8">Shopping Cart Page</h1>
    <main
      id="shopping-cart-container"
      class="flex flex-col lg:flex-row items-center gap-8"
    >
      <div id="products-container" class="w-full md:w-2/3 px-4">
        <div
          class="card flex flex-col lg:flex-row items-center gap-6 rounded-lg border border-gray-200 p-4 my-8 bg-gray-800"
        >
          <h2 class="text-lg lg:w-1/2">Ludwig Supraphonic Snare Drum Chrome</h2>
          <div class="btn-container flex items-center lg:w-1/4">
            <button
              class="decrement-btn bg-gray-800 hover:bg-gray-700 border border-gray-200 py-1 px-2 rounded-md"
              type="button"
              aria-label="Decrease quantity"
            >
              <span aria-hidden="true">-</span>
            </button>
            <span class="quantity mx-2">2</span>
            <button
              class="increment-btn bg-gray-800 hover:bg-gray-700 border border-gray-200 py-1 px-2 rounded-md"
              type="button"
              aria-label="Increase quantity"
            >
              <span aria-hidden="true">+</span>
            </button>
          </div>
          <p class="price lg:w-1/6 text-right">$1398.00</p>
          <button
            class="remove-button bg-red-700 hover:bg-red-800 py-1 px-2 rounded-md"
          >
            Remove
          </button>
        </div>

        <div
          class="card flex flex-col lg:flex-row items-center gap-6 rounded-lg border border-gray-200 p-4 my-8 bg-gray-800"
        >
          <h2 class="text-lg lg:w-1/2">Shure SM58S Mic With Switch Standard</h2>
          <div class="btn-container flex items-center lg:w-1/4">
            <button
              class="decrement-btn bg-gray-800 hover:bg-gray-700 border border-gray-200 py-1 px-2 rounded-md"
              type="button"
              aria-label="Decrease quantity"
            >
              <span aria-hidden="true">-</span>
            </button>
            <span class="quantity mx-2">1</span>
            <button
              class="increment-btn bg-gray-800 hover:bg-gray-700 border border-gray-200 py-1 px-2 rounded-md"
              type="button"
              aria-label="Increase quantity"
            >
              <span aria-hidden="true">+</span>
            </button>
          </div>
          <p class="price lg:w-1/6 text-right">$119.00</p>
          <button
            class="remove-button bg-red-700 hover:bg-red-800 py-1 px-2 rounded-md"
          >
            Remove
          </button>
        </div>

        <div
          class="card flex flex-col lg:flex-row items-center gap-6 rounded-lg border border-gray-200 p-4 my-8 bg-gray-800"
        >
          <h2 class="text-lg lg:w-1/2">
            Fender American Professional II Stratocaster
          </h2>
          <div class="btn-container flex items-center lg:w-1/4">
            <button
              class="decrement-btn bg-gray-800 hover:bg-gray-700 border border-gray-200 py-1 px-2 rounded-md"
              type="button"
              aria-label="Decrease quantity"
            >
              <span aria-hidden="true">-</span>
            </button>
            <span class="quantity mx-2">1</span>
            <button
              class="increment-btn bg-gray-800 hover:bg-gray-700 border border-gray-200 py-1 px-2 rounded-md"
              type="button"
              aria-label="Increase quantity"
            >
              <span aria-hidden="true">+</span>
            </button>
          </div>
          <p class="price lg:w-1/6 text-right">$1700.00</p>
          <button
            class="remove-button bg-red-700 hover:bg-red-800 py-1 px-2 rounded-md"
          >
            Remove
          </button>
        </div>

        <div
          class="card flex flex-col lg:flex-row items-center gap-6 rounded-lg border border-gray-200 p-4 my-8 bg-gray-800"
        >
          <h2 class="text-lg lg:w-1/2">
            Moog Subsequent 37 Analog Synthesizer
          </h2>
          <div class="btn-container flex items-center lg:w-1/4">
            <button
              class="decrement-btn bg-gray-800 hover:bg-gray-700 border border-gray-200 py-1 px-2 rounded-md"
              type="button"
              aria-label="Decrease quantity"
            >
              <span aria-hidden="true">-</span>
            </button>
            <span class="quantity mx-2">1</span>
            <button
              class="increment-btn bg-gray-800 hover:bg-gray-700 border border-gray-200 py-1 px-2 rounded-md"
              type="button"
              aria-label="Increase quantity"
            >
              <span aria-hidden="true">+</span>
            </button>
          </div>
          <p class="price lg:w-1/6 text-right">$1799.00</p>
          <button
            class="remove-button bg-red-700 hover:bg-red-800 py-1 px-2 rounded-md"
          >
            Remove
          </button>
        </div>
      </div>

      <div
        id="order-summary-container"
        class="rounded-lg border border-gray-200 p-8 bg-gray-800 w-full md:w-1/3 mt-8 lg:mt-0"
      >
        <h2 class="text-2xl mb-4">Order Summary</h2>
        <ul>
          <li>
            <span id="subtotal" class="font-semibold text-lg">Subtotal:</span>
            <span>$5,016.00</span>
          </li>
          <li class="my-6">
            <span id="shipping" class="font-semibold text-lg">Shipping:</span>
            <span>Calculated in checkout</span>
          </li>
          <li>
            <span id="total" class="font-semibold text-lg">Total:</span>
            <span id="total-amount">$5,016.00</span>
          </li>
        </ul>
        <a
          href="#"
          class="text-center rounded-lg mx-auto mt-4 block px-5 py-2.5 font-medium bg-blue-600 hover:bg-blue-700"
          >Checkout</a
        >
      </div>
    </main>
  </body>
</html>