-
Notifications
You must be signed in to change notification settings - Fork 840
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
[EuiFlexGroup][EuiFlexItem] Add polimorphism support for ReactHTML elements #7612
Comments
🗒 One side note on this topic: When allowing using it as any element, there is the added responsibility to ensure valid HTML structure on implementation side. We might want to add a note for users on that? For the example of using |
@mgadewoll good point, that's a follow-up responsibility on the developer side to respect the HTML structure. <EuiFlexGroup component="button">
<EuiIcon />
<span>Click me!</span>
</EuiFlexGroup> Is there any specific reason why we should always use the
As another side note on this part, I agree we should take care of this aspect, but it doesn't play well with some other components we have, like <EuiFlexGroup component="button">
<EuiText size="s"> // ❌ This renders a wrapping `div`
<strong>Clickable heading</strong>
</EuiText>
</EuiFlexGroup> The above code would break for the above-mentioned specification, is there any other recommended way to do it using our typography styles? |
@tkajtoch In our offline conversation regarding this issue, we discussed extending the I see there are some other scenarios where would be great to use existing components such that we can apply the flex properties, for example: function NameColumn(props: EuiFlexGroupProps) {
return <EuiFlexGroup grow={3} {...props} />;
}
function Sortable({ children, sortOrder = 'asc', onSort, ...props }: SortableProps) {
const handleSort = () => onSort(sortOrder === 'asc' ? 'desc' : 'asc')
return (
<button {...props} onClick={handleSort}>
{children}
</button>
);
}
<NameColumn
component={Sortable}
// @ts-expect-error ❌ sortOrder and osSort is not admitted as a property because it's not inferred from component={Sortable}
sortOrder={search.sortOrder}
onSort={handleNameSort}
/>; This would help for improved composition and code re-use, working similarly to the emotion |
@tonyghiani Those are good points!
I'm personally missing historic context on the component to know why it is suggested this way. From what I can see in code, the default applied styles to display: flex;
flex-direction: column;
flex-basis: 0%;
flex-grow: 1; According to the docs the suggested way to prevent stretching is to add extra wrappers. Personally I'd prefer not using EuiFlexItem + extra wrappers in the first place where reasonable for the use case (when flex is not needed for the children) to reduce nesting. 💭 I'm also wondering if the API is really fitting when using // component prop
<EuiFlexGroup component="button" {...buttonProps} > // <-- should buttonProps be put here onto EuiFlexGroup?
<EuiIcon />
<span>button content</span>
</EuiFlexGroup>
// cloneElement prop
<EuiFlexGroup cloneElement> // <-- this could clone the child element and pass any additional styles for flex behavior
<button {...buttonProps}>
<EuiIcon />
<span>button content</span>
</button>
</EuiFlexGroup>
Yes, this would be an issue for I'm again just thinking out loud, maybe we might need to do something like this and pass down styles. // outputs a button with text styles applied
<EuiText cloneElement> // <-- NOTE: cloneElement doesn't yet exist on EuiText
<EuiFlexGroup component="button">
<EuiIcon />
<span>button content</span>
</EuiFlexGroup>
</EuiText> But this starts to look really off in terms of nesting hierarchy as IMHO layout should wrap content and not the other way around. // maybe this is a bit better
<EuiFlexGroup cloneElement> // <--clones text wrapper and adds flex
<EuiText cloneElement> // <-- clones button and adds text styles + flex styles
<button {...buttonProps}>
<EuiIcon />
<span>button content</span>
</button>
</EuiFlexGroup> At this point it's also a question of what's still understandable and what's more painful: Extra nesting or the mental overhead to understand what's valid/invalid that's created by opening up components like that. |
Strongly agree. I'm not familiar with using the The default behaviour I've seen in most component libraries accepts any component to support polymorphism, supporting all the props of the passed component. With the idea of reducing the unnecessary DOM nesting, I feel this would give us enough flexibility. Regarding the |
It used to be for margins and negative margin offsetting, but once we switched to
Ya personally I'm not a fan of this / it's messed with my CSS wrangling in the past and I find it kinda confusing. It's legacy code / How Things Used To Be™️ 🤷 I'm very much always a ++ for getting rid of as many div wrappers as sanely possible.
FWIW This is the typing shenanigans I mentioned briefly in our sprint planning on Monday that I anticipated being a potential issue 😅 In theory we could solve it like so, perhaps... <EuiFlexGroup<HTMLButtonElement>
component="button"
type="submit"
onClick={() => {}}
> It's maybe not the most readable but I also think it's probably Fine. To be honest I really don't love our |
I think this could be a decent middle ground solution. At least it would be defined via type that it's a button 😅 It's still a bit Frankenstein-y but any solution in that direction is.
I agree, it did not expect a wrapper myself either. I'm also missing context here why it was done /kept this way though - maybe @cee-chen has additional insights? Could it be an idea to provide options to output single text elements without wrapper to address the nesting issues the wrapper would cause? 🤔 // keep EuiText to be backwards compatible
<EuiText />
// add new components that can output single text elements
<EuiTextItem component={ELEMENT_TAG} /> // could apply styles based on passed element tag
// maybe heading could be a standalone component
<EuiHeading /> |
The context is usually always "legacy code written 6+ years ago, sometimes by designers" 😅 I'm not against updating |
Agree that it would be quite a lift to update all components. But maybe it could be an option to add additional components and keep the current |
I'm splitting hairs at this point, but I'd be more tempted to enhance the current EuiText component with a new prop rather than adding a totally new component. I think we could do some fairly simply
Sorry for the early morning cynicism - usually once something's in Kibana, it's... in Kibana. We had a 1 year deprecation period for old page template components that went a full year without any movement. Usually devs don't come back to look at things unless we make them 😅 Not to say we shouldn't still do this / offer a better and more modern API, because we totally should, but we should also budget for the worst case scenario of having to update Kibana usages ourselves if we do plan on deprecating the old cloneElement logic at some point. |
I've opened a PR that adds support for any tag names defined in I explored ways to modify our APIs to use |
Is your feature request related to a problem? Please describe.
There are scenarios in which I'd like to reuse existing components by just changing the type of the top-level wrapper, like converting a group into a button and keeping the flex layout.
EuiFlexGroup does currently support, using the
component
property, thediv
andspan
elements, the request if for wider support to all the other HTML elements for building more semantic HTML and reducing the DOM pollution with multiple nodes used only for forced layout.EuiFlexItem already has full support for polymorphism, but lacks a strong typing inference when the component element is passed, would be great to also include this as part of the work.
Describe the solution you'd like
Given the following example, it would be great if the
component
property could accept any HTML tag recognized as part of React.ReactHTML, and have it typed correctly depending on the specified component.Describe alternatives you've considered
The alternative I used so far has been nesting/wrapping the additional nodes where I couldn't replace them, which makes the DOM more verbose and less semantic.
Desired timeline
This could greatly impact and improve the DX for all the contributors writing React components, but it is not a high priority unless it is a quick win.
The text was updated successfully, but these errors were encountered: