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

[EuiResizableContainer] Allow collapsible panels #3978

Merged
merged 75 commits into from
Dec 10, 2020

Conversation

andreadelrio
Copy link
Contributor

@andreadelrio andreadelrio commented Aug 31, 2020

Summary

Adds the ability for EuiResizablePanel components to be collapsible—shrinking the panel itself and hiding its content either via an out-of-the-box toggle button or through coordinated actions hooks provided to the parent component.

image

Features:

  • New mode prop specifies an EuiResizablePanel as collapsible or main, indicating the ability to collapse or the preference to gain space from collapsed panels.
  • Incorporated EuiPanel as the base element, allowing for more panel style options
  • Rewrote registry and interaction logic to use a React reducer pattern, making evening more reliable and providing more flexibility in exposing action hooks to consumers.

Breaking change:
Removed the size prop on EuiResizableButton. This was originally added as something of a spacing mechanism, but is less effective than the newly added panelProp.paddingSize prop.

Checklist

  • Check against all themes for compatibility in both light and dark modes
  • Checked in mobile
  • Checked in Chrome, Safari, Edge, and Firefox
  • Props have proper autodocs
  • Added documentation
  • Checked Code Sandbox works for the any docs examples
  • Added or updated jest tests
  • Checked for breaking changes and labeled appropriately
  • Checked for accessibility including keyboard-only and screenreader modes
  • A changelog entry exists and is marked appropriately

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_3978/

@cchaos
Copy link
Contributor

cchaos commented Aug 31, 2020

🎉 Great start! I think this will definitely meet Discover's needs and replicate a similar behavior that exists now.

I'd love to see a version that allows the toggle button to be free-floating, meaning, it doesn't have to live in the DOM layout (possibly pushing content down).

A somewhat static version could be just adding the button on focus that floats in the center where the original grab button was. For instance, we'd have these 4 states (the fourth showing the collapsed state that maybe always shows the uncollapse button).

Screen Shot 2020-08-31 at 17 02 38 PM

^^ That solution will work well with applications that are ok keeping at least a 24px wide area dedicated to the collapsible button.

However, we'd probably also need a solution for consumers who would like to provide their own toggle button anywhere in their UI that would then completely hide or show this panel (similar to like a flyout toggle). You could handle this by allowing toggle to accept any ReactElement which will then add the onClick behavior to the button they pass in.

One more enhancement could be to add a double-click behavior which will do the collapsing/uncollapsing behavior so that users don't have to hunt for the button itself. It would be a learned behavior, but that is ok since we're also providing the targetable button.

@chandlerprall
Copy link
Contributor

chandlerprall commented Sep 2, 2020

Because the collapse functionality - defined on one or more panels - affects the other panel(s) in a group, I think the majority of the logic needs to move to the panel registry which is already responsible for orchestrating the group.

Some additional concerns:

  • providing collapse/uncollapse feedback to the application
  • allowing the app to provide initial collapsed state and/or complete control during the panel lifespan, like we do for widths
  • when collapsed, what happens to reported widths? (see below)

Reported widths

As an example let's start with 3 panels all open and with 33% width.

|   A   |   B   |   C   |
|       |       |       |
|       |       |       |

Let's collapse the middle column. The total displayed width should remain the same even though A and C together still represent only 66% of the size.

|     A    |*|     C    |
|          | |          |
|          | |          |

Now resize column C larger

|   A   |*|      C      |
|       | |             |
|       | |             |

Numerically, what happened?

Today, with un-collapsible columns, resizing C would take away from B. Collapsed, we cannot take away from B because visually it wouldn't adjust C's width, because B is already collapsed (this is not strictly true, as proportionally A would still shrink to accommodate C's increased size, but that feels really awkward from a user's perspective). Does it look over further and take away from A? What sizes are expected when we expand B again? Would any of those answers be meaningfully different if we had shrunk C instead of enlarging?

@andreadelrio
Copy link
Contributor Author

Because the collapse functionality - defined on one or more panels - affects the other panel(s) in a group, I think the majority of the logic needs to move to the panel registry which is already responsible for orchestrating the group.

Some additional concerns:

  • providing collapse/uncollapse feedback to the application
  • allowing the app to provide initial collapsed state and/or complete control during the panel lifespan, like we do for widths
  • when collapsed, what happens to reported widths? (see below)

Reported widths

As an example let's start with 3 panels all open and with 33% width.

|   A   |   B   |   C   |
|       |       |       |
|       |       |       |

Let's collapse the middle column. The total displayed width should remain the same even though A and C together still represent only 66% of the size.

|     A    |*|     C    |
|          | |          |
|          | |          |

Now resize column C larger

|   A   |*|      C      |
|       | |             |
|       | |             |

Numerically, what happened?

Today, with un-collapsible columns, resizing C would take away from B. Collapsed, we cannot take away from B because visually it wouldn't adjust C's width, because B is already collapsed (this is not strictly true, as proportionally A would still shrink to accommodate C's increased size, but that feels really awkward from a user's perspective). Does it look over further and take away from A? What sizes are expected when we expand B again? Would any of those answers be meaningfully different if we had shrunk C instead of enlarging?

I made a diagram to better understand the situation

Group 3

I agree that changing the behavior of the EuiResizableButton upon collapsing one panel could be confusing but what would be the alternative? The other question that came to mind, should there we "limits" as to when to implement collapsible panels? What if consumers add toggle to two panels or to three? I think that we could fall into a loop of constantly changing the behavior of the EuiResizableButtons and that could get messy.

@chandlerprall
Copy link
Contributor

I agree that changing the behavior of the EuiResizableButton upon collapsing one panel could be confusing but what would be the alternative?

Not saying we shouldn't change the behaviour, only that we need to decide on what that change should be. I did a quick search to find this pattern elsewhere and didn't find anything. Perhaps the solution, at least for now, is disable the resize button if it borders a collapsed panel?

@andreadelrio
Copy link
Contributor Author

andreadelrio commented Sep 3, 2020

I agree that changing the behavior of the EuiResizableButton upon collapsing one panel could be confusing but what would be the alternative?

Not saying we shouldn't change the behaviour, only that we need to decide on what that change should be. I did a quick search to find this pattern elsewhere and didn't find anything. Perhaps the solution, at least for now, is disable the resize button if it borders a collapsed panel?

@chandlerprall I think that could be a good option. In particular, I think that when there are only two panels it wouldn't feel weird to the user. That's the use case in Discover. Things get trickier when there's more than two panels.

I found a similar interface elsewhere. There, because the UI is a bit different (basically the "resizable button" is the panels’ borders) it’s more clear that it makes sense to disable resizing of a panel if it borders one that's been collapsed.

In the gif below, if I could collapse HTML I wouldn’t be able to further resize JS or CSS and that would feel ok. JS only affects HTML (and viceversa) and CSS only affects HTML (and viceversa). With HTML collapsed, nothing should affect the JS panel's width.

beh

@chandlerprall
Copy link
Contributor

Works for me - let's go the route of disabling resizing any panel that neighbors a collapsed one. We'll get you engineering support on the panel registry changes.

@thompsongl
Copy link
Contributor

Works for me - let's go the route of disabling resizing any panel that neighbors a collapsed one. We'll get you engineering support on the panel registry changes.

I'll most likely be the one helping here. A question that immediately comes to mind:
If we move the logic up to the panel registry, then it seems like willExpand can be removed entirely. Unless there are situations where a panel next to a toggle panel should not expand, but that seems unlikely (what would happen if the neighboring panel didn't expand?). Does willExpand always need to accompany toggle?

@chandlerprall
Copy link
Contributor

it seems like willExpand can be removed entirely

Good catch, that's my interpretation as well.

@andreadelrio
Copy link
Contributor Author

andreadelrio commented Sep 9, 2020

Awesome. I think ideally willExpand should not exist. I only added it because it was the only way I found to mark which panels would be giving up their width but I was working under the assumption that all accompanying panels would be giving up their width. So toggle alone should be enough.

I'll most likely be the one helping here. A question that immediately comes to mind:
If we move the logic up to the panel registry, then it seems like willExpand can be removed entirely. Unless there are situations where a panel next to a toggle panel should not expand, but that seems unlikely (what would happen if the neighboring panel didn't expand?). Does willExpand always need to accompany toggle?

@thompsongl
Copy link
Contributor

I think ideally willExpand should not exist. [...] So toggle alone should be enough.

Perfect

@thompsongl
Copy link
Contributor

thompsongl commented Sep 10, 2020

Question on sizing for sibling panels after a panel is collapsed (with 3 or more panels):

Given:

|   A   |   B   |   C   |
|       |       |       |
|       |       |       |

And B collapses, above examples show as below, where A and C grow equally:

|     A    |*|     C    |
|          | |          |
|          | |          |

But what about collapsing A or C instead? Do we expect all sibling panels to grow equally even if not a direct sibling?

|*|     B    |     C    |
| |          |          |
| |          |          |

or

|*|       B      |    C   |
| |              |        |
| |              |        |

If all siblings do not grow equally, does that change the assumption about A and C growing equally in the first example?

Note that the equal growth idea might have been more a product of using CSS flex than anything; equal growth was the only real option. We have to do all the math ourselves now, so all options are on the table.

The fact that this was all done with CSS before is really cool, @andreadelrio

@thompsongl
Copy link
Contributor

This ^ all gets even more tricky if we try equal panel growth, but not all panels were sized the same to start.

@cchaos
Copy link
Contributor

cchaos commented Sep 10, 2020

I like to try to think these through in terms of real-world scenario/use-cases. For instance, I have a need to use these in Lens to be able to allow the user to resize the left and right sidebars as well as fully collapse them if they need the space. The sidebars have defined widths and the center takes up the remaining.

Screen Shot 2020-09-10 at 18 28 24 PM

When the user adjust the right side panel, it is resizing the right sidebar and the center is compensating.

Screen Shot 2020-09-10 at 18 32 44 PM

When the user collapses the right sidebar, the same thing happens. It adjusts the sizing of the right sidebar while the center grows to fill the space.

Screen Shot 2020-09-10 at 18 28 11 PM

Here is a video and a prototype of the collapsing behavior (drag to initiate toggle).


Essentially when one panel adjusts only it's direct sibling adjusts as well. If it has 2 siblings, some maths to find the percentage of growth needed to add to each panel?

@thompsongl
Copy link
Contributor

If it has 2 siblings, some maths to find the percentage of growth needed to add to each panel?

Yeah, it's the 2+ sibling case that I was really asking about. I think adding to each sibling makes sense, just want to be sure

@thompsongl
Copy link
Contributor

thompsongl commented Sep 16, 2020

Spent some time trying outlining a spec for behavior this morning, and I have some more questions 🙂

Based on all the discussion in this thread, it seems there are 2 ways to implement a toggle button, each having different impacts on expected behavior.

Use the following for both:

|   A   |   B   |   C   |
|       |       |       |
|       |       |       |

First: Toggle button on the drag handle

Given the example where B and C both have toggle=true, both drag handles would have toggle buttons:

Directionality is therefore implied.

  • The handle between A and B would have a single button
    • It would point to the right, indicating that B would collapse on click
  • The handle between B and C would have two buttons
    • One pointing left, indicating that B would collapse on click
    • One pointing right, indicating that C would collapse on click
  • Width would only be redistributed to the panel bordering the drag handle
    • For example, when collapsing C, only B would grow
    • When collapsing B, it growth would depend on which drag handle button was clicked

Second: Toggle button anywhere except the drag handle

Given the example where B and C both have toggle=true, and we place a single toggle button for each panel somewhere else in the UI. For example, "Collapse/Expand panel B" and that button is clicked:

Directionality cannot be assumed like in the first example.

  • Width could be redistributed in various ways
    • We could choose A, or C, or both to grow
    • We could provide additional configuration options here
  • Depending on which choices are made, the default toggle buttons inside the panels need to adapt

  • Are both of these options desired or am I off base?
  • Do the details that I've described for each sound correct?
  • If both, should both be possible at once (drag handle buttons and buttons elsewhere)?

Regardless of above:

  • Do we want the ability to collapse multiple panels at once?
  • I assume we also want to restore the previous width of a collapsed panel after it again becomes expanded?

@andreadelrio
Copy link
Contributor Author

Spent some time trying outlining a spec for behavior this morning, and I have some more questions 🙂

Based on all the discussion in this thread, it seems there are 2 ways to implement a toggle button, each having different impacts on expected behavior.

Use the following for both:

|   A   |   B   |   C   |
|       |       |       |
|       |       |       |

First: Toggle button on the drag handle

Given the example where B and C both have toggle=true, both drag handles would have toggle buttons:

Directionality is therefore implied.

  • The handle between A and B would have a single button

    • It would point to the right, indicating that B would collapse on click
  • The handle between B and C would have two buttons

    • One pointing left, indicating that B would collapse on click
    • One pointing right, indicating that C would collapse on click
  • Width would only be redistributed to the panel bordering the drag handle

    • For example, when collapsing C, only B would grow
    • When collapsing B, it growth would depend on which drag handle button was clicked

Second: Toggle button anywhere except the drag handle

Given the example where B and C both have toggle=true, and we place a single toggle button for each panel somewhere else in the UI. For example, "Collapse/Expand panel B" and that button is clicked:

Directionality cannot be assumed like in the first example.

  • Width could be redistributed in various ways

    • We could choose A, or C, or both to grow
    • We could provide additional configuration options here
  • Depending on which choices are made, the default toggle buttons inside the panels need to adapt

  • Are both of these options desired or am I off base?

  • Do the details that I've described for each sound correct?

  • If both, should both be possible at once (drag handle buttons and buttons elsewhere)?

@thompsongl I personally don't have a strong preference for either implementation. I'd say go with the simplest one which it sounds like it's the first one?

Regardless of above:

  • Do we want the ability to collapse multiple panels at once?

I think definitely. Think of the Lens use case Caroline shared above, the sidebar should also be collapsible.

  • I assume we also want to restore the previous width of a collapsed panel after it again becomes expanded?

Ideally yes.

@chandlerprall
Copy link
Contributor

I don't think this is something other than "works as intended" but wanted to point it out in case. In the Resizable panel options example, the middle panel can be resized so small that it hides behind the third panel.

resizable_width

@thompsongl
Copy link
Contributor

"works as intended"

Exactly. We could update the min-width on that example to be a bit more realistic, I bet.

Copy link
Contributor

@chandlerprall chandlerprall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed everything except the reducer's logic, will do that one in a second pass.

  • The Horizontal resizing with three panels example feels like it has been superseded by Resizable panel options
  • Navigating to the collapse button via keyboard, there is no focus state on the button

];

export default () => {
const collapseFn = useRef(() => {});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be refactored to not rely on setting collapseFn as a side effect of the render? 😁

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing is immediately coming to mind for a refactor. The idea was to handle this similar to how refs are handled.

src/components/resizable_container/helpers.ts Outdated Show resolved Hide resolved
src/components/resizable_container/helpers.ts Outdated Show resolved Hide resolved
src/components/resizable_container/helpers.ts Outdated Show resolved Hide resolved
src/components/resizable_container/helpers.ts Outdated Show resolved Hide resolved
src/components/resizable_container/resizable_container.tsx Outdated Show resolved Hide resolved
Co-authored-by: Chandler Prall <chandler.prall@gmail.com>
@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_3978/

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_3978/

Copy link
Contributor

@chandlerprall chandlerprall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first example is no longer resizable by mouse 😕

src/components/resizable_container/helpers.ts Show resolved Hide resolved
src/components/resizable_container/resizable_container.tsx Outdated Show resolved Hide resolved
src/components/resizable_container/helpers.ts Outdated Show resolved Hide resolved
src/components/resizable_container/helpers.ts Show resolved Hide resolved
src/components/resizable_container/helpers.ts Show resolved Hide resolved
src/components/resizable_container/helpers.ts Show resolved Hide resolved
src/components/resizable_container/helpers.ts Outdated Show resolved Hide resolved
src/components/resizable_container/helpers.ts Outdated Show resolved Hide resolved
src/components/resizable_container/helpers.ts Outdated Show resolved Hide resolved
src/components/resizable_container/helpers.ts Show resolved Hide resolved
@thompsongl
Copy link
Contributor

Still working on the unresolved comments.

The Horizontal resizing with three panels example feels like it has been superseded by Resizable panel options

I agree; removed.

Navigating to the collapse button via keyboard, there is no focus state on the button

Added some styles. Feedback welcome.

The first example is no longer resizable by mouse

Fixed.

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_3978/

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_3978/

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_3978/

Copy link
Contributor

@chandlerprall chandlerprall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes LGTM! The +2,675 −620 modified lines of code over 30 files does not fully represent the work y'all put into this feature. This is huge, thank you!

Copy link
Contributor

@cchaos cchaos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functionality all looks great. I'm sure we might find some more strange situations but, we won't know til we release 😄

Just had a few more suggestions for the docs.

@kibanamachine
Copy link

Preview documentation changes for this PR: https://eui.elastic.co/pr_3978/

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

Successfully merging this pull request may close these issues.

5 participants