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

accessibility improvements for quick format dialog #3464

Merged
merged 8 commits into from Feb 27, 2024

Conversation

abaevbog
Copy link
Contributor

Keyboard navigation and screen reader accessibility for VoiceOver and NVDA.

Changes:

  1. When the cursor is moved, the selected bubble is focused so that screen readers announce the name.
  2. _getSelectedBubble will take into account currently focused bubble. It prevents CMD-ArrowDown from opening citation-properties for a bubble different from the one that was announced.
  3. Added explicit keyboard navigation within reference panel. A selected reference will always be focused, and a focused reference is always selected.
    By default, VoiceOver will announce but not select, and NVDA will allow selection but without focus won't read it.
  4. Keyboard navigation and aria labels for citation-properties.
  5. Shift-tab from the input field will move focus to the Zotero-icon menu.
  6. Shift-tab from the reference panel moves focus back to the input field.
  7. Tab from outside of the reference panel will focus the first bubble.
  8. Space on the bubble will move focus from the bubble onto the editor and place cursor after the bubble. Required to force VoiceOver to let go of the bubble and allow to type after it.

JAWS:
These changes do not make the dialog accessible for JAWS due to the following issues:

  1. JAWS seems to read things out out either character by character or the entire paragraphs - in either case, the focused <span> is ignored.
  2. Trying to input any character into the dialog with JAWS activated triggers a message "There are no edit boxes on this page", and characters don't show up in the input field.

Some other issues I ran into worth noting:

  1. Type characters between 2 spans, move past that to the next bubble, hit CMD-ArrowDown, notice how the panel with citation properties does not appear (because selected bubble is not picked up).
  2. Type characters between 2 spans, move away, hit “Tab”, notice it creates an empty bubble with the typed text unchanged.

Notes for testing:
If testing with VoiceOver on a Mac, to move focus to the reference panel from the input field, press Options-ArrowDown, since VoiceOver takes over the usual arrow commands.

Questions:

  1. First thing to try to get the dialog working with JAWS, in my opinion, would be to use an input or textarea element instead of body as the parent for all thespans. Is there a reason why body with contenteditable=true is currently used, instead of input or textarea?
  2. With NVDA activated, there is a ~5 seconds delay between when the reference panel opens after some characters are typed in and when the keyboard actually becomes responsive. (E.g. Type characters, reference panel appears, you click down, nothing happens for a few seconds, then the click goes through, and all subsequent clicks are not affected). I'm still investigating that - but any input on this is welcome!

@abaevbog
Copy link
Contributor Author

@adomasven, I believe you would be the best person to look over this?

@adomasven
Copy link
Member

Testing with VoiceOver, things kind of break and not work as expected if you shift-tab to the Zotero button, and then try to tab back, especially if the reference panel is open and there are multiple citation bubbles. Like if you try to delete a bubble it deletes characters, the cursor gets tuck and unable to jump between bubbles, etc.

If I open a menu either on the Zotero button, or in the popup panel for locator selection, then VoiceOver gets stuck. Not sure how addressable that is in general.

We should probably have some aria property which make it say that you can search for items to cite when the cursor is in the search box.

  1. First thing to try to get the dialog working with JAWS, in my opinion, would be to use an input or textarea element instead of body as the parent for all thespans. Is there a reason why body with contenteditable=true is currently used, instead of input or textarea?

Yes, you cannot have span bubbles in input or textarea fields.


I've been wanting to rewrite the citation dialog ever since I joined Zotero and I have plans for that, but there are always other things that are higher priority. The bubble keyboard navigation is janky and the jankiness is probably unfixable, and then it's doubly so with accessibility tools. I don't know how far we can get with simple aria hints here without fixing the finickiness of the dialog itself, and I've tried to make it more reliable in the past, but it requires a ton of experimentation with non-obvious stuff.

@stakats
Copy link
Member

stakats commented Oct 30, 2023

On general citation bubble jankiness, the single worst aspect is the transition to opening the dropdown to add a locator. This is unintuitive, and undiscoverable, and often doesn’t seem to catch properly. And you 100% always want to do this via keyboard, accessibility issues aside.

@dstillman
Copy link
Member

(OT, but for the undiscoverable part, we should just add a dropmarker to the bubbles, at least on hover.)

@abaevbog
Copy link
Contributor Author

abaevbog commented Oct 30, 2023

@adomasven, thank you - I will tweak things with VoiceOver to try to mitigate the jankiness you pointed out.

I did have a more general suggestion that should make it easier to finish up the work with screen readers and also help with jankiness overall. I think it would also solve the 2 existing glitches I pointed out.

Most challenges I ran into come from the fact that the cursor combined with the focus on bubbles can confuse the screenreaders that are quite opinionated about how focus is handled. VoiceOver, as you noticed, gets thrown off when we jump between focusing on bubbles to focusing on input for typing and back. Other issues come from tricky handling of typed characters that were not turned into a bubble yet. For example, you can type characters in multiple places for what will be one new bubble, or when there's an out-of-bubble character, the cursor can be moved inside of another bubble, which I don't think we want. I believe these are more examples of the general jankiness you were referring to.

What if we remove contenteditable from the <body>, removing the cursor completely. Then, on keyboard navigation, we just move focus between bubbles, backspace deletes the selected bubble, and CMD-ArrowDown opens the locator, just like now. Handling focus without depending on the cursor would likely fix the "notice how the panel with citation properties does not appear" glitch I ran into, the VoiceOver backspace issues, and most other glitches that are caused by handling the cursor logic.

To add a bubble, onclick of the editor, we can find where in relation to other bubbles the click happened and insert an input field there. Similarly, input would be added on Space press from a focused tab during keyboard navigation. We would tweak the style of the input to make it look subtle but still clear that this is where the new bubble will be added. From there, the logic of the reference panel and the selection of the right reference would be unchanged. To get out of the input field, you either click away or click ESC, in which case the input is removed. This way, one cannot input characters between multiple bubbles, and I think, it would fix the "notice it creates an empty bubble with the typed text unchanged" issue I ran into.

To delete a bubble without using keyboard navigation, we could do a couple of different things. One option would be to add a cross icon on hover appearing after the text but still within the bubble. Clicking that would remove the bubble. Alternatively, clicking a bubble for the first time could only focus the bubble (from where backspace from above would apply), while the citation dialog would appear on second click. The subtle benefit of this is it would allow the usage of screen readers combined with mouse control (which is impossible now).

It's a general idea but let me know if you think it makes sense to explore it further. @dstillman, @stakats - please let me know if you having any input on this suggestion as well.

@abaevbog
Copy link
Contributor Author

abaevbog commented Oct 30, 2023

For some context, that's a very rough demo of what I meant regarding inserting the input field on click and not using the cursor

Screen.Recording.2023-10-30.at.5.37.45.PM.mov

@adomasven
Copy link
Member

What if we remove contenteditable from the , removing the cursor completely. Then, on keyboard navigation, we just move focus between bubbles, backspace deletes the selected bubble, and CMD-ArrowDown opens the locator, just like now. Handling focus without depending on the cursor would likely fix the "notice how the panel with citation properties does not appear" glitch I ran into, the VoiceOver backspace issues, and most other glitches that are caused by handling the cursor logic.

That seems like an excellent way to do it. My plan was always going to be to move them out of the text field completely. It only makes sense for them to be part of the text so that you can insert items in between items, but that is only relevant for some citation styles which do not automatically order cited items anyway, and you can reorder the bubbles by dragging. But this suggestion is actually better.

To delete a bubble without using keyboard navigation, we could do a couple of different things. One option would be to add a cross icon on hover appearing after the text but still within the bubble. Clicking that would remove the bubble.

Given Sean's comments above I think we actually want a dropmarker on the bubbles that is permanent, and adding a delete marker on hover too would clutter things too much.

Alternatively, clicking a bubble for the first time could only focus the bubble (from where backspace from above would apply), while the citation dialog would appear on second click. The subtle benefit of this is it would allow the usage of screen readers combined with mouse control (which is impossible now).

If I'm understanding correctly you mean opening a bubble would effectively require a double-click? I think that breaks expected behavior from the rest of the UI. How is this behavior impacting usage of screen readers with the mouse? I think backspace on an empty succeeding input field should move the cursor focus onto the bubble and then another backspace should remove it, effectively preserving current behavior. We can also add a remove button in the item popup.

@abaevbog
Copy link
Contributor Author

That seems like an excellent way to do it.

Great! I'll get started on putting this together.

adding a delete marker on hover too would clutter things too much

That is true. Now that I tried it, a dropmarker and a cross do look crowded together, so that's not a great idea.

How is this behavior impacting usage of screen readers with the mouse?

My thinking was that if you do use a mouse with a screen readers, moving focus on the bubble would cause the screen reader to announce it so that the user gets a confirmation of which bubble they're looking at instead of immediately opening the dialog. I agree, though, it would always require a double-click to open up the dialog which is not optimal. Now that I think about it, if this functionality of just focusing with a mouse click is important, it would be better served with a mouse click when another key (like Shift or Cmd/Ctrl) is being held

backspace on an empty succeeding input field should move the cursor focus onto the bubble and then another backspace should remove it

That makes sense. I think it would also be appropriate to have Tab/Shift-tab from the input move focus on the next/previous bubble - that also addresses the issue above of getting the focus onto the bubbles while using a mouse.

We can also add a remove button in the item popup.

I think that's a great idea. We have the main dialog for bubbles, might as well make it as functional as possible. As another addition, we could add small buttons to move the bubble left/right (similar to "Move up", "Move down" menuitems for creator rows in itemBox table) to allow reordering of bubbles without relying on drag and drop.

@abaevbog abaevbog marked this pull request as draft November 3, 2023 03:24
@abaevbog
Copy link
Contributor Author

abaevbog commented Nov 3, 2023

The general approach to remove contenteditable from body and insert input component between spans does seem to work out. It also allows us to remove a good amount of extra logic handling the cursor which is nice. Keyboard navigation is quite a bit more straightforward too, since there's only one thing to worry about at a time.

It does lead to a rewrite of a few other parts tied to the cursor, like drag-and-drop reordering but that's a good opportunity to slightly improve it as well (e.g. I'm adding a placeholder during drag to make it easier to see where the bubble will land).

I'm marking this as a draft for now just until all those things are reworked. If anybody has any other examples of jankiness from the dialog not mentioned earlier - feel free to point them out and I'll try to incorporate those fixes as well

@abaevbog
Copy link
Contributor Author

abaevbog commented Nov 9, 2023

@adomasven I think I have a generally working setup without the cursor as proposed earlier. This should work with voiceover, NVDA and JAWS (some notes on that one below). I also tried to address whatever jankiness I ran into with citation dialog and etc. Below is a general outline of my changes under this cursor-less setup and a few questions too. Let me know what you think! I assume there'll be more changes, so I will wait to squash all commits until later

Outline of proposed changes

  • Removed all cursor related logic.
  • On click between spans, an input field is created and placed where the click happened. Input field is removed on blur (unless focus is on the reference list). When the input field is removed, the reference list is cleared. The idea is that it shows only when the input is displayed.
  • Drag-drop reordering of spans had to be rewritten after the cursor logic went away. I added a placeholder to make it clearer where the bubble will drop, so I think it looks nicer. Whenever the input is displayed, drag does not happen.
  • Keyboard navigation:
    • arrowRight/arrowLeft arrows move focus from a bubble to its neighbor
    • arrowDown from bubble opens up the citation properties dialog
    • arrowDown/arrowUp from the input starts navigating the reference list
    • Shift-tab from reference list moves focus back to the input
    • arrowRight/arrowLeft from first/last bubble will create and focus an input field
    • Space from a focused bubble creates and focuses an input field after the bubble
    • Selected reference from reference list is turned into a bubble on Enter, Space, Tab or ;
    • Tab from anywhere else focuses the first bubble or creates an input (if there are no bubbles)
    • Shift-tab from anywhere else focuses on the zotero-icon if enabled
    • arrowRight/arrowLeft or backspace from empty input will focus on the next/previous bubble
  • Explicit keyboard navigation within the reference panel. A selected reference will always be focused, and a focused reference is always selected. By default, VoiceOver will focus but not select, and NVDA will allow selection but without focus won't read it.
  • Bubbles updates: text is not selectable, added dropmarker on hover or select and a hand cursor to make it clear they’re clickable. Changed component type from span to button so that aria-description is not ignored.
  • Restructuring of keypress listeners: separate listeners for input, bubbles, and overall search field. I think it makes it easier to be intentional with the behavior of different components.
  • Citation properties updates:
    • no trigger of onCitationPropertiesClosed when menulist is closed
    • ensure the dialog is hidden if focus moves away to a bubble (possible with voiceover)
    • added manual keyboard navigation
    • aria-labels for info fields
    • prevent “selected” property of a bubble from remaining if you click on another bubble when citation properties are opened
  • Added aria-descriptions to read out available keypress commands from bubble, input and reference items.

Comments/questions

  • On ArrowDown from input, the user navigates to the reference list. This moves focus from the input field so that screen readers announce results. This is different from the previous setup where the focus remained on the input field, which allowed one to navigate references with arrows up/down and also type at the same time. Do we want to preserve the original behavior? If so, we could add a preference to not shift focus from the input?
  • Due to Ctrl being used in JAWS shortcuts, I changed the command to open citation dialog on keypress from Ctrl-ArrowDown to just ArrowDown when bubbles are focused. It’s not a huge change but still worth mentioning.
  • When a reference list has a selected item, Tab turns that item into a bubble. Using Tab for that purpose is somewhat odd to me (especially since space, enter and ; already do that), so would it make sense to keep Tab just for focusing on the first bubble? I think it could simplify navigation a bit, since it allows you to undo whatever you’re doing and start from the beginning if you’re stuck somewhere.
  • Aria-descriptions don’t play well with JAWS: for aria-description to work with other screen readers, I made bubbles buttons which causes JAWS to announce that you should click Enter when a bubble is focused (which is not how our dialog works). Even then, it still ignores the aria-description (possibly somewhat related to this). I am tempted to just append instructions to the aria-label but then it might sound strange when the entire thing is read out together with each citation without any delay (which is the main point of aria-description really, it’s read after the label with a small pause). But maybe it’s not a bad thing?
  • There is currently no way to reorder bubbles without a mouse. Do we want to be able to do that? I was thinking the most straightforward way to implement that would be to swap a focused bubble with the bubble next to it on, say, Shift-ArrowLeft/ArrowRight. Does that seem reasonable? It would be faster than opening up a popup, navigating to a button, etc.

@adomasven
Copy link
Member

adomasven commented Nov 10, 2023

General comments with testing:

  • Upon opening the dialog cursor input at the end should be autofocused
  • [Home] should jump cursor to start input, [End] should jump to end input, both from bubble and input focus. EDIT. Actually if the input has text it shouldn't jump somewhere else, unless we're at the start of the input already, but that may not be possible to implement, in that case this should only apply when the input is empty.
  • Up arrow from popover should close it and move focus back to the dialog
  • Popover open should autofocus locator input
  • Clicking on the popover to close loses focus, should stay on the bubble
  • You should be able to continue typing to search after using arrow keys to navigate the reference panel
  • Mouse-clicking on the input after navigating with the keyboard should not clear the input
  • If something is typed in the input, you should still be able to use arrow keys to navigate the bubbles and it should not clear the text box.
  • Dragging bubbles does not work when input is active.
  • Tab and Space should not select the reference from the list. Space should continue typing.
  • Tab from button should move the cursor to last input, same as shift-tab from reference panel
  • Button focus has no outline in Linux (this has always been the case, but we should actually fix that). It would probably even make sense to actually make the Z letter appear as a button, since I imagine it's not obvious at all for new users that you can click on that.

Do we want to preserve the original behavior? If so, we could add a preference to not shift focus from the input?

I imagine it's better for screen readers if focus is shifted to the reference panel, so we should do that, but typing should just move the focus back to input so that the user can continue searching.

Due to Ctrl being used in JAWS shortcuts, I changed the command to open citation dialog on keypress from Ctrl-ArrowDown to just ArrowDown when bubbles are focused. It’s not a huge change but still worth mentioning.

I think this is better.

Aria-descriptions don’t play well with JAWS: for aria-description to work with other screen readers, I made bubbles buttons which causes JAWS to announce that you should click Enter when a bubble is focused (which is not how our dialog works). Even then, it still ignores the aria-description (possibly somewhat related to FreedomScientific/standards-support#719). I am tempted to just append instructions to the aria-label but then it might sound strange when the entire thing is read out together with each citation without any delay (which is the main point of aria-description really, it’s read after the label with a small pause). But maybe it’s not a bad thing?

Can we do that just for Windows? VoiceOver is without a doubt the best screen reader and as such I imagine most users with vision impairment use MacOS as other platforms make their lives that much more difficult, so we shouldn't compromise it for JAWS support.

There is currently no way to reorder bubbles without a mouse. Do we want to be able to do that? I was thinking the most straightforward way to implement that would be to swap a focused bubble with the bubble next to it on, say, Shift-ArrowLeft/ArrowRight. Does that seem reasonable? It would be faster than opening up a popup, navigating to a button, etc.

This sounds excellent.


I see that dragging not working from input is deliberate, but I don't know why. Possibly because you clear inputs if focus is lost? The logic of clearing inputs should probably change to not do that until a new bubble is added somewhere from a reference panel and we should keep the values of all inputs until then. I imagine this is harder because you have to allow multiple inputs to be displayed and figure out which one the reference panel is being displayed for, but you should reasonably be able to type something, then click on a bubble to check it, then continue typing.

I have not looked at the code yet, but I know it was not great before that, so thanks for coming up with a great way to redo the dialog and actually doing it. These changes are awesome overall and going to be a huge improvement to the usability of the dialog and will also probably allow me to reuse this for the eventual citation dialog rewrite.

I also wonder if we can get rid of the iframe completely, since as far as I can remember it was only needed because contenteditable didn't work well within the XUL root element, but since we are no longer doing that, this could be simplified.

@abaevbog
Copy link
Contributor Author

@adomasven, great feedback! Thank you.

I see that dragging not working from input is deliberate, but I don't know why. Possibly because you clear inputs if focus is lost?

Yeah, in regards to the overall input logic, my thinking was to only allow one thing to be happening at a time: display only one input when one looks for a reference, remove it if the focus moves away, etc. Some jankiness and confusion with the interface earlier came from the fact that some of the actions would conflict with others (e.g. moving the cursor away after typing and checking out the reference panel doesn't quite make sense), so I tried to completely eliminate such possibilities. Maybe a little too much

you should reasonably be able to type something, then click on a bubble to check it, then continue typing.

This certainly should be possible, so I will change to keep the input around until bubble is made or everything is erased.

we should keep the values of all inputs until then

We certainly can keep all non-empty inputs, and since each input has it's own listener now, re-running search and updating the references list on input focus should not be a problem. I do wonder why would we want to have multiple inputs? I assumed one would not be adding multiple references in parallel jumping between inputs but would do so one bubble at a time. So my inclination would be to keep an input around after blur but still remove it if a user clicks away to an editor to make another input. I admit I may be missing some use case though

Dragging bubbles does not work when input is active.

There were in fact two reasons for this. First one is me trying to only allow one thing at a time like mentioned earlier. Second one was some odd drag-drop api behavior I encountered. With me removing an input on blur, I blame this open Mozilla bug for not firing dragend event during bubble reordering which made it quite hard to work with, so I intentionally made it not happen. This is mainly a comment on the context, since I will not be hiding the input on blur now, so whatever is the cause of this behaviour, it should not apply.

I also wonder if we can get rid of the iframe completely

Fantastic! Having an iframe makes us do some extra work and it also confuses the screen readers a bit since, well, it is a web area, so voiceover announces it as such. I will happily look into gutting it as well.


I also wanted to comment on your earlier mention of voiceover getting stuck when opening/closing menupopups: this seems to be an overall issue with voiceover and it happens across all menupopups within the desktop app that I tried. I don't know exactly how we can rectify it yet (since focus does remain where it should) but if there's a solution it should probably be a separate PR to fix all menus if possible.

@adomasven
Copy link
Member

I do wonder why would we want to have multiple inputs? I assumed one would not be adding multiple references in parallel jumping between inputs but would do so one bubble at a time. So my inclination would be to keep an input around after blur but still remove it if a user clicks away to an editor to make another input. I admit I may be missing some use case though

Just seems like we should not be discarding user input, although I guess if we're clearing all inputs after bubble addition, clearing an input upon jumping to a different one may not be too much of an issue. An in general this is probably not going to be something common anyway. Also we should make sure that shift-tab from reference panel jumps back to the last active input.

@abaevbog
Copy link
Contributor Author

abaevbog commented Nov 15, 2023

With the input sticking around even if it looses focus, we can have a reference panel opened with the focus being placed on a bubble, say if we open it to check something after typing into the input. In that case, we want ArrowDown to navigate the reference panel, as opposed to my earlier change that triggers the citation dialog to open. @adomasven, I have a few ideas on how to get it working given other changes that we have, and I wanted to ask for your thoughts on which way is best.

It would be easy to revert to how it was before, where Ctrl-ArrowDown opens the citation dialog, and ArrowDown navigates the reference panel. However, as I mentioned earlier, JAWS uses Ctrl for its own shortcuts, so that’s not a great option. We could come up with another shortcut of some kind (like Alt-ArrowDown, or something) though I am not a huge fan of shortcuts in general, since it becomes one of 100000 things you need to remember.

My next idea is to use Space to trigger the citation dialog when a bubble is focused and leave ArrowDown dedicated to navigation of the reference panel. I like Space because would be consistent with similar uses of Space to trigger a button click throughout the rest of the desktop app, so it doesn't quite count as a shortcut (and a bubble is basically a button, since you click it and it does stuff, plus it now has a dropmarker). However, Space currently is used to create an input after the focused bubble, without which it is impossible to add a reference between bubbles without using a mouse. Maybe some other key could be used to create the input instead of Space?

Lastly, I tried something a bit different. What if on ArrowRight/Left from a bubble, we first create and focus the input after/before the current bubble, and on the second arrow click the focus moves to the bubble next to the one where we started? Then, the Space can be allocated to trigger citation dialog on bubble click, and ArrowDown is dedicated to reference panel.
The main benefit of this, in my opinion, is that it would be intuitive, somewhat similar to what happens now in terms of the visual, and it would not require anyone to remember another shortcut. Besides, with bubbles having role=button attribute (that's required to aria-description to be announces) JAWS by itself instructs users to press Space to trigger a button click, so that would align with how our keypresses work.
The main drawback, of course, is that it takes a few extra clicks to traverse all bubbles. (Well, and if you use voiceOver, you would use Option-ArrowLeft/Right instead of just ArrowLeft/Right but that is VoiceOver specifics that I assume folks are familiar with).

These are my thoughts, let me know what you think! In my last commit, I have a tentative implementation of the last idea I tried so you can see what it would look like. (The dialog or bubbles may look slightly different than before - a consequence of iframe removal that I am working on)

@abaevbog
Copy link
Contributor Author

While we're working on this: https://forums.zotero.org/discussion/109173/possibly-a-bug-add-edit-citation-keeps-the-text-in-the-search-box-after-selecting-the-item

I am having a hard time reproducing this exact behavior. But it really just highlights the jankiness we discussed earlier. In any case, with the changes from this PR, when a bubble is created from a selected item, the <input> component with the searched text is deleted. So whatever is the root of this issue, it should already be addressed by the refactoring here.

@adomasven
Copy link
Member

With the input sticking around even if it looses focus, we can have a reference panel opened with the focus being placed on a bubble, say if we open it to check something after typing into the input. In that case, we want ArrowDown to navigate the reference panel, as opposed to my earlier change that triggers the citation dialog to open. @adomasven, I have a few ideas on how to get it working given other changes that we have, and I wanted to ask for your thoughts on which way is best.

Why not close the reference panel if a bubble is focused and reopen upon the input becoming active again?

Lastly, I tried something a bit different. What if on ArrowRight/Left from a bubble, we first create and focus the input after/before the current bubble, and on the second arrow click the focus moves to the bubble next to the one where we started? Then, the Space can be allocated to trigger citation dialog on bubble click, and ArrowDown is dedicated to reference panel.
The main benefit of this, in my opinion, is that it would be intuitive, somewhat similar to what happens now in terms of the visual, and it would not require anyone to remember another shortcut. Besides, with bubbles having role=button attribute (that's required to aria-description to be announces) JAWS by itself instructs users to press Space to trigger a button click, so that would align with how our keypresses work.

I was surprised that it's not how you decided to implement it initially regarding navigating inputs between bubbles, so this would make sense anyway. We would have to think how we'd close an item popover with the keyboard. It would probably have to be Escape. The only issue is that if the user accidentally double presses Escape the dialog will close along with any changes.

@abaevbog
Copy link
Contributor Author

Why not close the reference panel if a bubble is focused and reopen upon the input becoming active again?

My reluctance was I didn't think it would be immediately apparent that in order to see the reference panel after you, say, click on a bubble, you have to click back onto the input. Besides it would require a bit more work to see the reference panel again if you rely exclusively on keyboard navigation - one would have to Tab or press a bunch of arrows to reach the input field.

I was surprised that it's not how you decided to implement it initially regarding navigating inputs between bubbles, so this would make sense anyway.

Ok, great. The only reason why I didn't go there right away is because it makes you do more clicks ... Which is really not a concern here given the small number of entries. So I will keep going in this direction and have Space trigger the citation dialog when pressed on a focused bubble. Happy this idea made sense!

We would have to think how we'd close an item popover with the keyboard. It would probably have to be Escape. The only issue is that if the user accidentally double presses Escape the dialog will close along with any changes.

By the item popover, you mean the citation properties popup? Escape does work, though Enter achieves the same thing too (and this is what I've been defaulting to). I did notice that sometimes double clicking by accident can cause an unwanted closure of the entire dialog. I thought that maybe we can require a small delay to have passes between two Escape or Enter clicks (e.g. skip all Enter keypresses that come within 2 seconds of the last executed Enter). To tackle the accidental dialog closing we could even go as far as add a confirmation dialog that would pop up if you press Escape when there are some unsaved changes

@adomasven
Copy link
Member

So in terms of terminology, this is the citation (quick format) dialog, and a citation consists of many items. Given that, I'm calling the item properties popover the "item popover".

My reluctance was I didn't think it would be immediately apparent that in order to see the reference panel after you, say, click on a bubble, you have to click back onto the input. Besides it would require a bit more work to see the reference panel again if you rely exclusively on keyboard navigation - one would have to Tab or press a bunch of arrows to reach the input field.

Maybe, but even then, if the popover is open, arrow up/down should not navigate the reference panel, and Enter should not select a reference. Generally, after the popover is closed either via mouse or Enter, if there is an input with a value, then the focus should probably jump to that input anyway.

Escape does work, though Enter achieves the same thing too (and this is what I've been defaulting to)

Yeah, both should close it.

I thought that maybe we can require a small delay to have passes between two Escape or Enter clicks (e.g. skip all Enter keypresses that come within 2 seconds of the last executed Enter).

Sounds good.

@abaevbog
Copy link
Contributor Author

So in terms of terminology, this is the citation (quick format) dialog, and a citation consists of many items. Given that, I'm calling the item properties popover the "item popover".

I am realizing I did get slightly confused with the definitions. Thank you, this actually clears things up for me.

Maybe, but even then, if the popover is open, arrow up/down should not navigate the reference panel, and Enter should not select a reference.

Yes, I tried and when the citation properties popup is show, hiding the reference panel makes it looks cleaner and less busy too

Generally, after the popover is closed either via mouse or Enter, if there is an input with a value, then the focus should probably jump to that input anyway.

and then on focusout from the the popup for whatever reason (especially important for VoiceOver that moves focus around as it wishes), the focus goes back to the input - I think it's a really good user experience. It also resolves my concerns with it being hard to get back from the citation properties into the input with keyboard as well.

@abaevbog
Copy link
Contributor Author

Note for myself to make sure pasting like this works with the new input setup

https://forums.zotero.org/discussion/comment/450993/#Comment_450993

@abaevbog
Copy link
Contributor Author

abaevbog commented Jan 15, 2024

@adomasven, circling back here, this is the summary of my changes after your last feedback. These are all based on your last list and our discussion after it. Let me know what you think!

  • Upon opening the dialog, input is added after the last bubble and focused automatically
  • Home/End will create an input at the start/end of the editor and focus it if the cursor is at the beginning/end of the input. This will clear the previous input, since the intention is likely to start typing in another location. Same behavior for Home/End when a bubble is focused. ArrowRight/ArrowLeft from the end/beginning of the input moves focus to the next/previous bubble and removes the previous input if it’s empty. If it's not empty, it will remain and no new inputs will be created during arrow navigation between bubbles, since the intention is likely to check on one of the bubbles before continuing typing where you were before.
  • Up arrow from popover closes it and move focus back to the non-empty input if there is one, or the bubble otherwise.
  • Popover autofocuses locator input
  • Escape/clicking on another bubble when the popover is opened returns focus to the non-empty input or returns the focus on the bubble if there is none. Clicking between buttons to create an input does the usual thing of creating a blank input and focusing it, since the intention is probably to start typing.
  • You can continue typing in the input after using arrow keys to navigate the reference panel. The focus will be returned to the input from the reference panel.
  • Mouse-clicking on the input after navigating with the keyboard does not not clear the input
  • Mouse-clicking between bubbles will clear existing input, since the intention is probably to type in another location
  • Arrow navigation from the input to bubbles will not clear it, since you may be using keyboard navigation but want to check one of the bubbles before continuing typing.
  • Bubbles can be dragged when the input is active
  • Bubbles can be reordered with Shift-ArrowRight/Left from a focused bubble. It will swap the focused bubble with the one next to it.
  • Tab and Space do not select the reference from the list. Space continues typing. Tab will return focus to the last input. (It used to be Shift-Tab, but if tab is not used, might as well just have Tab do it?)
  • Tab from button moves the cursor to last input if there is one. Otherwise, it creates an input in the end and focuses that.
  • Added border, hover effect and dropmarker to make the Z-button look more like a clickable button when it is actually clickable
  • Completely removed iframe.
  • When the popover is opened, the reference panel is hidden and will be displayed when the popup goes away.
  • Throttled Escape and Enter keypresses so that a double Escape/Enter to count only as one (that way, an accidental double keypress of Escape to close the popover does not end up closing the entire dialog)

There are a few minor changes I am wrapping up now but none of them are significant for the overall behavior. These are the final tweaks I will be adding:

  • bubble's positioning needs to be tweaked a bit to have the same gap at the bottom as as the top
  • there's an edge case where an extra line appears during drag-drop ... sometimes
  • still trying to get the JAWS to properly announce area-description. There should be a combination of settings in the JAWS itself to force it to happen. If I can find out what it is, I think it would be sufficient to solve the issue of JAWS not reading out the instructions, since it's something that can be added to our documentation and it's specific to JAWS only. Otherwise, i will be appending instructions to aria-label on windows per your comment. It will sound busier, so it would be nicer if we didn't have to do that.
  • Rebase/squash everything and add better comments

@adomasven
Copy link
Member

Some general comments:

  • After pressing Enter to select a reference from the reference box, the cursor should move to the last input box
  • Typing from a focused bubble should jump back to the input and continue typing
  • It feels wrong to me that when the bubble is focused arrow down does not open the popover, and instead focus is moved to the reference box, and then you can only jump back to the last input. I think the reference box should close unless an input is focused and arrow-down should open the item popover when a bubble is focused.
  • The Z button looks bad on Linux. There's a lot of right margin for it that shouldn't be there and no vertical margin and it's padded too much. Arguably, we don't have good looking buttons for Linux at the moment, and that will also change soon anyway, but for now maybe just the chevron and some effect on hover/focus is enough.
  • There is some weirdness when multiple lines of citation bubbles appear.
    • If you type in the input and get to the point where it should jump to a different line, the popup doesn't resize and the input effectively "disappears".
    • If you have a line of bubbles and 1 bubble on a second line, and then drag it, or some other bubbles straight down, sometimes the popup resizes weirdly (I see you have noted this yourself)

Overall, after looking at this again, I really dislike the fact that the inputs are cleared. It's just not expected behavior for any UIs to clear inputs without performing some sort of significant context switch, which this isn't. I don't think we can or should make assumptions over what and why people might want to do in this dialog, because in my experience users often have very weird and inefficient workflows that they are "comfortable" and "fast" with and we should allow multiple inputs to have content without clearing them. If it makes thing hard, we could clear the inputs on Enter to add a bubble, but even then we should avoid it if possible.

Voiceover:

  • Typing in the text dialog does not announce the selected item in the reference list, presumably because the focus is still in the input.
  • You have to move the focus to the reference box manually by pressing alt-arrow. As you have noted voiceover users may be used to alt-arrow keys, but that was for moving horizontally, not in the reference box.
  • After moving into the reference box with alt-arrow, more alt-arrow commands don't do anything and then you have to use regular arrow keys.

So my thinking here is if we cannot get Voiceover to announce the selected item in the reference list upon typing, then maybe focus to the reference box should be moved with Tab. If we can, then alt-arrow should navigate the reference list even after it is focused. Also Voiceover should not be able to select the labels/separators in the reference list (Cited, My Library).

Overall: fantastic job! The dialog feels much better to use now than it did before, not even mentioning the much improved support for screen readers.

@abaevbog
Copy link
Contributor Author

@adomasven, great feedback, thank you!

After pressing Enter to select a reference from the reference box, the cursor should move to the last input box

As I understand, the idea is to provide an efficient workflow, where one doesn't have to spend time to click between multiple inputs, right? Since the actual last input box on Enter will become a bubble, we'll now keep track of the last focused input, as well as the second-to-last input. When an input is turned into a bubble, the focus will go to either the last input (after mouse click) or the second-to-last input (on Enter).

Typing from a focused bubble should jump back to the input and continue typing

Makes perfect sense! I made that change. The only thing I'm unsure above is the space character. Currently, it is treated as any other character but JAWS by default says "Press whitespace to activate this button", which can be confusing. Should we keep the whitespace as a trigger for bubble popover as well?

It feels wrong to me that when the bubble is focused arrow down does not open the popover

Fully agree - I tried it and it's way nicer. Reference panel is shown only when input is active, arrow down opens the popup, arrow up closes it, and the last focused input (if any) is focused when popover closes

The Z button looks bad on Linux.

I tweaked it a bit to have reasonable margins and to be a bit neater. How is it looking now to you?

If you type in the input and get to the point where it should jump to a different line

Good catch! If the input is too long, it just will move to a separate line and if it overflows that, the textfield will stop expanding. I had quite a few tweaks to drag behavior too, so it should not have any unexpected resizing as well.

Overall, after looking at this again, I really dislike the fact that the inputs are cleared.

I see what you mean with different people having different workflows. Handling multiple inputs was not really an issue: I tried to have nothing cleared automatically and the reference box refreshes when an input is focused, and it feels fairly natural to me.

So my thinking here is if we cannot get Voiceover to announce the selected item in the reference list upon typing, then maybe focus to the reference box should be moved with Tab.

Yeah, the focus remains on input and getting all screen readers to randomly announce things that are outside of the focus is a bit tricky. It can be a bit confusing too (you do want to hear the screen reader telling you what the input is after all). But Tabbing into the reference panel from the input seems nice to me! I redid the keyboard navigation within the reference panel too to have disabled rows are skipped when the CMD-ArrowUp/ArrowDown navigates the items.

Overall: fantastic job!

Thank you, half of the merit goes to you and your feedback! I do think it feels more robust than before and is a better experience. We certainly have a few tweaks to take care of but I think we're on the right track!

@abaevbog
Copy link
Contributor Author

There are 2 things I wanted to ask about.

  1. I've been battling with resizing behavior during dragging (as you mentioned earlier), and this is the specific issue I was trying to work around:
Screen.Recording.2024-01-19.at.3.52.09.PM.mov

The main issue is that calling _resize while dragging to make the window taller causes (some kind of) issues with the drag API that prevents the next component from being properly dragged (dragstart fires, but no actual dragging happens). In general, I noticed that DOM manipulations while dragging often lead to all sort of odd glitches (random mouseover events firing where they shouldn't, hover effect getting stuck, etc.). After multiple different iterations that I tried, I ended up setting the editor's height to be fixed during drag and making y-axis scrollable, which seems like a decent experience that doesn't break things. Let me know what you think about it.

  1. I've been trying to find the best way to get the JAWS to announce the description of the bubble (which has instructions on which buttons to press and what to do). I think the perfect solution would be to use aria-live component where we would add the message that the screen reader would announce. Theoretically, this approach would be in general useful to communicate arbitrary instructions to screen readers if we need to. The issue I am having is that the aria-live fields simply don't get announced, not matter what I've tried (though it does work ok with firefox for me). They're not used anywhere else in our codebase though. I was wondering if you aware of any any issue with aria-live fields that would prevent them from being announced?

@abaevbog
Copy link
Contributor Author

So I revised the smaller semantics (mainly citationPopup to itemPopover) and tweaked how the reference panel resizing is handled.

My intention was to simplify the logic of _resizeReferencePanel to make it easier to see when the panel will be resized or hidden. Another thing now is that the refreshing of results and resizing of the panel should only happen when it's actually needed, as opposed to on every focus of the input. For example, on tab from the reference panel or after the item popover closes, focus goes to the input but it does not trigger any refreshing or blinking of the ref panel.

I'm now going over the "Insert Note" dialog - I didn't realize that it shared quickFormat.js with the actual quick format dialog, so that requires some edits to get it working again. This is now the last thing from the necessary changes that I have planned - after that's done, we should be able to remove citationDialogIframe.html file.

@abaevbog
Copy link
Contributor Author

Fixing insertNoteDialog wasn't at all as bad as I expected! It really is just a mini-copy of quickFormat, so as far as i can tell it is working as expected now. That means we can now remove the citationDialogIframe.html.

@adomasven
Copy link
Member

The input after the 1st bubble jumped to the 0th position in the dialog and cursor focus went there.

I couldn't easily reproduce that issue but after simplifying the last-focused-input logic per your advice, hopefully, this got fixed. Let me know if you run into it again, the last thing we'd want is bubbles randomly changing order!

I'm still seeing this. Add multiple bubbles, then type some text in between them, then go to the final position of the input, type and [Enter] to insert a bubble. All the inputs that were in-between bubbles jump to the start of the dialog.

Otherwise this is absolutely terrific. Do you think there's anything else to be done here?

@abaevbog
Copy link
Contributor Author

I'm still seeing this.

Aha! I I finally ran into it myself. I've been using a citation style without any order requirements and bubble sorting is exactly what caused this.

I just pushed a tweak to keep track after which bubble which input went during reordering. Does that fix it for you?

Do you think there's anything else to be done here?

I don't think so ... I'm pretty sure there will be some minor tweaks required down the road since there is a ton of changes but in terms of major stuff, I think it's all in here. I will rebase and squash everything into one commit to keep the description up-to-date.

- removed all cursor-related logic. Instead, insert a new input component
each time the user wants to type. The input remain between bubbles
and focusing on one will open the reference panel. Leaving the input hides
the reference panel.
- during drag-drop reordering, lock the editor height so that it doesn't
get out of sync with the window.
- removed the iframe, since it was no longer needed.
- keyboard navigation: Home/End will place an input at the start/end
of the editor and focus it. Tab focuses the last active input if any, or
the input in the end otherwise. Shift-Tab from the editor focuses the
dropdown button if it is active. Tab from the input will focus the first
entry of the reference list. Tab from the reference list will focus the
active input. Shift-ArrowLeft/Right from focused bubble will swap the bubble
with its neighbor. ArrowDown/Up from bubble will open/close the citation dialog.
- when a reference item is selected, previously active input is re-focused.
- aria-properties to have voiceover, JAWS and NVDA read bubbles, inputs and
reference items, as well as announce hints about available keypresses.
- typing from bubble or the reference panel will refocus previously active
input
- different minor updates to make the functionality less janky
- refactoring of refreshing and resizing of the reference panel to
be more straightforward and to only do it when necessary.
E.g. clicking on a bubble and closing the
popover after will not rerun search and just display the old results.
- some throttling logic so that two escape keypresses one after another
when the itemPopover is open do not close the entire dialog
- renamed variables: qfb=dialog, qfe=editor, panel=itemPopover
@adomasven adomasven marked this pull request as ready for review February 23, 2024 11:51
@adomasven
Copy link
Member

@dstillman Do you want to look over the functionality here before merging?

@dstillman
Copy link
Member

Yep, will do.

@dstillman
Copy link
Member

Cool!

I'll test more later, but a few quick things I noticed (sorry if these are discussed above):

  • Until we're ready to make other changes, can we revert the visual changes here? 1) Border around the Z button (dropmarker is OK but on wrong side), 2) shorter bubbles, 3) missing inside border-radius. (Nice to fix the overlapping-rectangle bug on the right edge, though.)

Before:

Screenshot 2024-02-23 at 7 10 38 AM

After:

Screenshot 2024-02-23 at 7 10 18 AM

  • If you type "p" and a page number after a bubble, it adds the page number to the bubble but then leaves it in the text after the bubble.
  • This changed to showing "page 123" instead of "p. 123" in the bubble.
  • All our dropmarkers (and most dropmarkers?) are on the right side, so if we're adding these, seems like these should be on the right too.

- remove border, etc. from the Z-icon, move dropmarker to the right
- remove dropmarker from bubbles to avoid making them wider
- clear the input after p. * shortcut
@abaevbog
Copy link
Contributor Author

Until we're ready to make other changes, can we revert the visual changes here?

Gotcha - I moved the dropmarker after Z-icon to the right and removed the padding around bubbles. That padding was added for one reason: we had to make some space for the dropmarker to appear in. Other approach that I thought of was to allow the bubble to expand on focus/hover to make space for dropmarker then but it 1. either pushes neighboring bubbles away which looks glitchy 2. or the bubble expands to occupy all available space between itself and the next bubble which makes it hard to click in between bubbles to start typing.

I see there are plans to use that space to the right of the bubble for an "X" icon. So I removed the bubbles's dropmarkers altogether for now - correct me if there is a better way to add it without making bubbles wider.

missing inside border-radius. (Nice to fix the overlapping-rectangle bug on the right edge, though.)

Oh yeah, I did not notice that, thank you. The right edge must have ve been caused by the old iframe setup - i don't see it anymore.

If you type "p" and a page number after a bubble, it adds the page number to the bubble but then leaves it in the text after the bubble.

Thank you, it gets cleared now.

This changed to showing "page 123" instead of "p. 123" in the bubble.

Hm, I am actually a bit confused. What exactly is the intended behavior here? When I try this on master, I see that typing "p. 5" after a bubble does add "p. 5" to it. But if i click on that bubble and close the properties popover, the bubble changes to have "page 5" instead of "p. 5". After that typing "p. 7" will add "page. 7" and not "p. 7" to the end of the bubble. Does that seem a bit inconsistent or am I missing something?

@dstillman
Copy link
Member

This is really great.

One more regression I noticed: now, if you clear everything in the box, the search results don't reappear with Open/Selected like they did before.

Sorry if this is addressed above, but where did we end up with VoiceOver, NVDA, and JAWS?

But if i click on that bubble and close the properties popover, the bubble changes to have "page 5" instead of "p. 5".

Yeah, that shouldn't happen — it should always be "p. 5". We're getting the long form of locator strings from the CSL locales:

let elem = doc.querySelector(`term[name="${locator}"]:not([form="short"]) > single`);

(and additional lines below that)

But we can fix that separately to use the abbreviations for all locators.

- when only the last, non-removable, input remains, display the
reference panel as if the dialog just appeared
- add a sanity check to not delete the last non-removable input on blur
itemPopoverLocatorLabel.selectedIndex !== 0 would not allow to select
the first locator, Act. Instead, set or remove locator based on the
locator's value.
after the redesign-related rebase, the font was too big compared
to the old references panel
- when getLocatorString runs the first time, it saved to cache all
forms, including form="short" with a suffix added to the key in the
cache map. E.g. form="short" for page is saved as "page_short".
- when a locatorString is fetched, an additional parameter can be used
to request a specific form, e.g. getLocatorString("page", "short").
If no such form exists, string without any form is returned.
- when the locator string is fetched for a bubble, it asks for the
short locator string, e.g. "p." for page, "para." for paragraph, etc.
@abaevbog
Copy link
Contributor Author

One more regression I noticed: now, if you clear everything in the box, the search results don't reappear with Open/Selected like they did before.

Thank you - I added an extra condition to display the item list when everything but the last, non-deletable, input has been removed.

Sorry if this is addressed above, but where did we end up with VoiceOver, NVDA, and JAWS?

This is the brief summary of how everything is setup now:

  • bubbles now receive focus during arrow navigation with left/right, so they get announced
  • when the items from the reference panel are navigated with arrowUp/arrowDown, they get announced
  • the references list has an item that is selected but not focused when one is typing (e.g. the first row when the panel just opens). So tab can be used to move focus between the input and the selected item in the reference panel
  • item properties popover's inputs are linked to the labels, so those are announced too
  • now, each input is an ordinary <input> component, so that works fine with screen readers as well
  • we also added aria-describedBy elements with hints on which keypresses are available at which point. These are the additions in zotero.ftl - they may need to be tweaked to be more descriptive maybe?

In general, at least with my voiceover, NVDA and JAWS, everything works as expected. There are minor peculiarities, of course.

  • I had to tweak my JAWS settings to have those hints announced
  • The reference panel sometimes will get read out after it appears and sometimes it won't (depending on the screen reader) - this is just a consequence of it being a popup and it didn't seem to be an issue when I was testing it
  • Voiceover takes over the arrow navigation, so when it is being used, one should hold Cmd to allow the focus to be handled by us moving between bubbles and rows from the references panel.

The good thing is that, besides these last point above, voiceover works pretty much the same as JAWS or NVDA, so if you test it on a mac, that should be fairly representative of what other screen-readers would do a well.

@abaevbog
Copy link
Contributor Author

Yeah, that shouldn't happen — it should always be "p. 5". We're getting the long form of locator strings from the CSL locales:

So I went to have a look at how it happens, and I just pushed a suggestion to cache and fetch locator strings by the form as well, so this will use abbreviated versions for all the bubbles. E.g. "p." for "page", "para." for "paragraph" and so on. Is that somewhat what we're after here?

@abaevbog
Copy link
Contributor Author

Now that I was looking at the shortcuts (type "p. 5" to add page = 5 to the citation), I also want to make sure their behavior is correct.

In the docs, it says Type “p.45-48” or ”:45-48“ after a citation to cite a specific page or page range. This does work exactly like that the first time I type "p. 5" after bubble A. However, if I move the cursor after another bubble B and type something like "p.8", this updates original bubble A and bubble B remains as it was. The locatorNode gets shifted if I click on the bubble B to open the properties popover or create a new bubble: typing "page. 8" will update the last bubble after that. This is the current setup on master, and in this PR as well.

I think I got a bit confused because it felt like the locator shortcuts got "stuck" to bubble A. But I guess the idea is that you use this to quickly add a page right after you create a new bubble from the references list, so what I was doing isn't exactly the intended use case.

@dstillman
Copy link
Member

But I guess the idea is that you use this to quickly add a page right after you create a new bubble from the references list, so what I was doing isn't exactly the intended use case.

Yes, but it's definitely a bug if you click after another citation and it updates a previous one.

Shortcuts, such as "p. X", or ":X", will always apply X locator
value to the bubble right before the input. The only exception is when a
new bubble was just added from the reference panel - in this case, the
shortcuts will stick to the new bubble. This effect resets on the blur
of the input field.
@abaevbog
Copy link
Contributor Author

Yes, but it's definitely a bug if you click after another citation and it updates a previous one.

Ok, gotcha! Just wanted to make sure I wasn't misinterpreting anything.

I just pushed a change with significantly simplified logic of handling the locatorNode. Essentially, all shortcuts like "p. X" or ":X" always apply the X locator value to the bubble before them. The only exception is when a new bubble was just added, in which case they will "stick" to that new bubble until the focus moves elsewhere.

Correct me if there's anything that's now missing after this update

@dstillman dstillman merged commit 0007d07 into zotero:main Feb 27, 2024
1 check passed
@dstillman
Copy link
Member

In general, at least with my voiceover, NVDA and JAWS, everything works as expected.

Amazing!

@abaevbog
Copy link
Contributor Author

Awesome! Very exciting, hopefully this will address the screen readers issues and be a good starting point for further improvements. @adomasven, thank you for guiding me through this!

@adomasven
Copy link
Member

Excellent! Thanks for taking this on @abaevbog!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants