From: "Shannon Sterz" <s.sterz@proxmox.com>
To: "Dominik Csapak" <d.csapak@proxmox.com>
Cc: yew-devel@lists.proxmox.com
Subject: Re: [PATCH yew-comp 1/3] task (viewer): add task download button
Date: Tue, 31 Mar 2026 09:39:46 +0200 [thread overview]
Message-ID: <DHGSMHN0N6TA.292F4RLLAQUU3@proxmox.com> (raw)
In-Reply-To: <6e6fa075-2b27-4635-8fcf-ebdcdfdad3d8@proxmox.com>
On Mon Mar 30, 2026 at 4:44 PM CEST, Dominik Csapak wrote:
>
>
> On 3/30/26 4:39 PM, Shannon Sterz wrote:
>> On Mon Mar 30, 2026 at 4:23 PM CEST, Dominik Csapak wrote:
>>>
>>>
>>> On 3/30/26 4:17 PM, Shannon Sterz wrote:
>>>> On Mon Mar 30, 2026 at 3:48 PM CEST, Dominik Csapak wrote:
>>>>>
>>>>>
>>>>> On 3/30/26 3:35 PM, Shannon Sterz wrote:
>>>>>> On Mon Mar 30, 2026 at 2:53 PM CEST, Dominik Csapak wrote:
>>>>> [snip]
>>>>>>> @@ -273,18 +297,94 @@ impl PwtTaskViewer {
>>>>>>> let active = self.active;
>>>>>>> let link = ctx.link();
>>>>>>>
>>>>>>> - let toolbar = Toolbar::new().class("pwt-border-bottom").with_child(
>>>>>>> - Button::new(tr!("Stop"))
>>>>>>> - .disabled(!active)
>>>>>>> - .onclick(link.callback(|_| Msg::StopTask)),
>>>>>>> - );
>>>>>>> -
>>>>>>> let url = format!(
>>>>>>> "{}/{}/log",
>>>>>>> props.base_url,
>>>>>>> percent_encode_component(&task_id),
>>>>>>> );
>>>>>>>
>>>>>>> + let filename = match props.task_id.parse::<ProxmoxUpid>() {
>>>>>>> + Ok(upid) => {
>>>>>>> + format!(
>>>>>>> + "task-{}-{}-{}.log",
>>>>>>> + upid.node,
>>>>>>> + upid.worker_type,
>>>>>>> + epoch_to_rfc3339_utc(upid.starttime).unwrap_or(upid.starttime.to_string())
>>>>>>> + )
>>>>>>> + }
>>>>>>> + Err(_) => format!("task-{}.log", props.task_id),
>>>>>>> + };
>>>>>>> +
>>>>>>> + let download_btn: Html = if props.download_api {
>>>>>>> + Container::from_tag("a")
>>>>>>> + .attribute("disabled", active.then_some(""))
>>>>>>> + .attribute("target", "_blank")
>>>>>>> + .attribute("href", format!("/api2/extjs{url}?download=1"))
>>>>>>> + .attribute("filename", filename)
>>>>>>> + .with_child(
>>>>>>> + Button::new(tr!("Download"))
>>>>>>> + .disabled(active)
>>>>>>> + .icon_class("fa fa-download"),
>>>>>>> + )
>>>>>>> + .into()
>>>>>>
>>>>>> is there a reason you construct a new `<a>` element as a container here
>>>>>> instead of using the `download_as_file` helper in an `on_activate`
>>>>>> callback for the button?
>>>>>>
>>>>>
>>>>> if we have a static link, there is no need to create an 'a' element
>>>>> on the fly and clicking it programmatically. Since browsers use
>>>>> heuristics to detect 'user-initiated' actions to determine if things
>>>>> are allowed (e.g. auto-clicking on links) such helpers should be used
>>>>> sparingly.
>>>>>
>>>>> a static link is better expressed with native browser methods such as
>>>>> an a element that the user clicks. (the button inside is just for
>>>>> cosmetics in this case)
>>>>>
>>>>> e.g. if one would send a message in on_activate and call the helper in
>>>>> the update method, this might not work as intended since the browser
>>>>> can't detect that it was user-initiated.
>>>>
>>>> you could just call the helper directly instead of going through a
>>>> message though, no? however, im not sure if that is picked up as
>>>> user-initiated in all browsers either.
>>>>
>>>>> so my intent was to only use it when absolutely necessary.
>>>>>
>>>>> it's also what we do in PDM to download the report (though there we
>>>>> use a data url)
>>>>>
>>>>> the idea here was also that once all task log endpoints support the
>>>>> download api parameter, we can remove the button below again,
>>>>> and the helper too.
>>>>>
>>>>> do you see any advantage with using the helper in the first case too?
>>>>
>>>> my thinking was mostly about being able to re-use certain code paths
>>>> here.
>>>>
>>>> more importantly, though, from what i can tell the html 5 spec does not
>>>> allow interactive elements, like <button>, as descendants of <a>
>>>> elements:
>>>>
>>>>> Transparent, but there must be no interactive content descendant, a
>>>>> element descendant, or descendant with the tabindex attribute
>>>>> specified.
>>>>> - https://html.spec.whatwg.org/#the-a-element
>>>>
>>>> after looking into it some more, coding download buttons for static
>>>> resources as <a> elements is the right way to go accessibility-wise. so
>>>> maybe replacing the inner `Button` component with purely aesthetic
>>>> button.
>>>>
>>>> -->8 snip 8<--
>>>
>>> Sound good in general, I'll look into that, but since none of the part
>>> of the button is really interactive, i think it can be fine for now?
>>>
>>> (it's sadly not as simple as adding some class currently, since the e.g.
>>> ripple effect has to be done manually)
>>
>> i know it's a little trickier. sadly, this is definitively forbidden
>> regardless of "real interactivity". as per spec `<button>` is always
>> "interactive content":
>>
>>> § 3.2.5.2.7 Interactive content
>>>
>>> *Interactive content* is content that is specifically intended for
>>> user interaction.
>>> => `a` (if the `href` attribute is present), `audio` (if the controls
>>> attribute is present), `button`, `details`, `embed`, `iframe`,
>>> `img` (if the usemap attribute is present), `input` (if the type
>>> attribute is not in the Hidden state), `label`, `select`,
>>> `textarea`, `video` (if the controls attribute is present)
>>> - https://html.spec.whatwg.org/#interactive-content-2
>>
>> we also set a tabindex attribute on buttons unconditionally [1], this is
>> also explicitly forbidden for descendants of the `<a>` element.
>>
>> [1]: https://git.proxmox.com/?p=ui/proxmox-yew-widget-toolkit.git;a=blob;f=src/widget/button.rs;h=584f923c;hb=HEAD#l289
>
>
> yes it might be "forbidden", but still works for these purposes for now
> until we have a better way? Not sure if we want to block features
> just to be spec compliant when it clearly works in all relevant
> browsers, especially when I already said I'll fix it.
sorry if i misunderstood you here, i'm not saying this should be a
general blocker. i was just concerned that invalid markup might mess
with certain screen readers and other accessibility aids.
(added the list back as i accidentally dropped it along the way, sorry
for that)
next prev parent reply other threads:[~2026-03-31 7:39 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-30 12:53 [PATCH yew-comp 0/3] improve task/log viewer Dominik Csapak
2026-03-30 12:53 ` [PATCH yew-comp 1/3] task (viewer): add task download button Dominik Csapak
2026-03-30 13:36 ` Shannon Sterz
2026-03-30 13:48 ` Dominik Csapak
[not found] ` <DHG6K6Z0MPF2.1RFDEDL4NQM3M@proxmox.com>
[not found] ` <059e96c0-1b42-4c94-979a-6dfcf6ff5860@proxmox.com>
[not found] ` <DHG71AI5H1Z9.1E3IKBB4GT12K@proxmox.com>
[not found] ` <6e6fa075-2b27-4635-8fcf-ebdcdfdad3d8@proxmox.com>
2026-03-31 7:39 ` Shannon Sterz [this message]
2026-03-31 11:14 ` Maximiliano Sandoval
2026-03-31 12:05 ` Dominik Csapak
2026-03-31 12:14 ` Dominik Csapak
2026-03-31 12:55 ` Shannon Sterz
2026-03-30 12:53 ` [PATCH yew-comp 2/3] log-view: reorganize imports Dominik Csapak
2026-03-30 12:53 ` [RFC PATCH yew-comp 3/3] log-view: improve display of lines with '\r' Dominik Csapak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DHGSMHN0N6TA.292F4RLLAQUU3@proxmox.com \
--to=s.sterz@proxmox.com \
--cc=d.csapak@proxmox.com \
--cc=yew-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.