Skip to content

Commit

Permalink
[#2063] CSAT survey changes (#32781)
Browse files Browse the repository at this point in the history
- Add visual feedback to user choosing a value in a CSAT survey
- Disable survey after user gives feedback or if they ignore it and move on
  • Loading branch information
szarrougtw authored Nov 6, 2024
1 parent 334e01a commit 6263d4e
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 2 deletions.
62 changes: 61 additions & 1 deletion src/applications/virtual-agent/tests/utils/actions.unit.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import * as SessionStorageModule from '../../utils/sessionStorage';
import * as EventsModule from '../../utils/events';
import * as SubmitFormModule from '../../utils/submitForm';
import * as ProcessCSATModule from '../../utils/processCSAT';

describe('actions', () => {
let sandbox;
Expand Down Expand Up @@ -544,7 +545,7 @@ describe('actions', () => {
expect(submitFormStub.calledWithExactly(url, body)).to.be.true;
});

it('should call submitForm when activity is FormPostButton and component toggle is off', () => {
it('should not call submitForm when activity is FormPostButton and component toggle is off', () => {
const action = {
payload: {
activity: {
Expand Down Expand Up @@ -586,6 +587,65 @@ describe('actions', () => {

expect(submitFormStub.notCalled).to.be.true;
});

it('should call processCSAT when activity is CSATSurveyResponse and root bot toggle is on', () => {
const action = {
payload: {
activity: {
valueType: 'CSATSurveyResponse',
},
},
};

const processCSATStub = sandbox.stub(ProcessCSATModule, 'default');

processIncomingActivity({
action,
dispatch: sandbox.spy(),
isRootBotToggleOn: true,
})();

expect(processCSATStub.calledOnce).to.be.true;
});

it('should not call processCSAT when root bot toggle is off', () => {
const action = {
payload: {
activity: {
valueType: 'CSATSurveyResponse',
},
},
};

const processCSATStub = sandbox.stub(ProcessCSATModule, 'default');

processIncomingActivity({
action,
dispatch: sandbox.spy(),
isRootBotToggleOn: false,
})();

expect(processCSATStub.notCalled).to.be.true;
});

it('should not call processCSAT when activity is not CSATSurveyResponse', () => {
const action = {
payload: {
activity: {
valueType: 'other',
},
},
};

const processCSATStub = sandbox.stub(ProcessCSATModule, 'default');

processIncomingActivity({
action,
dispatch: sandbox.spy(),
})();

expect(processCSATStub.notCalled).to.be.true;
});
});

describe('processMicrophoneActivity', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import sinon from 'sinon';
import { expect } from 'chai';

import processCSAT, { BLUE_STAR } from '../../utils/processCSAT';

describe('processCSAT', () => {
let sandbox;
let columns;
let stars;

beforeEach(() => {
sandbox = sinon.sandbox.create();
columns = [{ style: { pointerEvents: 'not-none' } }];
stars = [
{
src: 'some-url',
},
{
src: 'some-url',
},
{
src: 'some-url',
},
{
src: 'some-url',
},
{
src: 'some-url',
},
];

sandbox.stub(document, 'querySelectorAll').returns([
{
querySelectorAll: sandbox.stub().callsFake(query => {
if (query === '#chatbot-csat-survey-columnset') {
return columns;
}
if (query === 'img') {
return stars;
}
return sandbox.stub();
}),
},
]);
});

afterEach(() => {
sandbox.restore();
});

it('Should disable the survey', () => {
processCSAT({ value: { response: '3' } });

expect(columns[0].style.pointerEvents).to.equal('none');
});
it('Should fill in the stars when a numerical rating is given', () => {
processCSAT({ value: { response: '3' } });

expect(stars[0].src).to.equal(BLUE_STAR);
expect(stars[1].src).to.equal(BLUE_STAR);
expect(stars[2].src).to.equal(BLUE_STAR);
expect(stars[3].src).to.equal('some-url');
expect(stars[4].src).to.equal('some-url');
});
it('Should not fill in the stars when a non-numerical rating is given', () => {
processCSAT({ value: { response: 'No response' } });

expect(stars[0].src).to.equal('some-url');
expect(stars[1].src).to.equal('some-url');
expect(stars[2].src).to.equal('some-url');
expect(stars[3].src).to.equal('some-url');
expect(stars[4].src).to.equal('some-url');
});
});
10 changes: 9 additions & 1 deletion src/applications/virtual-agent/utils/actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as _ from 'lodash';

import recordEvent from '@department-of-veterans-affairs/platform-monitoring/record-event';

import piiReplace from './piiReplace';
import {
getConversationIdKey,
Expand All @@ -14,6 +16,7 @@ import {
} from './sessionStorage';
import { sendWindowEventWithActionPayload } from './events';
import submitForm from './submitForm';
import processCSAT from './processCSAT';

const START_CONVERSATION = 'startConversation';
const EVENT = 'event';
Expand Down Expand Up @@ -59,7 +62,7 @@ function getEventName(action) {
function getEventValue(action, isRootBotToggleOn) {
// if toggle on then use this if off the just do action?.payload?.activity?.value
if (isRootBotToggleOn) {
return action?.payload?.activity?.value.value ?? '';
return action?.payload?.activity?.value?.value ?? '';
}

return action?.payload?.activity?.value ?? '';
Expand Down Expand Up @@ -148,6 +151,7 @@ export const processIncomingActivity = ({
const isMessageFromBot =
data.type === 'message' && data.text && data.from.role === 'bot';
const isFormPostButton = data.value?.type === 'FormPostButton';
const isCSATSurveyResponse = data.valueType === 'CSATSurveyResponse';

if (isAtBeginningOfConversation) {
setIsTrackingUtterances(true);
Expand All @@ -174,6 +178,10 @@ export const processIncomingActivity = ({
submitForm(data.value.url, data.value.body);
}

if (isRootBotToggleOn && isCSATSurveyResponse) {
processCSAT(data);
}

const trackingUtterances = getIsTrackingUtterances();
if (trackingUtterances) {
sendWindowEventWithActionPayload('webchat-message-activity', action);
Expand Down
19 changes: 19 additions & 0 deletions src/applications/virtual-agent/utils/processCSAT.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const BLUE_STAR =
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzYiIGhlaWdodD0iMzYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiA8Zz4KICA8dGl0bGU+TGF5ZXIgMTwvdGl0bGU+CiAgPHBhdGggc3Ryb2tlPSIjMDAzZTczIiBkPSJtMi44NjE5OCwxNC43MDc1bDExLjYzMTgxLDBsMy41OTQzMiwtMTEuMzU1NzVsMy41OTQzMiwxMS4zNTU3NWwxMS42MzE4MSwwbC05LjQxMDMyLDcuMDE4MTZsMy41OTQ1MSwxMS4zNTU3NWwtOS40MTAzMiwtNy4wMTgzNWwtOS40MTAzMiw3LjAxODM1bDMuNTk0NTEsLTExLjM1NTc1bC05LjQxMDMyLC03LjAxODE2eiIgaWQ9InN2Z18xIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiMwMDNlNzMiLz4KIDwvZz4KCjwvc3ZnPg==';

export default function processCSAT(data) {
const surveys = document.querySelectorAll('#chatbot-csat-survey');
const survey = surveys[surveys.length - 1];

const stars = survey.querySelectorAll('img');
const rating = Number(data.value.response);
if (!Number.isNaN(rating)) {
for (let i = 0; i < rating; i++) {
stars[i].src = BLUE_STAR;
}
}

// Make survey not clickable and appear disabled
const columns = survey.querySelectorAll('#chatbot-csat-survey-columnset');
columns[0].style.pointerEvents = 'none';
}

0 comments on commit 6263d4e

Please sign in to comment.