Skip to content
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

[EuiCollapsibleNavBeta] Various API updates and cleanups #7228

Merged
merged 7 commits into from
Sep 27, 2023

Conversation

cee-chen
Copy link
Member

@cee-chen cee-chen commented Sep 26, 2023

Summary

This PR does multiple different things:

Removes isGroupTitle logic in favor of a wildcard renderItem (c12fb27)

Old prop API:

<EuiCollapsibleNavItem items={[
  { isGroupTitle: true, title: 'something' },
}] />

New prop API:

<EuiCollapsibleNavItem items={[
  { renderItem: () => <EuiTitle size="xxxs"><div>something</EuiTitle></div> },
  { renderItem: () => <EuiSpacer size="m" /> },
  { renderItem: WhateverOtherCustomComponentYouWant },
}] />

I'm opting for this API over the old one because 1. it's simpler code and type-wise, and 2. it places the onus of consistent title and spacing styling directly on Kibana and not on EUI, and 3. it's a potential escape hatch for future customization (e.g. a button that toggles another flyout).

This new API should address elastic/kibana#167326

Adds EuiCollapsibleNav.[SubComponent] exports (3291371)

Old API:

<EuiCollapsibleNavBeta>
  <EuiFlyoutBody>
    <EuiCollapsibleNavItem />
  </EuiFlyoutBody>
  <EuiFlyoutFooter>
    <EuiCollapsibleNavItem />
  </EuiFlyoutFooter>
</EuiCollapsibleNavBeta>

New API:

<EuiCollapsibleNavBeta>
  <EuiCollapsibleNavBeta.Body>
    <EuiCollapsibleNavBeta.Item />
  </EuiCollapsibleNavBeta.Body>
  <EuiCollapsibleNavBeta.Footer>
    <EuiCollapsibleNavBeta.Item />
  </EuiCollapsibleNavBeta.Footer>
</EuiCollapsibleNavBeta>
// Note: For ease of migration, `EuiCollapsibleNavItem` is still available as a standalone export

The goal of this architecture is to 1. reduce separate imports, 2. more clearly scope subcomponents only intended to be used within the new EuiCollapsibleNavBeta component, and 3. match existing component architecture for template-level components, e.g. EuiPageTemplate.Section.

Adds new EuiCollapsibleNavBeta.Group top level component (#7208)

New API:

<EuiCollapsibleNavBeta>
  <EuiCollapsibleNavBeta.Body>
    <EuiCollapsibleNavBeta.Group
      title="Some solution"
      icon="logoElastic"
      items={[ ... ]}
    />
  </EuiCollapsibleNavBeta.Body>
</EuiCollapsibleNavBeta>

Please note that this new component DOES NOT address elastic/kibana#167323 and is not meant to. This new component, per Ryan's designs specifications and screencaps in #7208, is ONLY meant to be used as a top-level component (not as a subitem or in the footer), and has very specific styling and considerations for the desktop docked state.

I'd like to discuss the potential Kibana-side options for elastic/kibana#167323 more before implementing this at the EUI level. bc1e8a2 is one example of something Kibana could do with no intervention from EUI.

QA

General checklist

  • Browser QA
    • Checked in both light and dark modes
    • Checked in mobile
    • Checked in Chrome, Safari, Edge, and Firefox
      - [ ] Checked for accessibility including keyboard-only and screenreader modes
  • Docs site QA - N/A, beta component
  • Code quality checklist
  • Release checklist - N/A, beta component

+ provide Storybook examples of custom Kibana usage
@cee-chen cee-chen changed the title [EuiCollapsibleNavBeta] Various Kibana-requested updates [EuiCollapsibleNavBeta] Various API updates and cleanups Sep 26, 2023
- split it out from `EuiCollapsibleNavAccordion`
- instead of relying on consumers to use `EuiFlyoutBody` and `EuiFlyoutFooter` directly
@cee-chen cee-chen marked this pull request as ready for review September 27, 2023 00:45
@cee-chen cee-chen requested a review from a team as a code owner September 27, 2023 00:45
@cee-chen
Copy link
Member Author

@tsullivan @sebelga - I threw a quick sync on your calendars for tomorrow morning to quickly chat about the proposed API changes in the PR description and to make sure they actually meet your team's needs. Feel free to leave any comments/questions ahead of time for discussion.

- a la `EuiPageTemplate`

+ update stories to use namespaced components instead of direct imports

+ add stories for the new group component
@sebelga
Copy link
Contributor

sebelga commented Sep 27, 2023

Thanks for putting this together!

Some thoughts:

// instead of this...
<EuiCollapsibleNavItem items={[
  { renderItem: () => <EuiTitle size="xxxs"><div>something</EuiTitle></div> },
  { renderItem: () => <EuiSpacer size="m" /> },
  { renderItem: WhateverOtherCustomComponentYouWant },
}] />
// Why not this? ...
<EuiCollapsibleNavItem>
  <EuiTitle size="xxxs"><div>something</EuiTitle>
  <EuiSpacer size="m" />
  <WhateverOtherCustomComponentYouWant />
<EuiCollapsibleNavItem>

I just love declarative syntax 😊

You should be able to get the items with

import { Children } from 'react';

...

const EuiCollapsibleNavItem = () => {
   ...
   const items = Children.toArray(children) as React.ReactElement[];
}

For the EuiCollapsibleNavBeta.Group the same would apply. Instead of having a items prop we would have

<EuiCollapsibleNavBeta.Group title="myGroup">
  <EuiCollapsibleNavBeta.Item>
    <div>Any content>
  </EuiCollapsibleNavBeta.Item>
  <EuiCollapsibleNavBeta.Item>
    <div>Any content>
  </EuiCollapsibleNavBeta.Item>
</EuiCollapsibleNavBeta.Group title="myGroup">

If need be you can filter and only get those EuiCollapsibleNavItem and discard the other children

const allChildren = Children.toArray(children) as React.ReactElement[];
const onlyItemChildren = allChildren.filter((child) => child.type === EuiCollapsibleNavItem);

And solving elastic/kibana#167323 would be one step away 😊

<EuiCollapsibleNavBeta.Group title="myGroup" wrapInAccordion={false}>
  <EuiCollapsibleNavBeta.Item>
    <div>Any content>
  </EuiCollapsibleNavBeta.Item>
  ...
</EuiCollapsibleNavBeta.Group title="myGroup">

@cee-chen
Copy link
Member Author

cee-chen commented Sep 27, 2023

Using children isn't efficient with the underlying logic/concerns that EuiCollapsibleNavItem handles. Under the hood, the component is essentially doing this:

  • Am I a basic link, or am an accordion (do I contain child items)?
  • If so, render the appropriate component (either a EuiCollapsibleNavLink or EuiCollapsibleNavAccordion, both of which contain their own separate complex internal logic)

Using children for that is incredibly fragile for multiple reasons:

  • You need to analyze the name/type of the child component to see if it's the "correct" type, which fails if the consumer renames the component (e.g. adds another component wrapper around the component, or uses styled syntax, etc)
  • You need to recursively dive into grandchildren in case the consumer does something like <div class="wrapper"><TheActualChildren /></div>, otherwise your logic fails (either checking type or count)

EUI has had to face this kind of API decision before (whether to use a specific prop for a limited set of child types, or to use children) and typically trying to use children, especially for stricter requirements, is a massive headache for everyone involved. I don't see the benefit here of allowing wildcard children in terms of developer time spent writing and testing the new architecture, when EUI has other higher priorities. That being said, if Kibana/Shared UX would like to take these beta components and move them to Kibana, that's certainly an option, as these components have not been publicly released on EUI and can be removed at any time

- They're using the `Body` component now anyway which handles scrolling, and they need to be able to display security flyouts outside the nav
@kibanamachine
Copy link

Preview staging links for this PR:

@elasticmachine
Copy link
Collaborator

💚 Build Succeeded

History

Copy link
Contributor

@1Copenut 1Copenut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I have to say, Storybook feels like the piece that binds these big PRs together. Being able to review and edit data structures in real time is so helpful.

I like this sub-component approach a lot. Namespacing makes it clear what belongs to what.

// Since this is a nav, we can almost guarantee there will be clickable
// children/links that will enable scrolling. As such, we're optimistically
// removing the extra tab stop
scrollableTabIndex={-1}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After a couple of hours thinking about this, I agree with this thinking. The component is designed to house focusable links, so users should have no issue scrolling.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for sanity-checking this Trevor! FWIW it's overrideable if necessary as well, for edge cases, e.g. <EuiCollapsibleNavBeta.Body scrollableTabIndex={0}>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic. Sensible defaults and overrides where necessary.

@cee-chen cee-chen merged commit 507bd17 into elastic:main Sep 27, 2023
7 checks passed
@cee-chen cee-chen deleted the collapsible-nav-beta-updates branch September 27, 2023 18:05
@sebelga
Copy link
Contributor

sebelga commented Sep 28, 2023

@cee-chen I made a mistake in my explanation but that's ok, your API aligns with other EUI APIs (e.g. Tabs) so it is consistent 😊

I still prefer a declarative API

<SomeGroupElement title="Accordion title">
  {/* Render custom content */}
  <SomeGroupElement.Item id="foo">
    <div>I can render anything I want</div>
  </SomeGroupElement.Item>

  {/* Use the default UI */}
  <SomeGroupElement.Item id="bar" title="I use the default UI" icon="someIcon" />
</SomeGroupElement>

than using props

<SomeGroupElement title="Accordion title"
  items={[{
    id: "foo",
    renderItem: () => <div>I can render anything I want</div>
  }, {
    id:"bar",
    title:"I use the default UI",
    icon: "someIcon"
  }]}
/>

The former feels more "react" and declarative to me, but that's just a preference.
Also, with the first implementation I should be able to do this and still render an accordion with all the children inside.

<SomeGroupElement title="Accordion title">
  {/* Render custom content */}
  <SomeGroupElement.Item id="foo">
    <div>I can render anything I want</div>
  </SomeGroupElement.Item>
  
  {/* I need to add all this in my accordion! :) */}
  <EuiSpacer size="l" />
  <img src="/why-not-an-image.jpg" />
  <div>Those JSX are just rendered as they appear, total freedom</div>

  {/* ...back to nav items... */}
  <SomeGroupElement.Item id="bar" title="I use the default UI" icon="someIcon" />
</SomeGroupElement>

I am not trying to convince you to change anything though! 😊 Just sharing what a declarative API allows vs using props.

cee-chen added a commit to elastic/kibana that referenced this pull request Oct 4, 2023
`v88.5.0`⏩`v88.5.4`

This EUI upgrade helps unblock the Shared UX team with some beta
serverless nav updates not listed in the below changelog
(elastic/eui#7228 and
elastic/eui#7248).

---

## [`88.5.4`](https://github.com/elastic/eui/tree/v88.5.4)

- This release contains internal changes to a beta component needed by
Kibana.

## [`88.5.3`](https://github.com/elastic/eui/tree/v88.5.3)

**Bug fixes**

- Fixed `EuiComboBox` search input width not resetting correctly on
selection ([#7240](elastic/eui#7240))

## [`88.5.2`](https://github.com/elastic/eui/tree/v88.5.2)

**Bug fixes**

- Fixed broken `EuiTextTruncate` testenv mocks
([#7234](elastic/eui#7234))

## [`88.5.1`](https://github.com/elastic/eui/tree/v88.5.1)

- Improved the performance of `EuiComboBox` by removing the
`react-autosizer-input` dependency
([#7215](elastic/eui#7215))

**Dependency updates**

- Updated `react-element-to-jsx-string` to v5.0.0
([#7214](elastic/eui#7214))
- Removed unused `@types/vfile-message` dependency
([#7214](elastic/eui#7214))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[EuiCollapsibleNavBeta] Add support for a non-clickable, non-collapsible group heading
5 participants