-
Notifications
You must be signed in to change notification settings - Fork 85
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
Providing better way(s) to control the checkedness of a checkbox #205
Comments
Generally speaking, For instance, most I'm not going into details here since I plan to some day document these ideas more thoroughly in the
If a user needs to control checkedness they will set a One benefit to enforcing the So, unless there is another benefit other than catching typos, forcing If this is a real segment of There is a trade-off. By not forcing a
Whereas if we forced a I spent a few minutes thinking and couldn't come up with a legitimate use case for allowing users to implement So, it's possible that there aren't any legitimate use cases for it. Still, since forcing So, we should not require
We should start by just making this the user's responsibility. The user already needs an Now, of course the problem is that it's easy to forget to do that. So, we'd want to eventually either design a way for it to be impossible for the user to forget to take full control of checkedness, or to have automatically For instance:
My main point is that we can start by making dealing with the browser automatically changing Then in the meantime Recommendations
|
Sounds good.
I believe it's primary benefit would be discouraging the reliance on the browser's checkedness model, which is likely to be a footgun for any project with a single place where application state is stored. Not specifying
That works for me, turns out specifying I'd like to note that I think making this the default behavior would be beneficial for a similar reason as mentioned above, not allowing the checkbox's state to change if the user specified For example: let checked_checkbox = html! {<input type="checkbox" checked=true>}; let checked_checkbox_that_may_change = html! {<input type="checkbox" checked=true checkedness_toggles_on_click>}; // This is readable. This is also likely to happen if there's a condition where `checked` is intentionally unspecified
// based on a condition, where `checkedness_toggles_on_click` behavior is expected regardless of whether `checked`
// is specified or not.
let unspecified_checkbox_that_may_change = html! {<input type="checkbox" checkedness_toggles_on_click>}; // This is the odd one out, but it doesn't seem reasonable that a developer would
// intend to maintain the checkbox in an unspecified state.
// It's much more likely that the developer did not want to control the checkbox behavior themselves,
// and so specified nothing related to the checkedness.
let unspecified_checkbox_that_may_change = html! {<input type="checkbox">}; Again, no need for this now from us, but if further consideration is made on this in the future, I would like to suggest this alternative. I'll submit a PR with the recommended changes shortly 👍 |
Definitely an interesting alternative. Cool, planning to review |
As notes in a comment in the PR here #206 (comment) I wasn't accounting for server-side rendering (SSR) at all. That's fine for the behavior that's being changed in the PR (except for a detail that's currently unresolved, as discussed in the comment linked above). But in case any of this is referred to in the future, it's worth clarifying the problems. It's worth noting that I might be misunderstanding how SSR is typically used, as while I've read about it here and there, I've never worked with it. This is noteworthy because the best defaults for SSR and CSR are completely different (if I'm understanding SSR correctly, and how developers would use
Therefore,
This is also probably the wrong default when doing SSR. It's mildly concerning that |
Not necessarily.
It supports that, but it isn't the primary objective. The primary objective is supporting use cases where:
Often referred to as "server-side rendering with client-side hydration".
We're designing for the case where SSR and client-side rendering are both used together. For instance:
So,
The server-side rendered HTML is also controlled by application state. We want the This helps solve for "while also remaining maintainable as the application's scope grows tremendously".
This is the modus operandi of frameworks and libraries that market the idea of "keeping things simple" by keeping all of the logic / rendering server-side. The So, This is why we attempt to treat SSR and CSR the same. It reduces the amount of effort required to solve the core problem:
The default for SSR should be that it's very easy to start with SSR, and then have your client-side code pick up from there. So, app running server-side and same application code running client-side both start with the same application state, meaning they render the same thing, meaning there is a seamless transition. The least surprising for the user, both for SSR and CSR is:
|
Planning to re-read this and address the rest of the review items later today.
But after a quick scan:
percy being primarily designed around application state for both CSR and SSR sounds great.
Sacrificing a decent way to manually set the default checkedness when doing CSR (like the example I gave) seems okay. We can add in something like solution (1) after the fact if someone comes along with a real usecase for it.
…-------- Original Message --------
On 2024/09/24 18:16, Chinedu Francis Nwafili wrote:
> The default for SSR should be that the user gets the HTML they expect, and the browser does with that HTML what it usually would.
Not necessarily.
percy isn't focused on developers that need a simple SSR solution with no client-side behavior.
It supports that, but it isn't the primary objective.
The primary objective is supporting use cases where:
- Server renders the HTML
- Server serves the HTML
- Client-side replaces the HTML that the server sent down with client-side rendered elements
Often referred to as "server-side rendering with client-side hydration".
> SSR is likely to be used when there's no substantial "application" running on the client, therefore no application state to control the checkboxes.
We're designing for the case where SSR and client-side rendering are both used together.
For instance:
- Server application reads data from backend database
- Server application renders HTML
- Client receives HTML
- Client immediately sees content
- Client-side code starts running
- Client-side code makes HTTP requests to download data
- Client-side has the data it needs
- Client-side replaces the HTML elements that the server sent down with HTML elements rendered client-side
- As state changes, the client keeps the page updated
So, percy is aimed at the problem:
> (rough sketch) How do I build an application that shows users content as quickly as possible, while also remaining maintainable as the application's scope grows tremendously.
---------------------------------------------------------------
> The default for CSR would preferably be that the application has complete control of the GUI state, which is fully determined by the application state. No allowance for the application's state and the GUI state to desync.
The server-side rendered HTML is also controlled by application state.
We want the percy user to be able to run the same exact application code on the server, or on the client, with as close to zero changes as possible, unless there is a very good reason for a divergence.
This helps solve for "while also remaining maintainable as the application's scope grows tremendously".
> when percy's goals for CSR (application state is king) contradicts the modus operandi for SSR: serve the HTML and keep the client-side minimal.
This is the modus operandi of frameworks and libraries that market the idea of "keeping things simple" by keeping all of the logic / rendering server-side.
There is currently something of a movement for simple applications that aren't "over-engineered".
percy is not part of this movement. percy is squarely aimed at very complex applications that could never be fully server-side rendered, but still want the benefits of SSR (probably either SEO or faster time to first paint).
The percy project believes that the ceiling on those kinds of applications is very low. For some folks that might be okay.
So, percy's goal is not to avoid complexity by keeping the client-side minimal.
Rather, the goal is to reduce complexity without reducing the ceiling of the amount of functionality that one can implement while the application still remains reasonably maintainable.
This is why we attempt to treat SSR and CSR the same. It reduces the amount of effort required to solve the core problem:
- (rough sketch of core problem) How do I build an application that shows users content as quickly as possible, while also remaining maintainable as the application's scope grows tremendously.
---------------------------------------------------------------
> The default for SSR should be that the user gets the HTML they expect, and the browser does with that HTML what it usually would
The default for SSR should be that it's very easy to start with SSR, and then have your client-side code pick up from there.
So, app running server-side and same application code running client-side both start with the same application state, meaning they render the same thing, meaning there is a seamless transition.
---------------------------------------------------------------
The least surprising for the user, both for SSR and CSR is:
- checked={true,false} controls the "checked" attribute
- checked={true,false} controls the ".checked" property
- In case that you want to set default checkedness on the server, but then aren't using percy on the client, it means that you're accessing the element in some other way (maybe JavaScript, or jQuery or whatever).
- So, you have the ability to do whatever you want to the element. At that point you've left the percy world and can control things as you wish.
—
Reply to this email directly, [view it on GitHub](#205 (comment)), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/ALAPHR7QDXWORGPIU7UDWSTZYGF6NAVCNFSM6AAAAABOFLLEK2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGNZRG42DMMBWGE).
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
In summary, the current state of dealing with the checkedness of checkbox input elements in
percy
is unsatisfactory. Using thechecked
attribute is misleading (as it only controls default checkedness, not checkedness), andpercy
offers no easy way to control the actual checkedness of an element (instead, one would need to manually callset_checked
and usee.prevent_default()
in anonclick
handler or similar workarounds).There is a decision to be made, assuming that we're in agreement about rectifying the situation. Should we enforce specifying the checkedness of a checkbox? I've highlighted this question in bold below, and discussed the motivations and implications in the section "Should we provide a way to specify default checkedness?".
I've implemented most of the changed already, the design just needs to be finalized before I finish it and open a PR.
This situation is a bit confusing to write about so I'm going to define a few terms:
percy-checked
refers to the HTML attribute-like value specified usinghtml-macro
, e.g.let vnode = html! { <input type="checkbox" checked=true> };
checkedness
refers to the actual visual state of a useragent-rendered checkbox, unless otherwise specified. This corresponds toelem.checked()
but not necessarily toelem.has_attribute("checked")
checked
attribute refers to the HTML attributechecked
which determines the default checkedness of the element as described in the standard. The checkbox's checkedness corresponds to the presence of thechecked
attribute until the dirty flag is setelem.set_checked(boolean_val)
(ifboolean_val == elem.checked()
the dirty flag is not set). When the dirty flag is set, the checkedness no longer corresponds to the default checkedness, i.e. it no longer corresponds to the presense of thechecked
HTML attribute.Here's a concrete example of these concepts using
percy
:Currently,
percy-dom
providespercy-checked
. This is the extent of howpercy
allows for controlling the checkedness of a checkbox input element.percy-checked
currently controls whetherpercy
sets or removes thechecked
attribute from the HTML elements, which determines the default checkedness of the checkbox. It does NOT specify the checkedness of the checkbox (though if the dirty flag is not set, the checkedness changes if the default checkedness changes).So if you write
html! {<input checked=true>}
, you specify the default checkedness only. This leads to confusiion, as developers tend to assume that this would set the checkedness too, and naively it might seem that it does until you interact with the checkbox, at which point it will uncheck, despite continuing to renderhtml! {<input checked=true>}
.Notably, there is no way to specify the checkedness of a checkbox. If the user clicks on your checkbox,
percy
no longer offers a way to control the checkbox. Developers usingpercy
may useelem.set_checked(boolean_val)
before/after each render to make up for this, which is not ideal and is easily-forgotten boilerplate required for every checkbox.In summary, the problems are:
html-macro
doesn't allow for expressing the checkedness of the checkbox.percy-checked
misleads developers of its actual function: default checkedness.This motivates a change to
percy-dom
andhtml-macro
.Regarding solving issue (1.):
Is specifying the checkness useful?
Any time a backend exists that maintains a boolean value that's displayed by the checkbox, it would be better to render the client backend's value, rather than the useragent-checkbox state.
As an example I've been personally dealing with, a remotely-persisted boolean value's checkbox's checkedness should correspond to the boolean value of the remote data, insofar as is possible (i.e. what the client knows of the server state). Clicking the checkbox should not update the checkedness immediately. Checkedness should only change once the server lets the client know that the authoritative value (the server's value) was successfully changed (if the user loses connection before the request to change the bool was sent, then clicking the checkbox ideally wouldn't change its checkedness).
I think it's best to be able to control checkedness easily when using
percy
. I will discuss how to go about that further on.Regarding solving issue (2.):
Assuming we allow developers to set default checkedness:
percy-checked
probably should be renamed fromchecked
to something else, such asdefault_checked
which clearly communicates its behavior.html! {<input default_checked=true>}
Notably React takes this approach of having adefaultChecked
attribute.Should we provide a way to specify default checkedness?
This is assuming we allow developers to specify default checkedness. When is specifying default checkness useful?
If the checkbox is being useful; i.e. it's value might be used in some way while it's being used or interacted with AND checkedness is not being explicitly specified, it means that the developer is implicitly or explicitly having the user agent's checkbox state be authoritative.
There are a couple of dubious "benefits" to this with negative implications for nontrivial projects:
So the choice is, do we expect all apps using
percy
to have a fully authoritative backend that handles all desired functionality of the checkbox explicitly? (It's checkedness, form resetting.) Or do we allow for developers to make the checkbox state authoritative?Notably, this is a WASM Rust project, which sees a lot less "I just want to bodge something together quickly" than Javascript does. The best choice for a VDOM Javascript framework may not be the best choice for a Rust framework, as the projects that use JS and HTML directly without a backend, where specifying the default checkedness is easier than the checkedness.
[Vague] Usage information
Explicitly specifying
defaultChecked
or writing related code even just with web frameworks that have it, or when writing functions that do something related, returns 110k results using GitHub code search. https://github.com/search?q=defaultChecked+language%3AJavaScript+&type=code&p=2 This does not include anyone who sets the default checkedness by setting/removing thechecked
HTML attribute.A similar seach for "default_checked" and "defaultChecked" return about 450 results in total.
Note that most of these results are not clearly related to this issue explicitly. It's noise. It's also heavily confounded by many variables, obviously. I'm not sure how to best assess this. My experience with web development is limited.
If we do NOT allow for specifying the default checkedness easily AND allow the developer to NOT specify
checkedness
:A.
percy
chooses a default and let the user agent set the checkbox as it would normally. This default can be overridden withelem.set_attribute("checked", "")
andelem.remove_attribute("checked")
.B.
percy
always uses the last-specified checkedness, and applies its own default if checkedness has not yet been specified.These are the two most sensible possibilities I can think of.
(A) is not a good choice. It makes setting the default harder than necessary, while still having all of the pitfalls of the checkbox state either being out-of-sync with the authoritative value or authoritative itself.
(B) is similarly awkward. It might go out-of-sync with the authoritative value or be authoritative itself. Developers would be relying on
percy
to maintain the checkedness value. It also makes setting the default harder than necessary.I don't think NOT allow for specifying the default checkedness easily AND allow the developer to NOT specify
checkedness
has any benefits over allowing the developer to specify default checkedness. Therefore I don't think it should be considered.If we do NOT allow for specifying the default checkedness easily AND do force the developer to specify
checkedness
, then the behavior of the checkbox is simple. We just need to ensure that all<input type="checkbox>
elements have a defined checkedness in their definition.This is definitely the right choice for my use of
percy
, but it would inconvenience developers who want to use the useragent checkbox state as authoritative, presumably because it's easier for a tiny project.This is a tradeoff
percy
needs to make: does it force the checkedness to be explicitly specified or not?Explicitly specifying checkedness
Given that we want to explicitly specify checkedness with
html-macro
+perdy-dom
there are a couple implementation details to be explicit about:patch
ed the VDOM yet 2) might have not changed the checkedness when they dopatch
. Therefore, we need to force the checkbox to maintain it's value. There are some possibilities I'm aware of, all requiring adding some code toonchange
:event.prevent_default()
or using JS's timeout with a duration of zero to quickly reset the checkbox to the patched state. I'm not aware of the tradeoffs between these two approaches precisely.patch
a checkbox with a new value, we need to explicitly callset_checked
as adding/removing thechecked
HTML attribute only changed the default checkedness, not the checkbox's checkedness.The text was updated successfully, but these errors were encountered: