@kindly/react-chat 2.40.4 → 2.41.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kindly/react-chat",
3
- "version": "2.40.4",
3
+ "version": "2.41.1",
4
4
  "description": "Kindly Chat react component",
5
5
  "repository": "https://github.com/kindly-ai/kindly-chat/tree/main/packages/react-chat",
6
6
  "main": "dist/index.js",
@@ -8,14 +8,15 @@
8
8
  "scripts": {
9
9
  "start": "webpack-dev-server",
10
10
  "start:linked": "WRITETODISK=1 BABEL_ENV=production webpack-dev-server --config=webpack.prod.js",
11
- "start:prod": "webpack-dev-server --env=production",
12
- "build": "webpack -p --env=production --config=webpack.prod.js",
13
- "build:dev": "webpack -p --env=development --config=webpack.prod.js",
14
- "build:staging": "webpack -p --env=staging --config=webpack.prod.js",
15
- "build:local": "webpack -p",
11
+ "start:dev": "webpack-dev-server --env envConfig=development",
12
+ "start:prod": "webpack-dev-server --env envConfig=production",
13
+ "build": "webpack --env envConfig=production --config=webpack.prod.js",
14
+ "build:dev": "webpack --env envConfig=development --config=webpack.prod.js",
15
+ "build:staging": "webpack --env envConfig=staging --config=webpack.prod.js",
16
+ "build:local": "webpack",
16
17
  "prepublishOnly": "npm run build",
17
18
  "test": "jest src",
18
- "test:a11y": "test-storybook",
19
+ "test:storybook": "test-storybook",
19
20
  "update:browserslist": "npx browserslist@latest --update-db",
20
21
  "storybook": "start-storybook -p 6006",
21
22
  "build-storybook": "build-storybook"
@@ -47,84 +48,86 @@
47
48
  "react-hook-form": "^7.32.2",
48
49
  "react-idle-timer": "^4.2.12",
49
50
  "react-redux": "^7.1.1",
50
- "react-shadow": "^18.0.1",
51
+ "react-shadow": "^20.0.0",
51
52
  "react-textarea-autosize": "^8.3.2",
52
53
  "redux": "^4.0.4",
53
54
  "redux-localstorage": "^0.4.1",
54
55
  "redux-logger": "^3.0.6",
55
56
  "redux-thunk": "^2.3.0",
56
- "styled-components": "^5.3.1",
57
+ "stream-browserify": "^3.0.0",
58
+ "styled-components": "^5.3.6",
57
59
  "uuid": "^9.0.0"
58
60
  },
59
61
  "peerDependencies": {
60
- "pusher-js": ">=5.0.0",
62
+ "pusher-js": ">=8.0.0",
61
63
  "pusher-redux": ">=0.5.0",
62
64
  "react": ">=16.9.0",
63
65
  "react-dom": ">=16.9.0"
64
66
  },
65
67
  "devDependencies": {
66
- "@babel/core": "^7.19.3",
68
+ "@babel/core": "^7.20.12",
67
69
  "@babel/plugin-proposal-class-properties": "^7.18.6",
68
- "@babel/plugin-transform-runtime": "^7.19.1",
69
- "@babel/preset-env": "^7.19.3",
70
+ "@babel/plugin-transform-runtime": "^7.19.6",
71
+ "@babel/preset-env": "^7.20.2",
70
72
  "@babel/preset-react": "^7.18.6",
71
- "@storybook/addon-a11y": "^6.5.12",
72
- "@storybook/addon-actions": "^6.5.12",
73
- "@storybook/addon-essentials": "^6.5.12",
74
- "@storybook/addon-interactions": "^6.5.12",
75
- "@storybook/addon-links": "^6.5.12",
76
- "@storybook/builder-webpack4": "^6.5.12",
73
+ "@storybook/addon-a11y": "^6.5.16",
74
+ "@storybook/addon-actions": "^6.5.16",
75
+ "@storybook/addon-essentials": "^6.5.16",
76
+ "@storybook/addon-interactions": "^6.5.16",
77
+ "@storybook/addon-links": "^6.5.16",
78
+ "@storybook/builder-webpack5": "^6.5.16",
77
79
  "@storybook/jest": "^0.0.10",
78
- "@storybook/manager-webpack4": "^6.5.12",
79
- "@storybook/react": "^6.5.12",
80
- "@storybook/test-runner": "^0.7.2",
80
+ "@storybook/manager-webpack5": "^6.5.16",
81
+ "@storybook/react": "^6.5.16",
82
+ "@storybook/test-runner": "^0.8.0",
81
83
  "@storybook/testing-library": "^0.0.13",
82
84
  "@testing-library/jest-dom": "^5.16.5",
83
85
  "@testing-library/react": "^11.2.7",
84
86
  "@types/react": "^16.14.32",
85
87
  "@types/react-dom": "^16.9.16",
86
88
  "axe-playwright": "^1.1.11",
87
- "babel-jest": "^26.6.3",
88
- "babel-loader": "^8.2.5",
89
+ "babel-jest": "^29.4.1",
90
+ "babel-loader": "^9.1.2",
89
91
  "babel-plugin-styled-components": "^1.13.3",
90
92
  "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
91
93
  "babel-plugin-transform-remove-console": "^6.9.4",
92
94
  "browserslist": "^4.21.4",
93
95
  "chromatic": "^6.10.1",
94
- "clean-webpack-plugin": "^3.0.0",
95
- "compression": "1.7.4",
96
- "concurrently": "^7.4.0",
96
+ "clean-webpack-plugin": "^4.0.0",
97
97
  "dotenv": "8.6.0",
98
- "exports-loader": "^0.7.0",
99
- "express": "^4.18.1",
100
- "file-loader": "^4.3.0",
101
- "html-loader": "0.5.5",
102
- "html-webpack-plugin": "^3.2.0",
98
+ "express": "^4.18.2",
99
+ "file-loader": "^6.2.0",
100
+ "html-webpack-plugin": "^5.5.0",
103
101
  "husky": "3.1.0",
104
- "jest": "^26.6.3",
105
- "jest-each": "^26.6.2",
106
- "jest-junit": "^12.3.0",
107
- "jest-webpack-resolver": "^0.3.0",
102
+ "jest": "^29.4.1",
103
+ "jest-each": "^29.4.1",
104
+ "jest-environment-jsdom": "^29.4.1",
105
+ "jest-junit": "^15.0.0",
108
106
  "lodash.clonedeep": "^4.5.0",
109
- "pusher-js": "^5.1.1",
107
+ "process": "^0.11.10",
108
+ "pusher-js": "^8.0.1",
110
109
  "pusher-redux": "^0.5.0",
111
110
  "react": "^16.14.0",
112
111
  "react-dom": "^16.14.0",
113
112
  "react-svg-loader": "^3.0.3",
114
113
  "react-test-renderer": "16.14.0",
115
114
  "resolve-url-loader": "^5.0.0",
116
- "storybook-addon-mock": "^2.4.1",
117
- "url-loader": "2.3.0",
115
+ "storybook-addon-mock": "^3.2.0",
116
+ "url-loader": "^4.1.1",
118
117
  "wait-on": "^6.0.1",
119
- "webpack": "^4.46.0",
120
- "webpack-bundle-analyzer": "^3.9.0",
121
- "webpack-cli": "^3.3.12",
122
- "webpack-dev-server": "^3.11.3",
123
- "webpack-merge": "^4.2.2"
118
+ "webpack": "^5.75.0",
119
+ "webpack-bundle-analyzer": "^4.7.0",
120
+ "webpack-cli": "^5.0.1",
121
+ "webpack-dev-server": "^4.11.1",
122
+ "webpack-merge": "^5.8.0"
124
123
  },
125
- "jestWebpackResolver": {
126
- "silent": true,
127
- "webpackConfig": "./webpack.config.js"
124
+ "engines": {
125
+ "npm": ">=8.3.0"
128
126
  },
129
- "gitHead": "52b4d764eed71a0fbe3f5301c7114b6be833b50f"
127
+ "overrides": {
128
+ "pusher-redux": {
129
+ "pusher-js": "8.0.1"
130
+ }
131
+ },
132
+ "gitHead": "e8e325c85808d88ebc5b01cd1ca77375c791c4c4"
130
133
  }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": ["../../../.eslintrc.js"],
3
+ "overrides": [
4
+ {
5
+ "files": ["**/*.stories.jsx", "**/*.stories.tsx"],
6
+ "rules": {
7
+ "react/prop-types": "off"
8
+ }
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,406 @@
1
+ import { expect } from '@storybook/jest';
2
+ import { fireEvent, userEvent, within } from '@storybook/testing-library';
3
+ import React from 'react';
4
+
5
+ import { FIELDS } from '../../../src/components/Form/utils/constants';
6
+ import KindlyChatButton from '../../../src/features/KindlyChatButton/KindlyChatButton';
7
+ import settingsJSON from '../../assets/settingsJson';
8
+ import withContainer from '../../decorators/withContainer';
9
+ import withMockProvider from '../../decorators/withProvider';
10
+ import wait from '../../utils/wait';
11
+
12
+ const defaultBotSettings = {
13
+ welcomePage: settingsJSON.welcome_page,
14
+ feedbackForm: settingsJSON.feedback_form,
15
+ maintenanceAlert: settingsJSON.maintenance_alert,
16
+ ...settingsJSON.settings,
17
+ ...settingsJSON,
18
+ };
19
+
20
+ export default {
21
+ title: 'Screen/Chat/Form',
22
+ component: KindlyChatButton,
23
+ decorators: [withContainer, withMockProvider],
24
+ argTypes: {
25
+ initialStateModifierFromArgs: {
26
+ table: {
27
+ disable: true,
28
+ },
29
+ },
30
+ },
31
+ };
32
+
33
+ const defaultParameters = {
34
+ botSettings: defaultBotSettings,
35
+ initialStateModifier: {
36
+ chatbubble: {
37
+ active: true,
38
+ chatHasStarted: true,
39
+ currentLanguage: 'en',
40
+ },
41
+ },
42
+ };
43
+
44
+ function Template(args, context) {
45
+ return <KindlyChatButton {...args} {...context} />;
46
+ }
47
+
48
+ export const FormActive = Template.bind({});
49
+ FormActive.parameters = {
50
+ ...defaultParameters,
51
+ initialStateModifier: {
52
+ ...defaultParameters.initialStateModifier,
53
+ messages: {
54
+ chatMessages: [
55
+ {
56
+ chat_source: 'web',
57
+ chat_language_code: 'en',
58
+ from_bot: true,
59
+ sender: 'BOT',
60
+ message: '',
61
+ message_format: 'txt',
62
+ buttons: [],
63
+ created: '2022-06-15T19:03:46.186495Z',
64
+ id: '1',
65
+ form: {
66
+ submission_id: 'submissionId',
67
+ id: 'formId',
68
+ dialogue_id: '2',
69
+ submit_dialogue_id: 'c0d685d3-3f11-41b8-9cfd-e35d54df2c95',
70
+ abandon_dialogue_id: 'b1cde0f3-0715-48eb-8482-a9355fa11ba8',
71
+ languageCode: 'en',
72
+ texts: {
73
+ title: 'This is a form',
74
+ error_text: 'There was an error around here',
75
+ submit_button_text: 'Submit',
76
+ cancel_button_text: 'Exit',
77
+ unanswered_text: 'You failed to answer this form',
78
+ cancel_text: 'You have cancelled/exited this form',
79
+ },
80
+ fields: [
81
+ {
82
+ input_type: FIELDS.TEXT,
83
+ order: 0,
84
+ slug: 'first-name-field',
85
+ texts: {
86
+ label: 'First name',
87
+ help_text: 'Please input your name here',
88
+ placeholder_text: 'First name',
89
+ required_text: 'This field is absolutely required',
90
+ },
91
+ required: true,
92
+ validators: [
93
+ {
94
+ max_length: 30,
95
+ text: 'Maximum number of length is 30',
96
+ },
97
+ ],
98
+ },
99
+ {
100
+ input_type: FIELDS.TEXT,
101
+ order: 1,
102
+ slug: 'text-field',
103
+ texts: {
104
+ label: 'Text field',
105
+ placeholder_text: 'Free text',
106
+ required_text: 'This field is required',
107
+ },
108
+ required: true,
109
+ validators: [
110
+ {
111
+ min_length: 5,
112
+ text: 'This field should be at least 5 characters long',
113
+ },
114
+ {
115
+ max_length: 15,
116
+ },
117
+ ],
118
+ },
119
+ {
120
+ input_type: FIELDS.EMAIL,
121
+ order: 2,
122
+ slug: 'email-field',
123
+ texts: {
124
+ label: 'Email',
125
+ placeholder_text: 'Email',
126
+ },
127
+ },
128
+ {
129
+ input_type: FIELDS.NUMBER,
130
+ order: 3,
131
+ affix: 'PREFIX',
132
+ slug: 'number-field',
133
+ texts: {
134
+ label: 'Number',
135
+ affix_value: 'NOK',
136
+ placeholder_text: 'Number',
137
+ },
138
+ validators: [
139
+ {
140
+ minimum: 1,
141
+ },
142
+ {
143
+ maximum: 12,
144
+ },
145
+ ],
146
+ },
147
+ {
148
+ input_type: FIELDS.RANGE,
149
+ order: 4,
150
+ affix: 'SUFFIX',
151
+ slug: 'range-field',
152
+ attributes: {
153
+ default_value: '50',
154
+ step: 10,
155
+ },
156
+ texts: {
157
+ label: 'Slider',
158
+ affix_value: 'NOK',
159
+ placeholder_text: 'Slider',
160
+ },
161
+ validators: [
162
+ {
163
+ minimum: 1,
164
+ },
165
+ {
166
+ maximum: 100,
167
+ },
168
+ ],
169
+ },
170
+ {
171
+ input_type: FIELDS.SELECT,
172
+ order: 5,
173
+ required: true,
174
+ slug: 'select-field',
175
+ attributes: {
176
+ default_value: '5',
177
+ options: [
178
+ {
179
+ value: '1',
180
+ label: 'One',
181
+ },
182
+ {
183
+ value: '2',
184
+ label: 'Two',
185
+ },
186
+ {
187
+ value: '3',
188
+ label: 'Three',
189
+ },
190
+ {
191
+ value: '4',
192
+ label: 'Four',
193
+ },
194
+ {
195
+ value: '5',
196
+ label: 'Five',
197
+ },
198
+ ],
199
+ },
200
+ texts: {
201
+ label: 'Select',
202
+ placeholder_text: 'Please select option',
203
+ },
204
+ validators: [],
205
+ },
206
+ ],
207
+ },
208
+ },
209
+ ],
210
+ },
211
+ },
212
+ };
213
+ FormActive.play = async ({ canvasElement }) => {
214
+ await within(canvasElement).findByText('Please input your name here', undefined, { timeout: 5000 });
215
+ const textPlaceholderValue = 'Free text';
216
+ const textInput = await within(canvasElement).findByPlaceholderText(textPlaceholderValue);
217
+ await userEvent.type(textInput, 'This');
218
+ await expect(textInput.value).toBe('This');
219
+ await fireEvent(await within(canvasElement).getByRole('button', { name: /submit/i }), new MouseEvent('click'));
220
+ await within(canvasElement).findByText('This field is absolutely required');
221
+ await within(canvasElement).findByText('This field should be at least 5 characters long');
222
+ const textInputStyle = window.getComputedStyle(textInput);
223
+ expect(textInputStyle.border).not.toContain('#00000000');
224
+ expect(textInputStyle.border).not.toContain('transparent');
225
+ await userEvent.type(textInput, ' is too long');
226
+ await expect(textInput.value).toBe('This is too lon');
227
+ const emailPlaceholderValue = 'Email';
228
+ const emailInput = await within(canvasElement).findByPlaceholderText(emailPlaceholderValue);
229
+ await userEvent.type(emailInput, 'this@is@not@an@email');
230
+ const selectPlaceholderValue = 'Please select option';
231
+ const selectInput = await within(canvasElement).findByPlaceholderText(selectPlaceholderValue);
232
+ const selectInputStyle = window.getComputedStyle(selectInput);
233
+ expect(selectInputStyle.border).not.toContain('#00000000');
234
+ expect(selectInputStyle.border).not.toContain('transparent');
235
+ await userEvent.selectOptions(selectInput, '2');
236
+ await expect(selectInputStyle.border).toContain('rgba(0, 0, 0, 0)');
237
+
238
+ const chatBody = await within(canvasElement).findByTestId('chat-body-container');
239
+ await wait(300); // wait for the autoscroll to finish, so we can bottom for the Chromatic snapshot
240
+ fireEvent.scroll(chatBody, { target: { scrollTop: chatBody.clientHeight } });
241
+ };
242
+
243
+ export const FormWithoutTitle = Template.bind({});
244
+ FormWithoutTitle.parameters = {
245
+ ...defaultParameters,
246
+ initialStateModifier: {
247
+ ...defaultParameters.initialStateModifier,
248
+ messages: {
249
+ ...FormActive.parameters.initialStateModifier.messages,
250
+ chatMessages: [
251
+ {
252
+ chat_source: 'web',
253
+ chat_language_code: 'en',
254
+ from_bot: true,
255
+ sender: 'BOT',
256
+ message: '',
257
+ message_format: 'txt',
258
+ buttons: [],
259
+ created: '2022-06-15T19:03:46.186495Z',
260
+ id: '1',
261
+ form: {
262
+ submission_id: 'submissionId',
263
+ id: 'formId',
264
+ dialogue_id: '2',
265
+ submit_dialogue_id: 'c0d685d3-3f11-41b8-9cfd-e35d54df2c95',
266
+ abandon_dialogue_id: 'b1cde0f3-0715-48eb-8482-a9355fa11ba8',
267
+ languageCode: 'en',
268
+ texts: {
269
+ title: '',
270
+ error_text: 'There was an error around here',
271
+ submit_button_text: 'Submit',
272
+ cancel_button_text: 'Exit',
273
+ unanswered_text: 'You failed to answer this form',
274
+ cancel_text: 'You have cancelled/exited this form',
275
+ },
276
+ fields: [
277
+ {
278
+ input_type: FIELDS.TEXT,
279
+ order: 0,
280
+ slug: 'first-name-field',
281
+ texts: {
282
+ label: 'First name',
283
+ help_text: 'Please input your name here',
284
+ placeholder_text: 'First name',
285
+ required_text: 'This field is absolutely required',
286
+ },
287
+ required: true,
288
+ validators: [
289
+ {
290
+ max_length: 30,
291
+ text: 'Maximum number of length is 30',
292
+ },
293
+ ],
294
+ },
295
+ ],
296
+ },
297
+ },
298
+ ],
299
+ },
300
+ },
301
+ };
302
+
303
+ export const FormSuccess = Template.bind({});
304
+ FormSuccess.parameters = {
305
+ ...defaultParameters,
306
+ initialStateModifier: {
307
+ ...defaultParameters.initialStateModifier,
308
+ messages: {
309
+ ...FormActive.parameters.initialStateModifier.messages,
310
+ forms: {
311
+ // prettier-ignore
312
+ "formId_submission_submissionId": {
313
+ formKey: 'formId_submission_submissionId',
314
+ formId: 'formId',
315
+ formSubmitState: 'SUBMITTED',
316
+ formCompleteMessage: 'Should not display',
317
+ formValue: {
318
+ 'should-not-display': 'Should not display',
319
+ 'first-name-field': 'First Name',
320
+ 'text-field': 'Text',
321
+ 'email-field': 'email@kindly.ai',
322
+ 'number-field': '42',
323
+ 'range-field': '7',
324
+ 'select-field': '2',
325
+ },
326
+ },
327
+ },
328
+ },
329
+ },
330
+ };
331
+
332
+ FormSuccess.play = async ({ canvasElement }) => {
333
+ const selectFieldLabel = await within(canvasElement).findByText('Two');
334
+ await expect(selectFieldLabel).toBeInTheDocument();
335
+ const shouldNotDisplay = await within(canvasElement).queryByText('Should not display');
336
+ await expect(shouldNotDisplay).not.toBeInTheDocument();
337
+ };
338
+
339
+ export const FormErrored = Template.bind({});
340
+ FormErrored.parameters = {
341
+ ...defaultParameters,
342
+ initialStateModifier: {
343
+ ...defaultParameters.initialStateModifier,
344
+ messages: {
345
+ ...FormActive.parameters.initialStateModifier.messages,
346
+ forms: {
347
+ // prettier-ignore
348
+ "formId_submission_submissionId": {
349
+ formKey: 'formId_submission_submissionId',
350
+ formId: 'formId',
351
+ formSubmitState: 'ERRORED',
352
+ formCompleteMessage: 'Something went wrong',
353
+ formValue: {
354
+ 'text-field': 'Text',
355
+ },
356
+ },
357
+ },
358
+ },
359
+ },
360
+ };
361
+
362
+ export const FormCanceled = Template.bind({});
363
+ FormCanceled.parameters = {
364
+ ...defaultParameters,
365
+ initialStateModifier: {
366
+ ...defaultParameters.initialStateModifier,
367
+ messages: {
368
+ ...FormActive.parameters.initialStateModifier.messages,
369
+ forms: {
370
+ // prettier-ignore
371
+ "formId_submission_submissionId": {
372
+ formKey: 'formId_submission_submissionId',
373
+ formId: 'formId',
374
+ formSubmitState: 'CANCELED',
375
+ formCompleteMessage: 'This form was canceled',
376
+ formValue: {
377
+ 'text-field': 'Text',
378
+ },
379
+ },
380
+ },
381
+ },
382
+ },
383
+ };
384
+
385
+ export const FormUnanswered = Template.bind({});
386
+ FormUnanswered.parameters = {
387
+ ...defaultParameters,
388
+ initialStateModifier: {
389
+ ...defaultParameters.initialStateModifier,
390
+ messages: {
391
+ ...FormActive.parameters.initialStateModifier.messages,
392
+ forms: {
393
+ // prettier-ignore
394
+ "formId_submission_submissionId": {
395
+ formKey: 'formId_submission_submissionId',
396
+ formId: 'formId',
397
+ formSubmitState: 'UNANSWERED',
398
+ formCompleteMessage: 'This form was unanswered',
399
+ formValue: {
400
+ 'text-field': 'Text',
401
+ },
402
+ },
403
+ },
404
+ },
405
+ },
406
+ };