Skip to content

Commit

Permalink
Add notifications markdown extension
Browse files Browse the repository at this point in the history
Add a notifications extension, modelled on the built-in
admonition extension, to build notifications markup from an admonition
found in markdown.

This includes full test coverage.
  • Loading branch information
nottrobin committed Mar 21, 2017
1 parent 3ffebba commit 9f06aba
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 3 deletions.
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"Jinja2==2.8",
"Markdown==2.6.6",
"mdx-anchors-away==1.0.1",
"mdx-callouts==1.0.0",
"mdx-foldouts==1.0.0",
"python-frontmatter==0.2.1",
"pygments==2.2.0",
Expand Down
10 changes: 10 additions & 0 deletions tests/fixtures/builder/base/en/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@ An introduction.
- [missing page](missing.md)
- [external page](https://example.com/index.md)

## Notifications

!!! Note:
Things are reasonable

!!! Warning "":
I have no title

!!! Positive "Awesome":
Everything is now wonderful
22 changes: 22 additions & 0 deletions tests/fixtures/builder/output_basic/en/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,28 @@ <h2 id="subheading">Subheading<a class="anchor" href="#subheading"></a></h2>
<li><a href="missing.html">missing page</a></li>
<li><a href="https://example.com/index.md">external page</a></li>
</ul>

<h2 id="notifications">Notifications<a class="anchor" href="#notifications"></a></h2>

<div class="p-notification">
<p class="p-notification__response">
<span class="p-notification__status">Note:</span>
<span class="p-notification__line">Things are reasonable</span>
</p>
</div>

<div class="p-notification--warning">
<p class="p-notification__response">
<span class="p-notification__line">I have no title</span>
</p>
</div>

<div class="p-notification--positive">
<p class="p-notification__response">
<span class="p-notification__status">Awesome:</span>
<span class="p-notification__line">Everything is now wonderful</span>
</p>
</div>
</main>
</body>
</html>
21 changes: 21 additions & 0 deletions tests/fixtures/builder/output_custom_template/en/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,27 @@ <h2 id="subheading">Subheading<a class="anchor" href="#subheading"></a></h2>
<li><a href="missing.html">missing page</a></li>
<li><a href="https://example.com/index.md">external page</a></li>
</ul>

<h2 id="notifications">Notifications<a class="anchor" href="#notifications"></a></h2>

<div class="p-notification">
<p class="p-notification__response">
<span class="p-notification__status">Note:</span>
<span class="p-notification__line">Things are reasonable</span>
</p>
</div>

<div class="p-notification--warning">
<p class="p-notification__response">
<span class="p-notification__line">I have no title</span>
</p>
</div>

<div class="p-notification--positive">
<p class="p-notification__response">
<span class="p-notification__status">Awesome:</span>
<span class="p-notification__line">Everything is now wonderful</span>
</p>
</main>
</body>
</html>
21 changes: 21 additions & 0 deletions tests/fixtures/builder/output_media_path/en/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ <h2 id="subheading">Subheading<a class="anchor" href="#subheading"></a></h2>
<li><a href="missing.html">missing page</a></li>
<li><a href="https://example.com/index.md">external page</a></li>
</ul>

<h2 id="notifications">Notifications<a class="anchor" href="#notifications"></a></h2>

<div class="p-notification">
<p class="p-notification__response">
<span class="p-notification__status">Note:</span>
<span class="p-notification__line">Things are reasonable</span>
</p>
</div>

<div class="p-notification--warning">
<p class="p-notification__response">
<span class="p-notification__line">I have no title</span>
</p>
</div>

<div class="p-notification--positive">
<p class="p-notification__response">
<span class="p-notification__status">Awesome:</span>
<span class="p-notification__line">Everything is now wonderful</span>
</p>
</main>
</body>
</html>
21 changes: 21 additions & 0 deletions tests/fixtures/builder/output_media_url/en/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ <h2 id="subheading">Subheading<a class="anchor" href="#subheading"></a></h2>
<li><a href="missing.html">missing page</a></li>
<li><a href="https://example.com/index.md">external page</a></li>
</ul>

<h2 id="notifications">Notifications<a class="anchor" href="#notifications"></a></h2>

<div class="p-notification">
<p class="p-notification__response">
<span class="p-notification__status">Note:</span>
<span class="p-notification__line">Things are reasonable</span>
</p>
</div>

<div class="p-notification--warning">
<p class="p-notification__response">
<span class="p-notification__line">I have no title</span>
</p>
</div>

<div class="p-notification--positive">
<p class="p-notification__response">
<span class="p-notification__status">Awesome:</span>
<span class="p-notification__line">Everything is now wonderful</span>
</p>
</main>
</body>
</html>
15 changes: 15 additions & 0 deletions tests/test_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""

# Core modules
import re
from glob import glob
from os import path, utime
from shutil import rmtree
Expand Down Expand Up @@ -414,3 +415,17 @@ def _compare_html_parts(directory_a, directory_b):
b_img = b_imgs[index]
assert a_img['src'] == b_img['src']
assert a_img['alt'] == b_img['alt']

# Compare notifications
a_notifications = a_soup.findAll(
"div", {"class" : re.compile('p-notification.*')}
)
b_notifications = b_soup.findAll(
"div", {"class" : re.compile('p-notification.*')}
)

for index, a_notification in enumerate(a_notifications):
b_notification = b_notifications[index]
a_stripped = str(a_notification).replace(' ', '').replace("\n", '')
b_stripped = str(b_notification).replace(' ', '').replace("\n", '')
assert a_stripped == b_stripped
4 changes: 2 additions & 2 deletions ubuntudesign/documentation_builder/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from markdown.extensions.tables import TableExtension
from markdown.extensions.toc import TocExtension
from markdown.extensions.codehilite import CodeHiliteExtension
from mdx_callouts import makeExtension as CalloutsExtension
from mdx_anchors_away import AnchorsAwayExtension
from mdx_foldouts import makeExtension as FoldoutsExtension

Expand All @@ -30,6 +29,7 @@
version_paths,
write_html
)
from .extensions import NotificationsExtension


