Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
### Added

- Add support for decorators thanks to @su27 (#16, #17).
- Add support for configurable decorators and entities.
- Add support for decorators and entities in function form.

## [[v0.6.2]](https://github.com/springload/draftjs_exporter/releases/tag/v0.6.2) - 2017-01-18

Expand Down
2 changes: 1 addition & 1 deletion draftjs_exporter/composite_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def apply_decorators(decorators, text, block_type):
if pointer < begin:
yield DOM.create_text_node(text[pointer:begin])

yield decorator.render({
yield DOM.create_element(decorator, {
'children': match.group(0),
'match': match,
'block_type': block_type,
Expand Down
2 changes: 1 addition & 1 deletion draftjs_exporter/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# TODO Ideally would want double wrapping in pre + code.
# See https://github.com/sstur/draft-js-export-html/blob/master/src/stateToHTML.js#L88
BLOCK_TYPES.CODE: {'element': 'pre'},
BLOCK_TYPES.ATOMIC: {'element': 'fragment'},
BLOCK_TYPES.ATOMIC: {'element': None},
}

# Default style map to extend.
Expand Down
5 changes: 4 additions & 1 deletion draftjs_exporter/dom.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,12 @@ def create_element(type_=None, props=None, *children):
if prop is not None:
attributes[key] = prop

# "type" is either an entity with a render method, or a tag name.
if inspect.isclass(type_):
elt = type_().render(attributes)
elif callable(getattr(type_, 'render', None)):
elt = type_.render(attributes)
elif callable(type_):
elt = type_(attributes)
else:
elt = DOM.create_tag(type_, attributes)

Expand Down
2 changes: 1 addition & 1 deletion draftjs_exporter/entity_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,6 @@ def render_entitities(self, root_element, style_node):

props['children'] = style_node

new_element = decorator.render(props)
new_element = DOM.create_element(decorator, props)
DOM.append_child(element_stack[-1], new_element)
element_stack.append(new_element)
28 changes: 18 additions & 10 deletions example.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@
from draftjs_exporter.html import HTML


class HR:
def render(self, props):
return DOM.create_element('hr')
def HR(props):
return DOM.create_element('hr')


class Image:
Expand All @@ -29,11 +28,20 @@ def render(self, props):


class Link:
def __init__(self, use_new_window=False):
self.use_new_window = use_new_window

def render(self, props):
data = props.get('data', {})
href = data['url']
link_props = {
'href': data['url'],
}

if self.use_new_window:
link_props['target'] = '_blank'
link_props['rel'] = 'noreferrer noopener'

return DOM.create_element('a', {'href': href}, props['children'])
return DOM.create_element('a', link_props, props['children'])


class BR:
Expand Down Expand Up @@ -66,13 +74,13 @@ def render(self, props):

config = {
'entity_decorators': {
ENTITY_TYPES.LINK: Link(),
ENTITY_TYPES.IMAGE: Image(),
ENTITY_TYPES.HORIZONTAL_RULE: HR(),
ENTITY_TYPES.LINK: Link(use_new_window=True),
ENTITY_TYPES.IMAGE: Image,
ENTITY_TYPES.HORIZONTAL_RULE: HR,
},
'composite_decorators': [
BR(),
Hashtag(),
BR,
Hashtag,
],
# Extend/override the default block map.
'block_map': dict(BLOCK_MAP, **{
Expand Down
20 changes: 18 additions & 2 deletions tests/test_composite_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ class Linkify:
"""
SEARCH_RE = re.compile(r'(http://|https://|www\.)([a-zA-Z0-9\.\-%/\?&_=\+#:~!,\'\*\^$]+)')

def __init__(self, use_new_window=False):
self.use_new_window = use_new_window

def render(self, props):
match = props.get('match')
protocol = match.group(1)
Expand All @@ -30,6 +33,10 @@ def render(self, props):
'href': href,
}

if self.use_new_window:
link_props['target'] = '_blank'
link_props['rel'] = 'noreferrer noopener'

if href.startswith('www'):
link_props['href'] = 'http://' + href

Expand Down Expand Up @@ -89,11 +96,20 @@ def test_render_www(self):
def test_render_code_block(self):
match = next(Linkify.SEARCH_RE.finditer('test https://www.example.com'))

self.assertEqual(DOM.render(DOM.create_element(Linkify, {
self.assertEqual(DOM.create_element(Linkify, {
'block_type': BLOCK_TYPES.CODE,
'match': match,
'children': match.group(0),
})), match.group(0))
}), match.group(0))

def test_render_new_window(self):
match = next(Linkify.SEARCH_RE.finditer('test https://www.example.com'))

self.assertEqual(DOM.render(DOM.create_element(Linkify(use_new_window=True), {
'block_type': BLOCK_TYPES.UNSTYLED,
'match': match,
'children': match.group(0),
})), '<a href="https://www.example.com" rel="noreferrer noopener" target="_blank">https://www.example.com</a>')


class TestHashtag(unittest.TestCase):
Expand Down
3 changes: 3 additions & 0 deletions tests/test_dom.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ def test_create_element_none(self):
def test_create_element_entity(self):
self.assertEqual(DOM.render(DOM.create_element(Icon, {'name': 'rocket'})), '<svg class="icon"><use xlink:href="icon-rocket"></use></svg>')

def test_create_element_entity_configured(self):
self.assertEqual(DOM.render(DOM.create_element(Icon(icon_class='i'), {'name': 'rocket'})), '<svg class="i"><use xlink:href="icon-rocket"></use></svg>')

def test_create_document_fragment(self):
self.assertEqual(DOM.get_tag_name(DOM.create_document_fragment()), 'fragment')

Expand Down
41 changes: 12 additions & 29 deletions tests/test_entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@
from draftjs_exporter.dom import DOM


class Null:
def render(self, props):
return DOM.create_element()
def Null(props):
return DOM.create_element()


class HR:
def render(self, props):
return DOM.create_element('hr')
def HR(props):
return DOM.create_element('hr')


class Link:
Expand All @@ -39,9 +37,12 @@ def render(self, props):


class Icon:
def __init__(self, icon_class='icon'):
self.icon_class = icon_class

def render(self, props):
href = 'icon-%s' % props.get('name', '')
return DOM.create_element('svg', {'class': 'icon'}, DOM.create_element('use', {'xlink:href': href}))
return DOM.create_element('svg', {'class': self.icon_class}, DOM.create_element('use', {'xlink:href': href}))


class Button:
Expand All @@ -60,32 +61,20 @@ def render(self, props):


class TestNull(unittest.TestCase):
def test_init(self):
self.assertIsInstance(Null(), Null)

def test_render(self):
self.assertEqual(DOM.get_tag_name(DOM.create_element(Null, {})), 'fragment')
self.assertEqual(DOM.get_text_content(DOM.create_element(Null, {})), None)


class TestIcon(unittest.TestCase):
def test_init(self):
self.assertIsInstance(Icon(), Icon)

def test_render(self):
icon = DOM.create_element(Icon, {
'name': 'rocket',
})
self.assertEqual(DOM.get_tag_name(icon), 'svg')
self.assertEqual(DOM.get_text_content(icon), None)
self.assertEqual(DOM.get_class_list(icon), ['icon'])
self.assertEqual(DOM.render(icon), '<svg class="icon"><use xlink:href="icon-rocket"></use></svg>')
self.assertEqual(DOM.render(DOM.create_element(Icon, {'name': 'rocket'})), '<svg class="icon"><use xlink:href="icon-rocket"></use></svg>')

def test_render_configured(self):
self.assertEqual(DOM.render(DOM.create_element(Icon(icon_class='i'), {'name': 'rocket'})), '<svg class="i"><use xlink:href="icon-rocket"></use></svg>')

class TestImage(unittest.TestCase):
def test_init(self):
self.assertIsInstance(Image(), Image)

class TestImage(unittest.TestCase):
def test_render(self):
image = DOM.create_element(Image, {
'data': {
Expand All @@ -100,9 +89,6 @@ def test_render(self):


class TestLink(unittest.TestCase):
def test_init(self):
self.assertIsInstance(Link(), Link)

def test_render(self):
link = DOM.create_element(Link, {
'data': {
Expand All @@ -116,9 +102,6 @@ def test_render(self):


class TestButton(unittest.TestCase):
def test_init(self):
self.assertIsInstance(Button(), Button)

def test_render_with_icon(self):
button = DOM.create_element(Button, {
'data': {
Expand Down
11 changes: 5 additions & 6 deletions tests/test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@

config = {
'entity_decorators': {
ENTITY_TYPES.LINK: Link(),
ENTITY_TYPES.HORIZONTAL_RULE: HR(),
ENTITY_TYPES.LINK: Link,
ENTITY_TYPES.HORIZONTAL_RULE: HR,
},
'composite_decorators': [
Linkify(),
Hashtag(),
BR(),
Linkify,
Hashtag,
BR,
],
'block_map': dict(BLOCK_MAP, **{
BLOCK_TYPES.UNORDERED_LIST_ITEM: {
Expand Down Expand Up @@ -849,7 +849,6 @@ def test_render_with_default_style_map(self):
'element': 'li',
'wrapper': ['ul', {'className': 'steps'}],
},
BLOCK_TYPES.ATOMIC: {'element': 'span'},
})
}).render({
'entityMap': {},
Expand Down