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

feat: add syntax highlighting into markdown code block #5389

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.react.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ rules:
react-hooks/rules-of-hooks: error
react-hooks/exhaustive-deps:
- warn
- additionalHooks: ^(useLiveRegion|useMemoized)$
- additionalHooks: ^(useLiveRegion|useMemoized|useUpdater)$

# Conflicts with Adaptive Card schema.
# react/forbid-prop-types: error
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.
compulim marked this conversation as resolved.
Show resolved Hide resolved
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.
OEvgeny marked this conversation as resolved.
Show resolved Hide resolved
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.
OEvgeny marked this conversation as resolved.
Show resolved Hide resolved
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.
5 changes: 0 additions & 5 deletions __tests__/html/toast.customizeDebounceTimeout.js

This file was deleted.

Binary file modified __tests__/html2/activity/viewCodeButton.html.snap-2.png
50 changes: 50 additions & 0 deletions __tests__/html2/markdown/codeBlock/layout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<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>
<template id="messages">
<x-message>
```javascript
console.log('JavaScript code block using tripple ` delimiters');
```
----
Code block using indent
also has copy button
but no highlighting
</x-message>
</template>
<main id="webchat"></main>
<script>
run(async function () {
const {
WebChat: { renderWebChat, testIds }
} = window; // Imports in UMD fashion.

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

renderWebChat({ directLine, store }, document.getElementById('webchat'));

await pageConditions.uiConnected();

/** @type {HTMLElement[]} */
const messages = Array.from(window.messages.content.querySelectorAll('x-message'))
for (const message of messages) {
await directLine.emulateIncomingActivity({
text: message.innerText,
type: 'message'
});
if (!message.hasAttribute('snapshot-ignore')) {
await host.snapshot('local');
}
await pageConditions.numActivitiesShown(messages.indexOf(message) + 1);
}
});
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<!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" style="position: relative;"></main>
<script type="text/babel">
run(async function () {
await host.sendDevToolsCommand('Browser.setPermission', {
permission: { name: 'clipboard-write' },
setting: 'granted'
});

await expect(navigator.permissions.query({ name: 'clipboard-write' })).resolves.toHaveProperty(
'state',
'granted'
);

const {
ReactDOM: { render },
WebChat: { ReactWebChat, testIds }
} = window; // Imports in UMD fashion.

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

const App = () => (
<React.Fragment>
<ReactWebChat directLine={directLine} store={store} />
<div style={{ gap: 8, position: 'absolute', top: 0, width: '100%' }}>
<label>
<div>Paste box</div>
<textarea
data-testid="paste box"
spellCheck={false}
style={{ background: '#f0f0f0', border: 0, height: 100, padding: 0, resize: 'none', width: '100%' }}
/>
</label>
</div>
</React.Fragment>
);

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

await pageConditions.uiConnected();

await directLine.emulateIncomingActivity({
text: `Laboris ut proident dolore nisi sint ullamco proident veniam est.

\`\`\`python
import numpy as np
import matplotlib.pyplot as plt

def plot_sine_waves():
"""Create a beautiful visualization of sine waves with different frequencies."""
# Generate time points
t = np.linspace(0, 10, 1000)
\`\`\`

Ea sint elit anim enim voluptate aliquip aliqua nulla veniam.

<pre><code>Ea et pariatur sint Lorem ex veniam adipisicing.

Aliqua magna aliquip nisi quis.
</code></pre>

Cupidatat nulla duis dolor nulla ut pariatur minim incididunt quis adipisicing velit id Lorem.`,
type: 'message'
});

await pageConditions.numActivitiesShown(1);

// WHEN: Focus on the "Copy" button via keyboard.
await host.click(document.querySelector(`[data-testid="${WebChat.testIds.sendBoxTextBox}"]`));
await host.sendShiftTab(3);
await host.sendKeys('ENTER');

// THEN: Should focus on the "Copy" button
const copyButton = document.querySelector(`[data-testid="${WebChat.testIds.codeBlockCopyButton}"]`);

expect(document.activeElement).toBe(copyButton);
await host.snapshot('local');

// WHEN: Press ENTER on the "Copy" button.
await host.sendKeys('ENTER');

// THEN: The copy button should change to "Copied".
await host.snapshot('local');

// WHEN: Paste into plain text and rich text text box.
await host.click(document.querySelector('[data-testid="paste box"]'));
await host.sendKeys('+CONTROL', 'v', '-CONTROL');

await host.click(document.querySelector(`[data-testid="${WebChat.testIds.sendBoxTextBox}"]`));

// Sleep for 1 second for the checkmark to go away.
await testHelpers.sleep(500);

// WHEN: Hiding Web Chat and showing it back.
document.getElementById('webchat').style.display = 'none';
document.body.offsetWidth; // Need for browser to refresh the layout.
document.getElementById('webchat').style.display = '';

// THEN: The "Copy" button should kept at normal.
await host.snapshot('local');
});
</script>
</body>
</html>
Binary file modified __tests__/html2/markdown/math/layout.spec.html.snap-1.png
Binary file modified __tests__/html2/markdown/math/layout.spec.html.snap-2.png
Binary file modified __tests__/html2/markdown/math/layout.spec.html.snap-3.png
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,12 @@
<main id="webchat"></main>
<script>
run(async function () {
const clock = lolex.createClock();
const { directLine, store } = testHelpers.createDirectLineEmulator();

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

WebChat.renderWebChat({ directLine, ponyfill: clock, store }, document.getElementById('webchat'));
WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));

await pageConditions.webChatRendered();

clock.tick(600);

await pageConditions.uiConnected();

store.dispatch({
Expand All @@ -33,6 +29,7 @@

await pageConditions.toastShown(1);
await pageConditions.toastShown('Please read our privacy policy.');
await host.snapshot('local');

store.dispatch({
type: 'WEB_CHAT/SET_NOTIFICATION',
Expand All @@ -43,16 +40,9 @@
}
});

await pageConditions.toastShown(1);
await pageConditions.toastShown('Please read our privacy policy.');

clock.tick(400);

await pageConditions.toastShown('Please read our privacy policy.');

clock.tick(600);

await pageConditions.toastShown('Please read our privacy policy again.');
await host.snapshot('local');

store.dispatch({
type: 'WEB_CHAT/DISMISS_NOTIFICATION',
Expand All @@ -61,16 +51,8 @@
}
});

await pageConditions.toastShown(1);
await pageConditions.toastShown('Please read our privacy policy again.');

clock.tick(400);

await pageConditions.toastShown('Please read our privacy policy again.');

clock.tick(600);

await pageConditions.toastShown(0);
await host.snapshot('local');
});
</script>
</body>
Expand Down
56 changes: 28 additions & 28 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/bundle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
"prop-types": "15.8.1",
"punycode": "2.3.1",
"sanitize-html": "2.13.0",
"shiki": "^1.22.2",
OEvgeny marked this conversation as resolved.
Show resolved Hide resolved
"swiper": "8.4.7",
"url-search-params-polyfill": "8.2.5",
"uuid": "8.3.2",
Expand Down
Loading
Loading