yangzhang75 opened a new pull request, #9016:
URL: https://github.com/apache/texera/pull/9016

   ### What changes were proposed in this PR?
   
   Closes #8943. Part of the Form View feature (#8011); from the #8439 
refinements (item 5).
   
   A read-only input card -- what a viewer without write access sees -- is 
locked with `pointer-events: none`. That is hit testing only. The bound formly 
controls are disabled through `props.disabled`, but the controls a custom 
widget draws of its own are not: a file picker's Browse button, the HuggingFace 
file input. A keyboard user could Tab into one of those inside a read-only card 
and activate it with Enter or Space, and the upload went out.
   
   The card now also keeps the keyboard out, through a small directive on it 
(`texeraReadOnlyCard`, applied as `[texeraReadOnlyCard]="!canEdit"` beside the 
existing `read-only` class):
   
   - Every focusable descendant is taken out of the tab order (`tabindex="-1"`, 
its own value remembered) and marked `aria-disabled="true"`, so there is 
nothing for Tab to reach and assistive tech says why rather than going quiet.
   - A card that becomes editable gets its controls back, each with the 
tabindex it had, and only those this directive took.
   - Widgets render their own controls after the card is built and redraw them 
as they go, so the locking is re-applied on every change inside the card: a 
control added, and a control whose own tabindex is written back (ng-zorro 
writes one when it enables a control). The observer runs outside Angular and 
sets only what is not already set, so one pass settles it.
   
   `inert` is what the step panel uses for its read-only mount, and the issue 
suggested following it; this does not, deliberately. `inert` also removes the 
subtree from the accessibility tree, and a reader's card exists to be read -- 
the values are what a reader runs the workflow with -- so that would trade this 
bug for a worse one. Disabled controls stay in the accessibility tree and keep 
being announced. The directive's doc comment says so, next to the code.
   
   Nothing else changes: the pointer lock, the styles, the card's content and 
the author's editing path are untouched, and the directive does nothing at all 
while a card is editable.
   
   ### Any related issues, documentation, discussions?
   
   Closes #8943, which re-files #8758 (closed). Part of #8439 (item 5) and the 
Form View feature #8011. Raised by Copilot on #8437, #8438 and #8440.
   
   Two neighbouring things are deliberately left alone. The widgets' own 
controls still do not consult the field's disabled state -- fixing that is 
per-widget work (the HuggingFace uploaders, the dataset picker, the resource 
value selector), and the card-level lock covers every widget at once, including 
ones added later. And the step panel's `inert` mount has the same 
accessibility-tree problem described above; changing it is not this issue's 
scope, and it is worth its own look.
   
   ### How was this PR tested?
   
   Unit tests (vitest). The directive's own spec, on a host shaped like a card 
(a bound input, a widget's Browse button, a link, a span, a control with its 
own `tabindex`): every focusable control in a read-only card is out of the tab 
order; each is marked `aria-disabled` while the card is not inert and its text 
is still there; an editable card is left alone, keeping a control's own 
`tabindex="3"`; a card that becomes editable gets that `3` back, the untouched 
control gets no `tabindex`, and the `aria-disabled` marks go; a control a 
widget adds after the card was built is locked; a control whose widget writes 
its `tabindex` back is locked again; the observer is disconnected when the card 
goes away. The page's rendered spec wires it through the real template: a 
read-only viewer's card, with a button appended the way a widget renders one, 
has it out of the tab order and marked, and the card is not inert.
   
   Each guard was deletion-checked and turned a named test red (the directive 
not applied; the tabindex write; the aria-disabled write; the editable check; 
the unlock; the original-tabindex restore; the childList and the attribute 
halves of the observer; the disconnect). eslint, prettier and the production 
(AOT) build pass; every statement, function and branch this PR adds is covered. 
The full frontend suite passes; one unrelated spec (`preset.service.spec.ts`) 
failed once in a full run and passes alone and on a re-run of the whole suite.
   
   ### Was this PR authored or co-authored using generative AI tooling?
   
   Yes. Generated-by: Claude Code (Anthropic). Co-authored with Claude, 
reviewed line by line by the author before submission.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to