Skip to content

Commit

Permalink
Merge pull request #1461 from glific/enhamncement/template-variables
Browse files Browse the repository at this point in the history
added contact variables suggestion
  • Loading branch information
mdshamoon authored Jun 21, 2021
2 parents f283c3e + bc10dd2 commit 7ff20e4
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 26 deletions.
6 changes: 6 additions & 0 deletions src/components/UI/Form/AutoComplete/AutoComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export interface AutocompleteProps {
additionalOptionLabel?: string;
field: any;
icon?: any;
freeSolo?: boolean;
autoSelect?: boolean;
form: { dirty?: any; touched?: any; errors?: any; setFieldValue: any };
textFieldProps?: any;
helperText?: any;
Expand Down Expand Up @@ -49,6 +51,8 @@ export const AutoComplete: React.SFC<AutocompleteProps> = ({
questionText,
multiple = true,
disabled = false,
freeSolo = false,
autoSelect = false,
getOptions,
asyncValues,
roleSelection,
Expand Down Expand Up @@ -158,6 +162,8 @@ export const AutoComplete: React.SFC<AutocompleteProps> = ({
multiple={multiple}
data-testid="autocomplete-element"
options={optionValue}
freeSolo={freeSolo}
autoSelect={autoSelect}
disableClearable={disableClearable}
getOptionLabel={(option: any) => (option[optionLabel] ? option[optionLabel] : '')}
getOptionDisabled={getOptionDisabled}
Expand Down
71 changes: 61 additions & 10 deletions src/containers/Chat/ChatMessages/AddVariables/AddVariables.test.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,80 @@
import { fireEvent, render, waitFor } from '@testing-library/react';
import { act, fireEvent, render, screen, within } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import axios from 'axios';

import { AddVariables } from './AddVariables';
import { FLOW_EDITOR_API } from '../../../../config';
import { responseData, responseData1 } from '../../../../mocks/AddVariables';

jest.mock('axios', () => {
return {
get: jest.fn(),
};
});

const setVariableMock = jest.fn();
const mocks = [FLOW_EDITOR_API];

const defaultProps = {
setVariable: setVariableMock,
handleCancel: jest.fn(),
bodyText: 'Your OTP for {{1}} is {{2}}. This is valid for {{3}}.',
bodyText: 'Hi {{1}}, Please find the attached bill.',
updateEditorState: jest.fn(),
variableParams: jest.fn(),
variableParam: ['this', '4563', '5 minutes'],
};

test('it should render', () => {
const { getByTestId } = render(<AddVariables {...defaultProps} />);
const wrapper = (
<MockedProvider>
<MemoryRouter>
<AddVariables {...defaultProps} mocks={mocks} />
</MemoryRouter>
</MockedProvider>
);

const whenStable = async () => {
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 0));
});
};

const axiosApiCall = async () => {
axios.get.mockImplementationOnce(() => Promise.resolve(responseData1));

axios.get.mockImplementationOnce(() => Promise.resolve(responseData));
};

test('it should render variable options and save the form', async () => {
axiosApiCall();
const { container, getByTestId, getByText } = render(wrapper);

const getSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce(responseData);
await whenStable();

expect(getByTestId('variablesDialog')).toBeInTheDocument();
});
const autocomplete = screen.getByTestId('AutocompleteInput');
const input = within(autocomplete).getByRole('textbox');

test('save form', async () => {
const { getByText } = render(<AddVariables {...defaultProps} />);
autocomplete.focus();
// assign value to input field
fireEvent.change(input, { target: { value: '@contact.name' } });
// navigate to the first item in the autocomplete box
fireEvent.keyDown(autocomplete, { key: 'ArrowDown' });
// select the first item
fireEvent.keyDown(autocomplete, { key: 'Enter' });

fireEvent.click(getByText('Done'));
});

await waitFor(() => {
expect(setVariableMock).toHaveBeenCalled();
});
test('cancel button clicked', async () => {
axiosApiCall();
const { container, getByTestId, getByText } = render(wrapper);

const getSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce(responseData);
await whenStable();

expect(getByTestId('variablesDialog')).toBeInTheDocument();
fireEvent.click(getByText('Cancel'));
});
77 changes: 62 additions & 15 deletions src/containers/Chat/ChatMessages/AddVariables/AddVariables.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React, { useEffect, useState } from 'react';
import { Formik, Form, Field } from 'formik';
import * as Yup from 'yup';
import { useTranslation } from 'react-i18next';
import axios from 'axios';

import { FLOW_EDITOR_API } from '../../../../config/index';
import { getAuthSession } from '../../../../services/AuthService';

import { DialogBox } from '../../../../components/UI/DialogBox/DialogBox';
import { Input } from '../../../../components/UI/Form/Input/Input';
import { pattern } from '../../../../common/constants';
import { AutoComplete } from '../../../../components/UI/Form/AutoComplete/AutoComplete';

export interface AddVariablesPropTypes {
setVariable: any;
Expand All @@ -25,29 +28,70 @@ export const AddVariables: React.FC<AddVariablesPropTypes> = ({
variableParam,
}: AddVariablesPropTypes) => {
const [formFieldItems, setFormFieldItems] = useState<any>([]);
const [validation, setValidation] = useState<any>({});
const [initialValues, setInitialValues] = useState<any>({});
const { t } = useTranslation();
const [contactVariables, setContactVariables] = useState<any>([]);

let contacts: any = [];
let fields: any = [];
let selectedVariables: any = [];

const glificBase = FLOW_EDITOR_API;
const contactFieldsprefix = '@contact.fields.';
const contactVariablesprefix = '@contact.';
const headers = { Authorization: getAuthSession('access_token') };

useEffect(() => {
const getVariableOptions = async () => {
// get fields keys
const fieldsData = await axios.get(`${glificBase}fields`, {
headers,
});

fields = fieldsData.data.results.map((i: any) => ({
key: contactFieldsprefix.concat(i.key),
}));

// get contact keys
const contactData = await axios.get(`${glificBase}completion`, {
headers,
});
const properties = contactData.data.types[5];
contacts = properties.properties
.map((i: any) => ({ key: contactVariablesprefix.concat(i.key) }))
.concat(fields)
.slice(1);
setContactVariables(contacts);
};

getVariableOptions();
}, []);

useEffect(() => {
const isVariable = bodyText.match(pattern);
if (isVariable) {
const formFieldItem: any = [];
const validationObj: any = {};

for (let index = 1; index <= isVariable.length; index += 1) {
formFieldItem.push({
component: Input,
component: AutoComplete,
name: `variable${index}`,
type: 'text',
placeholder: `Variable ${index}`,
options: contactVariables,
optionLabel: 'key',
multiple: false,
freeSolo: true,
autoSelect: true,
textFieldProps: {
variant: 'outlined',
label: `Variable ${index}`,
},
});

validationObj[`variable${index}`] = Yup.string().required(`Variable ${index} is required.`);
}

setFormFieldItems(formFieldItem);
setValidation(validationObj);
}
}, [bodyText]);
}, [bodyText, contactVariables]);

useEffect(() => {
const initialValue: any = {};
Expand All @@ -59,20 +103,23 @@ export const AddVariables: React.FC<AddVariablesPropTypes> = ({

const updateText = (variable: any) => {
let body = bodyText;

Object.keys(variable).forEach((element: string, index: number) => {
body = body.replace(`{{${index + 1}}}`, variable[element]);
body = body.replace(
`{{${index + 1}}}`,
variable[element].key ? variable[element].key : variable[element]
);
});
updateEditorState(body);
variableParams(Object.values(variable));
};
selectedVariables = Object.values(variable).map((item: any) => (item.key ? item.key : item));

const validationSchema = Yup.object().shape(validation);
variableParams(selectedVariables);
};

const form = (
<Formik
enableReinitialize
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(itemData) => {
updateText(itemData);
setVariable(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export const MessagesWithLinks: React.FC<MessagesWithLinksProps> = (

// first element is the url, if the url is sent
const linkMessage = regexForLink.exec(message);

if (linkMessage) {
linkPreview = (
<div className={styles.LinkPreview}>
Expand Down
37 changes: 37 additions & 0 deletions src/mocks/AddVariables.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export const responseData1 = {
data: {
results: [
{ key: 'settings', label: 'Settings', name: 'Settings', value_type: 'text' },
{ key: 'dob', label: 'Date of Birth', name: 'Date of Birth', value_type: 'text' },
],
},
};
export const responseData = {
data: {
types: [
{ key_source: 'flow', name: 'flow', property_template: Array(3) },
{ key_source: 'fields', name: 'fields' },
{ key_source: 'results', name: 'results' },
{ name: 'urns', properties: Array(1) },
{ name: 'channel', properties: Array(4) },
{
name: 'contact',
properties: [
{ help: 'the name or URN', key: '__default__', type: 'text' },
{ help: 'the numeric ID of the contact', key: 'id', type: 'text' },
{ help: 'the name of the contact', key: 'name', type: 'text' },
{
help: 'the language of the contact as 3-letter ISO code',
key: 'language',
type: 'text',
},
{
help: 'the gender of the contact like male/female/others',
key: 'gender',
type: 'text',
},
],
},
],
},
};

0 comments on commit 7ff20e4

Please sign in to comment.