# Defaults
Expand All @@ -45,7 +45,7 @@
DefListExtension(),
AttrListExtension(),
TocExtension(marker='', baselevel=1),
CalloutsExtension(),
NotificationsExtension(),
CodeHiliteExtension(),
AnchorsAwayExtension(),
FoldoutsExtension()
Expand Down
160 changes: 160 additions & 0 deletions ubuntudesign/documentation_builder/extensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
"""
Custom markdown extensions for the documentation builder
"""

# Core
from __future__ import absolute_import
from __future__ import unicode_literals
import re
import jinja2

# Local
from markdown.extensions import Extension
from markdown.blockprocessors import BlockProcessor
from markdown.util import etree


class NotificationsExtension(Extension):
"""
# Notifications extension for Python Markdown
This provides the ability to create [vanilla notifications][1].
Usage:
!!! Note:
Things are reasonable
!!! Warning "":
I have no title
!!! Positive "Awesome":
Everything is now wonderful
Will generate the following markup:
<div class="p-notification">
<p class="p-notification__response">
<span class="p-notification__status">Note:</span>
<span class="p-notification__line">Things are reasonable</span>
</p>
</div>
<div class="p-notification--warning">
<p class="p-notification__response">
<span class="p-notification__line">I have no title</span>
</p>
</div>
<div class="p-notification--positive">
<p class="p-notification__response">
<span class="p-notification__status">Awesome:</span>
<span class="p-notification__line">
Everything is now wonderful
</span>
</p>
</div>
This builds on Python Markdown's existing [admonition extension][2].
[1]: https://docs.vanillaframework.io/en/patterns/notification
[2]: https://pythonhosted.org/Markdown/extensions/admonition.html
"""

def extendMarkdown(self, md, md_globals):
""" Add Notifications to Markdown instance. """
md.registerExtension(self)

md.parser.blockprocessors.add(
'notifications',
NotificationsProcessor(md.parser),
'_begin'
)


class NotificationsProcessor(BlockProcessor):

line_match = re.compile(r'(?:^|\n)!!!\ ?([\w\-]+)(?:\ "(.*?)")?')

def test(self, parent, block):
sibling = self.lastChild(parent)

return (
self.line_match.search(block) or
(
block.startswith(' ' * self.tab_length) and
sibling is not None and
sibling.get('class', '').startswith('p-notification')
)
)

def run(self, parent, blocks):
sibling = self.lastChild(parent)
block = blocks.pop(0)
match = self.line_match.search(block)

if match:
block = block[match.end() + 1:] # removes the first line

block, theRest = self.detab(block)
contents = block.replace('\n', ' ').replace('\r', '').strip()

if match:
template = jinja2.Template(
'<div class="{{ class }}">' +
' <p class="p-notification__response">' +
' {% if title %}' +
' <span class="p-notification__status">' +
' {{ title }}:' +
' </span>' +
' {% endif %}' +
' <span class="p-notification__line">{{body}}</span>' +
' </p>' +
'</div>'
)
notification_type, title = self.get_type_and_title(match)

type_classes = {
'warning': 'p-notification--warning',
'positive': 'p-notification--positive',
'negative': 'p-notification--negative',
}

markup = template.render(
{
'class': type_classes.get(
notification_type,
'p-notification'
),
'title': title,
'body': contents
}
)

parent.append(etree.fromstring(markup))
else:
response_paragraph = sibling.find(
"p[@class='p-notification__response']"
)

line_element = etree.fromstring(
'<span class="p-notification__line">' +
contents +
'</span>'
)

response_paragraph.append(line_element)

def get_type_and_title(self, match):
notification_type, title = match.group(1).lower(), match.group(2)
if title is None:
# no title was provided, use the capitalized classname as title
# e.g.: `!!! note` will render
# `<span class="p-notification__status">Note</span>`
title = notification_type.capitalize()
elif title == '':
# an explicit blank title should not be rendered
# e.g.: `!!! warning ""` will *not* render the
# `p-notification__status` title `span`
title = None
return notification_type, title

0 comments on commit 9f06aba

Please sign in to comment.