diff options
| -rw-r--r-- | examples/djangoproject/djangoproject/static/css/forms.css | 12 | ||||
| -rw-r--r-- | examples/djangoproject/djangoproject/templates/base.html | 1 | ||||
| -rw-r--r-- | examples/djangoproject/djangoproject/templates/styleguide.html | 21 | ||||
| -rw-r--r-- | pykss/contrib/django/templates/pykss/styleguideblock.html | 10 | ||||
| -rw-r--r-- | pykss/contrib/django/templatetags/pykss.py | 137 | ||||
| -rw-r--r-- | pykss/modifier.py | 4 | ||||
| -rw-r--r-- | pykss/section.py | 24 | ||||
| -rw-r--r-- | tests/templates/django.html | 9 | ||||
| -rw-r--r-- | tests/test_contrib/test_django/test_templatetags/test_pykss.py | 24 | ||||
| -rw-r--r-- | tests/test_modifier.py | 7 | ||||
| -rw-r--r-- | tests/test_section.py | 6 |
11 files changed, 168 insertions, 87 deletions
diff --git a/examples/djangoproject/djangoproject/static/css/forms.css b/examples/djangoproject/djangoproject/static/css/forms.css new file mode 100644 index 0000000..3830ebd --- /dev/null +++ b/examples/djangoproject/djangoproject/static/css/forms.css @@ -0,0 +1,12 @@ +/* +Form input. + +.error - When form error. + +Example: + <input type="text" class="text$modifier_class" /> + +Styleguide 2.1 +*/ +input.error { + border: 1px solid red; } diff --git a/examples/djangoproject/djangoproject/templates/base.html b/examples/djangoproject/djangoproject/templates/base.html index f52ed82..ef4a5c4 100644 --- a/examples/djangoproject/djangoproject/templates/base.html +++ b/examples/djangoproject/djangoproject/templates/base.html @@ -6,6 +6,7 @@ <title>Styleguide Example</title> <link rel="stylesheet" href="{% static "css/layout.css" %}" type="text/css" /> <link rel="stylesheet" href="{% static "css/buttons.css" %}" type="text/css" /> + <link rel="stylesheet" href="{% static "css/forms.css" %}" type="text/css" /> </head> <body> <header>Styleguide Example</header> diff --git a/examples/djangoproject/djangoproject/templates/styleguide.html b/examples/djangoproject/djangoproject/templates/styleguide.html index 1817a27..05eafb7 100644 --- a/examples/djangoproject/djangoproject/templates/styleguide.html +++ b/examples/djangoproject/djangoproject/templates/styleguide.html @@ -14,7 +14,7 @@ {% endcomment %} {% styleguideblock styleguide "1.1" %} - <button class="{{ modifier_class }}">Example Button</button> + <button class="$modifier_class">Example Button</button> {% endstyleguideblock %} {% verbatim %} @@ -22,9 +22,26 @@ <pre> <code> {% styleguideblock styleguide "1.1" %} - <button class="{{ modifier_class }}">Example Button</button> + <button class="$modifier_class">Example Button</button> {% endstyleguideblock %} </code> </pre> {% endverbatim %} + +{% comment %} + +{% renderstyleguide styleguide "2" %} +{% renderstyleguide styleguide "2" using "custom.html" %} + +{% endcomment %} +{% renderstyleguide styleguide "2" %} + +{% verbatim %} +<p>This block above was created with a simple template call:</p> +<pre> + <code> +{% renderstyleguide styleguide "2" %} + </code> +</pre> +{% endverbatim %} {% endblock %} diff --git a/pykss/contrib/django/templates/pykss/styleguideblock.html b/pykss/contrib/django/templates/pykss/styleguideblock.html index 8521ee1..138a2fd 100644 --- a/pykss/contrib/django/templates/pykss/styleguideblock.html +++ b/pykss/contrib/django/templates/pykss/styleguideblock.html @@ -18,20 +18,20 @@ {% block styleguide_element %} <div class="styleguide-element"> - {{ example_html }} + {{ section.example|safe }} </div> {% endblock %} {% block styleguide_modifier_elements %} - {% for example in modifier_examples %} + {% for modifier in section.modifiers %} <div class="styleguide-element styleguide-modifier"> - <span class="styleguide-modifier-name">{{ example.modifier.name }}</span> - {{ example.html }} + <span class="styleguide-modifier-name">{{ modifier.name }}</span> + {{ modifier.example|safe }} </div> {% endfor %} {% endblock %} {% block styleguide_code %} - <pre class="styleguide-code">{{ escaped_html }}</pre> + <pre class="styleguide-code">{{ section.example|escape }}</pre> {% endblock %} </div> diff --git a/pykss/contrib/django/templatetags/pykss.py b/pykss/contrib/django/templatetags/pykss.py index c1d1411..a711c5e 100644 --- a/pykss/contrib/django/templatetags/pykss.py +++ b/pykss/contrib/django/templatetags/pykss.py @@ -1,14 +1,12 @@ from django import template -from django.conf import settings from django.template.loader import render_to_string -from django.utils.safestring import mark_safe -from django.utils.html import escape +from django.utils.html import strip_spaces_between_tags register = template.Library() -class StyleguideBlockNode(template.Node): +class BaseStyleguideNode(template.Node): def __init__(self, styleguide, reference, template_name, nodelist): self.styleguide = styleguide @@ -19,84 +17,105 @@ class StyleguideBlockNode(template.Node): def __repr__(self): return '<StyleguideBlockNode>' + @classmethod + def as_tag(cls, parser, token): + bits = token.split_contents()[1:] + + if len(bits) < 2: + raise template.TemplateSyntaxError("styleguideblock expected at " + "least two arguments") + + elif len(bits) == 2: + styleguide, reference = bits + template_name = '"pykss/styleguideblock.html"' + + elif len(bits) >= 3 and bits[2] != 'using': + raise template.TemplateSyntaxError("styleguideblock expected using " + "as the third argument") + + elif len(bits) == 3: + raise template.TemplateSyntaxError("styleguideblock expects a " + "template name after 'using'") + + else: + styleguide, reference, _using, template_name = bits + + return cls.dispatch(parser, styleguide, reference, template_name) + + @classmethod + def dispatch(cls, parser, styleguide, reference, template_name): + raise NotImplementedError + def render(self, context): styleguide = self.styleguide.resolve(context) reference = self.reference.resolve(context) template_name = self.template_name.resolve(context) - try: - section = styleguide.section(reference) - except Exception as e: - if settings.TEMPLATE_DEBUG: - raise e - return '' + sections = [sec for ref, sec in styleguide.sections.iteritems() + if ref.startswith(reference)] + + if self.nodelist: + example = self.nodelist.render(context).strip() + for section in sections: + section.add_example(example) + + output = [] - example_html = self.nodelist.render(context).strip() + for section in sections: + context.update({'section': section}) + html = render_to_string(template_name, context) + output.append(strip_spaces_between_tags(html)) + context.pop() - modifier_examples = [] - for modifier in section.modifiers: - modifier_context = template.Context({ - 'modifier_class': ' %s' % modifier.class_name, - }) - html = self.nodelist.render(modifier_context).strip() - modifier_examples.append({ - 'modifier': modifier, - 'html': mark_safe(html), - }) + return ''.join(output) - context.update({ - 'section': section, - 'example_html': mark_safe(example_html), - 'modifier_examples': modifier_examples, - 'escaped_html': escape(example_html), - }) - output = render_to_string(template_name, context) +class StyleguideBlockNode(BaseStyleguideNode): - context.pop() + @classmethod + def dispatch(cls, parser, styleguide, reference, template_name): + nodelist = parser.parse(('endstyleguideblock',)) + parser.delete_first_token() + return cls( + styleguide=parser.compile_filter(styleguide), + reference=parser.compile_filter(reference), + template_name=parser.compile_filter(template_name), + nodelist=nodelist, + ) - return output + +class RenderStyleguideNode(BaseStyleguideNode): + + @classmethod + def dispatch(cls, parser, styleguide, reference, template_name): + return cls( + styleguide=parser.compile_filter(styleguide), + reference=parser.compile_filter(reference), + template_name=parser.compile_filter(template_name), + nodelist=None, + ) @register.tag def styleguideblock(parser, token): """ {% styleguideblock styleguide "1.1" %} - <button class="{{ modifier_class }}">Example Button</button> + <button class="$modifier_class">Example Button</button> {% endstyleguideblock %} {% styleguideblock styleguide "1.1" using "custom.html" %} - <button class="{{ modifier_class }}">Example Button</button> + <button class="$modifier_class">Example Button</button> {% endstyleguideblock %} """ - bits = token.split_contents()[1:] - - if len(bits) < 2: - raise template.TemplateSyntaxError("styleguideblock expected at " - "least two arguments") - - elif len(bits) == 2: - styleguide, reference = bits - template_name = '"pykss/styleguideblock.html"' - - elif len(bits) >= 3 and bits[2] != 'using': - raise template.TemplateSyntaxError("styleguideblock expected using " - "as the third argument") + return StyleguideBlockNode.as_tag(parser, token) - elif len(bits) == 3: - raise template.TemplateSyntaxError("styleguideblock expects a " - "template name after 'using'") - else: - styleguide, reference, _using, template_name = bits - - nodelist = parser.parse(('endstyleguideblock',)) - parser.delete_first_token() +@register.tag +def renderstyleguide(parser, token): + """ + {% renderstyleguide styleguide "1.1" %} - return StyleguideBlockNode( - styleguide=parser.compile_filter(styleguide), - reference=parser.compile_filter(reference), - template_name=parser.compile_filter(template_name), - nodelist=nodelist, - ) + {% renderstyleguide styleguide "1.1" using "custom.html" %} + """ + return RenderStyleguideNode.as_tag(parser, token) diff --git a/pykss/modifier.py b/pykss/modifier.py index 0c31dd1..969d164 100644 --- a/pykss/modifier.py +++ b/pykss/modifier.py @@ -3,7 +3,11 @@ class Modifier(object): def __init__(self, name, description): self.name = name self.description = description + self.example = '' @property def class_name(self): return self.name.replace('.', ' ').replace(':', ' pseudo-class-').strip() + + def add_example(self, example): + self.example = example.replace('$modifier_class', ' %s' % self.class_name) diff --git a/pykss/section.py b/pykss/section.py index 9a98d0f..8f3ed1a 100644 --- a/pykss/section.py +++ b/pykss/section.py @@ -6,6 +6,7 @@ from pykss.modifier import Modifier CLASS_MODIFIER = '.' PSEUDO_CLASS_MODIFIER = ':' MODIFIER_DESCRIPTION_SEPARATOR = ' - ' +EXAMPLE_START = 'Example:' REFERENCE_START = 'Styleguide' reference_re = re.compile('%s ([\d\.]+)' % REFERENCE_START) @@ -20,8 +21,11 @@ class Section(object): def parse(self): self._description_lines = [] self._modifiers = [] + self._example_lines = [] self._reference = None + in_example = False + for line in self.comment.splitlines(): if line.startswith(CLASS_MODIFIER) or line.startswith(PSEUDO_CLASS_MODIFIER): try: @@ -31,12 +35,21 @@ class Section(object): else: self._modifiers.append(Modifier(modifier.strip(), description.strip())) + elif line.startswith(EXAMPLE_START): + in_example = True + elif line.startswith(REFERENCE_START): + in_example = False self._reference = reference_re.match(line).groups()[0].rstrip('.') + + elif in_example is True: + self._example_lines.append(line) + else: self._description_lines.append(line) self._description = '\n'.join(self._description_lines).strip() + self.add_example('\n'.join(self._example_lines).strip()) @property def description(self): @@ -51,7 +64,18 @@ class Section(object): return self._modifiers @property + def example(self): + if not hasattr(self, '_modifiers'): + self.parse() + return self._example + + @property def section(self): if not hasattr(self, '_reference'): self.parse() return self._reference + + def add_example(self, example): + self._example = example.replace('$modifier_class', '') + for modifier in self._modifiers: + modifier.add_example(example) diff --git a/tests/templates/django.html b/tests/templates/django.html index 7c52f40..7a5cf38 100644 --- a/tests/templates/django.html +++ b/tests/templates/django.html @@ -8,11 +8,10 @@ ["{{ modifier.name }}","{{ modifier.description }}"]{% if not forloop.last %},{% endif %} {% endfor %} ], - "example_html": "{{ example_html|escapejs }}", + "example_html": "{{ section.example|escapejs }}", "modifier_examples": [ - {% for example in modifier_examples %} - ["{{ example.modifier.name }}","{{ example.html|escapejs }}"]{% if not forloop.last %},{% endif %} + {% for modifier in section.modifiers %} + ["{{ modifier.name }}","{{ modifier.example|escapejs }}"]{% if not forloop.last %},{% endif %} {% endfor %} - ], - "escaped_html": "{{ escaped_html|escapejs }}" + ] } diff --git a/tests/test_contrib/test_django/test_templatetags/test_pykss.py b/tests/test_contrib/test_django/test_templatetags/test_pykss.py index f066499..d24f1ae 100644 --- a/tests/test_contrib/test_django/test_templatetags/test_pykss.py +++ b/tests/test_contrib/test_django/test_templatetags/test_pykss.py @@ -7,7 +7,6 @@ from django.utils import simplejson from mock import patch, ANY -from pykss.exceptions import SectionDoesNotExist from pykss.parser import Parser @@ -17,25 +16,14 @@ class StyleguideBlockTestCase(TestCase): css = os.path.join(settings.PROJECT_ROOT, 'tests', 'fixtures', 'css') self.styleguide = Parser(css) - def test_when_section_does_not_exist_and_in_debug(self): + def test_when_section_does_not_exist(self): template = Template(""" {% load pykss %} {% styleguideblock styleguide "99" %} {% endstyleguideblock %} """) context = Context({'styleguide': self.styleguide}) - self.assertRaises(SectionDoesNotExist, template.render, context) - - def test_when_section_does_not_exist_and_not_in_debug(self): - with self.settings(TEMPLATE_DEBUG=False): - template = Template(""" - {% load pykss %} - {% styleguideblock styleguide "99" %} - {% endstyleguideblock %} - """) - context = Context({'styleguide': self.styleguide}) - results = template.render(context) - self.assertEqual(results.strip(), '') + self.assertEquals(template.render(context).strip(), '') @patch('pykss.contrib.django.templatetags.pykss.render_to_string') def test_uses_default_template(self, mock_render_to_string): @@ -79,7 +67,7 @@ class StyleguideBlockTestCase(TestCase): template = Template(""" {% load pykss %} {% styleguideblock styleguide "2.1.1" using "django.html" %} - <i class="main{{ modifier_class }}"></i> + <i class="main$modifier_class"></i> {% endstyleguideblock %} """) context = Context({'styleguide': self.styleguide}) @@ -90,4 +78,8 @@ class StyleguideBlockTestCase(TestCase): self.assertEqual(results['modifiers'][0], [':hover', 'Highlights when hovering.']) self.assertEqual(results['example_html'], '<i class="main"></i>') self.assertEqual(results['modifier_examples'][0], [':hover', '<i class="main pseudo-class-hover"></i>']) - self.assertEqual(results['escaped_html'], '<i class="main"></i>') + + +#class RenderStyleguideTestCase(TestCase): + +# def test_renders_correctly(self): diff --git a/tests/test_modifier.py b/tests/test_modifier.py index 1e109d8..8acb594 100644 --- a/tests/test_modifier.py +++ b/tests/test_modifier.py @@ -13,3 +13,10 @@ class ModiferTestCase(unittest.TestCase): def test_handles_multiple_classes(self): self.assertTrue('callout extreme' in self.modifier.class_name) + + def test_add_example(self): + example = '<i class="icon$modifier_class"></i>' + expected = '<i class="icon callout extreme pseudo-class-hover"></i>' + + self.modifier.add_example(example) + self.assertEqual(self.modifier.example, expected) diff --git a/tests/test_section.py b/tests/test_section.py index 7ba8496..6ece110 100644 --- a/tests/test_section.py +++ b/tests/test_section.py @@ -16,6 +16,9 @@ Your standard form button. .primary - Indicates button is the primary action. .smaller - A smaller button +Example: + <a href="button$modifier_class">Button</a> + Styleguide 2.1.1. """ self.section = Section(comment.strip(), 'example.css') @@ -32,5 +35,8 @@ Styleguide 2.1.1. def test_parses_modifier_descriptions(self): self.assertEqual(self.section.modifiers[0].description, 'Highlights when hovering.') + def test_parses_the_example(self): + self.assertEqual(self.section.example, '<a href="button">Button</a>') + def test_parses_the_styleguide_reference(self): self.assertEqual(self.section.section, '2.1.1') |
