-
Notifications
You must be signed in to change notification settings - Fork 842
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
feat: EuiFlexGroup
and EuiFlexItem
component prop type improvements
#7688
Conversation
EuiFlexGroup
and EuiFlexItem
component prop type improvements
* Customize the component type that is rendered. | ||
* | ||
* It can be any valid React component type like a tag name string | ||
* such as `'div'` or `'span'`, a React component (a function, a class, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I generally like the idea of allowing to pass React components with regards that it helps to make it more scoped to what's actually used and how this is readable.
There might be limitations to this though:
The output might at times be a bit unexpected as it depends on the passed component where the props will be applied to.
<EuiFlexGroup
direction="column"
component={EuiButton}
>
<EuiFlexItem component="span">Foo</EuiFlexItem>
<EuiFlexItem component="span">Bar</EuiFlexItem>
</EuiFlexGroup>
This direction="column"
will be applied to the outer wrapper but EuiButton
places children in an inner wrapper with it's own flex behavior, meaning the direction change won't be applied or would need manual overwriting the inner styles from the outside.
Of course, there is no preventing users from passing more than a single component when using a wrapping component.
const someComponent = (props) => (
<>
<EuiSomeComponent {...props}>
...
</EuiSomeComponent>
<EuiSomeOtherComponent {...props}>
...
</EuiSomeOtherComponent>
</>
)
This could lead to some weird creations 😅
💭 Just some further ideas: Maybe providing those layout functionality as shared hooks could be an idea too? 🤔 That would keep the components more clean in what they are/do but enables shared behavior anyway. It would need a bigger lift to add those though.
const flexGroupStyles = useFlexGroup(options)
<EuiSomeComponent css={flexGroupStyles} />
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We recently explored shared hooks (or headless components), and I really like the general idea. However, this isn't a pattern we use right, and we'd need to be extra careful when introducing it to avoid duplicate functionality or—what's worse—having two almost exact things that do the same thing and confuse users.
There will be components where the component
prop won't work as good, and your EuiButton
example is a good example of it. We could consider passing EuiFlexGroup
/EuiFlexItem
props down to the child as an easy workaround 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you refer with "down to the child"
to the inner wrapper within EuiButton?
If so, I'm not sure that's overall a scalable solution? To pass those props along might differ between components and would need manual adjustment for any component where the spread ...rest
is on an unexpected level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I meant having something like this:
const EuiFlexGroup = (props) => {
[...]
return <Component {...props} />
}
instead of just {...rest}
, but that passes all props to the custom component even if they're unwanted, and would need to be limited to custom components only, since native HTML elements would convert props to DOM attributes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's up to the passed component
how it handles props. We shouldn't do anything special for the flex components to handle cases such as EuiButton
@@ -246,7 +246,7 @@ export const FlexExample = { | |||
), | |||
}, | |||
{ | |||
title: 'Spans instead of divs', | |||
title: 'Override output component type', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be an idea to add additional examples for variants of passing a custom component?
// props are all passed to the component
<EuiFlexGroup
component={EuiSomeComponent}
/>
// props need to be passed along to apply
const component = (props) => <EuiSomeComponent {...props} />
<EuiFlexGroup
component={component}
/>
As it would be important to pass the props
along for the flex styles to be applied it might be good to document it? Not sure how descriptive we need to be. 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be completely honest, I didn't want to spend too much time on these examples, considering we're moving to a new docs platform soon, and this isn't a major feature.
In my opinion, it should be pretty straightforward to the majority of React developers that wrapping a component in another component won't implicitly pass props down from EuiFlexGroup
two levels deep. If we wanted to support passing props to both ComponentType
and JSX elements, it would require a conditional cloneElement
call, and I'm not sure if that's something we'd be 100% happy to introduce
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair enough. I generally also think that should be clear to developers as is default JS/React but I don't yet know how descriptive we want to be 🙂
Regarding the JSX - I think that goes into the general direction around how should component
be, like how much are concerns separated. The EuiFlexGroup would not need to be able to be anything else in that case. But yes, then the component
would need to be cloned again 🙈
// current
<EuiFlexGroup
component={EuiButton}
...buttonProps
/>
// variant
<EuiFlexGroup
component={<EuiButton {...buttonProps} />}
...flexProps
/>
cc50871
to
8607f2e
Compare
); | ||
// Cast forwardRef return type to work with the generic TComponent type | ||
// and not fallback to implicit any typing | ||
export const EuiFlexGroup = forwardRef(EuiFlexGroupInternal) as < |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type cast (and the one below) is here because forwardRef
doesn't support creating generic components. The issue is described here if you wanna learn more :)
Preview staging links for this PR:
|
💚 Build Succeeded
History
cc @tkajtoch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚢 🐈⬛ Looks good to me as implementation. Any other potential adjustments we could think about once the need arises 👍
`v94.1.0-backport.0` ⏩ `v94.2.1-backport.0` _[Questions? Please see our Kibana upgrade FAQ.](https://github.com/elastic/eui/blob/main/wiki/eui-team-processes/upgrading-kibana.md#faq-for-kibana-teams)_ --- ## [`v94.2.1-backport.0`](https://github.com/elastic/eui/releases/v94.2.1-backport.0) **This is a backport release only intended for use by Kibana.** - Reverted the `EuiFlexGroup`/`EuiFlexItem` `component` prop feature due to Kibana typing issues ## [`v94.2.1`](https://github.com/elastic/eui/releases/v94.2.1) **Bug fixes** - Fixed an `EuiTabbedContent` edge case bug that occurred when updated with a completely different set of `tabs` ([#7713](elastic/eui#7713)) - Fixed the `@storybook/test` dependency to be listed in `devDependencies` and not `dependencies` ([#7719](elastic/eui#7719)) ## [`v94.2.0`](https://github.com/elastic/eui/releases/v94.2.0) - Updated `getDefaultEuiMarkdownPlugins()` to allow excluding the following plugins in addition to `tooltip`: ([#7676](elastic/eui#7676)) - `checkbox` - `linkValidator` - `lineBreaks` - `emoji` - Updated `EuiSelectable`'s `isPreFiltered` prop to allow passing a configuration object, which allows disabling search highlighting in addition to search filtering ([#7683](elastic/eui#7683)) - Updated `EuiFlexGroup` and `EuiFlexItem` prop types to support passing any valid React component type to the `component` prop and ensure proper type checking of the extra props forwarded to the `component`. ([#7688](elastic/eui#7688)) - Updated `EuiSearchBar` to allow the `@` special character in query string searches ([#7702](elastic/eui#7702)) - Added a new, optional `optionMatcher` prop to `EuiSelectable` and `EuiComboBox` allowing passing a custom option matcher function to these components and controlling option filtering for given search string ([#7709](elastic/eui#7709)) **Bug fixes** - Fixed an `EuiPageTemplate` bug where prop updates would not cascade down to child sections ([#7648](elastic/eui#7648)) - To cascade props down to the sidebar, `EuiPageTemplate` now explicitly requires using the `EuiPageTemplate.Sidebar` rather than `EuiPageSidebar` - Fixed `EuiFieldNumber`'s typing to accept an icon configuration shape ([#7666](elastic/eui#7666)) - Fixed `EuiFieldText` and `EuiFieldNumber` to render the correct paddings for icon shapes set to `side: 'right'` ([#7666](elastic/eui#7666)) - Fixed `EuiFieldText` and `EuiFieldNumber` to fully ignore `icon`/`prepend`/`append` when `controlOnly` is set to true ([#7666](elastic/eui#7666)) - Fixed `EuiColorPicker`'s input not setting the correct right padding for the number of icons displayed ([#7666](elastic/eui#7666)) - Visual fixes for `EuiRange`s with `showInput`: ([#7678](elastic/eui#7678)) - Longer `append`/`prepend` labels no longer cause a background bug - Inputs can no longer overwhelm the actual range in width - Fixed a visual text alignment regression in `EuiTableRowCell`s with the `row` header scope ([#7681](elastic/eui#7681)) - Fixed `toolTipProps` type on `EuiSuperUpdateButton` to use `Partial<EuiToolTipProps>` ([#7692](elastic/eui#7692)) - Fixes missing prop type for `popperProps` on `EuiDatePicker` ([#7694](elastic/eui#7694)) - Fixed a focus bug with `EuiDataGrid`s with `leadingControlColumns` when moving columns to the left/right ([#7701](elastic/eui#7701)) ([#7698](elastic/eui#7698)) - Fixed `EuiSuperDatePicker` to validate date string with respect of locale on `EuiAbsoluteTab`. ([#7705](elastic/eui#7705)) - Fixed a visual bug with `EuiSuperDatePicker`'s absolute tab on small mobile screens ([#7708](elastic/eui#7708)) - Fixed i18n of empty and loading state messages for the `FieldValueSelectionFilter` component ([#7718](elastic/eui#7718)) **Dependency updates** - Updated `@hello-pangea/dnd` to v16.6.0 ([#7599](elastic/eui#7599)) - Updated `remark-rehype` to v8.1.0 ([#7601](elastic/eui#7601)) **Accessibility** - Improved `EuiBasicTable` and `EuiInMemoryTable`'s selection checkboxes to have unique aria-labels per row ([#7672](elastic/eui#7672)) - Added `aria-valuetext` attributes to `EuiRange`s with tick labels for improved screen reader UX ([#7675](elastic/eui#7675)) - Updated `EuiAccordion` to keep focus on accordion trigger instead of moving to content on click/keypress ([#7696](elastic/eui#7696)) - Added `aria-disabled` attribute to `EuiHorizontalSteps` when status is "disabled" ([#7699](elastic/eui#7699)) --------- Co-authored-by: Tomasz Kajtoch <[email protected]>
Summary
This resolves #7612 by updating
EuiFlexGroup
andEuiFlexItem
to support dynamic prop type resolution with generics. It also cleans up the code and adds ref forwarding toEuiFlexItem
to keep these two consistent.The generic argument falls back to
'div'
to keep the API backward compatible in cases where a customer would import and use theEuiFlexGroupProps
orEuiFlexItemProps
type.There are a couple of type casts added to ensure TypeScript and IDEs can resolve the full complex type and provide meaningful code suggestions.
QA
EuiFlexGroup
/EuiFlexItem
and confirm the types are correctly resolved, suggesting and type checking props of the passedcomponent
.General checklist
@default
if default values are missing) and playground toggles