Skip to content

Commit

Permalink
Add React Hooks for customization (part 9) (#2550)
Browse files Browse the repository at this point in the history
* Add multiple simple property hooks

* Fix test

* Fix ESLint
  • Loading branch information
compulim authored and corinagum committed Nov 22, 2019
1 parent 3826e98 commit f5fa609
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- PR [#2547](https://github.com/microsoft/BotFramework-WebChat/pull/2547): `useEmitTypingIndicator`, `usePeformCardAction`, `usePostActivity`, `useSendEvent`, `useSendFiles`, `useSendMessage`, `useSendMessageBack`, `useSendPostBack`
- PR [#2548](https://github.com/microsoft/BotFramework-WebChat/pull/2548): `useDisabled`
- PR [#2549](https://github.com/microsoft/BotFramework-WebChat/pull/2549): `useSuggestedActions`
- PR [#2550](https://github.com/microsoft/BotFramework-WebChat/pull/2550): `useConnectivityStatus`, `useGroupTimestamp`, `useTimeoutForSend`, `useUserID`, `useUsername`
- PR [#2551](https://github.com/microsoft/BotFramework-WebChat/pull/2551): `useLastTypingAt`, `useSendTypingIndicator`, `useTypingIndicator`
- PR [#2552](https://github.com/microsoft/BotFramework-WebChat/pull/2552): `useFocusSendBox`, `useScrollToEnd`, `useSendBoxValue`, `useSubmitSendBox`, `useTextBoxSubmit`, `useTextBoxValue`
- Bring your own Adaptive Cards package by specifying `adaptiveCardsPackage` prop, by [@compulim](https://github.com/compulim) in PR [#2543](https://github.com/microsoft/BotFramework-WebChat/pull/2543)
Expand Down
24 changes: 24 additions & 0 deletions __tests__/hooks/useConnectivityStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { timeouts } from '../constants.json';
import uiConnected from '../setup/conditions/uiConnected';

// selenium-webdriver API doc:
// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html

jest.setTimeout(timeouts.test);

test('getter should return online', async () => {
const { driver, pageObjects } = await setupWebDriver();

await driver.wait(uiConnected(), timeouts.directLine);

const [connectivityStatus] = await pageObjects.runHook('useConnectivityStatus');

expect(connectivityStatus).toMatchInlineSnapshot(`"connected"`);
});

test('setter should be falsy', async () => {
const { pageObjects } = await setupWebDriver();
const [_, setConnectivityStatus] = await pageObjects.runHook('useConnectivityStatus');

expect(setConnectivityStatus).toBeFalsy();
});
43 changes: 43 additions & 0 deletions __tests__/hooks/useGroupTimestamp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { timeouts } from '../constants.json';

// selenium-webdriver API doc:
// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html

jest.setTimeout(timeouts.test);

test('getter should return group timestamp set in props', async () => {
const { pageObjects } = await setupWebDriver({
props: {
groupTimestamp: 1000
}
});

const [groupTimestamp] = await pageObjects.runHook('useGroupTimestamp');

expect(groupTimestamp).toMatchInlineSnapshot(`1000`);
});

test('getter should return default group timestamp if not set in props', async () => {
const { pageObjects } = await setupWebDriver();

const [groupTimestamp] = await pageObjects.runHook('useGroupTimestamp');

expect(groupTimestamp).toMatchInlineSnapshot(`true`);
});

test('getter should return false if group timestamp is disabled', async () => {
const { pageObjects } = await setupWebDriver({
props: { groupTimestamp: false }
});

const [groupTimestamp] = await pageObjects.runHook('useGroupTimestamp');

expect(groupTimestamp).toMatchInlineSnapshot(`false`);
});

test('setter should be falsy', async () => {
const { pageObjects } = await setupWebDriver();
const [_, setGroupTimestamp] = await pageObjects.runHook('useGroupTimestamp');

expect(setGroupTimestamp).toBeFalsy();
});
36 changes: 36 additions & 0 deletions __tests__/hooks/useTimeoutForSend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { timeouts } from '../constants.json';

// selenium-webdriver API doc:
// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html

jest.setTimeout(timeouts.test);

test('getter should return timeout for sending activity', async () => {
const { pageObjects } = await setupWebDriver({
props: {
sendTimeout: 1000
}
});

const [timeoutForSend] = await pageObjects.runHook('useTimeoutForSend');

expect(timeoutForSend).toMatchInlineSnapshot(`1000`);
});

test('getter should return default timeout for sending activity if not set in props', async () => {
const { pageObjects } = await setupWebDriver();

const [timeoutForSend] = await pageObjects.runHook('useTimeoutForSend');

expect(timeoutForSend).toMatchInlineSnapshot(`20000`);
});

test('setter should set the timeout for sending activity', async () => {
const { pageObjects } = await setupWebDriver();

await pageObjects.runHook('useTimeoutForSend', [], result => result[1](1000));

const [timeoutForSend] = await pageObjects.runHook('useTimeoutForSend');

expect(timeoutForSend).toMatchInlineSnapshot(`1000`);
});
33 changes: 33 additions & 0 deletions __tests__/hooks/useUserID.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { timeouts } from '../constants.json';

// selenium-webdriver API doc:
// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html

jest.setTimeout(timeouts.test);

test('getter should return user ID set in props', async () => {
const { pageObjects } = await setupWebDriver({
props: {
userID: 'u-12345'
}
});

const [userID] = await pageObjects.runHook('useUserID');

expect(userID).toMatchInlineSnapshot(`"u-12345"`);
});

test('getter should return empty string if not set in props', async () => {
const { pageObjects } = await setupWebDriver();

const [userID] = await pageObjects.runHook('useUserID');

expect(userID).toMatchInlineSnapshot(`""`);
});

test('setter should be falsy', async () => {
const { pageObjects } = await setupWebDriver();
const [_, setUserID] = await pageObjects.runHook('useUserID');

expect(setUserID).toBeFalsy();
});
33 changes: 33 additions & 0 deletions __tests__/hooks/useUsername.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { timeouts } from '../constants.json';

// selenium-webdriver API doc:
// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html

jest.setTimeout(timeouts.test);

test('getter should return username set in props', async () => {
const { pageObjects } = await setupWebDriver({
props: {
username: 'u-12345'
}
});

const [username] = await pageObjects.runHook('useUsername');

expect(username).toMatchInlineSnapshot(`"u-12345"`);
});

test('getter should return undefined if not set in props', async () => {
const { pageObjects } = await setupWebDriver();

const [username] = await pageObjects.runHook('useUsername');

expect(username).toMatchInlineSnapshot(`"Happy Web Chat user"`);
});

test('setter should be falsy', async () => {
const { pageObjects } = await setupWebDriver();
const [_, setUsername] = await pageObjects.runHook('useUsername');

expect(setUsername).toBeFalsy();
});
11 changes: 5 additions & 6 deletions packages/component/src/BasicTranscript.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import connectToWebChat from './connectToWebChat';
import ScrollToEndButton from './Activity/ScrollToEndButton';
import SpeakActivity from './Activity/Speak';
import useActivities from './hooks/useActivities';
import useGroupTimestamp from './hooks/useGroupTimestamp';
import useStyleOptions from './hooks/useStyleOptions';
import useStyleSet from './hooks/useStyleSet';

Expand Down Expand Up @@ -59,10 +60,11 @@ function sameTimestampGroup(activityX, activityY, groupTimestamp) {
return false;
}

const BasicTranscript = ({ activityRenderer, attachmentRenderer, className, groupTimestamp, webSpeechPonyfill }) => {
const [activities] = useActivities();
const BasicTranscript = ({ activityRenderer, attachmentRenderer, className, webSpeechPonyfill }) => {
const [{ activities: activitiesStyleSet, activity: activityStyleSet }] = useStyleSet();
const [{ hideScrollToEndButton }] = useStyleOptions();
const [activities] = useActivities();
const [groupTimestamp] = useGroupTimestamp();

const { speechSynthesis, SpeechSynthesisUtterance } = webSpeechPonyfill || {};

Expand Down Expand Up @@ -131,24 +133,21 @@ const BasicTranscript = ({ activityRenderer, attachmentRenderer, className, grou

BasicTranscript.defaultProps = {
className: '',
groupTimestamp: true,
webSpeechPonyfill: undefined
};

BasicTranscript.propTypes = {
activityRenderer: PropTypes.func.isRequired,
attachmentRenderer: PropTypes.func.isRequired,
className: PropTypes.string,
groupTimestamp: PropTypes.oneOfType([PropTypes.bool.isRequired, PropTypes.number.isRequired]),
webSpeechPonyfill: PropTypes.shape({
speechSynthesis: PropTypes.any,
SpeechSynthesisUtterance: PropTypes.any
})
};

export default connectToWebChat(({ activityRenderer, attachmentRenderer, groupTimestamp, webSpeechPonyfill }) => ({
export default connectToWebChat(({ activityRenderer, attachmentRenderer, webSpeechPonyfill }) => ({
activityRenderer,
attachmentRenderer,
groupTimestamp,
webSpeechPonyfill
}))(BasicTranscript);
10 changes: 5 additions & 5 deletions packages/component/src/SendBox/ConnectivityStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import connectToWebChat from '../connectToWebChat';
import ErrorNotificationIcon from '../Attachment/Assets/ErrorNotificationIcon';
import ScreenReaderText from '../ScreenReaderText';
import SpinnerAnimation from '../Attachment/Assets/SpinnerAnimation';
import useConnectivityStatus from '../hooks/useConnectivityStatus';
import useLocalize from '../hooks/useLocalize';
import useStyleSet from '../hooks/useStyleSet';
import WarningNotificationIcon from '../Attachment/Assets/WarningNotificationIcon';
Expand Down Expand Up @@ -51,7 +52,8 @@ DebouncedConnectivityStatus.propTypes = {
const connectConnectivityStatus = (...selectors) =>
connectToWebChat(({ connectivityStatus, language }) => ({ connectivityStatus, language }), ...selectors);

const ConnectivityStatus = ({ connectivityStatus }) => {
const ConnectivityStatus = () => {
const [connectivityStatus] = useConnectivityStatus();
const [
{
connectivityNotification: connectivityNotificationStyleSet,
Expand Down Expand Up @@ -175,8 +177,6 @@ const ConnectivityStatus = ({ connectivityStatus }) => {
);
};

ConnectivityStatus.propTypes = {
connectivityStatus: PropTypes.string.isRequired
};
export default ConnectivityStatus;

export default connectConnectivityStatus()(ConnectivityStatus);
export { connectConnectivityStatus };
12 changes: 11 additions & 1 deletion packages/component/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import useLastTypingAt from './useLastTypingAt';
import useLocalize from './useLocalize';
import useLocalizeDate from './useLocalizeDate';
import usePerformCardAction from './usePerformCardAction';
import useConnectivityStatus from './useConnectivityStatus';
import useGroupTimestamp from './useGroupTimestamp';
import usePostActivity from './usePostActivity';
import useReferenceGrammarID from './useReferenceGrammarID';
import useRenderMarkdownAsHTML from './useRenderMarkdownAsHTML';
Expand All @@ -24,6 +26,9 @@ import useStyleOptions from './useStyleOptions';
import useStyleSet from './useStyleSet';
import useSubmitSendBox from './useSubmitSendBox';
import useSuggestedActions from './useSuggestedActions';
import useTimeoutForSend from './useTimeoutForSend';
import useUserID from './useUserID';
import useUsername from './useUsername';

import { useSendBoxDictationStarted } from '../BasicSendBox';
import { useTextBoxSubmit } from '../SendBox/TextBox';
Expand All @@ -36,6 +41,8 @@ export {
useDisabled,
useEmitTypingIndicator,
useFocusSendBox,
useConnectivityStatus,
useGroupTimestamp,
useLanguage,
useLastTypingAt,
useLocalize,
Expand All @@ -58,5 +65,8 @@ export {
useSubmitSendBox,
useSuggestedActions,
useTextBoxSubmit,
useTypingIndicatorVisible
useTimeoutForSend,
useTypingIndicatorVisible,
useUserID,
useUsername
};
5 changes: 5 additions & 0 deletions packages/component/src/hooks/useConnectivityStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { useSelector } from '../WebChatReduxContext';

export default function useConnectivityStatus() {
return [useSelector(({ connectivityStatus }) => connectivityStatus)];
}
7 changes: 7 additions & 0 deletions packages/component/src/hooks/useGroupTimestamp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useContext } from 'react';

import WebChatUIContext from '../WebChatUIContext';

export default function useGroupTimestamp() {
return [useContext(WebChatUIContext).groupTimestamp];
}
8 changes: 8 additions & 0 deletions packages/component/src/hooks/useTimeoutForSend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useContext } from 'react';

import { useSelector } from '../WebChatReduxContext';
import WebChatUIContext from '../WebChatUIContext';

export default function useTimeoutForSend() {
return [useSelector(({ sendTimeout }) => sendTimeout), useContext(WebChatUIContext).setSendTimeout];
}
7 changes: 7 additions & 0 deletions packages/component/src/hooks/useUserID.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useContext } from 'react';

import WebChatUIContext from '../WebChatUIContext';

export default function useUserID() {
return [useContext(WebChatUIContext).userID];
}
7 changes: 7 additions & 0 deletions packages/component/src/hooks/useUsername.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useContext } from 'react';

import WebChatUIContext from '../WebChatUIContext';

export default function useUsername() {
return [useContext(WebChatUIContext).username];
}

0 comments on commit f5fa609

Please sign in to comment.