Gutenberg Quiz

16 May 2022 TechnologyWordpress

For a customer we needed to have a quiz – multiple questions with multiple answers each. So I developed a Gutenberg block and its JavaScript (clean JS) functionality. The block and its JS can be seen on the link bellow

Table of contents.

How does it function

Structure

Gutenberg Block

JavaScript Functionality

How does it function

The quiz shows the different questions one after another. When we click an answer – it adds a certain percentage (based on 100/count(questions)), fires a dataLayer event (which can be configured in google analytics) and stores a cookie that contains the current score, and the active question (after we click an answers – it considers the next question as active). At the end, based on the score – it redirects to one of three pages (configured in quiz.js).
Each question has its own cover image, and each answers can have its own image, which will replace the question cover on click.

The whole gutenberg component, as well as its corresponding JS can be found on my github page.

Structure

The Quiz consists of three parts – quiz.js – the parent Quiz element, quiz-question.js – a child component, both of which need to reside in the Gutenberg repo (currently in {theme}/blocks/src/{directory_name}) and the quiz.js, which contain the JS logic that makes it work.

Gutenberg Block

The quiz.js is a standard container, configured to have quiz-question as its only available children. On the other hand quiz-question.js has few things worth mentioning.

The editor is created in a different way than the usual

class blockEdit extends Component {}

The reason for this is so we can use state inside it. I decided to use React state in order to be able to save the different changes on the current answer in react state and use the state when we create the answer.

  constructor(props) {
    super(props);

    this.state = {
      answer: {
        mediaId: null,
        mediaUrl: null,
        text: null
      }
    }
  }

The answers is an array of objects – this way we can create a repeater of sorts and add infinite amount of answers.

  answers: {
    type: 'array',
    default: []
  },

each of which the data of the answer – mediaId, mediaUrl and text. The order of display of the answers is based on their index in the array, and this can be configured with the moveUp and moveDown methods in the editor.

When adding a new question – we use state to store its different settings.
MediaUpload:

  onSelect={(media) => {
    let tempAnswer = {...this.state.answer};
    tempAnswer.mediaId = media.id;
    tempAnswer.mediaUrl = media.url;
    this.setState({answer: tempAnswer});
  }}

PlainText (you can use other text editor components)

    onChange={(text) => {
      let tempAnswer = {...this.state.answer};
      tempAnswer.text = text;
      this.setState({answer: tempAnswer});
    }}

Then we use state to determine if answer text is added (required) so it either shows or not the button and if it is – uses addAnswer method to add the answers from state.

  <Button
    disabled={!Boolean(this.state.answer.text)}
    className='button small success'
    onClick={() => {
      this.addAnswer(answers);
    }}
  >
    Add Answer
  </Button>

When we render the editor – we first loop through the answers so they can be visualized and we can build the array of elements , which we use for the SelectControl component, through which we choose the correct answer. The value stored is the answer’s index.

The save method of the component is simple and straightforward.

JavaScript functionality

The JS functionality has simple functions for cookie management.
The processClick is the core of the JS – it triggers on an answer click – it checks if the answer is correct (correct answer is set in a data-attribute), sets the answer image if one is set, fires the dataLayer event (which can be used for google analytics), marks the correct answer (through styling of classes) and sets the text for the “next” button (as configured per answer’s correct/incorrect response). It also manages cookies. The next button fires the setActiveQuestion method.
The setActiveQuestion sets the relevant classes for the currently active question, resets the quiz if the button is pressed or finished it, upon completion.
So JS loops through the questions and sets the proper event listeners.