Skip to content

UI Tutorial

Juan Snyman edited this page May 3, 2020 · 1 revision

The goal of this tutorial is to teach you how to implement UI components according to the conventions of the Cloudnine project. As an example, you will be guided step by step through the process that was taken to implement the mood rating buttons.

First, you'll want to take a look at the UI mockups in issue #11 and note the visual appearance of the component we are implementing. The mood rating component consists of three buttons: a happy face, neutral face and sad face. The mockup is just a static image and so it does not define the hover states, what should happen when the buttons are pressed, or any other behaviour. We will figure these out as we go on.

CSS directory structure

Most of our work will take place in the app/assets/stylesheets directory. You'll notice that this folder contains a number of subfolders:

app/assets/stylesheets
β”œβ”€β”€ 1_settings
β”œβ”€β”€ 2_tools
β”œβ”€β”€ 3_generic
β”œβ”€β”€ 4_elements
β”œβ”€β”€ 5_components
└── application.scss

1_settings contains variables that are used across SCSS files (such as the colour variables in _colours.scss).
2_tools contains functions and mixins that are used across SCSS files.
3_generic mainly contains just the reset/normalize stylesheet.
4_elements contains the default styles for various HTML elements (e.g. the default font size for paragraph tags).
5_components contains the styles for user interface components.

All of the files in these directories are imported in the application.scss file.

Because we are implementing a user interface component, we will create a new file in the app/assets/stylesheets/5_components directory called _mood_rater.scss (the SCSS files we create should always begin with an underscore). In the application.scss file, add the following line at the end:

@import '5_components/mood_rater';

HTML

Next, we will want to create a partial to contain our mood rater HTML. We will do so in the app/views/playlists directory and not the app/view/mood_ratings directory because our mood rater will only appear in the playlist view. The mood_ratings view is only for displaying past mood ratings to the user, not for recording new ratings. Name the new file _mood_rater.html.erb.

Now we will define our basic HTML structure. Remember that our mood rating component consists of three buttons. In the app/views/playlists/_mood_rater.html.erb file add the following:

<div class="mood-rater" data-js-mood-rater>
  <%= link_to mood_ratings_path(value: 1), method: :post, class: "mood-rater_happy", data_js_happy: "" do %>
    <!-- image will go here -->
  <% end %>
  <%= link_to mood_ratings_path(value: 0.5), method: :post, class: "mood-rater_neutral", data_js_neutral: "" do %>
    <!-- image will go here -->
  <% end %>
  <%= link_to mood_ratings_path(value: 0), method: :post, class: "mood-rater_sad", data_js_sad: "" do %>
    <!-- image will go here -->
  <% end %>
</div>

This creates three links that direct the user to the update action of the mood_ratings controller and wraps them in a div. The div has a class of mood-rater so that we can apply styles to it. Similarly, the three buttons that form part of the mood rater have classes applied so that we can style them individually. The parts of a component should always have a class name that follows the format COMPONENT-NAME_PART-NAME.

The data-js-___ attributes exist to provide hooks for future JavaScript code.

You'll notice that the happy, neutral and sad face images haven't been added. To add them, we will copy the SVG code from the mood_happy.svg, mood_neutral.svg and mood_sad.svg files (these are in the app/assets/images directory) and paste them in:

<div class="mood-rater" data-js-mood-rater>
  <%= link_to mood_ratings_path(value: 1), method: :post, class: "mood-rater_happy", data_js_happy: "" do %>
    <svg width="44" height="44" xmlns="http://www.w3.org/2000/svg">
      <g transform="translate(2 2)">
        <circle stroke-width="3" cx="20" cy="20" r="20" fill="none"/>
        <path d="M12.549 24.314s7.451 6.029 14.902 0" stroke-width="3" stroke-linecap="round" fill="none"/>
        <circle cx="14.51" cy="16.078" r="2.745" stroke="none"/>
        <circle cx="26.275" cy="16.078" r="2.745" stroke="none"/>
      </g>
    </svg>
  <% end %>
  <%= link_to mood_ratings_path(value: 0.5), method: :post, class: "mood-rater_neutral", data_js_neutral: "" do %>
    <svg width="44" height="44" xmlns="http://www.w3.org/2000/svg">
      <g transform="translate(2 2)">
        <circle stroke-width="3" cx="20" cy="20" r="20" fill="none"/>
        <path d="M12.549 25.882h14.902" stroke-width="3" stroke-linecap="round" fill="none"/>
        <circle cx="14.51" cy="16.078" r="2.745" stroke="none"/>
        <circle cx="26.275" cy="16.078" r="2.745" stroke="none"/>
      </g>
    </svg>
  <% end %>
  <%= link_to mood_ratings_path(value: 0), method: :post, class: "mood-rater_sad", data_js_sad: "" do %>
    <svg width="44" height="44" xmlns="http://www.w3.org/2000/svg">
      <g transform="translate(2 2)">
        <circle stroke-width="3" cx="20" cy="20" r="20" fill="none"/>
        <path d="M27.451 26.993s-7.451-6.029-14.902 0" stroke-width="3" stroke-linecap="round" fill="none"/>
        <circle cx="14.51" cy="16.078" r="2.745" stroke="none"/>
        <circle cx="26.275" cy="16.078" r="2.745" stroke="none"/>
      </g>
    </svg>
  <% end %>
</div>

Displaying the partial

We have added the HTML our mood rating component but it is currently not being displayed anywhere. Let's change this. The user's playlist is rendered using the app/views/playlists/show_mine.html.erb template. In that file, add the following line of code:

<%= render 'mood_rater' %>

Defining our styles

Even though our mood rater is now showing up on the playlist page, it isn't very pretty or very usable. We will add some CSS to change this. In the app/assets/stylesheets/5_components/_mood_rater.scss file created earlier, add the following code:

.mood-rater {
  a:link, a:visited {
    display: inline-block; // allows transform property to be used below
    text-decoration: none; // remove underline that appears by default for links
    fill: black; // make the eyes of the face black
    stroke: black; // make the outline of the face and the mouth black
  }

  a:hover {
    transform: translateY(-8px); // moves face up by 8px
  }
}

The result is that the faces will jump up when the user hovers their mouse over them.

To be continued

The basic styles and functionality for this component have now been added. In the future, we will add some JavaScript to control what happens when the buttons are clicked.

Clone this wiki locally