-
-
Notifications
You must be signed in to change notification settings - Fork 151
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added React-based SelectOtherWidget and SelectOtherField
- Loading branch information
Showing
16 changed files
with
378 additions
and
3 deletions.
There are no files selected for viewing
2 changes: 1 addition & 1 deletion
2
src/senaite/core/browser/static/bundles/senaite.core.widgets.js
Large diffs are not rendered by default.
Oops, something went wrong.
2 changes: 1 addition & 1 deletion
2
src/senaite/core/browser/static/bundles/senaite.core.widgets.js.map
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
from senaite.core.schema.fields import BaseField | ||
from senaite.core.schema.interfaces import ISelectOtherField | ||
from zope.interface import implementer | ||
from zope.schema import Choice | ||
|
||
|
||
@implementer(ISelectOtherField) | ||
class SelectOtherField(Choice, BaseField): | ||
"""A field that handles a value from a predefined vocabulary or custom | ||
""" | ||
def _validate(self, value): | ||
super(SelectOtherField, self)._validate(value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# -*- coding: utf-8 -*- |
35 changes: 35 additions & 0 deletions
35
src/senaite/core/z3cform/widgets/selectother/configure.zcml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<configure | ||
xmlns="http://namespaces.zope.org/zope" | ||
xmlns:z3c="http://namespaces.zope.org/z3c"> | ||
|
||
<!-- SelectOther Widget --> | ||
<adapter | ||
factory=".widget.SelectOtherWidgetFactory" | ||
for="senaite.core.schema.interfaces.ISelectOtherField | ||
senaite.core.interfaces.ISenaiteFormLayer" /> | ||
|
||
<!-- SelectOther data converter --> | ||
<adapter factory=".widget.SelectOtherDataConverter" /> | ||
|
||
<!-- SelectOther input widget template --> | ||
<z3c:widgetTemplate | ||
mode="input" | ||
widget=".widget.SelectOtherWidget" | ||
template="input.pt" | ||
layer="senaite.core.interfaces.ISenaiteFormLayer" /> | ||
|
||
<!-- SelectOther display widget template --> | ||
<z3c:widgetTemplate | ||
mode="display" | ||
widget=".widget.SelectOtherWidget" | ||
template="display.pt" | ||
layer="senaite.core.interfaces.ISenaiteFormLayer" /> | ||
|
||
<!-- SelectOther hidden widget template --> | ||
<z3c:widgetTemplate | ||
mode="hidden" | ||
widget=".widget.SelectOtherWidget" | ||
template="hidden.pt" | ||
layer="senaite.core.interfaces.ISenaiteFormLayer" /> | ||
|
||
</configure> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<html xmlns="http://www.w3.org/1999/xhtml" | ||
xmlns:tal="http://xml.zope.org/namespaces/tal" | ||
tal:omit-tag=""> | ||
|
||
<span tal:content="python:view.get_display_value()"></span> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<html xmlns="http://www.w3.org/1999/xhtml" | ||
xmlns:tal="http://xml.zope.org/namespaces/tal" | ||
tal:omit-tag=""> | ||
|
||
<input type="hidden" | ||
tal:attributes="name python:view.name; | ||
name python:view.id; | ||
value view.value;"/> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<html xmlns="http://www.w3.org/1999/xhtml" | ||
xmlns:tal="http://xml.zope.org/namespaces/tal" | ||
tal:omit-tag=""> | ||
|
||
<div tal:attributes="python:view.get_input_widget_attributes(); | ||
class python:'{}-input'.format(view.klass)"> | ||
|
||
<!-- ReactJS controlled component --> | ||
|
||
</div> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# This file is part of SENAITE.CORE. | ||
# | ||
# SENAITE.CORE is free software: you can redistribute it and/or modify it under | ||
# the terms of the GNU General Public License as published by the Free Software | ||
# Foundation, version 2. | ||
# | ||
# This program is distributed in the hope that it will be useful, but WITHOUT | ||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
# details. | ||
# | ||
# You should have received a copy of the GNU General Public License along with | ||
# this program; if not, write to the Free Software Foundation, Inc., 51 | ||
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
# | ||
# Copyright 2018-2024 by it's authors. | ||
# Some rights reserved, see README and LICENSE. | ||
|
||
import json | ||
from bika.lims import _ | ||
from senaite.core.i18n import translate as t | ||
from senaite.core.interfaces import ISenaiteFormLayer | ||
from senaite.core.schema.interfaces import ISelectOtherField | ||
from senaite.core.z3cform.interfaces import ISelectOtherWidget | ||
from senaite.core.z3cform.widgets.basewidget import BaseWidget | ||
from z3c.form.browser import widget | ||
from z3c.form.browser.widget import HTMLTextInputWidget | ||
from z3c.form.converter import BaseDataConverter | ||
from z3c.form.interfaces import IFieldWidget | ||
from z3c.form.widget import FieldWidget | ||
from zope.component import adapter | ||
from zope.component import queryUtility | ||
from zope.interface import implementer | ||
from zope.schema.interfaces import IVocabularyFactory | ||
|
||
OTHER_OPTION_VALUE = "__other__" | ||
|
||
|
||
@adapter(ISelectOtherField, ISelectOtherWidget) | ||
class SelectOtherDataConverter(BaseDataConverter): | ||
"""Converts the value between the field and the widget | ||
""" | ||
|
||
def toWidgetValue(self, value): | ||
"""Converts from field value to widget | ||
""" | ||
return value | ||
|
||
def toFieldValue(self, value): | ||
"""Converts from widget to field value | ||
""" | ||
if isinstance(value, list): | ||
if value[0] == OTHER_OPTION_VALUE: | ||
return str(value[1]) | ||
value = value[0] | ||
return str(value) | ||
|
||
|
||
@implementer(ISelectOtherWidget) | ||
class SelectOtherWidget(HTMLTextInputWidget, BaseWidget): | ||
"""Widget for the selection of an option from a pre-populated list or | ||
manual introduction | ||
""" | ||
klass = u"senaite-selectother-widget-input" | ||
|
||
def get_display_value(self): | ||
"""Returns the value to display | ||
""" | ||
choices = self.get_choices() | ||
choices = dict(choices) | ||
return choices.get(self.value) or self.value | ||
|
||
def get_input_widget_attributes(self): | ||
"""Return input widget attributes for the ReactJS component | ||
""" | ||
option = "" | ||
other = "" | ||
|
||
# find out if the value is a predefined option | ||
choices = self.get_choices() | ||
options = dict(choices).keys() | ||
if self.value in options: | ||
option = self.value | ||
elif self.value: | ||
option = OTHER_OPTION_VALUE | ||
other = self.value | ||
|
||
attributes = { | ||
"data-id": self.id, | ||
"data-name": self.name, | ||
"data-choices": choices, | ||
"data-option": option, | ||
"data-option_other": OTHER_OPTION_VALUE, | ||
"data-other": other, | ||
} | ||
|
||
# convert all attributes to JSON | ||
for key, value in attributes.items(): | ||
attributes[key] = json.dumps(value) | ||
|
||
return attributes | ||
|
||
def update(self): | ||
"""Computes self.value for the widget templates | ||
see z3c.form.widget.Widget | ||
""" | ||
super(SelectOtherWidget, self).update() | ||
widget.addFieldClass(self) | ||
|
||
def get_vocabulary(self): | ||
if not self.field: | ||
return None | ||
|
||
vocabulary = getattr(self.field, "vocabularyName", None) | ||
if not vocabulary: | ||
return None | ||
|
||
factory = queryUtility(IVocabularyFactory, vocabulary,) | ||
if not factory: | ||
return None | ||
|
||
return factory(self.context) | ||
|
||
def get_choices(self): | ||
"""Returns the predefined options for this field | ||
""" | ||
# generate a list of tuples (value, text) from vocabulary | ||
vocabulary = self.get_vocabulary() | ||
choices = [(term.value, t(term.title)) for term in vocabulary] | ||
|
||
# insert the empty option | ||
choices.insert(0, ("", "")) | ||
|
||
# append the "Other..." choice | ||
other = (OTHER_OPTION_VALUE, t(_("Other..."))) | ||
choices.append(other) | ||
|
||
return choices | ||
|
||
|
||
@adapter(ISelectOtherField, ISenaiteFormLayer) | ||
@implementer(IFieldWidget) | ||
def SelectOtherWidgetFactory(field, request): | ||
"""Widget factory for SelectOther field | ||
""" | ||
return FieldWidget(field, SelectOtherWidget(request)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.