Implement main `Survey` component
See original GitHub issueFeature 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
Surveycomponent should be created inassets/js/components/surveys/as a functional, hook-based, hybrid-connected component - It should (for now) retrieve its array of
questionobjects (see.questionin the survey JSON) and array ofcompletionobjects (see.completionin the survey JSON) from thecurrentSurveyin 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_shownevent when rendered for the first time (e.g. on mount)- See
sendSurveyEventaction added in #3355 for all events that are sent (i.e. nottrackEvent)
- See
- 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
ratingbut the proper component-per-question-type should be considered here (for any other typenullshould be rendered for now) - An
answerQuestionprop should be passed to the question which when called should set and submit the answer for the question by sending aquestion_answeredevent which should include event data with theQuestionAnswerobject answer which will vary in shape based on the question type - All
question_answeredevents for all question types will have the following top-level properties:question_ordinal– from the current questionanswer[object] – value varies by question type
- For the
question_answeredevent for aratingtype question, the full event data should have the following shape:
Using the above as an example, the following should be how{ "question_ordinal": 1, "answer": { "answer": { "answer_ordinal": 4 } } }answerQuestionwould be called in the child for a rating component:
The fullanswerQuestion( { answer: { answer_ordinal: 4 } } )eventobject is composed and sent to the server by thesendSurveyEventaction added in #3355 - Only the
ratingquestion type needs to be handled as part of this issue
- Note that the only question type that will be implemented at this point will be
- Once all questions have been answered, the first qualifying completion should be rendered using the
SurveyCompletioncomponent (implemented in #3379)- For qualifying logic, see the
trigger_conditiondefined 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_shownevent when its completion is rendered for the first time (e.g. on mount)- The event data should include the
completion_ordinalof the completion
- The event data should include the
- A
ctaOnClickfunction prop should be passed which when called sends afollow_up_link_clickedsurvey event with thecompletion_ordinalof the current completion- This should also cause the survey to no longer be displayed
- For qualifying logic, see the
- A
dismissSurveyprop should be passed to both question and completion components which when invoked:- Sends a
survey_closedevent (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
- Sends a
- Intermediate state should be stored in
core/formswith 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.jsto house the newSurveycomponent.- It should take a
questionsprop—an array of questions, see the ACs for the format - It should take a
completionsprop—an array of completed questions, see the ACs for the format - It should accept an
idprop used to get/store the survey’s data in thecore/formsdatastore (see below). A key likeuser-survey-form-${id}should suffice. The ID supplied should probably be thesession.session_idvalue, but could also besurvey_id. - Keep track of each question’s state in the
core/formsdata store using theidprop. - Keep track of which question in the array of questions the user is “on”/eg. is active in the
core/formsdata store using the ID. - (The ACs mention keeping track of both of these internally, but using the
core/formsstore instead should be fine and more robust. - Using
sendSurveyEvent, send a'survey_shown'event when it is first mounted, egsendSurveyEvent( '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
SurveyQuestioncomponent and passing theanswerQuestionprop.- The
answerQuestionprop should be a function that dispatches an action that sends thequestion_answeredevent. - The
answerQuestionprop function should accept ananswerobject 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. Thequestion_ordinalprop can be pre-set/handled internally because the question we’re answering is always the same despite the answer. So theanswerQuestionfunction should allow each answer to set theanswerobject so that an action like this is dispatched:
Using the above as an example, the following should be how// 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. } } } );answerQuestionwould be called in the child for a rating component:answerQuestion( { answer: { answer_ordinal: 4 } } )
- The
- It should take a
- Once an entire survey is marked as answered, render
SurveyCompletioninstead 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
ctaOnClickevent handler to theSurveyCompletionif there is a follow-up link to click in the survey completion. If it is clicked, usesendSurveyEvent( '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 sendsendSurveyEvent( 'survey_closed' )so the proxy knows the survey shouldn’t be shown. - If there is no follow-up link, then sending the
completion_shownevent will be sufficient to tell the proxy not to show the survey anymore.
- Pass a
- Pass a
dismissSurveyprop (function) to both “question” and “completed” components to allow the user to dismiss the survey. This would send asurvey_closedevent. This tells the proxy not to show the survey. - Render
nullfrom theSurveycomponent 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
SurveyCompletioncomponent instead of any questions. - Add tests to ensure
nullis output if the survey is “fully-completed” or dismissed.
Visual Regression Changes
- N/A.
QA Brief
- Visit the
CurrentSurveycomponents in Storybook, disabling thefetchMock.postcalls 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:
- Created 2 years ago
- Comments:25 (9 by maintainers)
Top 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 >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

@felixarntz That works for me; I’ll adjust the component names in the PR for this issue 👍🏻
@felixarntz after discussing with @tofumatt, we decided to use
currentSurvey*selectors inside ofSurveyrather than taking the questions and completions as props for now. The main reason being that thesendSurveyEventis 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 fromCurrentSurveysince 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.