Skip to content

arlagonix/interactive-rating-frontendmentor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Interactive Rating Component

Coded with HTML, SCSS, TypeScript

ℹ️ About

GitHub repo size

This is a solution to the Interactive rating component challenge on Frontend Mentor.

The challenge is to build out this interactive rating component and get it looking as close to the design as possible.

See task.md for more details about the task.

Your users should be able to:

  • View the optimal layout for the app depending on their device's screen size
  • See hover states for all interactive elements on the page
  • Select and submit a number rating
  • See the "Thank you" card state after submitting a rating

⚙️ Tools

  • HTML5
    • Semantic HTML
  • SASS
    • Flexbox
    • BEM methodology
    • Responsive design
    • Desktop first
  • TypeScript
  • NPM
    • Install TypeScript
  • Github Pages - for hosting
  • VS Code
    • Live Server - VS Code extension that launches local servers
    • Live Sass Complier - VS Code extension that transpiles SCSS/SASS files in CSS

📁 File Structure

Path Description
index.html Main HTML file
build Folder with genereated files
build / index.css CSS, generated with SCSS transpiler
build / index.js JavaScript, generated with TypeScript
src Source files needed for application development
src / assets Images and other media used on the webpage
src / index.scss Main SCSS file, used to later generate CSS
src / index.ts Main file with TS
__tests__ Folder with tests
__tests__ / sumbit-feedback.feature Business logic tests written with Gherkin syntax
docs Folder with additional information, documentation
docs / design Folder with images of how the interface must look like
docs / results Folder with screenshots of how the application works after being fully developed
docs / README-template.md Template for README.md, don't use it (from FrontendMentor)
docs / style-guide.md Style information: color palette, fonts, etc. (from FrontendMentor)
docs / task.md Detailed task description (from FrontendMentor)
docs / interactive-rating-component-main.zip Initial archive provided by Frontend Mentor
LICENSE MIT License
package-lock.json JSON file that keeps track of the exact version of every package that is installed so that a product is 100% reproducible in the same way even if packages are updated by their maintainers
package.json JSON file that holds various metadata relevant to the project. This file is used to give information to npm that allows it to identify the project as well as handle the project's dependencies
tsconfig.json JSON file that holds different compiler options for TypeScript

🔨 How to build project

run npm install

💡 Details

Working with TypeScript

It didn't help much. Rather it just showed a pile of little errors with types. Hopefully it will appear to be more useful in the future.

For now I don't see any difference with JS, but you have to just add some type description here and there.


Transition between screens

Intially there is a visible screen and an invisible screen hidden with a class containing display: none. When you need to switch screens, add a class with animation to the screen you want to hide and by the end of animation add a class with display: none. Then add a class with animation to the screen you need to show and remove the class with display: none.

// Hides the rating card, shows the result card
submitButton!.addEventListener("click", () => {
  conclusionText!.innerHTML = `You selected ${feedbackScore} out of 5`;

  (ratingCard as HTMLElement).style.animation = "move-center-to-left .5s forwards";
  (resultCard as HTMLElement).style.animation = "move-right-to-center .5s forwards";

  ratingCard!.addEventListener(
    "animationend",
    () => {
      ratingCard!.classList.add("page--no-display");
      resultCard!.classList.remove("page--no-display");
    },
    { once: true } // The absense of this little boy caused me so much trouble!
  );
});
@keyframes move-center-to-left {
  100% {
    translate: -500px 0px;
    opacity: 0;
  }
}

@keyframes move-left-to-center {
  0% {
    translate: -500px 0px;
    opacity: 0;
  }
}

And don't you forget to make the animation work only once { once: true }! I spent about an hour trying to figure out why the animation kept going and going in an endless loop.

Also I discovered that there is a great event called onanimationend! I accidentally stumbled upon it, and it helped me to avoid working with setTimeout like I did in previous project with generating advices. Also overall it helped to tidy my code and make it work without visual glitches that might happen in certain scenarios.


Activate the button with Enter depending on the screen being showed

I rely on absense or presense of display: none class in order to bind the Enter to a specific button. I assume that only one of them is visible at any given point of time.

And it made the code a little bit superfluous because of TypeScript not willing to work with my initial code. Look at that as HTMLElement, Array.from()

// Allows to submit the feedback on "Enter"
document.addEventListener("keydown", (e: KeyboardEvent) => {
  switch (e.key) {
    case "Enter":
      if (!Array.from(ratingCard!.classList).includes("page--no-display")) {
        (submitButton as HTMLElement).focus();
        submitButton!.dispatchEvent(new Event("click"));
      }
      if (!Array.from(resultCard!.classList).includes("page--no-display")) {
        (changeDecisionButton as HTMLElement).focus();
        changeDecisionButton!.dispatchEvent(new Event("click"));
      }
  }
});

Disabling a submit button and allowing to choose between options

Initially disable the submit button. Enable the button when there is at least one active element. Disable an active option when you activate another option. Disable the submit button when there are no active elements. Double activation of the same option deactivates the option. As simple as that.

let feedbackScore: number = 1;
let activeFeedbackOption: Element | undefined;

// ...

// Choose between options and enable/disable the submit button
for (let element of Array.from(scores!.children)) {
  element.addEventListener("click", (e: Event) => {
    feedbackScore = +(e.target as Element).innerHTML;

    // If there is an active option, make it inactive
    if (activeFeedbackOption !== undefined) {
      activeFeedbackOption.classList.remove("score--active");
    }

    // If we choose another option, make the button active
    if (activeFeedbackOption !== e.target) {
      (e.target as Element).classList.add("score--active");
      submitButton!.removeAttribute("disabled");
      activeFeedbackOption = e.target as Element;
    }

    // If we choose the same option, disable the button
    else {
      activeFeedbackOption = undefined;
      submitButton!.setAttribute("disabled", "");
    }
  });
}

Figma prototype

I use a free version of FrontendMentor. Thus I don't have access to Figma prototypes. But that's not a problem - I made my own one

Here it is: https://www.figma.com/file/8VPjsKdjo7LMSUJrjLEi6J/Interactive-Rating-Component.-Frontend-Mentor?node-id=0%3A1

That helped me to make the application as close to the photos as possible


Gherkin syntax

I thought about including Jest to add unit test my scripts, but in this case it seems redundant. All functions are tightly related to the DOM elements and depend on their state. Thus it's extremely difficult to test this functions in isolations.

So instead I decided to add Gherkin "Given When Then" scenarios to describe the business logic. I wrote them prior to coding the implemenation. They describe how the interface must work with as few as possible interface details like "press button" and so on.

Feature: Submit feedback

In order to share my attitude towards the support request
As a User
I want to be able to submit my feedback

Scenario: Default
Given User opened the page
Then User is unable to submit the feedback

Scenario: Select only 1 option
Given User opened the page
Then User can select only 1 feedback option

Other notes

  • Added an option to change the feedback, even though it's not included into the task. Some users might surely want that. Examined solutions of other people and it came to me that my solution might only win from this
  • Added a possibility to disable a button when there are no chosen options. That protects the users from accidentally submitting invalid feedback
  • Added a nice little animation when pressin on a star on the initial screen with options. Stole that idea from one of the solutions. I spent quite much time to disable the interaction with the element till the end of the animation.
  • Changed hover and active states. They look differently in designs
  • Added an underline hover effect to links

🔗 Useful resources

👤 Author