Use data- attributes to preserve macro configs
This commit is contained in:
+34
-20
@@ -6,8 +6,6 @@ const macros = [
|
||||
{
|
||||
name: 'user',
|
||||
toHTML: () => '<a href="/user">TestUser</a>',
|
||||
selector: 'A[href="/user"]',
|
||||
toMarkdown: () => '@user',
|
||||
},
|
||||
{
|
||||
name: 'npc',
|
||||
@@ -15,14 +13,10 @@ const macros = [
|
||||
const name = keywords.join(' ');
|
||||
return '<a href="/NPC/' + name.replace(/ /g, '') + '">' + name + '</a>';
|
||||
},
|
||||
selector: 'A[href^="/NPC/"]',
|
||||
toMarkdown: (el: any) => '@npc(' + el.textContent + ')',
|
||||
},
|
||||
{
|
||||
name: 'style',
|
||||
toHTML: ({ keywords, content }: any) => '<div class="' + keywords.join(' ') + '">' + (content || '') + '</div>',
|
||||
selector: 'DIV[class]',
|
||||
toMarkdown: (el: any, convert: any) => '\n\n@style(' + el.className + '\n' + convert.children(el) + '\n)\n\n',
|
||||
},
|
||||
{
|
||||
name: 'toc',
|
||||
@@ -36,10 +30,12 @@ const M = (html: string) => h.toMarkdown(html);
|
||||
|
||||
describe('Macros', () => {
|
||||
describe('self-closing', () => {
|
||||
it('bare name', () => expect(H('hello @user world')).toBe('<p>hello <a href="/user">TestUser</a> world</p>'));
|
||||
it('empty parens', () => expect(H('hello @user() world')).toBe('<p>hello <a href="/user">TestUser</a> world</p>'));
|
||||
it('keywords', () => expect(H('@npc(Goblin King)')).toBe('<p><a href="/NPC/GoblinKing">Goblin King</a></p>'));
|
||||
it('params', () => expect(H('@toc(depth="2")')).toContain('data-depth="2"'));
|
||||
it('bare name renders', () => expect(H('hello @user world')).toContain('<a href="/user">TestUser</a>'));
|
||||
it('bare name wrapped', () => expect(H('hello @user world')).toContain('data-macro="user"'));
|
||||
it('empty parens', () => expect(H('hello @user() world')).toContain('data-macro="user"'));
|
||||
it('keywords', () => expect(H('@npc(Goblin King)')).toContain('Goblin King'));
|
||||
it('keywords in data attr', () => expect(H('@npc(Goblin King)')).toContain('data-keywords="Goblin King"'));
|
||||
it('params', () => expect(H('@toc(depth="2")')).toContain('data-param-depth="2"'));
|
||||
});
|
||||
|
||||
describe('unknown macros', () => {
|
||||
@@ -52,8 +48,8 @@ describe('Macros', () => {
|
||||
|
||||
describe('block macros', () => {
|
||||
it('content processed', () => expect(H('@style(box\n**bold**\n)')).toContain('<strong>bold</strong>'));
|
||||
it('wraps in div', () => expect(H('@style(box\ncontent\n)')).toContain('<div class="box">'));
|
||||
it('multiple keywords', () => expect(H('@style(box center\ncontent\n)')).toContain('class="box center"'));
|
||||
it('wrapped with data-macro', () => expect(H('@style(box\ncontent\n)')).toContain('data-macro="style"'));
|
||||
it('keywords in data attr', () => expect(H('@style(box center\ncontent\n)')).toContain('data-keywords="box center"'));
|
||||
});
|
||||
|
||||
describe('verbatim', () => {
|
||||
@@ -61,23 +57,41 @@ describe('Macros', () => {
|
||||
it('no strong tag', () => expect(H('@style(box verbatim\n**bold**\n)')).not.toContain('<strong>'));
|
||||
it('escapes html', () => expect(H('@style(box verbatim\n<b>tag</b>\n)')).toContain('<b>'));
|
||||
it('preserves newlines', () => expect(H('@style(box verbatim\nline1\nline2\n)')).toContain('line1<br>'));
|
||||
it('strips keyword', () => expect(H('@style(box verbatim\ncontent\n)')).not.toContain('verbatim'));
|
||||
it('data-verbatim set', () => expect(H('@style(box verbatim\ncontent\n)')).toContain('data-verbatim="true"'));
|
||||
it('keyword stripped from data-keywords', () => {
|
||||
const html = H('@style(box verbatim\ncontent\n)');
|
||||
expect(html).toContain('data-keywords="box"');
|
||||
expect(html).not.toMatch(/data-keywords="[^"]*verbatim/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('nesting', () => {
|
||||
it('inline inside bold', () => expect(H('**@npc(Goblin King)**')).toContain('<strong><a href="/NPC/GoblinKing">'));
|
||||
it('inline inside bold', () => expect(H('**@npc(Goblin King)**')).toContain('<strong>'));
|
||||
it('block contains list', () => expect(H('@style(box\n- item 1\n- item 2\n)')).toContain('<ul>'));
|
||||
it('inline inside block', () => expect(H('@style(box\nhello @user world\n)')).toContain('<a href="/user">TestUser</a>'));
|
||||
it('inline inside block', () => expect(H('@style(box\nhello @user world\n)')).toContain('data-macro="user"'));
|
||||
});
|
||||
|
||||
describe('fenced code protection', () => {
|
||||
it('not in code block', () => expect(H('```\n@user\n```')).not.toContain('<a href="/user">'));
|
||||
it('not in code block', () => expect(H('```\n@user\n```')).not.toContain('data-macro'));
|
||||
it('literal in code block', () => expect(H('```\n@user\n```')).toContain('@user'));
|
||||
it('not in inline code', () => expect(H('`@user`')).not.toContain('<a href="/user">'));
|
||||
it('not in inline code', () => expect(H('`@user`')).not.toContain('data-macro'));
|
||||
});
|
||||
|
||||
describe('round-trips', () => {
|
||||
it('npc', () => expect(M(H('@npc(Goblin King)'))).toBe('@npc(Goblin King)'));
|
||||
it('user', () => expect(M(H('hello @user world'))).toBe('hello @user world'));
|
||||
describe('generic round-trip via data- attributes', () => {
|
||||
it('inline macro', () => expect(M(H('hello @user world'))).toBe('hello @user world'));
|
||||
it('inline with keywords', () => expect(M(H('@npc(Goblin King)'))).toBe('@npc(Goblin King)'));
|
||||
it('inline with params', () => expect(M(H('@toc(depth="2")'))).toBe('@toc(depth="2")'));
|
||||
it('block macro', () => {
|
||||
const md = '@style(box\n**bold** content\n)';
|
||||
const result = M(H(md)).trim();
|
||||
expect(result).toContain('@style(box');
|
||||
expect(result).toContain('**bold** content');
|
||||
expect(result).toContain(')');
|
||||
});
|
||||
it('verbatim round-trip preserves keyword', () => {
|
||||
const md = '@style(box verbatim\n<b>literal</b>\n)';
|
||||
const result = M(H(md)).trim();
|
||||
expect(result).toContain('@style(box verbatim');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user