-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
html: add radio buttons #435
Conversation
7971f34
to
3d61e56
Compare
Codecov ReportPatch coverage:
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## master #435 +/- ##
==========================================
+ Coverage 72.63% 72.77% +0.13%
==========================================
Files 84 85 +1
Lines 5760 5866 +106
Branches 1238 959 -279
==========================================
+ Hits 4184 4269 +85
- Misses 1311 1329 +18
- Partials 265 268 +3
☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to see some progress here :)
I like that even the RadioGroup
feels like an HTML element in this context (even if HTML does not specify such node).
Do you see some room for a (specific) high-level interface like the one suggested here in Lona? Or should such interfaces live somewhere else? It feels wrong to have a custom widget in every project.
# value | ||
@property | ||
def value(self): | ||
checked_radio_button = self.get_checked_radio_button() | ||
|
||
if not checked_radio_button: | ||
return | ||
|
||
return checked_radio_button.value | ||
|
||
@value.setter | ||
def value(self, new_value): | ||
with self.lock: | ||
for radio_button in self.get_radio_buttons(): | ||
radio_button.checked = radio_button.value == new_value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess returning None
if no RadioButton
is checked is a good way to handle this state.
So in the same way setting value = None
creates a list where no RadioButton
is checked.
But: If I try to set a value that is not part of a RadioGroup
I would suggest to raise an ValueError
instead of silently not checking any RadioButton
.
|
||
|
||
class RadioGroup(Node): | ||
TAG_NAME = 'form' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HTML5 specification explicitly does not allow nesting of <form>
. Even if it feels like there are not many use-cases where one want's to use actual HTML forms in Lona. Why make using them extra hard?
Is there any gain from using form
here instead of an element like div
that is intended for nesting?
Yeah, sorry :D
I was thinking about something like <label>
My Label
<input type="radio" value="my-value">
</label> This seems to work fine in all cases I encountered but the "more HTML5" layout would be like this: <label for="my-radio-button>My Label</label>
<input type="radio" id="my-radio-button" value="my-value">
I agree.
Unfortunately, radio groups have no dedicated HTML tag but are identified by multiple radio buttons using the same name. When using a div as radio group, your code would have to look like this: RadioGroup(
RadioButton(value='foo', name='my-group'),
RadioButton(value='bar', name='my-group'),
RadioButton(value='baz', name='my-group', checkd=True),
)```
If possible I don't want a mandatory name field that is not used by the application code since the `RadioGroup` object is the handle to the checked value. When using a `<form>` `RadioButton.name` can be generic, when using `<div>` not. |
So, if both work, why care which one to use? (But I personally would prefer to stick closer to what HTML5 suggests.)
OK, and when using a |
Fair enough. If we decide on a
from lona.html import Select
Select(
values=[
# value, label, is_selected
('foo', 'Foo', True),
('bar', 'Bar', False),
],
) The comment pretty much marks the problem: It is easy to get the order of the options wrong, and there are cryptic flags needed to set properties on the resulting Therefore s = Select2()
s.add_option(Option2('Option 1', value='option-1', selected=True))
s.add_option(Option2(Span('Option 2'), value='option-2', style={'color': 'red'})) It's more verbose, but also more flexible. To be consistent with this API r = RadioGroup()
r.add_button(
Label('Option 1', style={'color': 'red'}),
RadioButton(value='option-1'),
)
r.add_button(
Div( # using a div to make the listing go top to bottom instead left to right
Label('Option 1', style={'color': 'red'}),
RadioButton(value='option-1'),
),
) The |
3d61e56
to
870619a
Compare
@SmithChart: I added an implementation for radio_group.add_button(Label('foo'), RadioButton(value='foo'))
radio_group.add_button(Div(Label('foo'), RadioButton(value='foo')))
radio_group.add_button(Label('foo', RadioButton(value='foo'))) For convenience, I added a special case that makes code like this work too: radio_group.add_button('Foo', 'foo') I am not sure if this is too much magic, but it seems to be useful. What do you think? |
I get the point on magic arguments and flags you made for the Select. So: I think I like this API. |
@SmithChart: Great! Thanks for your opinion on that. I will finish this PR, update the docs, and ping you for the final review |
Signed-off-by: Florian Scherf <mail@florianscherf.de>
Signed-off-by: Florian Scherf <mail@florianscherf.de>
With the `issuer` argument it is possible to make changes that don't get rolled out to the original issuer of the change. In this particular case this is meant to be used for radio buttons to synchronize the `checked` property on the backend with the frontend. Signed-off-by: Florian Scherf <mail@florianscherf.de>
870619a
to
c974645
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fscherf I've reviewed your changes and gave it a try on a project I had open.
@@ -1,5 +1,5 @@ | |||
SHELL=/bin/bash | |||
PYTHON=python3.10 | |||
PYTHON=python3.11 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be part of this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessarily, but it isn't a problem either because it is a separate patch. While implementing this, I ran into some issues and wanted to use the new exception pretty printing of Python 3.11.
doc/content/api-reference/html.rst
Outdated
radio_group = RadioGroup( | ||
Label('Option 1', RadioButton(value=1)), | ||
Label('Option 2', RadioButton(value=2.0)), | ||
Label('Option 3', RadioButton(value='3'), checked=True), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checked=True
is an argument for the RadioButton
, not the Label
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Fixed.
doc/content/api-reference/html.rst
Outdated
radio_group = RadioGroup( | ||
Label('Option 1', RadioButton(value=1)), | ||
Label('Option 2', RadioButton(value=2.0)), | ||
Label('Option 3', RadioButton(value='3'), checked=True), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initializing a RadioGroup via the constructor is not implemented! Using it this way Labels and RadioButtons are not linked together with the for=
attribute.
Using radio_group.add_button()
works however.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is due to the weird implementation of radio buttons in HTML. Correlating which Label
is linked to which RadioButton
is tricky when you get a whole tree of both. That's why, in this example, the RadioButton
objects are inside of the Label
objects. That way they are linked together by the browser implicitly. In add_button()
that's not a problem, because you are supposed to provide only one label and one input with each call.
The RadioGroup.value
is initialized correctly in this example because value
is a property that searches for a RadioButton
that is checked on every call. It is not a static value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. Good point. Having the nodes in this way makes the result look funny. But add_button()
works as expected. So no further action needed here.
Signed-off-by: Florian Scherf <mail@florianscherf.de>
c974645
to
bd7638e
Compare
@SmithChart: This PR should be ready to merge now I think. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@fscherf feel free to close the resolved discussions. I can't do that from here.
@SmithChart: Great! Thanks a lot for your help on this topic, and for your patience. I know it took a long time. |
This PR adds initial support for HTML radio buttons (#229)