No-Brainer HTML, JS, CSS and cypress.io testing starter tutorial — Part 4

This tutorial series is aimed as notes for participants of workshop which will be held in OKE Software Poland. It assumes that participants have very basic understanding of HTML, CSS and JS technology. It doesn’t provide complex overview of current frontend state of the art, but rather should be treated as encouragement to further pursue the knowledge needed to become frontend dev or automated tester.

Plan — Searching the ticket, Your tickets, localStorage integration

We’ll start by adding ability to filter our ticket list by city or artist name.

Then we’ll build the second view of our ticket app, where user ticket list is displayed. We’ll use the localStorage mechanism to store the data about user tickets. This is the design we’ll be using as reference:

Let’s jump right in!

Filtering our data

We have the input in place as well as the search button. We’ll break it down in pieces:

  • we can reuse the function that we already have to render rows. It is the function that we pass to map in table js.
  • we can make our code a little bit more readable if we extract the code responsible for creating the input for the number of tickets as well as the button for adding the tickets to the list.
  • When user clicks search button we will take what’s in input and use to filter our data and call the function that is rendering rows again

Seems easy!

Ok let’s take code from map function and extract it. We can extract the button and input functions, too:

Now it reads really nice! On now on to the searching! In js folder create searchHeader.js file and add it to index.html in <script> tag. Ok let’s try getting our input and button using getElementsByName:

The problem is that as you can tell by the plural in the name of the function, it returns an array of elements whereas we’d like to get only first element of that array. No problem, we’ll just grab them by the first index [0]:

Ok now we can add an event listener on our button:

And then we can actually filter our data. The DATA variable as well as function to create rows is globally accessible due to global context binding.

Ok so now to the function itself. It will have to do few things:

  1. create new set of table rows by filtering the DATA and maping the result of it.
  2. Remove the old rows from the table, because we’d like to display only the filtered ones.
  3. If there are any rows that are effects of the filtering we should render them.
  4. If there are no tickets based on the searchTerm then we’d like to display infromation about that to the user.

Ok let’s handle 3 first points:

Ok filtering seems to be working nice! We also get nice and shiny error message in console, when there are no tickets. Now let’s actually add an information about that. I’ll create a little insertAfter helper function to help me with that. This function we’ll take two DOM nodes, one to insert after the second one:

