Skip to content

Commit

Permalink
Support for aria-labels on link (#3022)
Browse files Browse the repository at this point in the history
* Support for aria-labels on link using [link](url){aria-label="Screen reader description"} format.

* Added doc

* Sort

Co-authored-by: William Wong <compulim@hotmail.com>
  • Loading branch information
patniko and compulim authored Mar 17, 2020
1 parent be430e1 commit 31291d5
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added

- Resolves [#2897](https://github.com/microsoft/BotFramework-WebChat/issues/2897). Moved from JUnit to VSTest reporter with file attachments, by [@compulim](https://github.com/compulim) in PR [#2990](https://github.com/microsoft/BotFramework-WebChat/pull/2990)
- Added `aria-label` attribute support for built-in Markdown engine, by [@patniko](https://github.com/patniko) in PR [#3022](https://github.com/microsoft/BotFramework-WebChat/pull/3022)

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion docs/HOOKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ const renderMarkdown = useRenderMarkdown();
renderMarkdown('Hello, World!') === '<p>Hello, World!</p>\n';
```

To modify this value, change the value in the style options prop passed to Web Chat.
The Markdown engine can be reconfigured by passing `renderMarkdown` prop to Web Chat. The default engine is a customized [Markdown-It](https://npmjs.com/package/markdown-it) with [HTML sanitizer](https://npmjs.com/package/sanitize-html) and [support `aria-label` attribute](https://npmjs.com/package/markdown-it-attrs). The customization can be found in [bundle/src/renderMarkdown.js](https://github.com/microsoft/BotFramework-WebChat/tree/master/packages/bundle/src/renderMarkdown.js).

## `useRenderToast`

Expand Down
5 changes: 5 additions & 0 deletions packages/bundle/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 @@ -37,6 +37,7 @@
"botframework-webchat-core": "0.0.0-0",
"core-js": "^3.6.4",
"markdown-it": "^10.0.0",
"markdown-it-attrs": "^3.0.2",
"markdown-it-for-inline": "^0.1.1",
"memoize-one": "^5.1.1",
"microsoft-cognitiveservices-speech-sdk": "1.6.0",
Expand Down
7 changes: 7 additions & 0 deletions packages/bundle/src/__tests__/renderMarkdown.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ describe('renderMarkdown', () => {
);
});

it('should render aria-labels', () => {
const options = { markdownRespectCRLF: true };
expect(renderMarkdown('[example](sample.com){aria-label="Sample label"}', options)).toBe(
'<p><a href="sample.com" aria-label="Sample label" target="_blank">example</a></p>\n'
);
});

it('should render sip protocol links correctly', () => {
const options = { markdownRespectCRLF: true };
expect(renderMarkdown(`[example@test.com](sip:example@test.com)`, options)).toBe(
Expand Down
37 changes: 20 additions & 17 deletions packages/bundle/src/renderMarkdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

import iterator from 'markdown-it-for-inline';
import MarkdownIt from 'markdown-it';
import markdownItAttrs from 'markdown-it-attrs';
import sanitizeHTML from 'sanitize-html';

const SANITIZE_HTML_OPTIONS = {
allowedAttributes: {
a: ['href', 'name', 'target', 'title'],
a: ['aria-label', 'href', 'name', 'target', 'title'],
img: ['alt', 'src']
},
allowedSchemes: ['data', 'http', 'https', 'ftp', 'mailto', 'sip', 'tel'],
Expand Down Expand Up @@ -53,25 +54,27 @@ const customMarkdownIt = new MarkdownIt({
linkify: true,
typographer: true,
xhtmlOut: true
}).use(iterator, 'url_new_win', 'link_open', (tokens, index) => {
// TODO: [P4] This is copied from v3 and looks clunky
// We should refactor this code
const targetAttrIndex = tokens[index].attrIndex('target');
})
.use(markdownItAttrs)
.use(iterator, 'url_new_win', 'link_open', (tokens, index) => {
// TODO: [P4] This is copied from v3 and looks clunky
// We should refactor this code
const targetAttrIndex = tokens[index].attrIndex('target');

if (~targetAttrIndex) {
tokens[index].attrs[targetAttrIndex][1] = '_blank';
} else {
tokens[index].attrPush(['target', '_blank']);
}
if (~targetAttrIndex) {
tokens[index].attrs[targetAttrIndex][1] = '_blank';
} else {
tokens[index].attrPush(['target', '_blank']);
}

const relAttrIndex = tokens[index].attrIndex('rel');
const relAttrIndex = tokens[index].attrIndex('rel');

if (~relAttrIndex) {
tokens[index].attrs[relAttrIndex][1] = 'noopener noreferrer';
} else {
tokens[index].attrPush(['target', 'noopener noreferrer']);
}
});
if (~relAttrIndex) {
tokens[index].attrs[relAttrIndex][1] = 'noopener noreferrer';
} else {
tokens[index].attrPush(['target', 'noopener noreferrer']);
}
});

export default function render(markdown, { markdownRespectCRLF }) {
if (markdownRespectCRLF) {
Expand Down

0 comments on commit 31291d5

Please sign in to comment.