-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Abstract
Applications will typically use a router like React Router to handle client-side routing concerns; namely, defining what UI is rendered for a given URL, and navigating between supported URLs. However, these routers only facilitate routing based on the URL's pathname, not other sources of global application state like URL search parameters.
Since path segments in the URL's pathname conceptually represent distinct pages (even if some pages share common UI like a shared page header), it feel inappropriate to use path segments to control the open/closed state of overlay elements like Drawers and Dialogs. This makes it considerably harder to persist the open/closed sate of these overlay elements to the URL, which leads teams toward managing their state locally in React which, in turn, leads to a degraded user experience (refreshing the page will not return the user to where they were, shared URLs will not take users to where their colleagues may expect and so on).
To help teams achieve a quality user experience around drawers, Reapit PM's engineering team introduced the concept of a DrawerRouter, a collection of components that provides a router-like development experience for Drawers, but where the drawer's open state is persisted to the URL via a URL search parameter with a path-like value.
For example:
<DrawerRouter>
<DrawerRoute
pathPattern="/email-events/:emailId"
content={({ params }) => (
<EmailEventsDrawerContent {...params} />
)}
/>
... more drawers can be added here ...
</DrawerRouter>We want to generalise this idea (render any UI based on a path-like URL search param) and make it available via Elements.
DrawerRouter
A few issues with Reapit PM's DrawerRouter that would be good to improve upon include:
- It is tightly coupled with React Router for path matching logic;
- React Router's path matching supports more features than we need for query-string-based paths (e.g., we don't really need optional path segments);
- Navigating between drawer open/closed states requires dedicated
DrawerLinkandDrawerButtoncomponents; - We don't know what drawer URLs are supported by a given
DrawerRouter(because the DrawerRouter is locked away in another component's render function). - Not easy to leverage the drawer router's path to conditionally render other UI (e.g. tabs within the drawer).
A few things it gets right include:
- It provides a simple, declarative API for rendering drawers who's open/closed state is stored in a URL search param;
- It ensures Drawer's are always rendered, but only "open" when the related pattern matches the current path. This is important because drawers must already be present in the DOM for their open animation to work correctly.
- I allows for nested DrawerRouter's, which enables drawer content components to completely encapsulate any other stacked drawers.
Wishlist
The following items describe features that would be good to support with Elements' version of DrawerRouter:
- Router-agnostic implementation. Ideally, we can avoid any router-integration concerns to the application layer rather than dealing with them directly within Elements.
- Easy generation of valid URLs for navigating between known routes.
- Nested routing that works for overlay and non-overlay content. This is particularly important for drawers, as some may involve a number of different UI states that can be navigated between, such as different tabs, or stacked drawers for editing entity details.
- Minimal API. Ideally, we can avoid needing to provide thin wrappers around other Elements components (like
AnchorButtonandLinketc) to facilitate navigation between the URL-search-param-routed UI.
Items of work
This feature can be broken into a few distinct items of work:
- Basic path matching functionality. Likely one or more helper functions in
@reapit/elements/utils/paths. - Basic route ranking. If multiple routes match the current path value, which one do we show? It's possible we can keep this simple using a first match heuristic rather than the more complex behaviour of "real" routers.
- The router. This needs more thought/design, but we'll need some kind of component + context to facilitate the nesting behaviour we want and to control/determine what UI is rendered.
- URLSearchParam interop. This also needs more thought/design, but conceptually, we need something to help us extract a particular search param and provide it to the router so that changes to the param result in the routed content updating.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status