function insertAfter(newNode, referenceNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

So we can write it like this:

Than in ticket-table.css we can add our 'no-ticket-warning' class. Like this:

.no-ticket-warning {
text-align: center;
color: rebeccapurple;
}

Your tickets

Ok so now we should think about adding, displaying, deleting and paying for our tickets. If we had been building real app, we would probably use a database for storing our tickets and than use some API to add, get and delete it. For simplicity sake, we’ll be using localStorage. It’s a dictionary we can use to store some data on the client part of the web. It is persisted after the browser is closed and will be ideal for us to store the ids, amount and status of the ticket that user wants to buy.

We’ll be using the functionality of tickets through 2 of our 3 screens, so it would be nice to have it all in seperate file. Let’s create ticketService.js in our js folder. This will encapsulate our integration with localStorage. First thing we’d like to do is the possibility to add given number of clients with given id.

Ok so given that we have this service (which we do not implemented for the time being), what we’d like to do is when user clicks the button add the proper number of tickets with proper id to localStorage, but how to obtain that information with user click event?

Let’s start with information about the amount of tickets. We can find the input that is next to our button. All events listener in their callback function get event argument for free. This object has target property pointing us to the element that caused listener to fire. Target is a DOM node, therefore it has previousSibling and nextSibling property. Those can be used to find elements on the same level of DOM tree. Since the previousSibling of our add button is our input, we can easily get the value of that one.

What about the id of the ticket? It’s a bit trickier, but we can get it using custom html attribute. We know the attributes already, for example class to link html node to css style. We can also use attributes to store some simple data specific to given row. In our case this will be the id of given ticket:

Changes are in lines 18,19 and 28. Ok so this gives us ability to actually call the ticketService to add clicked ticket to localStorage.

Ok we can now finally attend to writing our ticketService. Let us begin with the very basic functionality of adding tickets to localStorage. We’ll be using JSON.stringify which converts object to JSON string notation. This will help us actually see what are we holding in our localStorage. We can also encapsulate those functionalities in an object just to provide context, namespace and avoid naming collisions:

We can use the Developer Tools in Chrome to check out that we really added our data to localStorage:

There are few things missing still while we are adding the ticket:

  • If someone adds tickets with id that is already in localStorage, we’d like to increase the number of tickets instead of inserting new number
  • When someone chooses another ticket we don’t want to override current one, but append to array of tickets

Let’s handle first case now:

What we do here is finding the index in the array of our tickets and than we update the number of tickets in that array. JS has weak types so we must be sure that we actually add number instead of strings. That’s why we use parseInt to get the number of tickets. We also add parseInt while adding tickets in table.js:

ticketService.addTicket(ticketId, parseInt(ticketAmount));

Ok the last case is rather easy now. If we don’t have ticket of given id in the array, push new ticket into tickets array:

My Tickets

Ok so now we can move to building the next view. We’ll start with the header again. Let’s setup a basic markup for our site and the header:

https://gist.github.com/wojciech-bilicki/7a1a1776d579c6456a39fbbe0f3b41db

Ok we can already see that it looks rather nice. We are reusing the styles defined previously, but they will need some tweaking. Let’s start by tweaking the header text and the buttons:

We must get rid of the standard button background-image property to override it with our background-color. Also we set the margin-left:auto on our pay-button to push it from our header text h1. Ok now we can actually add our table and the header labels:

Pretty straight forward as we did this already in our main/home page. Worth mentioning is that we have now an Actions section in which we’ll store some additional buttons for each rows. Let’s now move to JS to actually render our rows. Let’s create ticketList.js file and add it alongside with DATA.js file:

Ok now let’s think how we’d like our data to get loaded:

  1. If there’s no data in localStorage we’d like to inform our user that he didn’t choose any tickets yet.
  2. We’ll be using data stored in localStorage to determine which tickets should be displayed in My Tickets table. We’ll use map and filter to achieve this.

Let’s start simply be writing in our ticketList.js:

Of course we don’t have getMyTickets function implemented yet, so let’s move to ticketService and write it:

Nothing hard here. We parse the stored tickets to turn them into JS array.

Then we map over them to create the objects that we’d like to display in our ticket list. We find the matching eventc/concert for given ticket by filtering out the DATA array and we add needed properties to the ticket. In the end we return array of our tickets from the function.

What I’d like to do now is write createRow function for our ticketList, but we already have one function which is called exactly like that and we can’t have two functions named the same because they would overlap each other. There fore we’ll add another namespace to help us solve this issue:

And we can fill our ticketList.js properly. It’ll look similiar to our table.js:

The only thing that is different and is worth mentioning is how we use map function. We pass this (one of the most confusing things in JS) so later on we can call this.createActionButtons. In this (ahahahahahaha stop) particular example this is pointing back to our ticketList object/namespace in which we are calling those functions. I encourage you to read more on this:

Ok what is left to do is apply some styling to make our tickets table look nice. We’ll be lazy and just add id="ticket-table" and reuse all our styling from previous table.

What’s left for us to do is style our pay and delete buttons. We’ll start by using classList property of DOM Node, to add proper classes for our buttons:
https://gist.github.com/wojciech-bilicki/bcbcfbb55f65111c6f85c157969141f9

And then we’ll actually write them inside table.css:

Ok, that’s all for now. In the next part we’ll handle the empty state case and plug event listeners to all our buttons in My Tickets List! Cheers!

--

--

--

We create new multi-screen looks and functionalities including software for TV platforms, hybrid TV services and other features such as VOD, EPG, Catch-up TV, social media sharing, and mobile device functions.

Recommended from Medium

Style Development Log 2 — Endpoint

Express js CRUD Tutorial using mongodb.(Show)

All about React’s shouldComponentUpdate()

Mocking APIs with JSON-Server

Thinking in a JavaScript Way

JavaScript Code Snippet

https://youtu.be/cBTk2wH0gnE

The difference between Angular’s Emulated Shadow DOM and native Shadow DOM

//platform.twitter.com/widgets.js from Twitter https://twitter.com/krushfitnessca

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Wojciech Bilicki

Wojciech Bilicki

Mobile & FrontEnd Developer at OKE Software Poland. Marvel fan. Can't live without music and books.

More from Medium

Introduction to React JS

The Enigmatic Modal

How to create custom search bar without JavaScript

Stylify vs Master UI/Styles: next-generation Tailwind-like CSS libraries