Skip to content
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

Invokers API #920

Closed
1 task done
keithamus opened this issue Dec 8, 2023 · 9 comments
Closed
1 task done

Invokers API #920

keithamus opened this issue Dec 8, 2023 · 9 comments
Assignees
Labels
Mode: breakout Work done during a time-limited breakout session Resolution: satisfied The TAG is satisfied with this design Review type: CG early review An early review of general direction from a Community Group Venue: Open UI Venue: WHATWG

Comments

@keithamus
Copy link

keithamus commented Dec 8, 2023

こんにちは TAG-さん!

I'm requesting a TAG review of the "Invokers API", as described in the Open UI explainer.

Adding invoketarget and invokeaction attributes to <button> and <input type="button"> / <input type="reset"> elements would allow authors to assign behaviour to buttons in a more accessible and declarative way, while reducing bugs and simplifying the amount of JavaScript pages are required to ship for interactivity. Buttons with invoketarget will - when clicked, touched, or enacted via keypress - dispatch an InvokeEvent on the element referenced by invoketarget, with some default behaviours.

<button invoketarget="my-dialog">This opens a dialog</button>
<dialog id="my-dialog">This is the dialog</dialog>

While there are many ideas for what those behaviours will be, for the initial implementation we want to ship:

  • Showing, hiding and toggling popovers. (This is very similar to popovertarget).
  • Showing a Dialog as modal, and closing a dialog.

Further details:

  • I have reviewed the TAG's Web Platform Design Principles
  • The group where the incubation/design work on this is being done (or is intended to be done in the future): OpenUI
  • The group where standardization of this work is intended to be done ("unknown" if not known): WHATWG
  • Existing major pieces of multi-stakeholder review or discussion of this design: https://github.com/openui/open-ui/issues?q=is%3Aissue+label%3Ainvokers
  • Major unresolved issues with or opposition to this design:
    • Mostly to do with the naming of the actions, and how they fit within the existing attribute naming scheme. At current we have showpopover, hidepopover, togglepopover, showmodal, close and auto. It seems as though these should include dashes, but we've reserved dashes for custom actions (see openui/open-ui#900 and openui/open-ui#969).
  • This work is being funded by: Keith Cirkel

You should also know that...

We are looking for help with resolving the naming of actions, and how we can best fit the existing system. We're explicitly interested in feedback from the TAG!

This works surrounds some larger work pieces, for example there is follow on work to add more actions, follow on work to add an interesttarget which will do the same but for hover/focus style interactions, and we are also looking to deprecate popovertarget in favour of this API. While we're not seeking review for those parts, I hope they can paint a broader context of what is planned here.

We'd prefer the TAG provide feedback as:

🐛 open issues in our GitHub repo for each point of feedback

@martinthomson
Copy link
Contributor

@plinss and I discussed this briefly and while we think the overall capability looks good, we have some questions:

Is there a way to detect that this feature exists?

Why is there no generic .invoke() method for invocation targets? If targets have default action (called "auto"?) this might make it more ergonomic for authors rather than needing to work out how to achieve an action.

Why does <details> have "toggle" and "auto" actions doing the same thing?

Why does the <dialog> "toggle" action do the same thing as "cancel"? Why does this not show the dialog when it is not already open?

Is there a polyfill planned?

Does invoking alter focus? If something is invoked, does that receive focus? It seems like this would usually be the case, but some actions might not need that. Pausing a video doesn't need to draw focus, but you would want to focus <select> or <dialog> or a popover. Whatever the behavior is, it should be specified so to be consistent across UAs.

@lukewarlow
Copy link

lukewarlow commented Feb 26, 2024

Is there a way to detect that this feature exists?

Checking if an element has an invokeTargetElement property could be used as a feature detection mechanism.

Why is there no generic .invoke() method for invocation targets? If targets have default action (called "auto"?) this might make it more ergonomic for authors rather than needing to work out how to achieve an action.

While this is possible it would be potentially superfluous, popovers already have a togglePopover() function for example. It has been considered that actions should have a corresponding JS API though but idk if it makes sense to have the two go hand in hand always.

Why does <details> have "toggle" and "auto" actions doing the same thing?

Auto is the missing value default for the invokeaction attribute. `toggle' is just an explicit token for said action in the details case. This is something we're not entirely sure on and intentionally it's left as a question for future as it's not a blocker for v1 actions.

Why does the <dialog> "toggle" action do the same thing as "cancel"?

Apologies this is a mistake in the explainer.

Is there a polyfill planned?

https://github.com/keithamus/invokers-polyfill

@lukewarlow
Copy link

lukewarlow commented Feb 26, 2024

Does invoking alter focus? If something is invoked, does that receive focus?

I Think this ultimately depends on the action itself like you say for showPicker the select should be focused, for pause a video probably not. We'll ensure these are concretely specced.

@lukewarlow
Copy link

To further help in your review of the explainer (though your feedback and input on the extended feature design is definitely welcome and encouraged) it may be worth noting that the initial version only has actions for popover and Modal Dialogs.

Along with the concept of custom actions and their associated naming restrictions.

@keithamus
Copy link
Author

keithamus commented Feb 26, 2024

To add to Luke's commentary:

Is there a way to detect that this feature exists?

'invokeTargetElement' in HTMLButtonElement.prototype or 'invokeAction' in HTMLButtonElement.prototype, either one will do.

Why is there no generic .invoke() method for invocation targets? If targets have default action (called "auto"?) this might make it more ergonomic for authors rather than needing to work out how to achieve an action.

Generally speaking invoke actions map to imperative APIs, where "auto" is an alias for "do the most common action". So if an invoker is pointing to a popover, the default action is to call togglePopover(), if it is pointing to a dialog, it will call either close() or showModal() depending on the state of the dialog. We spoke of adding a toggleModal() to Dialog, and have openui/open-ui#954 raised to decide if we should more closely map the actions to imperative APIs, which may mean adding new methods.

Why does <details> have "toggle" and "auto" actions doing the same thing?

"auto" is the stand-in for when the attribute value is missing, so <button invoketarget=my_details> (no invokeaction) will do the "auto" behaviour. This is being changed to be "the auto state" (as in a null or empty string of the attribute) so explicitly "auto" will do nothing. See openui/open-ui#1006

Does invoking alter focus? [...] Whatever the behavior is, it should be specified so to be consistent across UAs.

"It depends". When it does, it will most certainly be specified. Specifically for a popover it runs the popover focusing steps - which may or may not change focus, and for <dialog> elements it runs the dialog focusing steps, which definitely does change focus. Those are the only two specced right now but the others we will be sure to specify whether they change focus or not.

@mfreed7
Copy link

mfreed7 commented Feb 28, 2024

"It depends". When it does, it will most certainly be specified. Specifically for a popover it runs the popover focusing steps - which may or may not change focus, and for <dialog> elements it runs the dialog focusing steps, which definitely does change focus. Those are the only two specced right now but the others we will be sure to specify whether they change focus or not.

I think another way to think about this is that the invoker logic should (always?) execute an existing imperative algorithm/function. And those should already be doing the right thing. So for popover and dialog, the showPopover and showModal functions should already correctly manage the focus.

@martinthomson
Copy link
Contributor

@hober, @plinss, and I discussed this today.

We're broadly in favor of invokers because it unifies some existing interaction patterns.

We feel it makes sense to have an invoke(action, invoker) method because it lays a path for future interactions with elements. We think that this might be more useful for custom elements or future features that are added to the platform.

We observe that a lack of additional arguments to invocations (or invoke()) are a concession toward the declarative nature of the core feature. Rather than a flaw, we see this as an important characteristic of the design. It simplifies this design and this characteristic seems like it is worth protecting. Richer interactions can remain the domain of script.

Having invoke() also helps discoverability. We think that this would be more useful if it were possible to get the list of available actions from elements.

(A footnote from me: when we ask questions, like the one above about focus, we appreciate your helpful answers. We appreciate those answers being added to documentation even more.)

@lukewarlow
Copy link

Firstly I'd like to say thank you for taking the time to review this proposal and I want to apologise for the time it’s taken to respond, please don’t take this as a sign we don’t value the feedback.


We feel it makes sense to have an invoke(action, invoker) method because it lays a path for future interactions with elements. We think that this might be more useful for custom elements or future features that are added to the platform.

Regarding the suggestion for an invoke() function, this was discussed we don’t believe this is needed at this time.
Custom elements can have their custom commands dispatched via calling dispatchEvent with the appropriate command event instantiated, see code snippet below.
Built-in actions will have an existing JavaScript function to achieve the same thing. Specifically in the case of popover these were recently updated to support supplying the invoker
this allows the imperative APIs to support the various benefits of the declarative approach.

const customElement = document.querySelector(‘custom-element’);const source = document.querySelector(#source’);
const commandEvent = new CommandEvent('command', {source: source, command: '--custom-action'});
customElement.dispatchEvent(commandEvent);

We think that this would be more useful if it were possible to get the list of available actions from elements.


This was also discussed, we see that this would be usable but not necessary for v1. For v1 all browsers will ship the same set of commands so it’s not needed for feature detection. It’s worth also pointing out that you can do some level of feature detection using the command IDL attributes reflection, it only reflects valid or custom values, this should solve most of the use cases.

If we were to add something like this in future it would be good to hear from the TAG what API shape you would like to see. Should this be an isValidCommand(command) function, or should this be a method that returns an array of values? How contextual do you think the validation should be? e.g. Should toggle-popover be valid on a <div> when it doesn’t have a popover attribute?

const button = document.createElement('button');
button.command = 'invalid-command-to-validate';
button.command === ''
button.command = 'close';
button.command === 'close';

@martinthomson
Copy link
Contributor

As far as custom invokers, that makes sense. We discussed this and we're OK with this being something that gets added later. After all, it isn't that hard to add something like the following if it proves useful:

Whatever.prototype.invoke = function(cmd, options) {
  this.dispatchEvent(new CommandEvent(cmd, options));
}

After doing some digging on the discoverability thing, we agreed that it isn't worth burdening you with solving a longstanding platform issue for this case. This is a pretty serious gap in our discoverability, but that's a shared responsibility. That said, I suspect that even if that problem were solved for other APIs, some of those solutions would not help with this specific API, given it's shape, so I'd encourage you to think about what isValidCommand() or something like it might need to look like to be useful, if only for platform (non-custom) invocation targets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Mode: breakout Work done during a time-limited breakout session Resolution: satisfied The TAG is satisfied with this design Review type: CG early review An early review of general direction from a Community Group Venue: Open UI Venue: WHATWG
Projects
None yet
Development

No branches or pull requests

7 participants