build survey 1-10 control in reactjs using radio button groups

 I'm a skier and skied Palisades Tahoe recently. After which they sent me a survey with an interesting survey control as follows:


It's a pretty cool ux. We can see it has a: 

  • title
  • a range 0 - 9 laid out as adjacent squares in a row
  • there's a prompt on the left and right of the control to explain lower and upper values
  • hover state when hover over a choice box which shows a blue border
  • when user makes a choice then it shows as a blue circle (same color as borer) and number color is changed (see choice 4 in the screenshot)
This is cool, so I built this myself.

Here's how I organized the html
  • overall container containing
    • a header, h3 perhaps
    • survey body which contains
      • range start and end prompts
      • list of survey choices which contains for each choice 0 - 9
        • a survey choice box which contains
          • survey choice
css notes:
  • use border-box so the container does not grow when change border on hover
  • use flexbox in a number of places to:
    • layout the row of survey choices and start and end prompts
    • centering vertically and horizontally each number
  • define a "selected" state to style when user clicks a choice

As an interim here's a screenshot of my work in progress


Each survey choice is a div and I added a click handler to a div. But there are some problems with using divs for survey choices:

  • semantics: a div is used for layout has no semantic meaning. these survey number choices are really buttons/selections which are mutually exclusive...basically radio buttons (or a select dropdown perhaps)
  • accessibility: a div adds nothing for accessibility, we'd have to add:
    • decorate with aria role such as role="radio" 
    • add aria-checked="false" or aria-checked="true"
    • add tabindex="0" (tabIndex={0} for react) to be able to tab to the div  choices
      • interestingly the original survey does not support tabbing through every choice (though hitting spacebar to make a choice does work)
    • add support for keyboard clicking (as well as mouse click)
  • events:
Divs are not the right solution. 
What is the right fit is radio buttons. Basically the behaviors of radios but the style of boxes. Which means clickable but hidden radios.

Which is how the original is implemented. 
The key is the input radio is hidden (opacity: 0) but still have to occupy the full width and height of the box its in. For the radio, the original uses position absolute starting at top left and 100% width and height (clever). I'm using the same. This is what it looks like with opacity of 1 (the big circles are the radios)
In the html I give the radios the same name value so they're grouped together. I add the same onChange handler for each radio and also set the radio value to 0/1/2 etc. So when user clicks in the box, the hidden radio onChange handler is called with event and event.target.value is the value number of the choice.

To build this as a reusable react survey control I identified some react components needed
  • SurveyAnswerContainer - a generic container for all kinds of survey questions; it includes an optional title followed by one of the following components:
    • SurveyAnswerRankExclusiveChoices which contains
    • SurveyAnswerRankExclusiveChoice 
      • renders one radio box (would be 10 of these for 0-9). But could also be 1-5 or 1-3 or 0-12
    • SurveyAnswerTextArea
      • answer question as a textarea
    • SurveyAnswerYesOrNo
      • a binary choice, yes or not, choose from 2 radios 
    • SurveyQuestionsListCompact
      • a list of questions in compact form with prompt on left and SurveyAnswerRankExclusiveChoice to the right
I built the components highlighted in pink.

I am using composition over configuration so SurveyAnswerContainer is a container for kinds of SurveyAnswers

I added a few features to 
SurveyAnswerTextArea including a simple character count as well as auto resize when type text. I got the approach for autosize from this mdn article


Naming is important. Is this a poll or survey? I think it's "ranking" zero to 9. I think it's an Exclusive Choice, not multiple choice because the latter could be more than 1 selection. I started using "poll" e.g. poll-container but I refactored to use "survey" since that makes more semantic sense to me. There's the concept of questions and answers. All this is rolled into my component naming.


Comments

Popular posts from this blog

deep dive into Material UI TextField built by mui

angular js protractor e2e cheatsheet

react-router v6.4+ loaders, actions, forms and more