Closed brynwhyman closed 5 years ago
Here's the beautiful DND examples: https://react-beautiful-dnd.netlify.com/?selectedKind=single%20vertical%20list&selectedStory=basic&full=0&addons=1&stories=1&panelRight=0&addonPanel=storybook%2Factions%2Factions-panel
The DND PR is good to go - would be interested to look at a refactor to this library but it might mean starting from scratch.
Don't think this requires designs, react-beautiful-dnd seems to have built-in drag-n drop for screen readers. https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/guides/screen-reader.md
I'm not an expert on accessibility, and for this reason I think transferring to the new react-beautiful-dnd
library would be a superior option to attempting to self-engineer (self as in team) a retrofit of accessibility hints, controls, and usage flows to the current solution. So the outcome of this investigation has been based/biased around this. Both libraries are well maintained, I don't see one as being preferable to the other on this criterion alone.
react-dnd has it's roots in silverstripe/admin
so we'd have to update that to take advantage of a new library. To my knowledge we're the only ones consuming this export - but I did not attempt to check this assumption.
We are using DragSource
and DropTarget
from the "Legacy decorator API" in react-dnd
rather than the newer useDrag
and useDrop
, where I personally find the legacy API a little easier to follow - plus this is more relatable to the Draggable
and Droppable
API provided by react-beautiful-dnd
. This is a kind of win for an easy port I think - the implementation details are mostly transferrable; The wrappers provide props to child components that update with dragging state. The details change, but the concept remains the same.
react-dnd
is activated via lib/withDragDropContext
in admin, which is a simple wrapper around exporting the library's wrapper with a backend attached (the HTML5 backend, specifically). This is an internal that could hopefully be updated without need for making a new release of webpack config.
The react-beautiful-dnd
library supplies an element of the same name DragDropContext
for the creation of the drag and drop area, however takes different properties - the important difference being that the hooks for updating before, during and after a drag are contained here, rather than on the Draggable
/DragSource
and Droppable
/DropTarget
as per react-dnd
.
Other properties living on Draggable
/DragSource
and Droppable
/DropTarget
seem mostly transferrable, although they do change name (e.g. canDrop
becomes isDropDisabled
). I don't think this would be a huge burden at all.
Switching library here would also have a knock on affect, in that react-beautiful-dnd
supports dragging between lists, which is asked about every so often in the community, by people adding more than one ElementalArea
to their objects.
I also think the combining feature would provide a head start for #332 - allowing existing elements t be dragged in and out of a Layout Block.
Implementation intro: https://egghead.io/lessons/react-reorder-a-list-with-react-beautiful-dnd
TL;DR react-beautiful-dnd
:heavy_check_mark:
More functionality, less maintenance burden, and cursory future proofing provided out of the box for a very similar API - with hopefully mostly transferrable implementation concepts. Minor concerns around how this impacts silverstripe/admin
on a greater scale.
There's still a couple of oddities about our use case that might need to be considered.
We have drag areas within drag areas - that's why admin exposes a HOC (withDragDropContext
). ReactDND has an issue with two separate contexts being created so they need to share the same one. Not sure if this is going to be an issue if we have ReactDND inside a React-Beautiful-DND? For reference this is when you have an UploadField
within an in-line edit form.
Our UI for dragging is a little custom. We leave an indication of the current position within our list of blocks and we insert a line to show where the element will drop. I assume that this will probably have to change with ReactDND? It forces you down the route of showing where it'll land and not indicating the existing location...
Currently we use another "power feature" (of sorts) with ReactDND - the ability to have custom drag previews for our elements. We do this because the in-line edit forms can be pretty massive to be dragging up and down the screen. I remember seeing an issue saying this wasn't possible with ReactDND - although you could re-render the component by using some isDragging
prop. We might need to do some POC work to see if this works though as I have a feeling this could cause issues when the draggable area suddenly changes size on you.
Playing a little bit of a devils' advocate here but I think we need to be sure that we're not going to create a big project for ourselves by diving into "switch out the DND library" - especially when there's this quote on the beautiful DND page:
However, it does not provide the breadth of functionality offered by react-dnd. So react-beautiful-dnd might not be for you depending on what your use case is.
I'm not overly familiar with the implementation of the drag and drop library, but I'd tend to agree with @ScopeyNZ's comments - replacing the library seems to me like it'd be more work than implementing keyboard accessibility in the library we already have.
I can't find anything obvious in the docs or issue tracker for it relating to being able to control the drag/drop events with keyboard buttons, however I think that we could either add it ourselves, or simply add a "Move down" and "Move up" option to the actions dropdown to compensate for it if that's too difficult.
I wouldn't be too quick to assume creating a good a11y experience on our own will be easier than switching library, particularly when react-dnd
makes no mention of it in their documentation whilst react-beautiful-dnd
goes step by step through the different audible descriptions it provides.
It was discussed at standup that we've missed another option, though: Using standard buttons or options in the Actions panel to enable shifting blocks up or down for keyboard access / screen readers. This would be less efficient from a usability perspective, but easier to implement well. @newleeland might have some designs for this?
EDIT: I realise now that @robbieaverill mentioned the button approach above.
There's still a couple of oddities about our use case that might need to be considered.
We have drag areas within drag areas - that's why admin exposes a HOC (
withDragDropContext
). ReactDND has an issue with two separate contexts being created so they need to share the same one. Not sure if this is going to be an issue if we have ReactDND inside a React-Beautiful-DND? For reference this is when you have anUploadField
within an in-line edit form.
This would pose a challenge, and I did not look into it I'm afraid (as I didn't realise this complexity at the time).
Our UI for dragging is a little custom. We leave an indication of the current position within our list of blocks and we insert a line to show where the element will drop. I assume that this will probably have to change with ReactDND? It forces you down the route of showing where it'll land and not indicating the existing location...
I don't think this is true. I note the difference between a DragLayer (react-dnd
) and not having one, but I believe this is replaced by 'Responder' properties on the context (https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/guides/responders.md#ondragupdate-optional), which I think can still achieve the same thing (showing drop indicators, etc.)
Currently we use another "power feature" (of sorts) with ReactDND - the ability to have custom drag previews for our elements. We do this because the in-line edit forms can be pretty massive to be dragging up and down the screen. I remember seeing an issue saying this wasn't possible with ReactDND - although you could re-render the component by using some
isDragging
prop. We might need to do some POC work to see if this works though as I have a feeling this could cause issues when the draggable area suddenly changes size on you.
This may be true. I did not see any indication of the functionality for horseface to be shown on a drag. I think it would probably be possible, but as with the link above about the responders, the update function docs do warn: It is important that you not do too much work as a result of this function as it will slow down the drag.
Playing a little bit of a devils' advocate here but I think we need to be sure that we're not going to create a big project for ourselves by diving into "switch out the DND library" - especially when there's this quote on the beautiful DND page:
However, it does not provide the breadth of functionality offered by react-dnd. So react-beautiful-dnd might not be for you depending on what your use case is.
Reference: Not for everyone ✌️
This quote is in the context that react-beautiful-dnd
is build specifically for ordering lists, where react-dnd
is more generic and one should consider their use case. I did, an ElementalEditor
is made up of a single vertical list of elements. On the other hand the example for react-dnd
is dragging a knight around a chessboard - not quite a single list.
I can't find anything obvious in the docs or issue tracker for it relating to being able to control the drag/drop events with keyboard buttons, however I think that we could either add it ourselves, or simply add a "Move down" and "Move up" option to the actions dropdown to compensate for it if that's too difficult.
I agree with @Cheddam on this - I would not presume that creating good a11y interfaces should be simple, otherwise I do not think there would be an industry specialisation around this. Keeping in mind that anything we build will be ours to maintain, I think the ability to 'outsource' or offload as much of that as possible is a good choice.
Adding 'move up' and 'move down' options to a menu seems only a viable option if the menu were to stay open and maintain focus between move operations, so one could move an item up and down in succession quickly, rather than tedious menu interactions each time.
On this though I have forgotten to mention that I also considered that we have a drag handle - although we don't use it. Selecting a block currently and activating it will open or collapse the inline edit form - this will need to change, or we would have to activate the drag only when the handle is active - which would mean making that 'tab-able' - but would also be a tricky consideration as it is the entire element header that is the mouse activated drag handle. This will be a challenge in either library. Which ultimately puts more weight on the "buttons" approach.
Sorry, I wasn't trying to suggest that it would be simple, but rather that we should weigh up the effort versus reward. Here's where I see the options for this issue now:
React with 🎉 to vote for option 1
Pros:
Cons:
React with ❤️ to vote for option 2
Pros:
Cons:
React with 🚀 to vote for option 3
Example:
Pros:
Cons:
React with 👀 to vote for option 4
Example:
(Except use icons instead of text)
Pros:
Cons:
@silverstripe/creative-commoners please vote on this so we can agree on the way forward, and then close this spike =) cc @silverstripeux
For Option 4, I don't think the up / down could live in the "add new element" area - the ownership of the buttons would be unclear. Instead, I'd see them living next to the Actions menu. This would require more tabbing to navigate to the Actions menu, making the interaction less convenient - but accessible controls are far more important than convenient ones.
Given the reluctance to switch, and the fact that react-dnd
is used elsewhere in SilverStripe, I'm opting to vote for Option 2 at this stage - on the basis that we actually take the time to make this work well. I'd also personally be happy with, and am voting for, Option 4 (with the caveats above).
My suggestion would be to make an area near the drag icon tab-able with the arrow icon actions to move the item up or down. The drag handle would help to provide context as to what the actions do. The options dropdown is going to get overloaded with more actions soon or through a project's development (duplicate, move, other tabs).
Also to be clear:
Option 4 is not viable; This menu (or this hover bar rather) is not accessible via keyboard navigation - it only appears on hover - which is of course a mouse event.
updated to reflect edits top options list, option 4 has since been changed to match the suggestion immediately above
That sounds like a fixable bug to me :-)
I also don't agree that it's a good solution (but that's what voting is for). I like Paul's suggestion above better :)
Yeah I agree- feel fre esto change option 4 to Paul’s suggestion. My vote would stay the same
OK, I did that, and added my vote for it :)
I voted ❤️ 🚀 and 👀 . I think that ❤️ is a bit of a pipe-dream but I like the idea of doing both 🚀 and 👀 . I even think 🚀 will be minimal effort.
Neat, so the outcome of this SPIKE is to implement "move up" and "move down" buttons on the ElementHeader, with a stretch goal of having them available as actions in the more options menu also. As a side note the drag handle will need to be permanently visible when an element is focussed at least, probably always, at best.
I'll create a new issue.
It is currently not possible on
master
to re-order blocks on a page with keyboard actions. We should investigate the best way to implement functionality that provides keyboard access to reordering functionality.Drag and drop will be implemented via #285 but
https://github.com/react-dnd/react-dnd
doesn't provide accessible functionality out of the box.Ideas (in likely priority) ~1. Ensure inline adding of blocks can be tabbed to:~ split to #627
react-dnd/react-dnd
https://github.com/atlassian/react-beautiful-dnd
although additional development will be expected.