@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.
@@ -1,18 +1,16 @@
1
1
  import { expect } from '@storybook/jest';
2
- /* eslint-disable react-hooks/exhaustive-deps */
3
- /* eslint-disable react/prop-types */
4
- import { fireEvent, screen, userEvent } from '@storybook/testing-library';
2
+ import { fireEvent, userEvent, waitFor, within } from '@storybook/testing-library';
5
3
  import React from 'react';
6
- import withFetchMock from 'storybook-addon-mock';
7
4
 
8
5
  import { IMAGE_WIDTH } from 'app/constants';
9
6
 
10
- import { chromaticViewports } from '../../.storybook/preview';
11
- import { FIELDS } from '../../src/components/Form/utils/constants';
12
- import KindlyChatButton from '../../src/features/KindlyChatButton/KindlyChatButton';
13
- import settingsJSON from '../assets/settingsJson';
14
- import withContainer from '../decorators/withContainer';
15
- import withMockProvider from '../decorators/withProvider';
7
+ import { chromaticViewports } from '../../../.storybook/preview';
8
+ import KindlyChatButton from '../../../src/features/KindlyChatButton/KindlyChatButton';
9
+ import mockMessage from '../../../testing/mockMessage';
10
+ import settingsJSON from '../../assets/settingsJson';
11
+ import withContainer from '../../decorators/withContainer';
12
+ import withMockProvider from '../../decorators/withProvider';
13
+ import wait from '../../utils/wait';
16
14
 
17
15
  const defaultBotSettings = {
18
16
  welcomePage: settingsJSON.welcome_page,
@@ -29,7 +27,7 @@ function Template(args, context) {
29
27
  export default {
30
28
  title: 'Screen/Chat',
31
29
  component: KindlyChatButton,
32
- decorators: [withFetchMock, withContainer, withMockProvider],
30
+ decorators: [withContainer, withMockProvider],
33
31
  argTypes: {
34
32
  initialStateModifierFromArgs: {
35
33
  table: {
@@ -225,7 +223,11 @@ const MESSAGES = {
225
223
  };
226
224
  MultilineBotMessage.storyName = 'Multiline Bot & User Messages';
227
225
  MultilineBotMessage.parameters = {
228
- ...defaultParameters,
226
+ ...Regular.parameters,
227
+ botSettings: {
228
+ ...defaultBotSettings,
229
+ typing_duration: 0,
230
+ },
229
231
  initialStateModifier: {
230
232
  ...defaultParameters.initialStateModifier,
231
233
  messages: {
@@ -276,24 +278,54 @@ MultilineBotMessage.parameters = {
276
278
  },
277
279
  },
278
280
  };
279
- MultilineBotMessage.play = async () => {
280
- const chatBody = await screen.findByTestId('chat-body-container');
281
- await screen.findAllByText('Single-line lonely message');
281
+ MultilineBotMessage.play = async ({ canvasElement }) => {
282
+ const chatBody = await within(canvasElement).findByTestId('chat-body-container');
283
+ await within(canvasElement).findAllByText('Single-line lonely message');
284
+ // wait for the auto-scroll to execute
285
+ await wait(300);
286
+
287
+ // Some screen sizes won't have a scrollbar
288
+ const hasScrollbar = chatBody.scrollTop > 0;
289
+
290
+ if (hasScrollbar) {
291
+ fireEvent.scroll(chatBody, { target: { scrollTop: 0 } });
292
+
293
+ const chatNewMessage = await within(canvasElement).findByTestId('chat-new-message');
294
+ expect(chatNewMessage).not.toBeVisible();
295
+
296
+ await waitFor(
297
+ async () => {
298
+ await expect(chatNewMessage).toBeVisible();
299
+ },
300
+ { timeout: 5000 },
301
+ );
302
+ }
303
+ };
304
+
305
+ export const NewUnreadMessages = Template.bind({});
306
+ NewUnreadMessages.parameters = MultilineBotMessage.parameters;
307
+ NewUnreadMessages.play = async ({ canvasElement }) => {
308
+ const chatBody = await within(canvasElement).findByTestId('chat-body-container');
282
309
  // wait for the auto-scroll to execute
283
- await new Promise((resolve) => {
284
- setTimeout(resolve, 50);
285
- });
286
- fireEvent.scroll(chatBody, { target: { scrollTop: 0 } });
310
+ await wait(300);
287
311
 
288
- const chatNewMessage = await screen.findByTestId('chat-new-message');
312
+ // Some screen sizes won't have a scrollbar
313
+ const hasScrollbar = chatBody.scrollTop > 0;
289
314
 
290
- // TODO: We used to use `waitFor`, but it seems to fail in Chromatic.
291
- // Wait for the newMessage button to be visible
292
- await new Promise((resolve) => {
293
- setTimeout(resolve, 1400);
294
- });
315
+ if (hasScrollbar) {
316
+ fireEvent.scroll(chatBody, { target: { scrollTop: 0 } });
317
+ const chatNewMessage = await within(canvasElement).findByTestId('chat-new-message');
295
318
 
296
- await expect(chatNewMessage).toBeVisible();
319
+ await waitFor(
320
+ async () => {
321
+ await expect(chatNewMessage).toBeVisible();
322
+ },
323
+ { timeout: 5000 },
324
+ );
325
+ mockMessage(window.store, 'This is a new message');
326
+ await wait(50);
327
+ await expect(chatNewMessage).toHaveTextContent('This is a new message');
328
+ }
297
329
  };
298
330
 
299
331
  export const GroupsOfMessages = Template.bind({});
@@ -379,11 +411,11 @@ HandoverRequested.parameters = {
379
411
 
380
412
  export const HandoverCancelled = Template.bind({});
381
413
  HandoverCancelled.parameters = HandoverRequested.parameters;
382
- HandoverCancelled.play = async () => {
383
- await screen.findByText(defaultBotSettings.text.takeover_queue_text.en);
384
- const leaveQueueButton = await screen.findByText(defaultBotSettings.text.takeover_queue_link.en);
414
+ HandoverCancelled.play = async ({ canvasElement }) => {
415
+ await within(canvasElement).findByText(defaultBotSettings.text.takeover_queue_text.en);
416
+ const leaveQueueButton = await within(canvasElement).findByText(defaultBotSettings.text.takeover_queue_link.en);
385
417
  await userEvent.click(leaveQueueButton);
386
- await screen.findByText(defaultBotSettings.text.leave_queue_text.en);
418
+ await within(canvasElement).findByText(defaultBotSettings.text.leave_queue_text.en);
387
419
  };
388
420
 
389
421
  export const Checkbox = Template.bind({});
@@ -698,200 +730,6 @@ Slider.parameters = {
698
730
  },
699
731
  };
700
732
 
701
- export const Form = Template.bind({});
702
- Form.parameters = {
703
- ...defaultParameters,
704
- initialStateModifier: {
705
- ...defaultParameters.initialStateModifier,
706
- messages: {
707
- chatMessages: [
708
- {
709
- chat_source: 'web',
710
- chat_language_code: 'en',
711
- from_bot: true,
712
- sender: 'BOT',
713
- message: '',
714
- message_format: 'txt',
715
- buttons: [],
716
- created: '2022-06-15T19:03:46.186495Z',
717
- id: '1',
718
- form: {
719
- submission_id: '1',
720
- id: '2',
721
- dialogue_id: '2',
722
- slug: 'this-is-a-form',
723
- submit_dialogue_id: 'c0d685d3-3f11-41b8-9cfd-e35d54df2c95',
724
- abandon_dialogue_id: 'b1cde0f3-0715-48eb-8482-a9355fa11ba8',
725
- languageCode: 'en',
726
- texts: {
727
- title: 'This is a form',
728
- error_text: 'There was an error around here',
729
- submit_button_text: 'Submit',
730
- cancel_button_text: 'Exit',
731
- unanswered_text: 'You failed to answer this form',
732
- cancel_text: 'You have cancelled/exited this form',
733
- },
734
- fields: [
735
- {
736
- input_type: FIELDS.TEXT,
737
- order: 0,
738
- slug: 'first-name-field',
739
- texts: {
740
- label: 'First name',
741
- help_text: 'Please input your name here',
742
- placeholder_text: 'First name',
743
- required_text: 'This field is absolutely required',
744
- },
745
- required: true,
746
- validators: [
747
- {
748
- max_length: 30,
749
- text: 'Maximum number of length is 30',
750
- },
751
- ],
752
- },
753
- {
754
- input_type: FIELDS.TEXT,
755
- order: 1,
756
- slug: 'text-field',
757
- texts: {
758
- label: 'Text field',
759
- placeholder_text: 'Free text',
760
- required_text: 'This field is required',
761
- },
762
- required: true,
763
- validators: [
764
- {
765
- min_length: 5,
766
- text: 'This field should be at least 5 characters long',
767
- },
768
- {
769
- max_length: 15,
770
- },
771
- ],
772
- },
773
- {
774
- input_type: FIELDS.EMAIL,
775
- order: 2,
776
- slug: 'email-field',
777
- texts: {
778
- label: 'Email',
779
- placeholder_text: 'Email',
780
- },
781
- },
782
- {
783
- input_type: FIELDS.NUMBER,
784
- order: 3,
785
- affix: 'PREFIX',
786
- slug: 'number-field',
787
- texts: {
788
- label: 'Number',
789
- affix_value: 'NOK',
790
- placeholder_text: 'Number',
791
- },
792
- validators: [
793
- {
794
- minimum: 1,
795
- },
796
- {
797
- maximum: 12,
798
- },
799
- ],
800
- },
801
- {
802
- input_type: FIELDS.RANGE,
803
- order: 4,
804
- affix: 'SUFFIX',
805
- slug: 'range-field',
806
- attributes: {
807
- default_value: '50',
808
- step: 10,
809
- },
810
- texts: {
811
- label: 'Slider',
812
- affix_value: 'NOK',
813
- placeholder_text: 'Slider',
814
- },
815
- validators: [
816
- {
817
- minimum: 1,
818
- },
819
- {
820
- maximum: 100,
821
- },
822
- ],
823
- },
824
- {
825
- input_type: FIELDS.SELECT,
826
- order: 5,
827
- affix: '',
828
- required: true,
829
- slug: 'select-field',
830
- attributes: {
831
- default_value: '5',
832
- options: [
833
- {
834
- value: 1,
835
- label: 1,
836
- },
837
- {
838
- value: 2,
839
- label: 2,
840
- },
841
- {
842
- value: 3,
843
- label: 3,
844
- },
845
- {
846
- value: 4,
847
- label: 4,
848
- },
849
- {
850
- value: 5,
851
- label: 5,
852
- },
853
- ],
854
- },
855
- texts: {
856
- label: 'Select',
857
- affix_value: '',
858
- placeholder_text: 'Please select option',
859
- },
860
- validators: [],
861
- },
862
- ],
863
- },
864
- },
865
- ],
866
- },
867
- },
868
- };
869
- Form.play = async () => {
870
- await screen.findByText('Please input your name here');
871
- const textPlaceholderValue = 'Free text';
872
- const textInput = await screen.findByPlaceholderText(textPlaceholderValue);
873
- await userEvent.type(textInput, 'This');
874
- await expect(textInput.value).toBe('This');
875
- await fireEvent(await screen.getByRole('button', { name: /submit/i }), new MouseEvent('click'));
876
- await screen.findByText('This field is absolutely required');
877
- await screen.findByText('This field should be at least 5 characters long');
878
- const textInputStyle = window.getComputedStyle(textInput);
879
- expect(textInputStyle.border).not.toContain('#00000000');
880
- expect(textInputStyle.border).not.toContain('transparent');
881
- await userEvent.type(textInput, ' is too long');
882
- await expect(textInput.value).toBe('This is too lon');
883
- const emailPlaceholderValue = 'Email';
884
- const emailInput = await screen.findByPlaceholderText(emailPlaceholderValue);
885
- await userEvent.type(emailInput, 'this@is@not@an@email');
886
- const selectPlaceholderValue = 'Please select option';
887
- const selectInput = await screen.findByPlaceholderText(selectPlaceholderValue);
888
- const selectInputStyle = window.getComputedStyle(selectInput);
889
- expect(selectInputStyle.border).not.toContain('#00000000');
890
- expect(selectInputStyle.border).not.toContain('transparent');
891
- await userEvent.selectOptions(selectInput, '2');
892
- await expect(selectInputStyle.border).toContain('rgba(0, 0, 0, 0)');
893
- };
894
-
895
733
  export const LeaveContactDetails = Template.bind({});
896
734
  LeaveContactDetails.parameters = {
897
735
  ...defaultParameters,
@@ -956,8 +794,8 @@ LeaveContactDetails.parameters = {
956
794
  },
957
795
  },
958
796
  };
959
- LeaveContactDetails.play = async () => {
960
- const LeaveContactButton = await screen.findByText(
797
+ LeaveContactDetails.play = async ({ canvasElement }) => {
798
+ const LeaveContactButton = await within(canvasElement).findByText(
961
799
  LeaveContactDetails.parameters.initialStateModifier.messages.chatMessages[0].buttons[2].label,
962
800
  );
963
801
  await userEvent.click(LeaveContactButton);
@@ -1,6 +1,4 @@
1
- /* eslint-disable react/prop-types */
2
1
  import React from 'react';
3
- import withMock from 'storybook-addon-mock';
4
2
 
5
3
  import { chromaticViewports } from '../../.storybook/preview';
6
4
  import KindlyChat from '../../src/KindlyChat';
@@ -24,7 +22,7 @@ function Template({ options, ...args }) {
24
22
  export default {
25
23
  title: 'Screen/Button',
26
24
  component: KindlyChat,
27
- decorators: [withMock],
25
+ decorators: [],
28
26
  argTypes: {
29
27
  options: {
30
28
  table: {
@@ -1,7 +1,4 @@
1
- /* eslint-disable react-hooks/exhaustive-deps */
2
- /* eslint-disable react/prop-types */
3
1
  import React from 'react';
4
- import withFetchMock from 'storybook-addon-mock';
5
2
 
6
3
  import { feedbackFormTypes } from 'app/constants';
7
4
 
@@ -26,7 +23,7 @@ function Template(args, context) {
26
23
  export default {
27
24
  title: 'Screen/Feedback',
28
25
  component: KindlyChatButton,
29
- decorators: [withFetchMock, withContainer, withMockProvider],
26
+ decorators: [withContainer, withMockProvider],
30
27
  };
31
28
 
32
29
  export const Emojis = Template.bind({});
@@ -1,6 +1,4 @@
1
- /* eslint-disable react/prop-types */
2
1
  import React from 'react';
3
- import withMock from 'storybook-addon-mock';
4
2
 
5
3
  import { chromaticViewports } from '../../.storybook/preview';
6
4
  import KindlyChat from '../../src/KindlyChat';
@@ -37,7 +35,6 @@ function Template({ slug, nudgeLayout, trackEvent, options, ...args }) {
37
35
  export default {
38
36
  title: 'Screen/Nudges',
39
37
  component: KindlyChat,
40
- decorators: [withMock],
41
38
  };
42
39
 
43
40
  export const Product = Template.bind({});
@@ -1,8 +1,5 @@
1
- /* eslint-disable react-hooks/exhaustive-deps */
2
- /* eslint-disable react/prop-types */
3
- import { screen, userEvent } from '@storybook/testing-library';
1
+ import { userEvent, within } from '@storybook/testing-library';
4
2
  import React from 'react';
5
- import withFetchMock from 'storybook-addon-mock';
6
3
 
7
4
  import { chromaticViewports } from '../../.storybook/preview';
8
5
  import KindlyChatButton from '../../src/features/KindlyChatButton/KindlyChatButton';
@@ -25,7 +22,7 @@ function Template(args, context) {
25
22
  export default {
26
23
  title: 'Screen/Options',
27
24
  component: KindlyChatButton,
28
- decorators: [withFetchMock, withContainer, withMockProvider],
25
+ decorators: [withContainer, withMockProvider],
29
26
  };
30
27
 
31
28
  const defaultParameters = {
@@ -165,8 +162,8 @@ LanguageChoice.parameters = {
165
162
  ...defaultBotSettings,
166
163
  },
167
164
  };
168
- LanguageChoice.play = async () => {
169
- const languageButton = await screen.findByText(defaultBotSettings.text.change_language_button.en);
165
+ LanguageChoice.play = async ({ canvasElement }) => {
166
+ const languageButton = await within(canvasElement).findByText(defaultBotSettings.text.change_language_button.en);
170
167
  await userEvent.click(languageButton);
171
168
  };
172
169
 
@@ -177,17 +174,17 @@ LanguageChoiceConfirm.args = {
177
174
  };
178
175
  LanguageChoiceConfirm.argTypes = LanguageChoice.argTypes;
179
176
  LanguageChoiceConfirm.parameters = LanguageChoice.parameters;
180
- LanguageChoiceConfirm.play = async () => {
181
- const languageButton = await screen.findByText(defaultBotSettings.text.change_language_button.en);
177
+ LanguageChoiceConfirm.play = async ({ canvasElement }) => {
178
+ const languageButton = await within(canvasElement).findByText(defaultBotSettings.text.change_language_button.en);
182
179
  await userEvent.click(languageButton);
183
- const norskButton = await screen.findByText(availableLanguages[1].name);
180
+ const norskButton = await within(canvasElement).findByText(availableLanguages[1].name);
184
181
  await userEvent.click(norskButton);
185
182
  };
186
183
 
187
184
  export const DeleteConfirmation = Template.bind({});
188
185
  DeleteConfirmation.parameters = defaultParameters;
189
- DeleteConfirmation.play = async () => {
190
- const deleteButton = await screen.findByText(defaultBotSettings.text.delete_button.en);
186
+ DeleteConfirmation.play = async ({ canvasElement }) => {
187
+ const deleteButton = await within(canvasElement).findByText(defaultBotSettings.text.delete_button.en);
191
188
  await userEvent.click(deleteButton);
192
189
  };
193
190
 
@@ -204,8 +201,8 @@ DeleteSuccess.parameters = {
204
201
 
205
202
  export const DownloadChat = Template.bind({});
206
203
  DownloadChat.parameters = defaultParameters;
207
- DownloadChat.play = async () => {
208
- const downloadButton = await screen.findByText(defaultBotSettings.text.download_button.en);
204
+ DownloadChat.play = async ({ canvasElement }) => {
205
+ const downloadButton = await within(canvasElement).findByText(defaultBotSettings.text.download_button.en);
209
206
  await userEvent.click(downloadButton);
210
207
  };
211
208
 
@@ -220,7 +217,7 @@ QuitChat.parameters = {
220
217
  },
221
218
  },
222
219
  };
223
- QuitChat.play = async () => {
224
- const deleteButton = await screen.findByTestId('end-chat-button');
220
+ QuitChat.play = async ({ canvasElement }) => {
221
+ const deleteButton = await within(canvasElement).findByTestId('end-chat-button');
225
222
  await userEvent.click(deleteButton);
226
223
  };
@@ -1,7 +1,4 @@
1
- /* eslint-disable react-hooks/exhaustive-deps */
2
- /* eslint-disable react/prop-types */
3
1
  import React from 'react';
4
- import withFetchMock from 'storybook-addon-mock';
5
2
 
6
3
  import { chromaticViewports } from '../../.storybook/preview';
7
4
  import KindlyChatButton from '../../src/features/KindlyChatButton/KindlyChatButton';
@@ -24,7 +21,7 @@ function Template(args, context) {
24
21
  export default {
25
22
  title: 'Screen/PushGreeting',
26
23
  component: KindlyChatButton,
27
- decorators: [withFetchMock, withContainer, withMockProvider],
24
+ decorators: [withContainer, withMockProvider],
28
25
  };
29
26
 
30
27
  export const Default = Template.bind({});
@@ -1,5 +1,3 @@
1
- /* eslint-disable react-hooks/exhaustive-deps */
2
- /* eslint-disable react/prop-types */
3
1
  import React from 'react';
4
2
 
5
3
  import { chromaticViewports } from '../../.storybook/preview';
@@ -0,0 +1,2 @@
1
+ /* eslint-disable no-promise-executor-return */
2
+ export default (waitTime) => new Promise((resolve) => setTimeout(resolve, waitTime));