question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Implement main `Survey` component

See original GitHub issue

Feature Description

The Survey component is the main component for rendering a single survey. This component is responsible for managing state for answers and rendering the appropriate inner components implemented in #3379.


Do not alter or remove anything below. The following sections will be managed by moderators only.

Acceptance criteria

  • A Survey component should be created in assets/js/components/surveys/ as a functional, hook-based, hybrid-connected component
  • It should (for now) retrieve its array of question objects (see .question in the survey JSON) and array of completion objects (see .completion in the survey JSON) from the currentSurvey in the data store (added in #3355)
    • The component can assume that this exists if it is being rendered
  • The component should keep track of the answered state of each question
    For the initial implementation there will only be a single question, but it should be capable of handling multiple
  • The component should send a survey_shown event when rendered for the first time (e.g. on mount)
    • See sendSurveyEvent action added in #3355 for all events that are sent (i.e. not trackEvent)
  • The component should keep track of the current question (if any are left to be answered) and render the proper SurveyQuestion* component based on the question type (these components are implemented in other issues)
    • Note that the only question type that will be implemented at this point will be rating but the proper component-per-question-type should be considered here (for any other type null should be rendered for now)
    • An answerQuestion prop should be passed to the question which when called should set and submit the answer for the question by sending a question_answered event which should include event data with the QuestionAnswer object answer which will vary in shape based on the question type
    • All question_answered events for all question types will have the following top-level properties:
      • question_ordinal – from the current question
      • answer [object] – value varies by question type
    • For the question_answered event for a rating type question, the full event data should have the following shape:
      {
      	"question_ordinal": 1,
      	"answer": {
      		"answer": {
      			"answer_ordinal": 4
      		}
      	}
      }
      
      Using the above as an example, the following should be how answerQuestion would be called in the child for a rating component:
      answerQuestion( { answer: { answer_ordinal: 4 } } )
      
      The full event object is composed and sent to the server by the sendSurveyEvent action added in #3355
    • Only the rating question type needs to be handled as part of this issue
  • Once all questions have been answered, the first qualifying completion should be rendered using the SurveyCompletion component (implemented in #3379)
    • For qualifying logic, see the trigger_condition defined for each completion object. The first completion that matches all conditions should be used. There should always be one that matches, but in the event that one does not the first completion is to be used as a fallback.
    • The component should send a completion_shown event when its completion is rendered for the first time (e.g. on mount)
      • The event data should include the completion_ordinal of the completion
    • A ctaOnClick function prop should be passed which when called sends a follow_up_link_clicked survey event with the completion_ordinal of the current completion
      • This should also cause the survey to no longer be displayed
  • A dismissSurvey prop should be passed to both question and completion components which when invoked:
    • Sends a survey_closed event (no additional data)
    • Causes the survey to be no longer displayed
      Note this does not need to be persisted at this point as surveys are not cached/persisted either
  • Intermediate state should be stored in core/forms with a survey-specific form ID
  • Jest component tests should be added to cover the behavior of this component

Implementation Brief

(Form ID should come from the Session object (session.session_id) returned by the /survey/trigger/ request/selector.)

  • Create assets/js/components/surveys/Survey.js to house the new Survey component.
    • It should take a questions prop—an array of questions, see the ACs for the format
    • It should take a completions prop—an array of completed questions, see the ACs for the format
    • It should accept an id prop used to get/store the survey’s data in the core/forms datastore (see below). A key like user-survey-form-${id} should suffice. The ID supplied should probably be the session.session_id value, but could also be survey_id.
    • Keep track of each question’s state in the core/forms data store using the id prop.
    • Keep track of which question in the array of questions the user is “on”/eg. is active in the core/forms data store using the ID.
    • (The ACs mention keeping track of both of these internally, but using the core/forms store instead should be fine and more robust.
    • Using sendSurveyEvent, send a 'survey_shown' event when it is first mounted, eg sendSurveyEvent( 'survey_shown' ) Every time the survey is mounted this should be shown, not just the first time it’s shown.
    • Render each survey question using the SurveyQuestion component and passing the answerQuestion prop.
      • The answerQuestion prop should be a function that dispatches an action that sends the question_answered event.
      • The answerQuestion prop function should accept an answer object that can be filled-in according to the question type and answer selected by the user. Each survey answer has a different shape, see: https://docs.google.com/document/d/1jqUg4blDUFu6BCyHQVgzlz9smxK0UGrzsOpPojo2EJM/edit?ts=609c08e3#heading=h.qxbkeiyvcd7o. The question_ordinal prop can be pre-set/handled internally because the question we’re answering is always the same despite the answer. So the answerQuestion function should allow each answer to set the answer object so that an action like this is dispatched:
            // Example action for a **`rating`** type question.
            sendSurveyEvent( 'question_answered', {
                "question_ordinal": 1, // Set by the SurveyQuestion component.
                "answer": {
                    // The following would be customised by each answer and passed to the `answerQuestion` function prop.
                    "answer": {
                        "answer_ordinal": 4 // If this is the 4th answer.
                    }
                }
            } );
        
        Using the above as an example, the following should be how answerQuestion would be called in the child for a rating component: answerQuestion( { answer: { answer_ordinal: 4 } } )
  • Once an entire survey is marked as answered, render SurveyCompletion instead of any question. (See ACs for implementation, as they’re quite thorough.). Note: We know a survey is completed if the question answered is the same index as the total number of questions for this survey.
    • Pass a ctaOnClick event handler to the SurveyCompletion if there is a follow-up link to click in the survey completion. If it is clicked, use sendSurveyEvent( 'follow_up_link_clicked' ) to send an event letting the proxy know the user has clicked the “completion follow-up link”. After that event, also send sendSurveyEvent( 'survey_closed' ) so the proxy knows the survey shouldn’t be shown.
    • If there is no follow-up link, then sending the completion_shown event will be sufficient to tell the proxy not to show the survey anymore.
  • Pass a dismissSurvey prop (function) to both “question” and “completed” components to allow the user to dismiss the survey. This would send a survey_closed event. This tells the proxy not to show the survey.
  • Render null from the Survey component if the survey is “fully-completed” or dismissed and shoud not be shown.

Test Coverage

  • Add Jest/unit tests to ensure that each type of question we support (right now only “rating”) is rendered appropriately.
  • Add tests to ensure if an unknown question type is encountered we render null.
  • Add tests to ensure the proper events/action (via sendSurveyEvent) are dispatched based on user interaction/component loading.
  • Add tests to ensure a fully-completed survey (“question-complete”) shows the SurveyCompletion component instead of any questions.
  • Add tests to ensure null is output if the survey is “fully-completed” or dismissed.

Visual Regression Changes

  • N/A.

QA Brief

  • Visit the CurrentSurvey components in Storybook, disabling the fetchMock.post calls in the stories. Then test each story to ensure it is behaving as-expected and dispatching the proper survey events as outlined in the ACs.

Changelog entry

  • Add React components for rendering and managing user surveys.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:25 (9 by maintainers)

github_iconTop GitHub Comments

2reactions
tofumattcommented, Jun 3, 2021

@felixarntz That works for me; I’ll adjust the component names in the PR for this issue 👍🏻

2reactions
aaemnnosttvcommented, Jun 3, 2021

@felixarntz after discussing with @tofumatt, we decided to use currentSurvey* selectors inside of Survey rather than taking the questions and completions as props for now. The main reason being that the sendSurveyEvent is still scoped to the current survey anyways and it doesn’t make much sense to decouple this right now (e.g. also passing as a prop). It still makes sense to keep this separate from CurrentSurvey since that handles the portal and conditional logic, but for now they will both be current-survey specific. @tofumatt felt this would be easier to implement for now as well so I’ll go ahead and amend the ACs with this minor change and we can revisit decoupling these more in the future as we iterate on survey infrastructure as a whole.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Parts of a Survey: A Simple Guide - Qualaroo
At most, a survey consists of three parts: the introduction, the questions themselves, and the conclusion. We've outlined each one below. Introduction (Optional ......
Read more >
4 Basic Components of a Market Research Survey
In this blog post, our market research company discusses 4 basic components of a market research survey including an introduction, ...
Read more >
Add Survey Related Lists and Lightning Components to ...
Customize the record detail pages for objects commonly used with surveys so that users can easily find invitations and responses, and work efficiently...
Read more >
Survey Basic Overview - Qualtrics
Block Basics. All survey questions are created, edited, and stored inside blocks. Every survey includes at least one block initially called the Default ......
Read more >
Survey Components - Outcome Measures (OM)
The Integrated Postsecondary Education Data System (IPEDS), established as the core postsecondary education data collection program for NCES, is a system of ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found