Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fluent theme] [P0] Add Fluent theme pack #5120

Merged
merged 18 commits into from
Apr 8, 2024
1 change: 1 addition & 0 deletions .github/workflows/pull-request-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ jobs:
./package.json
./package-lock.json
./packages/bundle/dist/
./packages/fluent-theme/dist/
./packages/test/harness/
./packages/test/page-object/dist/
./serve-test.json
Expand Down
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Resolves [#5083](https://github.com/microsoft/BotFramework-WebChat/issues/5083). Added `sendAttachmentOn` style option to send attachments and text in a single activity, by [@ms-jb](https://github.com/ms-jb) and [@compulim](https://github.com/compulim)
- `useSendMessage` hook is updated to support sending attachments with a message
- `useSendBoxAttachments` hook is added to get/set attachments in the send box
- Resolves [#5081](https://github.com/microsoft/BotFramework-WebChat/issues/5081). Added `uploadAccept` and `uploadMultiple` style options, by [@ms-jb](https://github.com/ms-jb)
- Resolves [#5081](https://github.com/microsoft/BotFramework-WebChat/issues/5081). Added `uploadAccept` and `uploadMultiple` style options, by [@ms-jb](https://github.com/ms-jb), in PR [#5048](https://github.com/microsoft/BotFramework-WebChat/pull/5048)
- Added `sendBoxMiddleware` and `sendBoxToolbarMiddleware`, by [@compulim](https://github.com/compulim), in PR [#5120](https://github.com/microsoft/BotFramework-WebChat/pull/5120)
- Added `botframework-webchat-fluent-theme` package for applying Fluent UI theme to Web Chat, by [@compulim](https://github.com/compulim), in PR [#5120](https://github.com/microsoft/BotFramework-WebChat/pull/5120)
- Added `<ThemeProvider>` component to apply theme pack to Web Chat, by [@compulim](https://github.com/compulim), in PR [#5120](https://github.com/microsoft/BotFramework-WebChat/pull/5120)

### Fixed

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.
12 changes: 0 additions & 12 deletions __tests__/hooks/useGroupTimestamp.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,6 @@ test('getter should return group timestamp set in styleOptions', async () => {
expect(groupTimestamp).toMatchInlineSnapshot(`1000`);
});

test('getter should return group timestamp set in props (deprecated)', async () => {
compulim marked this conversation as resolved.
Show resolved Hide resolved
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 styleOptions', async () => {
const { pageObjects } = await setupWebDriver();

Expand Down
15 changes: 1 addition & 14 deletions __tests__/hooks/useTimeoutForSend.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,8 @@ test('getter should return timeout for sending activity', async () => {
expect(timeoutForSend).toMatchInlineSnapshot(`1000`);
});

test('getter should return timeout for sending activity if set in props', async () => {
compulim marked this conversation as resolved.
Show resolved Hide resolved
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 () => {
test('getter should return default timeout for sending activity', async () => {
const { pageObjects } = await setupWebDriver();

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

expect(timeoutForSend).toMatchInlineSnapshot(`20000`);
Expand Down
54 changes: 54 additions & 0 deletions __tests__/html/fluentTheme/simple.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
<script crossorigin="anonymous" src="/__dist__/botframework-webchat-fluent-theme.production.min.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="text/babel">
run(async function () {
const {
React,
ReactDOM: { render },
WebChat: { FluentThemeProvider, ReactWebChat }
} = window; // Imports in UMD fashion.

const { directLine, store } = testHelpers.createDirectLineEmulator();

const App = () => <ReactWebChat directLine={directLine} store={store} />;

render(
<FluentThemeProvider>
<App />
</FluentThemeProvider>,
document.getElementById('webchat')
);

await pageConditions.uiConnected();

await directLine.emulateIncomingActivity(
'Eiusmod anim adipisicing cupidatat adipisicing officia sint qui consequat veniam id aute.'
);

await pageConditions.numActivitiesShown(1);

// THEN: Should render the activity.
await host.snapshot();

// THEN: Fluent theme should be loaded.
expect(
document.querySelector('meta[name="botframework-webchat-fluent-theme:version"]').getAttribute('name')
).toBeTruthy();

expect(window.WebChat.FluentThemeProvider).toBeTruthy();
});
</script>
</body>
</html>
5 changes: 5 additions & 0 deletions __tests__/html/fluentTheme/simple.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */

describe('Fluent theme applied', () => {
test('should render', () => runHTML('fluentTheme/simple'));
});
141 changes: 141 additions & 0 deletions __tests__/html/sendBoxMiddleware/changeModality.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script type="text/babel">
run(async function () {
const {
React,
React: { createContext, useContext, useMemo, useState },
ReactDOM: { render },
WebChat: {
Components: { BasicWebChat, Composer },
hooks: { useActivities }
}
} = window; // Imports in UMD fashion.

const AppContext = createContext({ showBorder: false });

const Border = ({ children }) => {
const { showBorder } = useContext(AppContext);

return (
<div
style={{
outlineColor: 'Red',
outlineOffset: -2,
outlineStyle: 'dashed',
outlineWidth: showBorder ? 2 : 0
}}
>
{children}
</div>
);
};

const { directLine, store } = testHelpers.createDirectLineEmulator();

const OnActivity = () => {
const [activities] = useActivities();
const { setEndOfConversation, setShowBorder } = useContext(AppContext);

const lastActivity = activities[activities.length - 1];

useMemo(
() => setEndOfConversation(lastActivity?.inputHint === 'ignoring'),
[lastActivity, setEndOfConversation]
);

useMemo(() => setShowBorder(lastActivity?.from.role === 'bot'), [lastActivity, setShowBorder]);

return false;
};

const App = () => {
const [showBorder, setShowBorder] = useState(false);
const [endOfConversation, setEndOfConversation] = useState(false);
const context = useMemo(
() => ({ endOfConversation, setEndOfConversation, setShowBorder, showBorder }),
[endOfConversation, setEndOfConversation, setShowBorder, showBorder]
);

const attentionMiddleware = useMemo(
() =>
Object.freeze([
() => next => request => {
const NextComponent = next(request);

return props => (
<Border>
<NextComponent {...props} />
</Border>
);
}
]),
[]
);

const endOfConversationMiddleware = useMemo(
() => Object.freeze([() => () => () => () => <div>Thank you. You can close the chat now.</div>]),
[]
);

const sendBoxMiddleware = useMemo(
() => (endOfConversation ? endOfConversationMiddleware : attentionMiddleware),
[attentionMiddleware, endOfConversation, endOfConversationMiddleware]
);

return (
<AppContext.Provider value={context}>
<Composer directLine={directLine} store={store} sendBoxMiddleware={sendBoxMiddleware}>
<OnActivity />
<BasicWebChat />
</Composer>
</AppContext.Provider>
);
};

render(<App />, document.getElementById('webchat'));

await pageConditions.uiConnected();

// SCENARIO: If the last message is from bot, show a border around the send box.

// WHEN: Received a message from bot.
await directLine.emulateIncomingActivity(
'Culpa qui aliqua officia pariatur sit commodo in occaecat deserunt excepteur ad irure.'
);

await pageConditions.numActivitiesShown(1);

// THEN: Should render a border around the default send box.
await host.snapshot();

// WHEN: Send a message.
await (await directLine.emulateOutgoingActivity('Hello, World!')).resolveAll();

// THEN: Should not render a border around the default send box because the last message is not from bot.
await host.snapshot();

// WHEN: Received a message from bot with inputHint of "ignoring".
await directLine.emulateIncomingActivity({
inputHint: 'ignoring',
text: 'Irure incididunt excepteur incididunt sunt occaecat excepteur.',
type: 'message'
});

// THEN: Should hide the send box because it changed to a different set of middleware.
await host.snapshot();
});
</script>
</body>
</html>
5 changes: 5 additions & 0 deletions __tests__/html/sendBoxMiddleware/changeModality.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */

describe('sendBoxMiddleware', () => {
test('should change modality based on last activity received', () => runHTML('sendBoxMiddleware/changeModality'));
});
47 changes: 47 additions & 0 deletions __tests__/html/sendBoxMiddleware/decorate.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script>
run(async function () {
const { directLine, store } = testHelpers.createDirectLineEmulator();
const Border = ({ children }) =>
React.createElement('div', { style: { outline: 'solid 2px Red', outlineOffset: -2 } }, children);

WebChat.renderWebChat(
{
directLine,
store,
sendBoxMiddleware: [
() => next => request => {
const NextComponent = next(request);

return props =>
React.createElement(Border, {}, NextComponent ? React.createElement(NextComponent, props) : false);
}
]
},
document.getElementById('webchat')
);

await pageConditions.uiConnected();

await directLine.emulateIncomingActivity(
'Culpa qui aliqua officia pariatur sit commodo in occaecat deserunt excepteur ad irure.'
);

await pageConditions.numActivitiesShown(1);

// THEN: Should render a border around the default send box.
await host.snapshot();
});
</script>
</body>
</html>
5 changes: 5 additions & 0 deletions __tests__/html/sendBoxMiddleware/decorate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */

describe('sendBoxMiddleware', () => {
test('should decorate default send box', () => runHTML('sendBoxMiddleware/decorate'));
});
55 changes: 55 additions & 0 deletions __tests__/html/sendBoxMiddleware/immutable.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script crossorigin="anonymous" src="/test-harness.js"></script>
<script crossorigin="anonymous" src="/test-page-object.js"></script>
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
</head>
<body>
<main id="webchat"></main>
<script>
run(async function () {
const { directLine, store } = testHelpers.createDirectLineEmulator();
const sendBoxMiddleware = [
() => next => request => () => React.createElement('div', {}, 'Anim sint in ut id.')
];

const render = bubbleBackground =>
WebChat.renderWebChat(
{ directLine, store, sendBoxMiddleware, styleOptions: { bubbleBackground } },
document.getElementById('webchat')
);

render('#fee');

await pageConditions.uiConnected();

await directLine.emulateIncomingActivity(
'Culpa qui aliqua officia pariatur sit commodo in occaecat deserunt excepteur ad irure.'
);

await pageConditions.numActivitiesShown(1);

// THEN: Should render the custom send box.
await host.snapshot();

// WHEN: "sendBoxMiddleware" if mutated.
sendBoxMiddleware[0] = () => next => request => () =>
React.createElement('div', {}, 'Sunt amet veniam id minim.');

render('#efe');

await directLine.emulateIncomingActivity(
'Eiusmod irure laboris amet velit eiusmod eiusmod nulla in exercitation exercitation.'
);

await pageConditions.numActivitiesShown(2);

// THEN: Should not render the new sendBoxMiddleware.
await host.snapshot();
});
</script>
</body>
</html>
5 changes: 5 additions & 0 deletions __tests__/html/sendBoxMiddleware/immutable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */

describe('sendBoxMiddleware', () => {
test('when mutated should be treated as immutable', () => runHTML('sendBoxMiddleware/immutable'));
});
Loading
Loading