diff --git a/CHANGELOG.md b/CHANGELOG.md index 771b1a0d66..faf5b3952e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fix [#2379](https://github.com/microsoft/BotFramework-WebChat/issues/2379). Speech synthesis can be configured off by passing `null`, by [@compulim](https://github.com/compulim) in PR [#2408](https://github.com/microsoft/BotFramework-WebChat/pull/2408) - Fix [#2418](https://github.com/microsoft/BotFramework-WebChat/issues/2418). Connectivity status should not waste-render every 400 ms, by [@compulim](https://github.com/compulim) in PR [#2419](https://github.com/microsoft/BotFramework-WebChat/pull/2419) - Fix [Emulator:#1823](https://github.com/microsoft/BotFramework-Emulator/issues/1823). Fix Sendbox "Type your message" being read twice by AT, by [@corinagum](https://github.com/corinagum) in PR [#2423](https://github.com/microsoft/BotFramework-WebChat/pull/2423) +- Fix [#2415](https://github.com/microsoft/BotFramework-WebChat/issues/2415) and [#2416](https://github.com/microsoft/BotFramework-WebChat/issues/2416). Fix receipt card rendering, by [@compulim](https://github.com/compulim) in PR [#2417](https://github.com/microsoft/BotFramework-WebChat/issues/2417) +- Fix [#2415](https://github.com/microsoft/BotFramework-WebChat/issues/2415) and [#2416](https://github.com/microsoft/BotFramework-WebChat/issues/2416). Fix Adaptive Cards cannot be disabled on-the-fly, by [@compulim](https://github.com/compulim) in PR [#2417](https://github.com/microsoft/BotFramework-WebChat/issues/2417) ### Added diff --git a/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-breakfast-card-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-breakfast-card-1-snap.png new file mode 100644 index 0000000000..9a7fdd7e3b Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-breakfast-card-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-breakfast-card-with-custom-host-config-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-breakfast-card-with-custom-host-config-1-snap.png new file mode 100644 index 0000000000..d7cb9856b2 Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-breakfast-card-with-custom-host-config-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-disable-card-inputs-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-disable-card-inputs-1-snap.png new file mode 100644 index 0000000000..ba6a36849a Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-disable-card-inputs-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-disable-card-inputs-2-snap.png b/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-disable-card-inputs-2-snap.png new file mode 100644 index 0000000000..b90d0f878a Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-disable-card-inputs-2-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-disable-card-inputs-3-snap.png b/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-disable-card-inputs-3-snap.png new file mode 100644 index 0000000000..100dac33a3 Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/adaptive-cards-js-disable-card-inputs-3-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-audio-card-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-audio-card-1-snap.png new file mode 100644 index 0000000000..976554597a Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-audio-card-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-breakfast-card-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-breakfast-card-1-snap.png new file mode 100644 index 0000000000..9a7fdd7e3b Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-breakfast-card-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-hero-card-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-hero-card-1-snap.png new file mode 100644 index 0000000000..3198745df5 Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-hero-card-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-oauth-card-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-oauth-card-1-snap.png new file mode 100644 index 0000000000..e421458f44 Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-oauth-card-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-receipt-card-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-receipt-card-1-snap.png new file mode 100644 index 0000000000..7a22e096d3 Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-receipt-card-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-sign-in-card-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-sign-in-card-1-snap.png new file mode 100644 index 0000000000..d54760534a Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-sign-in-card-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-thumbnail-card-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-thumbnail-card-1-snap.png new file mode 100644 index 0000000000..601f061760 Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/rich-cards-js-thumbnail-card-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/scroll-to-bottom-js-clicking-new-messages-button-should-scroll-to-end-and-stick-to-bottom-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/scroll-to-bottom-js-clicking-new-messages-button-should-scroll-to-end-and-stick-to-bottom-1-snap.png new file mode 100644 index 0000000000..73d52d9ee3 Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/scroll-to-bottom-js-clicking-new-messages-button-should-scroll-to-end-and-stick-to-bottom-1-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/scroll-to-bottom-js-clicking-new-messages-button-should-scroll-to-end-and-stick-to-bottom-2-snap.png b/__tests__/__image_snapshots__/chrome-docker/scroll-to-bottom-js-clicking-new-messages-button-should-scroll-to-end-and-stick-to-bottom-2-snap.png new file mode 100644 index 0000000000..df78ec7df3 Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/scroll-to-bottom-js-clicking-new-messages-button-should-scroll-to-end-and-stick-to-bottom-2-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/scroll-to-bottom-js-clicking-new-messages-button-should-scroll-to-end-and-stick-to-bottom-3-snap.png b/__tests__/__image_snapshots__/chrome-docker/scroll-to-bottom-js-clicking-new-messages-button-should-scroll-to-end-and-stick-to-bottom-3-snap.png new file mode 100644 index 0000000000..22c4f648ba Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/scroll-to-bottom-js-clicking-new-messages-button-should-scroll-to-end-and-stick-to-bottom-3-snap.png differ diff --git a/__tests__/adaptiveCards.js b/__tests__/adaptiveCards.js new file mode 100644 index 0000000000..def884d217 --- /dev/null +++ b/__tests__/adaptiveCards.js @@ -0,0 +1,79 @@ +import { imageSnapshotOptions, timeouts } from './constants.json'; + +import allImagesLoaded from './setup/conditions/allImagesLoaded'; +import minNumActivitiesShown from './setup/conditions/minNumActivitiesShown'; +import scrollToBottomCompleted from './setup/conditions/scrollToBottomCompleted'; +import uiConnected from './setup/conditions/uiConnected'; + +import createAdaptiveCardsHostConfig from '../packages/bundle/src/adaptiveCards/Styles/adaptiveCardHostConfig'; +import defaultStyleOptions from '../packages/component/src/Styles/defaultStyleOptions'; + +// selenium-webdriver API doc: +// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html + +jest.setTimeout(timeouts.test); + +test('breakfast card', async () => { + const { driver, pageObjects } = await setupWebDriver(); + + await driver.wait(uiConnected(), timeouts.directLine); + await pageObjects.sendMessageViaSendBox('card breakfast', { waitForSend: true }); + + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allImagesLoaded(), 2000); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); +}); + +test('breakfast card with custom host config', async () => { + const adaptiveCardHostConfig = createAdaptiveCardsHostConfig({ ...defaultStyleOptions, bubbleTextColor: '#FF0000' }); + + const { driver, pageObjects } = await setupWebDriver({ + props: { + adaptiveCardHostConfig + } + }); + + await driver.wait(uiConnected(), timeouts.directLine); + await pageObjects.sendMessageViaSendBox('card breakfast', { waitForSend: true }); + + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allImagesLoaded(), 2000); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); +}); + +test('disable card inputs', async () => { + const { driver, pageObjects } = await setupWebDriver(); + + await driver.wait(uiConnected(), timeouts.directLine); + await pageObjects.sendMessageViaSendBox('card inputs', { waitForSend: true }); + + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allImagesLoaded(), 2000); + await driver.wait(scrollToBottomCompleted(), timeouts.scrollToBottom); + + await driver.executeScript(() => { + document.querySelector('.ac-input input[type="checkbox"]').checked = true; + }); + + expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions); + + await pageObjects.updateProps({ disabled: true }); + + expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions); + + await pageObjects.updateProps({ disabled: false }); + await driver.executeScript(() => { + document.querySelector('.ac-actionSet button:nth-of-type(2)').click(); + }); + + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); + await driver.wait(scrollToBottomCompleted(), timeouts.scrollToBottom); + + expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions); +}); diff --git a/__tests__/richCards.js b/__tests__/richCards.js new file mode 100644 index 0000000000..9563cdb394 --- /dev/null +++ b/__tests__/richCards.js @@ -0,0 +1,96 @@ +import { imageSnapshotOptions, timeouts } from './constants.json'; + +import allImagesLoaded from './setup/conditions/allImagesLoaded'; +import minNumActivitiesShown from './setup/conditions/minNumActivitiesShown'; +import scrollToBottomCompleted from './setup/conditions/scrollToBottomCompleted'; +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('audio card', async () => { + const { driver, pageObjects } = await setupWebDriver(); + + await driver.wait(uiConnected(), timeouts.directLine); + await pageObjects.sendMessageViaSendBox('audiocard', { waitForSend: true }); + + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allImagesLoaded(), 2000); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); +}); + +test('hero card', async () => { + const { driver, pageObjects } = await setupWebDriver(); + + await driver.wait(uiConnected(), timeouts.directLine); + await pageObjects.sendMessageViaSendBox('herocard', { waitForSend: true }); + + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allImagesLoaded(), 2000); + await driver.wait(scrollToBottomCompleted(), timeouts.scrollToBottom); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); +}); + +test('oauth card', async () => { + const { driver, pageObjects } = await setupWebDriver({ useProductionBot: true }); + + await driver.wait(uiConnected(), timeouts.directLine); + await pageObjects.sendMessageViaSendBox('oauth', { waitForSend: true }); + + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allImagesLoaded(), 2000); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); +}); + +test('receipt card', async () => { + const { driver, pageObjects } = await setupWebDriver(); + + await driver.wait(uiConnected(), timeouts.directLine); + await pageObjects.sendMessageViaSendBox('receiptcard', { waitForSend: true }); + + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allImagesLoaded(), 2000); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); +}); + +test('sign-in card', async () => { + const { driver, pageObjects } = await setupWebDriver({ useProductionBot: true }); + + await driver.wait(uiConnected(), timeouts.directLine); + await pageObjects.sendMessageViaSendBox('signin', { waitForSend: true }); + + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allImagesLoaded(), 2000); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); +}); + +test('thumbnail card', async () => { + const { driver, pageObjects } = await setupWebDriver(); + + await driver.wait(uiConnected(), timeouts.directLine); + await pageObjects.sendMessageViaSendBox('thumbnailcard', { waitForSend: true }); + + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allImagesLoaded(), 2000); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); +}); diff --git a/__tests__/scrollToBottom.js b/__tests__/scrollToBottom.js index 9cbfd3b117..65b24c0f19 100644 --- a/__tests__/scrollToBottom.js +++ b/__tests__/scrollToBottom.js @@ -34,3 +34,29 @@ test('should stick to bottom if submitting an Adaptive Card while suggested acti expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); }); + +test('clicking "New messages" button should scroll to end and stick to bottom', async () => { + const { driver, pageObjects } = await setupWebDriver(); + + await driver.wait(uiConnected(), timeouts.directLine); + + await pageObjects.sendMessageViaSendBox('help'); + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(scrollToBottomCompleted(), timeouts.scrollToBottom); + + await driver.executeScript(() => { + document.querySelector('[role="log"] > *').scrollTop = 0; + }); + + expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions); + + await pageObjects.clickScrollToBottomButton(); + await driver.wait(scrollToBottomCompleted(), timeouts.scrollToBottom); + + expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions); + + await pageObjects.sendMessageViaSendBox('Hello, World!'); + await driver.wait(scrollToBottomCompleted(), timeouts.scrollToBottom); + + expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions); +}); diff --git a/__tests__/setup/conditions/scrollToBottomCompleted.js b/__tests__/setup/conditions/scrollToBottomCompleted.js index 2fee80e1ea..57380e0b01 100644 --- a/__tests__/setup/conditions/scrollToBottomCompleted.js +++ b/__tests__/setup/conditions/scrollToBottomCompleted.js @@ -1,28 +1,11 @@ import { Condition } from 'selenium-webdriver'; -import { timeouts } from '../../constants.json'; - export default function scrollToBottomCompleted() { - return new Condition('for UI to scroll to bottom', async driver => { - const done = await driver.executeAsyncScript((timeoutInMS, callback) => { + return new Condition('for UI to scroll to bottom', driver => + driver.executeScript(() => { const scrollable = document.querySelector('[role="log"] > *'); - // If we do not receive any "scroll" event at all, probably we are at the bottom. - const defaultTimeout = setTimeout(() => callback(true), timeoutInMS); - let timeout; - const handleScroll = () => { - clearTimeout(defaultTimeout); - clearTimeout(timeout); - - timeout = setTimeout(() => { - scrollable.removeEventListener('scroll', handleScroll); - callback(true); - }, 200); - }; - - scrollable.addEventListener('scroll', handleScroll); - }, timeouts.scrollToBottom); - - return done; - }); + return scrollable && scrollable.offsetHeight + scrollable.scrollTop === scrollable.scrollHeight; + }) + ); } diff --git a/__tests__/setup/elements/getScrollToBottomButton.js b/__tests__/setup/elements/getScrollToBottomButton.js new file mode 100644 index 0000000000..093bbf8a9a --- /dev/null +++ b/__tests__/setup/elements/getScrollToBottomButton.js @@ -0,0 +1,5 @@ +import { By } from 'selenium-webdriver'; + +export default async function getScrollToBottomButton(driver) { + return await driver.findElement(By.css('[role="log"] > button:last-child')); +} diff --git a/__tests__/setup/marshal.js b/__tests__/setup/marshal.js new file mode 100644 index 0000000000..1a3e39263f --- /dev/null +++ b/__tests__/setup/marshal.js @@ -0,0 +1,22 @@ +export default function marshal(props) { + return ( + props && + Object.keys(props).reduce( + (nextProps, key) => { + const { [key]: value } = props; + + if (typeof value === 'function') { + nextProps[key] = `() => ${value.toString()}`; + nextProps.__evalKeys.push(key); + } else { + nextProps[key] = value; + } + + return nextProps; + }, + { + __evalKeys: [] + } + ) + ); +} diff --git a/__tests__/setup/pageObjects/clickScrollToBottomButton.js b/__tests__/setup/pageObjects/clickScrollToBottomButton.js new file mode 100644 index 0000000000..ed04bf2be1 --- /dev/null +++ b/__tests__/setup/pageObjects/clickScrollToBottomButton.js @@ -0,0 +1,5 @@ +import getScrollToBottomButton from '../elements/getScrollToBottomButton'; + +export default async function clickScrollToBottomButton(driver) { + (await getScrollToBottomButton(driver)).click(); +} diff --git a/__tests__/setup/pageObjects/index.js b/__tests__/setup/pageObjects/index.js index 8b275e67e0..01cd2855b2 100644 --- a/__tests__/setup/pageObjects/index.js +++ b/__tests__/setup/pageObjects/index.js @@ -1,4 +1,5 @@ import clickMicrophoneButton from './clickMicrophoneButton'; +import clickScrollToBottomButton from './clickScrollToBottomButton'; import clickSendButton from './clickSendButton'; import clickSuggestedActionButton from './clickSuggestedActionButton'; import dispatchAction from './dispatchAction'; @@ -19,6 +20,7 @@ import sendMessageViaSendBox from './sendMessageViaSendBox'; import sendTextToClipboard from './sendTextToClipboard'; import startSpeechSynthesize from './startSpeechSynthesize'; import typeOnSendBox from './typeOnSendBox'; +import updateProps from './updateProps'; function mapMap(map, mapper) { return Object.keys(map).reduce((final, key) => { @@ -32,6 +34,7 @@ export default function pageObjects(driver) { return mapMap( { clickMicrophoneButton, + clickScrollToBottomButton, clickSendButton, clickSuggestedActionButton, dispatchAction, @@ -51,7 +54,8 @@ export default function pageObjects(driver) { sendMessageViaSendBox, sendTextToClipboard, startSpeechSynthesize, - typeOnSendBox + typeOnSendBox, + updateProps }, fn => fn.bind(null, driver) ); diff --git a/__tests__/setup/pageObjects/updateProps.js b/__tests__/setup/pageObjects/updateProps.js new file mode 100644 index 0000000000..ab33f05de5 --- /dev/null +++ b/__tests__/setup/pageObjects/updateProps.js @@ -0,0 +1,7 @@ +import marshal from '../marshal'; + +export default function updateProps(driver, mergeProps) { + return driver.executeScript(mergeProps => { + window.WebChatTest.updateProps(unmarshal(mergeProps)); + }, marshal(mergeProps)); +} diff --git a/__tests__/setup/setupTestFramework.js b/__tests__/setup/setupTestFramework.js index e201307f53..56fcd2a4a1 100644 --- a/__tests__/setup/setupTestFramework.js +++ b/__tests__/setup/setupTestFramework.js @@ -7,6 +7,7 @@ import getPort from 'get-port'; import handler from 'serve-handler'; import createPageObjects from './pageObjects/index'; +import marshal from './marshal'; import retry from './retry'; import setupTestEnvironment from './setupTestEnvironment'; @@ -15,29 +16,6 @@ const BROWSER_NAME = process.env.WEBCHAT_TEST_ENV || 'chrome-docker'; // const BROWSER_NAME = 'chrome-local'; const NUM_RETRIES = 3; -function marshal(props) { - return ( - props && - Object.keys(props).reduce( - (nextProps, key) => { - const { [key]: value } = props; - - if (typeof value === 'function') { - nextProps[key] = `() => ${value.toString()}`; - nextProps.__evalKeys.push(key); - } else { - nextProps[key] = value; - } - - return nextProps; - }, - { - __evalKeys: [] - } - ) - ); -} - expect.extend({ toMatchImageSnapshot: configureToMatchImageSnapshot({ customSnapshotsDir: join(__dirname, '../__image_snapshots__', BROWSER_NAME) diff --git a/__tests__/setup/web/index.html b/__tests__/setup/web/index.html index 93a402ad38..f8b469d126 100644 --- a/__tests__/setup/web/index.html +++ b/__tests__/setup/web/index.html @@ -102,7 +102,7 @@ await loadScript('/webchat-instrumented.js'); let directLine; - + if (!useProductionBot && !createDirectLine) { directLine = window.MockBotAdapter.createDirectLine({}); } else { @@ -118,11 +118,11 @@ throw err; } }, 3); - + createDirectLine || (createDirectLine = window.WebChat.createDirectLine); directLine = (createDirectLine || window.WebChat.createDirectLine)({ token }); } - + const store = window.WebChatTest.store = window.WebChat.createStore(storeInitialState, store => { const setupMiddleware = storeMiddleware(store); @@ -137,15 +137,29 @@ }; }); - window.WebChat.renderWebChat({ + props = { directLine, store, styleSet: createStyleSet && createStyleSet(props.styleOptions), username: 'Happy Web Chat user', ...props - }, document.getElementById('webchat')); + }; + + renderWebChat = props => { + console.log(props); + + window.WebChat.renderWebChat(props, document.getElementById('webchat')); + }; + + renderWebChat(props); document.querySelector('#webchat > *').focus(); + + window.WebChatTest.updateProps = mergeProps => { + props = { ...props, ...mergeProps }; + + renderWebChat(props); + }; } window.WebChatTest = { diff --git a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.js b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.js index e6e634c740..b06be59035 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.js +++ b/packages/bundle/src/adaptiveCards/Attachment/AdaptiveCardRenderer.js @@ -28,12 +28,29 @@ class AdaptiveCardRenderer extends React.PureComponent { componentDidMount() { this.renderCard(); + this.refreshDisabled(); } - componentDidUpdate({ adaptiveCard: prevAdaptiveCard }) { - const { adaptiveCard } = this.props; + componentDidUpdate({ adaptiveCard: prevAdaptiveCard, disabled: prevDisabled }) { + const { adaptiveCard, disabled } = this.props; prevAdaptiveCard !== adaptiveCard && this.renderCard(); + !prevDisabled !== !disabled && this.refreshDisabled(); + } + + refreshDisabled() { + const { disabled } = this.props; + const { current } = this.contentRef; + + if (current) { + const { + children: [element] + } = current; + + [].forEach.call(element.querySelectorAll('button, input, select, textarea'), input => { + input.disabled = disabled; + }); + } } handleClick({ target }) { @@ -91,7 +108,7 @@ class AdaptiveCardRenderer extends React.PureComponent { renderCard() { const { contentRef: { current }, - props: { adaptiveCard, adaptiveCardHostConfig, disabled, renderMarkdown }, + props: { adaptiveCard, adaptiveCardHostConfig, renderMarkdown }, state: { error } } = this; @@ -139,22 +156,17 @@ class AdaptiveCardRenderer extends React.PureComponent { error && this.setState(() => ({ error: null })); - if (disabled) { - const hyperlinks = element.querySelectorAll('a'); - const inputs = element.querySelectorAll('button, input, select, textarea'); + [].forEach.call(element.querySelectorAll('a'), hyperlink => { + hyperlink.addEventListener('click', event => { + const { disabled } = this.props; - [].forEach.call(inputs, input => { - input.disabled = true; - }); - - [].forEach.call(hyperlinks, hyperlink => { - hyperlink.addEventListener('click', event => { + if (disabled) { event.preventDefault(); event.stopImmediatePropagation(); event.stopPropagation(); - }); + } }); - } + }); const [firstChild] = current.children; diff --git a/packages/bundle/src/adaptiveCards/Attachment/ReceiptCardAttachment.js b/packages/bundle/src/adaptiveCards/Attachment/ReceiptCardAttachment.js index c8953c2d20..b84c80b16a 100644 --- a/packages/bundle/src/adaptiveCards/Attachment/ReceiptCardAttachment.js +++ b/packages/bundle/src/adaptiveCards/Attachment/ReceiptCardAttachment.js @@ -21,16 +21,16 @@ const ReceiptCardAttachment = ({ const builtCard = useMemo(() => { const builder = new AdaptiveCardBuilder(adaptiveCards, options); const { HorizontalAlignment, TextSize, TextWeight } = adaptiveCards; - const { buttons, fact, items, tax, title, total, vat } = content; + const { buttons, facts, items, tax, title, total, vat } = content; const { richCardWrapTitle } = options; if (content) { builder.addTextBlock(title, { size: TextSize.Medium, weight: TextWeight.Bolder, wrap: richCardWrapTitle }); - if (fact) { + if (facts) { const [firstFactColumn, lastFactColumn] = builder.addColumnSet([75, 25]); - fact.map(({ key, value }) => { + facts.map(({ key, value }) => { builder.addTextBlock(key, { size: TextSize.Medium }, firstFactColumn); builder.addTextBlock( value, @@ -62,48 +62,48 @@ const ReceiptCardAttachment = ({ ); builder.addTextBlock(subtitle, { size: TextSize.Medium, wrap: richCardWrapTitle }, itemTitleColumn); builder.addTextBlock(price, { horizontalAlignment: HorizontalAlignment.Right }, itemPriceColumn); + }); - if (!nullOrUndefined(vat)) { - const vatCol = builder.addColumnSet([75, 25]); + if (!nullOrUndefined(vat)) { + const vatCol = builder.addColumnSet([75, 25]); - builder.addTextBlock( - localize('VAT', language), - { size: TextSize.Medium, weight: TextWeight.Bolder }, - vatCol[0] - ); - builder.addTextBlock(vat, { horizontalAlignment: HorizontalAlignment.Right }, vatCol[1]); - } + builder.addTextBlock( + localize('VAT', language), + { size: TextSize.Medium, weight: TextWeight.Bolder }, + vatCol[0] + ); + builder.addTextBlock(vat, { horizontalAlignment: HorizontalAlignment.Right }, vatCol[1]); + } - if (!nullOrUndefined(tax)) { - const taxCol = builder.addColumnSet([75, 25]); + if (!nullOrUndefined(tax)) { + const taxCol = builder.addColumnSet([75, 25]); - builder.addTextBlock( - localize('Tax', language), - { size: TextSize.Medium, weight: TextWeight.Bolder }, - taxCol[0] - ); - builder.addTextBlock(tax, { horizontalAlignment: HorizontalAlignment.Right }, taxCol[1]); - } + builder.addTextBlock( + localize('Tax', language), + { size: TextSize.Medium, weight: TextWeight.Bolder }, + taxCol[0] + ); + builder.addTextBlock(tax, { horizontalAlignment: HorizontalAlignment.Right }, taxCol[1]); + } - if (!nullOrUndefined(total)) { - const totalCol = builder.addColumnSet([75, 25]); - - builder.addTextBlock( - localize('Total', language), - { size: TextSize.Medium, weight: TextWeight.Bolder }, - totalCol[0] - ); - builder.addTextBlock( - total, - { horizontalAlignment: HorizontalAlignment.Right, size: TextSize.Medium, weight: TextWeight.Bolder }, - totalCol[1] - ); - } + if (!nullOrUndefined(total)) { + const totalCol = builder.addColumnSet([75, 25]); + + builder.addTextBlock( + localize('Total', language), + { size: TextSize.Medium, weight: TextWeight.Bolder }, + totalCol[0] + ); + builder.addTextBlock( + total, + { horizontalAlignment: HorizontalAlignment.Right, size: TextSize.Medium, weight: TextWeight.Bolder }, + totalCol[1] + ); + } - builder.addButtons(buttons); + builder.addButtons(buttons); - return builder.card; - }); + return builder.card; } }, [adaptiveCards, content, language, options]); @@ -122,7 +122,7 @@ ReceiptCardAttachment.propTypes = { attachment: PropTypes.shape({ content: PropTypes.shape({ buttons: PropTypes.array, - fact: PropTypes.arrayOf( + facts: PropTypes.arrayOf( PropTypes.shape({ key: PropTypes.string, value: PropTypes.string