[RFC] Component Overrides Standardization & Documentation
See original GitHub issueCurrent State of Overrides API
We have a fairly powerful overrides API. However, I think there are two issues we need to address sooner rather than later.
-
It is not clearly documented to the users and is not easily understood at each component’s level. I have received a few repeated questions from users about how to override parts of their components.
-
There is a lack of standardization on how we approach overrides for components that re-use our other components. Do we expose a top-level override API for the entire component that’s re-used, and expect users to make granular overrides by writing their own layer on top, or do we expose all the granular overrides of the re-used component?
Documentation Issue & Proposal
Our overrides
API could be used in two ways:
const overrides = {
Root: ReactComponent
};
// OR
const overrides = {
Root: {
component: ReactComponent,
props: {...}
style: {...}
// OR
style: ({$theme /* and other presentational props */}) => {...}
}
};
This is not documented as a general API that every component follows. We do document this for every component but it’s not obvious at first glance. There are also some test coverage concerns raised in the style: ({$theme}) => {...}
approach (anon function declared inline).
Secondly, it’s not obvious which component the user should override to achieve a particular behavior. In the case of Pagination
, we have the following overrides
overrides: {PrevButton, NextButton, DropdownContainer, DropdownMenu, DropdownButton}
How can the user know the difference between DropdownContainer
and DropdownMenu
and decide which one to override without 1) reading the source code or 2) trial and error.
And in the case of the Modal
component, which exposes the following overrides:
overrides: {Root, Backdrop, Dialog, DialogContainer, Close}
I’m not able to immediately understand the role of Root
, Dialog
, DialogContainer
in relation to one another from this.
This extends to all of our other components that use the overrides
API. A subset of the overrides are obvious, Close
, PrevButton
, etc. but many are not.
In order to fully utilize this overrides API, the user must have a clear understanding of the DOM structure and what each sub-component does.
Proposal
- We need a high-level
overrides
API documentation with some examples of how it’s used. - Standardize on some sub-components, like
Root
- i.e. always the outermost<div>
of the component. - Have an
Overrides API
section for each component’s RFC that describes the presentational responsibility of each sub-component. - It would be great to have a visual layered description of the component made up of its subcomponent. I don’t know how to achieve this though.
Components Re-use & Overrides
Select
, for example, currently uses Menu
underneath. There is no way to override the entire Menu
component at the top level. Pagination
also uses Button
, but its API only allows overriding of the BaseButton
sub-component inside Button
. It’s also gearing up to re-use Select
entirely.
Should both of these components allow only top-level override of Menu
, Button
, and Select
? Or should, say, Select
continue to expose more granular override options. But what if the user wants to override parts of Menu
that’s not exposed as an override by Select
?
Proposal
- Allow only high-level override of re-used components at the top API. If the user wants to override the internals of those component, they can do so separately:
function CustomMenu() {
return (
<Menu
overrides={{
Option: {...}
}}
/>
);
}
function MyComponent() {
return (
<Select
overrides={{
Menu: CustomMenu
}}
/>
);
}
- Expose all overrides of the re-used component, and reconstruct the re-used component internally with those overrides. This would expand the override API of
Select
to include all the overrides offered byMenu
, and expand all ofPagination
overrides to include all the overrides offered bySelect
(which includes all the overrides ofMenu
and so on…).
Conclusion
I don’t have a clear answer to any of these. But I figured that with our components count increasing, it would be good to align on these issues, before we start building more complex components.
Thoughts?
Issue Analytics
- State:
- Created 5 years ago
- Reactions:3
- Comments:11 (9 by maintainers)
Just wanted to chime in with my “overrides” implementation using hooks or higher-order components to eliminates all the manual prop spreading: https://github.com/tlrobinson/overrides
(caveat: I just hacked this together last night and haven’t used it for anything real yet. It’s possible I’m missing a reason why this is a terrible idea. The only downside I’ve come up with is the extra layer of wrapper components it introduces)
FWIW I’ve implemented nested hooks without flattening. It’s very simple to implement and I think it’s better to be explicit. And
overrides={{ Calender: { Month: ... } }}
isn’t much more verbose thanoverrides={{ CalenderMonth: ... }}
, for example.This issue is stale because it has been open 30 days with no activity. If it’s still valid, please remove the stale label or comment on the issue, otherwise this ticket will be closed in 5 days