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

feat: export TabPanel #6896

Merged
merged 37 commits into from
Jul 25, 2024
Merged

Conversation

bvandercar-vt
Copy link
Contributor

@bvandercar-vt bvandercar-vt commented Jul 5, 2024

Fixes #0000

Checklist

  • Includes tests
  • Update documentation

Changes proposed in this pull request:

Extract the tab panel wrapper from renderTabPanel to its own component that is exported. This is useful when rendering a tab panel when using Tabs in controlled mode.

@changelog-app
Copy link

changelog-app bot commented Jul 5, 2024

Generate changelog in packages/core/changelog/@unreleased

What do the change types mean?
  • feature: A new feature of the service.
  • improvement: An incremental improvement in the functionality or operation of the service.
  • fix: Remedies the incorrect behaviour of a component of the service in a backwards-compatible way.
  • break: Has the potential to break consumers of this service's API, inclusive of both Palantir services
    and external consumers of the service's API (e.g. customer-written software or integrations).
  • deprecation: Advertises the intention to remove service functionality without any change to the
    operation of the service itself.
  • manualTask: Requires the possibility of manual intervention (running a script, eyeballing configuration,
    performing database surgery, ...) at the time of upgrade for it to succeed.
  • migration: A fully automatic upgrade migration task with no engineer input required.

Note: only one type should be chosen.

How are new versions calculated?
  • ❗The break and manual task changelog types will result in a major release!
  • 🐛 The fix changelog type will result in a minor release in most cases, and a patch release version for patch branches. This behaviour is configurable in autorelease.
  • ✨ All others will result in a minor version release.

Type

  • Feature
  • Improvement
  • Fix
  • Break
  • Deprecation
  • Manual task
  • Migration

Description

feat: TabPanel

Check the box to generate changelog(s)

  • Generate changelog entry

@bvandercar-vt bvandercar-vt changed the title feat: TabPanel feat: export TabPanel Jul 9, 2024
Copy link
Contributor

@evansjohnson evansjohnson left a comment

Choose a reason for hiding this comment

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

Some comments here but I'm generally in support of this. There's not too much this component does, but it hooks up some accessibility roles that are otherwise liable to not be set.

Comment on lines +96 to +100
export function generateTabIds(parentId: TabId, tabId: TabId) {
return {
tabPanelId: `${Classes.TAB_PANEL}_${parentId}_${tabId}`,
tabTitleId: `${Classes.TAB}-title_${parentId}_${tabId}`,
} satisfies TabIdProps;
Copy link
Contributor

Choose a reason for hiding this comment

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

nice!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's not too much this component does, but it hooks up some accessibility roles that are otherwise liable to not be set.

Bingo, exactly!

@@ -0,0 +1,5 @@
type: improvement
fix:
description: '[core] a11y(Tooltip): wrap contents in "tooltip" aria role'
Copy link
Contributor

Choose a reason for hiding this comment

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

✂️

@@ -325,25 +326,19 @@ export class Tabs extends AbstractPureComponent<TabsProps, TabsState> {
}

private renderTabPanel = (tab: TabElement) => {
const { className, panel, id, panelClassName } = tab.props;
const { className, id, panel, panelClassName } = tab.props;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: no diff here please

Comment on lines 26 to 28
/**
* Used for setting `aria-hidden` prop.
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/**
* Used for setting `aria-hidden` prop.
*/
/**
* If true, will set required accessibility roles and set `display` to `none`.
*/

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm thinking maybe we just make this required if the expected usage is that one TabPanel is provided for each tab, like is done in the uncontrolled usage of Tabs. In your example you essentially share a TabPanel but I'm wondering if making this optional encourages that pattern.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure

packages/core/test/tabs/tabsTests.tsx Outdated Show resolved Hide resolved
import { type TabProps } from "./tab";
import { generateTabIds, type TabTitleProps } from "./tabTitle";

export interface TabPanelProps extends Pick<TabProps, "className" | "id" | "panel">, Pick<TabTitleProps, "parentId"> {
Copy link
Contributor

Choose a reason for hiding this comment

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

re panel - do we want to inherit the If omitted, no panel will be rendered for this tab. Can either be an element or a renderer. behavior?

if so we probably want something similar to https://github.com/palantir/blueprint/pull/6896/files#diff-cf85fdaa862aad83538297ecd6e3a3b6a5a3fee30bd8aaaea4a2294f462c7933R330-R332 where we early return if panel is undefined

Copy link
Contributor Author

@bvandercar-vt bvandercar-vt Jul 16, 2024

Choose a reason for hiding this comment

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

Yep. I had to remove TabPanel from the isomorphic tests then. 8f202ae

@evansjohnson evansjohnson self-assigned this Jul 15, 2024
import { type TabProps } from "./tab";
import { generateTabIds, type TabTitleProps } from "./tabTitle";

export interface TabPanelProps extends Pick<TabProps, "className" | "id" | "panel">, Pick<TabTitleProps, "parentId"> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we should also support the renderActiveTabPanelOnly prop here and do an early return when isHidden as a convenience without needing to set that up yourself and to keep the same feature set offered by uncontrolled tabs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If someone is using TabPanel and wants to render active tab panel only, they would be handling the visibility logic themselves outside of the TabPanel component.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But it wouldn't hurt anything to add your reccomendation as a bonus 🤷

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done fc9d945

@bvandercar-vt bvandercar-vt mentioned this pull request Jul 16, 2024
1 task
Copy link
Contributor

@evansjohnson evansjohnson left a comment

Choose a reason for hiding this comment

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

we're you able to figure out getting TabPanel to show up in the docs? just one more small suggestion otherwise curious to hear your thoughts on it then this one looks ready to go

{Utils.isFunction(panel) ? panel({ tabTitleId, tabPanelId }) : panel}
</div>
className={classNames(className, panelClassName)}
isHidden={id !== this.state.selectedTabId}
Copy link
Contributor

Choose a reason for hiding this comment

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

since any consumer would need to set this same logic up I wonder if we should have the prop be selectedTabId instead so that users can't do weird things like have multiple tab panels showing

Copy link
Contributor Author

@bvandercar-vt bvandercar-vt Jul 23, 2024

Choose a reason for hiding this comment

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

Eh the logic may not always be the same. Take, for example, this case where isHidden is always false because there's always only 1 TabPanel being rendered:

import * as React from "react";
import { Tab, Tabs, TabPanel, type TabId } from "@blueprintjs/core";
...

const TABS_PARENT_ID = React.useId();
const [selectedTabId, setSelectedTabId] = React.useState<TabId>("Home");

<Tabs
    id={TABS_PARENT_ID}
    onChange={setSelectedTabId}
    selectedTabId={selectedTabId}
>
    <Tab id="Home" title="Home" />
    <Tab id="Files" title="Files" />
</Tabs>
...
<TabPanel
    id={selectedTabId}
    isHidden={false}
    parentId={TABS_PARENT_ID}
    panel={<p>The current panel id is: "{selectedTabId}"</p>}
/>

But then again, if we changed it to selectedTabId they could just have it be

<TabPanel
    id={selectedTabId}
    selectedTabId={selectedTabId}
    parentId={TABS_PARENT_ID}
    panel={<p>The current panel id is: "{selectedTabId}"</p>}
/>

I'm down for either, let me know which you like better.

Copy link
Contributor

Choose a reason for hiding this comment

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

yea I think I'm leaning towards selectedTabId because then we control more of the implementation - I'm worried about trying to use the same TabPanel for multiple tabs causing issues down the line somehow

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done 12a665b

@bvandercar-vt
Copy link
Contributor Author

we're you able to figure out getting TabPanel to show up in the docs?

Just got that done!

Comment on lines +168 to +169
id={this.state.navbarTabId}
selectedTabId={this.state.navbarTabId}
Copy link
Contributor

Choose a reason for hiding this comment

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

lol I guess I didn't see this coming where id could just also be dynamic 🤷

that's fine I can't think of an easy way to prevent this and maybe it is okay. if we ever actually see an accessibility issue with changing the id dynamically we can update the docs at that time but no need to go searching for if this would be an issue

packages/core/src/components/tabs/tabPanel.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

@evansjohnson evansjohnson left a comment

Choose a reason for hiding this comment

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

LGTM - thanks for the contribution and getting the docs updated! 🎉

@evansjohnson evansjohnson enabled auto-merge (squash) July 25, 2024 16:38
@evansjohnson evansjohnson merged commit 6491c01 into palantir:develop Jul 25, 2024
5 of 9 checks passed
@bvandercar-vt bvandercar-vt deleted the bvandercar/tabPanel branch November 7, 2024 19:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants