Logo

dev-resources.site

for different kinds of informations.

Creating Custom Component for NPS Feedback

Published at
4/30/2023
Categories
nps
component
vaadin
java
Author
samiekblad
Categories
4 categories in total
nps
open
component
open
vaadin
open
java
open
Author
10 person written this
samiekblad
open
Creating Custom Component for NPS Feedback

(Update 2023-05-24: The component is available as add-on at vaadin.com/directory/component/nps.)

We had a project that needed to implement the famous Net Promoter Score or NPS feedback. Quite boring, TBH. Just one of those relatively trivial UIs that you need to have. ("Does this spark joy?" It would most definitely be a clear "No." ).

Image description

However, there is a good reason for collecting user feedback like this, and we wanted to have it. I immediately thought that something like this would make sense as a separate component (mainly to avoid doing it again), so I decided to do that.

Here is the process I went through to build a reusable version. This blog is again for Vaadin, but the principles and the "build reusable components" -thinking makes sense on any web or IU framework.

Create a new add-on project

The first step in reusability: We want to create a separate add-on project. I used the Vaadin default addon-template from GitHub and created a new repository under my account. This project is a good starting point as it provides the boilerplate for packaging and distribution.

Clone the project locally

Next, clone the repository project locally in Visual Studio Code. I'm using the Dev Containers to run the project separately from others. There is a ready-made Java container to which I add Maven as a build system.

Vaadin Tip: Upgrade to V24
At the time of writing, the template project was still on older Vaadin 23. So a minor update is needed to pom.xml to make it compatible with the latest Vaadin version.

  • Vaadin version to 24.0.5
  • Java version to 17
  • Jetty version to 11.0.15

The UI Code

Like I said in the beginning, the UI is pretty simple, and the code to create that it's not too much:

public NPS() {
    Span title = new Span("On a scale of 1 to 10, how likely would you recommend this to your friend or colleague?");
    HorizontalLayout buttons = new HorizontalLayout();
    buttons.add(new Text("Not likely"));
    for (int i = 1; i <= 10; i++) {
        Button button = new Button(String.valueOf(i), e -> {
            int score = Integer.parseInt(e.getSource().getText());
            Notification.show("Changed to "+score);
        });  
        buttons.add(button);
    }
    buttons.add(new Text("Very likely"));
    this.add(title, buttons);    
}
Enter fullscreen mode Exit fullscreen mode

So the title and list of buttons in a horizontal layout. This is already usable as-is in your Vaadin application if you like to try it and use "copy-paste reusability".

Thinking about component reusability

When making reusable components, you want to consider a few things. The simplest way to document your component is to create a self-explanatory API.

  • Component UX. Do you need keyboard navigation? Mouse hover? Shortcuts? I suggest staying safe, combining existing components, and making your custom solutions sparingly.
  • Component configuration or DX. You want to be able to change the text and behaviour in various ways. The combinations can get tricky, so you want to have various scenarios well-tested.
  • UI integration. Is your component to be used alone (i.e., single-component app)? Will it be in a pop-up, or will it be part of that other UI? Data integration. Are your component emitting events? Does it support data binding? How much data to cache on the server, and how much can the browser handle?
  • Compatibility. What framework version are you using? What version do you expect your user to be using? How about the Java version? Remember, dependencies work much like Venn diagrams, so keep the list short.
  • Testing. How to automate tests. As said, the configuration of states might get tricky, so you want to be good here to make sure to find the edge cases.
  • Documentation. This part should be straightforward if you did good job with the API. Focus on describing the developer use cases and skip the "this method does X" style docs altogether.

It might take a round or two to get everything right, but best to start with a "real app" where you use it and write code that uses your component. Not aiming for full-blown TDD, this is what I made for testing:

final NPS nps = new NPS();
nps.setId("nps");  // This is for automated tests

// Button to start the feed process
add(new Button("Ask NPS", e -> {
    add(nps);
}));

// Get the score and remove component
nps.addValueChangeListener(e-> {
    Notification.show("Value changed from "+e.getOldValue()+" to "+e.getValue());
    add(new Paragraph("NPS: " +e.getOldValue()+" -> "+e.getValue()));
    remove(nps);
});

// Edit the the score
add(new Button("Edit score", e -> {
    nps.setValue((int)Math.ceil(Math.random()*10));
    add(nps);
}));
Enter fullscreen mode Exit fullscreen mode

This is implemented in AddonView.java, also used for automated tests. It is not the real thing, but simulating scenarios we had in the actual application.

The basic business logic

Since the NPS component is clearly a "field component" with a single editable value, we can reuse the framework functionality here. Extending CustomField gives us most of the functions we need for value input and events. We only need to implement the following:

protected Integer generateModelValue() {
    return this.score;
}

protected void setPresentationValue(Integer score) {
    this.score = score;        
}
Enter fullscreen mode Exit fullscreen mode

Our test case already revealed a problem with the read-only support. We want the value to be visible, yet the input is disabled. The requires two more methods to be implemented.

First, highlighting the selection. We use the "success color" to do that.

private void updateButtonSate() {
    this.buttons
        .getChildren()
        .filter(c -> c instanceof Button)
        .forEach(c -> {
            Button b = (Button)c;
            if (score != null && score.equals(Integer.parseInt(b.getText()))) {
                b.addClassName(LumoUtility.Background.SUCCESS_50);
                b.addClassName(LumoUtility.TextColor.SUCCESS_CONTRAST);
            } else {
                b.removeClassName(LumoUtility.Background.SUCCESS_50);
                b.removeClassName(LumoUtility.TextColor.SUCCESS_CONTRAST);
            };
        });
}
Enter fullscreen mode Exit fullscreen mode

Then another one to disable the buttons when we are in read-only state.

public void setReadOnly(boolean readOnly) {
    super.setReadOnly(readOnly);
    this.buttons
        .getChildren()
        .filter(c -> c instanceof Button)
        .forEach(c -> ((Button)c).setEnabled(!readOnly));
}
Enter fullscreen mode Exit fullscreen mode

Now we have the events, we got the component value bookkeeping.

Release, release

"Release early, release often", correct? This is all you need to create a simple application that collects NPS feedback from your users. As you see, it is only the web UI part, and you still need to store data and calculate the final score.

You may have your custom database, and we still need to package the component for distribution, but I'll show some simple options in the next blog post. In the meantime: checkout the component at github.com/samie/nps.

This reusability actually sparks joy, but now it is your turn: On a scale of 1 to 10, how likely would you recommend this NPS survey to your friend or colleague? Leave a comment below.

component Article's
30 articles in total
Favicon
Build a note app with JavaScript component.
Favicon
Key characteristic of Component-Based Architecture
Favicon
React Component Libraries: Overview of 19 Top Libs
Favicon
Styling Components in React 🧢
Favicon
Building a Seamless OTP Input Field in React: A Step-by-Step Guide
Favicon
MithrilJS component with state management
Favicon
[Off Topic] Nano introdução do framework Angular para Devs do back
Favicon
Comparing Vue Component Documentation tools
Favicon
Laravel 11 Custom Component File Structure
Favicon
Building Message Component in Vue3
Favicon
Aplicando paginação em um componente Select
Favicon
How much does it cost to repair an outdoor LED display?
Favicon
Global toast in Vue3
Favicon
Unveiling the Hidden Gem of React: The Power of Compound Components
Favicon
Controlled vs Uncontrolled Components in React
Favicon
React components -(Class component v/s function component)
Favicon
3 Ways to Create React Components with Bit
Favicon
Client or Server component ?
Favicon
Desenvolvimento de Componentes Assíncronos com Vue.js
Favicon
NAND Flash vs NOR Flash: Differences between them
Favicon
Link Component in React Router
Favicon
Guia de Components - para quem tem pressa!
Favicon
How to exchange data between Angular components
Favicon
Component Testing with Cypress and Reactjs
Favicon
React - Higher Order Components (HOC)
Favicon
How to Create Component Library Like Material UI or Mantine UI?
Favicon
Looking forward to adding Algolia's DOCSEARCH to Mantine DataTable
Favicon
Cypress Component Testing vs React Testing Library - the complete comparison
Favicon
Creating Custom Component for NPS Feedback
Favicon
Maximize your Angular code reusability using <NgTemplateOutlet>

Featured ones: