etn-ccis / blui-react-component-library

Re-usable React components for use in Brightlayer UI applications
BSD 3-Clause "New" or "Revised" License
16 stars 10 forks source link

I'd like to use two PX Blue Drawers in one application #141

Closed huayunh closed 4 years ago

huayunh commented 4 years ago

Describe the desired behavior

I should be able to use two pxblue drawers, with one drawer passed into the drawer props of the drawerLayout, the other drawer lies inside the children.

Describe the current behavior

In react-design-pattern, image I want to show an example where it uses two drawers, one of which is the global drawer currently displayed on the left, the other one is shown when you click the icon button on the top right (the drawer that goes with the internationalization)

image

Currently, we can only use the material UI drawer for the second drawer. Using two pxb drawers will set the paddingLeft of the first drawer's drawer layout to 0.

Is this request related to a current issue?

Additional Context

This raised up from https://github.com/pxblue/react-design-patterns/pull/12

joebochill commented 4 years ago

You should be able to use the drawer inside a drawer layout, or not. The drawer logic right now looks for a drawer layout to modify its padding (even if that DrawerLayout is not a parent of the Drawer in question). We just need to beef up the logic here to be a little more discerning when we do the check to update the padding of any drawerLayouts (only update drawerlayout if the current drawer is a child of it, and only update the nearest drawerLayout in the event there are multiple).

joebochill commented 4 years ago

This turns out to be much tricker than I initially thought. Because the DrawerLayout will let you pass in a component that returns a 'Drawer-like' element, there's no good way to directly link the Drawer and the DrawerLayout.

The simplest way to do this is by adding a callback function prop to the drawer (onOpenChanged) that we could use in the DrawerLayout to update the padding, something like:

const newDrawer = React.cloneElement(props.drawer, {onOpenChanged: (params) => updatePadding(params)}

However, this only works if the value passed in to the drawer prop is a Drawer:

<DrawerLayout drawer={<Drawer />} />

If we pass in a function that returns a drawer (<DrawerLayout drawer={<MyDrawer/>} />), which is a common use case when the user wants to add some behaviors to their drawer component or locate it in a different file that the DrawerLayout, we no longer have direct access to the Drawer component to add the callback prop. If we clone props.drawer in this case, we are cloning the wrapper component and adding props to that doesn't really help us.

Another approach that I had thought of was to use Context to store the callback to update the parent, but this suffers from the same shortcoming as the current implementation, namely that there is no direct link between the Drawer and the DrawerLayout - a Drawer will simply look for the nearest context provider and update it (effectively the same thing as searching for a DrawerLayout by id).

Some other approaches that we might explore further are: