Skip to content

Commit

Permalink
Add nub to chat bubbles (#2137)
Browse files Browse the repository at this point in the history
* First version

* Clean up

* Fix layout

* Precise size

* Update border color

* Clean up

* Clean up

* Clean up

* Revert

* Use DOM for parsing bubble border

* Add style bubble options

* Fix style bubble

* Prettier

* Fix eslint

* Update tests

* Add entry

* Update entry

* Fix conflict

* Revert changes

* Rename to handleBubbleBorderChange

* Clean up CSS
  • Loading branch information
compulim authored Jul 18, 2019
1 parent 84d194b commit 9d1a9ab
Show file tree
Hide file tree
Showing 42 changed files with 834 additions and 61 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]

### Changed

- `*`: Bumps all dev dependencies to latest version, by [@compulim](https://github.com/compulim), in PR [#2182](https://github.com/microsoft/BotFramework-WebChat/pull/2182), notably
- [`@babel/*@7.5.4`](https://www.npmjs.com/package/@babel/core)
- [`[email protected]`](https://www.npmjs.com/package/jest)
Expand All @@ -47,6 +48,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fix [#2160](https://github.com/microsoft/BotFramework-WebChat/issues/2160). Clear suggested actions after clicking on a suggested actions of type `openUrl`, by [@tdurnford](https://github.com/tdurnford) in PR [#2190](https://github.com/microsoft/BotFramework-WebChat/pull/2190)
- Fix [#2187](https://github.com/microsoft/BotFramework-WebChat/issues/2187). Bump core-js and update core-js modules on index-es5.js, by [@corinagum](https://github.com/corinagum) in PR [#2195](https://github.com/microsoft/BotFramework-WebChat/pull/2195)

### Added

- Added bubble nub and style options, by [@compulim](https://github.com/compulim), in PR [#2137](https://github.com/Microsoft/BotFramework-WebChat/pull/2137)

## [4.5.0] - 2019-07-10

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
131 changes: 131 additions & 0 deletions __tests__/bubbleBorder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { imageSnapshotOptions, timeouts } from './constants.json';

import allImagesLoaded from './setup/conditions/allImagesLoaded';
import minNumActivitiesShown from './setup/conditions/minNumActivitiesShown';
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);

async function sendMessageAndMatchSnapshot(driver, pageObjects, message) {
await driver.wait(uiConnected(), timeouts.directLine);
await pageObjects.sendMessageViaSendBox(message);

await driver.wait(minNumActivitiesShown(3), timeouts.directLine);

const base64PNG = await driver.takeScreenshot();

expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions);
}

describe('bubble border', () => {
test('with color, radius, style, and width set', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
styleOptions: {
bubbleBorderColor: 'Red',
bubbleBorderRadius: 10,
bubbleBorderStyle: 'dashed',
bubbleBorderWidth: 2,

bubbleFromUserBorderColor: 'Green',
bubbleFromUserBorderRadius: 20,
bubbleFromUserBorderStyle: 'dotted',
bubbleFromUserBorderWidth: 3
}
},
zoom: 3
});

await sendMessageAndMatchSnapshot(driver, pageObjects, 'echo Hello, World!');
});

describe('with deprecated border style', () => {
test('with color, radius, style, and width set', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
styleOptions: {
bubbleBorder: 'dashed 2px Red',
bubbleBorderRadius: 10,

bubbleFromUserBorder: 'dotted 3px Green',
bubbleFromUserBorderRadius: 20
}
},
zoom: 3
});

await sendMessageAndMatchSnapshot(driver, pageObjects, 'echo Hello, World!');
});

test('of "dashed 2px Red" and "dotted 2px Green"', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
styleOptions: {
bubbleBorder: 'dashed 2px Red',
bubbleFromUserBorder: 'dotted 2px Green'
}
}
});

await driver.wait(uiConnected(), timeouts.directLine);
await pageObjects.sendMessageViaSendBox('Hello, World!');
await driver.wait(minNumActivitiesShown(2), timeouts.directLine);

expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions);
});

test('of "dashed" and "dotted"', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
styleOptions: {
bubbleBorder: 'dashed',
bubbleFromUserBorder: 'dotted'
}
}
});

await driver.wait(uiConnected(), timeouts.directLine);
await pageObjects.sendMessageViaSendBox('Hello, World!');
await driver.wait(minNumActivitiesShown(2), timeouts.directLine);

expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions);
});

test('of "2px"', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
styleOptions: {
bubbleBorder: '2px',
bubbleFromUserBorder: '2px'
}
}
});

await driver.wait(uiConnected(), timeouts.directLine);
await pageObjects.sendMessageViaSendBox('Hello, World!');
await driver.wait(minNumActivitiesShown(2), timeouts.directLine);

expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions);
});

test('of "Red" and "Green"', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
styleOptions: {
bubbleBorder: 'Red',
bubbleFromUserBorder: 'Green'
}
}
});

await driver.wait(uiConnected(), timeouts.directLine);
await pageObjects.sendMessageViaSendBox('Hello, World!');
await driver.wait(minNumActivitiesShown(2), timeouts.directLine);

expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions);
});
});
});
216 changes: 216 additions & 0 deletions __tests__/bubbleNub.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import { imageSnapshotOptions, timeouts } from './constants.json';

import allImagesLoaded from './setup/conditions/allImagesLoaded';
import minNumActivitiesShown from './setup/conditions/minNumActivitiesShown';
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);

async function sendMessageAndMatchSnapshot(driver, pageObjects, message) {
await driver.wait(uiConnected(), timeouts.directLine);
await pageObjects.sendMessageViaSendBox(message);

await driver.wait(minNumActivitiesShown(2), timeouts.directLine);
await driver.wait(allImagesLoaded(), timeouts.fetch);

const base64PNG = await driver.takeScreenshot();

expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions);
}

describe('bubble nub', () => {
let props;

test('with standard setup', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
styleOptions: {
bubbleNubOffset: 0,
bubbleNubSize: 10,
bubbleFromUserNubOffset: 0,
bubbleFromUserNubSize: 10
}
},
zoom: 3
});

await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout carousel');
});

beforeEach(() => {
props = {
styleOptions: {
bubbleBorderColor: 'red',
bubbleBorderRadius: 10,
bubbleBorderWidth: 2,
bubbleFromUserBorderColor: 'green',
bubbleFromUserBorderRadius: 10,
bubbleFromUserNubOffset: 0,
bubbleFromUserNubSize: 10,
bubbleFromUserBorderWidth: 2,
bubbleNubOffset: 0,
bubbleNubSize: 10
}
};
});

describe('with avatar initials', () => {
beforeEach(() => {
props = {
...props,
styleOptions: {
...props.styleOptions,
botAvatarInitials: 'WC',
userAvatarInitials: 'WW'
}
};
});

test('and carousel with a message should have nub on message only', async () => {
const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 });

await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout carousel');
});

test('and carousel without a message should not have nubs', async () => {
const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 });

await sendMessageAndMatchSnapshot(driver, pageObjects, 'carousel');
});

test('and stacked without a message should not have nubs', async () => {
const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 });

await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout single');
});

test('and a single message should have nub', async () => {
const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 });

await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!');
});

test('and carousel with a single attachment should have nub on message only', async () => {
const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 });

await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout single carousel');
});
});

describe('without avatar initials', () => {
test('and carousel with a message should have nub on message only and indented', async () => {
const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 });

await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout carousel');
});

test('and carousel without a message should not have nubs and indented', async () => {
const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 });

await sendMessageAndMatchSnapshot(driver, pageObjects, 'carousel');
});

test('and stacked without a message should not have nubs and indented', async () => {
const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 });

await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout single');
});

test('and a single message should have nub and indented', async () => {
const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 });

await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!');
});

test('and carousel with a single attachment should have nub on message only', async () => {
const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 });

await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout single carousel');
});
});

describe('at corner with offset', () => {
test('of 5px should have corner radius of 5px', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
...props,
styleOptions: {
...props.styleOptions,
bubbleFromUserNubOffset: 5,
bubbleNubOffset: 5
}
},
zoom: 3
});

await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!');
});

test('of 10px should have corner radius of 10px', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
...props,
styleOptions: {
...props.styleOptions,
bubbleFromUserNubOffset: 10,
bubbleNubOffset: 10
}
},
zoom: 3
});

await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!');
});

test('of minus 5px should have corner radius of 5px and flipped nub', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
...props,
styleOptions: {
...props.styleOptions,
bubbleFromUserNubOffset: -5,
bubbleNubOffset: -5
}
},
zoom: 3
});

await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!');
});

test('of minus 10px should have corner radius of 10px and flipped nub', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
...props,
styleOptions: {
...props.styleOptions,
bubbleFromUserNubOffset: -10,
bubbleNubOffset: -10
}
},
zoom: 3
});

await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!');
});

test('at bottom should have flipped nub', async () => {
const { driver, pageObjects } = await setupWebDriver({
props: {
...props,
styleOptions: {
...props.styleOptions,
bubbleFromUserNubOffset: 'bottom',
bubbleNubOffset: 'bottom'
}
},
zoom: 3
});

await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!');
});
});
});
6 changes: 3 additions & 3 deletions __tests__/setup/setupTestEnvironment.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Options } from 'selenium-webdriver/chrome';

export default function setupTestEnvironment(browserName, builder, { height, width } = {}) {
export default function setupTestEnvironment(browserName, builder, { height = 640, width = 360, zoom = 1 } = {}) {
switch (browserName) {
case 'chrome-local':
return {
baseURL: 'http://localhost:$PORT/index.html',
builder: builder
.forBrowser('chrome')
.setChromeOptions(
(builder.getChromeOptions() || new Options()).windowSize({ height: height || 640, width: width || 360 })
(builder.getChromeOptions() || new Options()).windowSize({ height: height * zoom, width: width * zoom })
)
};

Expand All @@ -22,7 +22,7 @@ export default function setupTestEnvironment(browserName, builder, { height, wid
.setChromeOptions(
(builder.getChromeOptions() || new Options())
.headless()
.windowSize({ height: height || 640, width: width || 360 })
.windowSize({ height: height * zoom, width: width * zoom })
)
};
}
Expand Down
Loading

0 comments on commit 9d1a9ab

Please sign in to comment.