fix(editor): Render checkboxes in markdown (#9549)

This commit is contained in:
Milorad FIlipović 2024-05-30 20:13:52 +02:00 committed by GitHub
parent a221215b87
commit 47d774100b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 74 additions and 1 deletions

View file

@ -51,3 +51,18 @@ Markdown.args = {
},
],
};
const TemplateWithCheckboxes: StoryFn = (args, { argTypes }) => ({
setup: () => ({ args }),
props: Object.keys(argTypes),
components: {
N8nMarkdown,
},
template: '<n8n-markdown v-bind="args"></n8n-markdown>',
});
export const WithCheckboxes = TemplateWithCheckboxes.bind({});
WithCheckboxes.args = {
content: '__TODO__\n- [ ] Buy milk\n- [X] Buy socks\n',
loading: false,
};

View file

@ -23,7 +23,7 @@ import Markdown from 'markdown-it';
import markdownLink from 'markdown-it-link-attributes';
import markdownEmoji from 'markdown-it-emoji';
import markdownTaskLists from 'markdown-it-task-lists';
import xss, { friendlyAttrValue } from 'xss';
import xss, { friendlyAttrValue, whiteList } from 'xss';
import N8nLoading from '../N8nLoading';
import { escapeMarkdown } from '../../utils/markdown';
@ -72,6 +72,7 @@ const props = withDefaults(defineProps<MarkdownProps>(), {
},
},
tasklists: {
enabled: true,
label: true,
labelAfter: true,
},
@ -84,6 +85,11 @@ const md = new Markdown(options.markdown)
.use(markdownEmoji)
.use(markdownTaskLists, options.tasklists);
const xssWhiteList = {
...whiteList,
label: ['class', 'for'],
};
const htmlContent = computed(() => {
if (!props.content) {
return '';
@ -130,6 +136,13 @@ const htmlContent = computed(() => {
}
// return nothing, keep tag
},
onIgnoreTag(tag, tagHTML) {
// Allow checkboxes
if (tag === 'input' && tagHTML.includes('type="checkbox"')) {
return tagHTML;
}
},
whiteList: xssWhiteList,
});
return safeHtml;

View file

@ -0,0 +1,45 @@
import { render } from '@testing-library/vue';
import N8nMarkdown from '../Markdown.vue';
describe('components', () => {
describe('N8nMarkdown', () => {
it('should render unchecked checkboxes', () => {
const wrapper = render(N8nMarkdown, {
props: {
content: '__TODO__\n- [ ] Buy milk\n- [ ] Buy socks\n',
},
});
const checkboxes = wrapper.getAllByRole('checkbox');
expect(checkboxes).toHaveLength(2);
checkboxes.forEach((checkbox) => {
expect(checkbox).not.toBeChecked();
});
});
it('should render checked checkboxes', () => {
const wrapper = render(N8nMarkdown, {
props: {
content: '__TODO__\n- [X] Buy milk\n- [X] Buy socks\n',
},
});
const checkboxes = wrapper.getAllByRole('checkbox');
expect(checkboxes).toHaveLength(2);
checkboxes.forEach((checkbox) => {
expect(checkbox).toBeChecked();
});
});
it('should render inputs as plain text', () => {
const wrapper = render(N8nMarkdown, {
props: {
content:
'__TODO__\n- [X] Buy milk\n- <input type="text" data-testid="text-input" value="Something"/>\n',
},
});
const checkboxes = wrapper.getAllByRole('checkbox');
expect(checkboxes).toHaveLength(1);
expect(wrapper.queryByTestId('text-input')).toBeNull();
expect(wrapper.html()).toContain(
'&lt;input type=“text” data-testid=“text-input” value=“Something”/&gt;',
);
});
});
});