diff --git a/CHANGES.rst b/CHANGES.rst index 73d0e66..ff7409a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,17 @@ History 2.2.1 (unreleased) ------------------ +- Add ``readonly`` as possible attribute of input elements. + [thet] + +- Use predefined lists of managed attributes for documentation where common or + full generic input renderer are used. + [thet] + +- When creating a blueprint chain with a factory, allow blueprints defined + after blueprints based in ``generic_input_renderer``. + [thet] + - Introduce ``hybrid_renderer`` and ``leaf`` widget property which gets considered in ``hybrid_renderer`` and ``hybrid_extractor``. Use ``hybrid_renderer`` in ``div`` blueprint. @@ -13,7 +24,7 @@ History - Consider data attributes in div renderer. [rnix] -- Fix rendering of empty div renderer. +- Fix rendering of empty in div ``div_renderer``. [rnix] - Explicitely check for ``None`` and ``UNSET`` before rendering empty value in diff --git a/src/yafowil/common.py b/src/yafowil/common.py index 70eced0..294e8d5 100644 --- a/src/yafowil/common.py +++ b/src/yafowil/common.py @@ -131,6 +131,11 @@ Disables input. """ +factory.defaults['readonly'] = None +factory.doc['props']['readonly'] = """\ +Readonly input. +""" + factory.defaults['required_class_default'] = 'required' factory.doc['props']['required_class_default'] = """\ CSS-class to apply if required condition was not met - if no specific class @@ -160,6 +165,23 @@ input. """ +managed_props__input_common = css_managed_props + [ + 'autofocus', + 'data', + 'disabled', + 'maxlength', + 'placeholder', + 'readonly', + 'required', + 'size', + 'title' +] + + +managed_props__input_full = managed_props__input_common + [ + 'autocomplete' +] + ############################################################################### # generic @@ -276,11 +298,14 @@ def input_attributes_common(widget, data, excludes=list(), value=None): autofocus = attr_value('autofocus', widget, data) and 'autofocus' or None disabled = attr_value('disabled', widget, data) disabled = bool(disabled) and 'disabled' or None + readonly = attr_value('readonly', widget, data) + readonly = bool(readonly) and 'readonly' or None required = attr_value('required', widget, data) and 'required' or None input_attrs = { 'autofocus': autofocus, 'class_': cssclasses(widget, data), 'disabled': disabled, + 'readonly': readonly, 'id': cssid(widget, 'input'), 'name_': widget.dottedpath, 'placeholder': attr_value('placeholder', widget, data), @@ -308,13 +333,18 @@ def input_attributes_full(widget, data, value=None): return input_attrs -@managedprops(*css_managed_props) -def input_generic_renderer(widget, data, custom_attrs={}): +@managedprops(*managed_props__input_full) +def input_generic_renderer(widget, data, pos='before', custom_attrs={}): """Generic HTML ``input`` tag render. """ input_attrs = input_attributes_full(widget, data) input_attrs.update(custom_attrs) - return data.tag('input', **input_attrs) + rendered = data.tag('input', **input_attrs) + if pos == 'before': + rendered = rendered + (data.rendered or u'') + else: + rendered = (data.rendered or u'') + rendered + return rendered # multivalued is not documented, because its only valid for specific blueprints @@ -333,7 +363,7 @@ def display_proxy_renderer(widget, data): input_attrs = input_attributes_full(widget, data, value=val) rendered += data.tag('input', **input_attrs) else: - rendered += input_generic_renderer(widget, data) + rendered = input_generic_renderer(widget, data, pos='after') if orgin_type: widget.attrs['type'] = orgin_type else: @@ -389,10 +419,11 @@ def generic_positional_rendering_helper(tagname, message, attrs, rendered, pos, pos position how to place the newtag relative to the prior rendered: 'before'='messagerendered', - 'after' ='message' + 'after' ='renderedmessage' 'inner-before'= message rendered 'inner-after'= rendered message """ + rendered = rendered or u'' if pos not in ['before', 'after', 'inner-before', 'inner-after']: raise ValueError('Invalid value for position "{0}"'.format(pos)) if pos.startswith('inner'): @@ -448,8 +479,7 @@ def tag_renderer(widget, data): # text ############################################################################### -@managedprops('data', 'title', 'size', 'disabled', 'autofocus', - 'placeholder', 'autocomplete', *css_managed_props) +@managedprops(*managed_props__input_full) def text_edit_renderer(widget, data): return input_generic_renderer(widget, data) @@ -484,9 +514,6 @@ def text_edit_renderer(widget, data): factory.defaults['text.class'] = 'text' factory.defaults['text.disabled'] = False -factory.doc['props']['text.disabled'] = """\ -Flag input field is disabled. -""" factory.defaults['text.persist'] = True @@ -856,8 +883,7 @@ def _pwd_value(widget, data): return attr_value('default', widget, data) -@managedprops('data', 'title', 'size', 'disabled', 'autofocus', - 'placeholder', 'autocomplete', *css_managed_props) +@managedprops(*managed_props__input_common) def password_edit_renderer(widget, data): """Render password widget. """ @@ -958,8 +984,7 @@ def checkbox_extractor(widget, data): ) -@managedprops('data', 'title', 'size', 'disabled', 'autofocus', - 'format', 'disabled', 'checked', *css_managed_props) +@managedprops('format', *managed_props__input_common) def checkbox_edit_renderer(widget, data): tag = data.tag input_attrs = input_attributes_common(widget, data) @@ -1051,9 +1076,6 @@ def checkbox_display_renderer(widget, data): factory.defaults['checkbox.class'] = 'checkbox' factory.defaults['checkbox.disabled'] = False -factory.doc['props']['checkbox.disabled'] = """\ -Flag whether checkbox is disabled. -""" factory.defaults['checkbox.checked'] = None factory.doc['props']['checkbox.checked'] = """\ @@ -1435,8 +1457,7 @@ def file_extractor(widget, data): return value -@managedprops('accept', 'placeholder', 'autofocus', - 'required', *css_managed_props) +@managedprops('accept', *managed_props__input_common) def input_file_edit_renderer(widget, data): tag = data.tag input_attrs = input_attributes_common(widget, data, excludes=['value']) @@ -1553,8 +1574,8 @@ def file_options_renderer(widget, data): # submit ############################################################################### -@managedprops('label', 'class', 'action', 'handler', - 'next', 'skip', 'expression') +@managedprops('label', 'action', 'handler', 'next', 'skip', 'expression', + *managed_props__input_common) def submit_renderer(widget, data): expression = attr_value('expression', widget, data) if not expression: @@ -1783,6 +1804,11 @@ def number_extractor(widget, data): return val +@managedprops('min', 'max', 'step', *managed_props__input_full) +def number_edit_renderer(widget, data): + return input_generic_renderer(widget, data) + + factory.register( 'number', extractors=[ @@ -1792,7 +1818,7 @@ def number_extractor(widget, data): generic_datatype_extractor, number_extractor, ], - edit_renderers=[input_generic_renderer], + edit_renderers=[number_edit_renderer], display_renderers=[ generic_display_renderer, display_proxy_renderer diff --git a/src/yafowil/common.rst b/src/yafowil/common.rst index a1ee33b..f8d81d3 100644 --- a/src/yafowil/common.rst +++ b/src/yafowil/common.rst @@ -188,7 +188,29 @@ Render with title attribute:: u'' -Generic HTML5 Data:: + Render disabled:: + + >>> widget = factory( + ... 'text', + ... name='MYTEXT', + ... props={ + ... 'disabled': True + ... }) + >>> widget() + u'' + + Render readonly:: + + >>> widget = factory( + ... 'text', + ... name='MYTEXT', + ... props={ + ... 'readonly': True + ... }) + >>> widget() + u'' + + Generic HTML5 Data:: >>> widget = factory( ... 'text', @@ -449,8 +471,7 @@ hidden field:: >>> wrapped_pxml(widget())
lorem ipsum
- +
@@ -470,6 +491,23 @@ Skip mode renders empty string.:: >>> widget() u'' +Multiple blueprints:: + + >>> widget = factory( + ... 'label:text:help', + ... name="textinput", + ... props={ + ... 'label': 'label before input', + ... 'help': 'help after input', + ... } + ... ) + >>> wrapped_pxml(widget()) +
+ + +
help after input
+
+ Datatype extraction -------------------