-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
feat: export TabPanel #6896
Conversation
Generate changelog in
|
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.
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.
export function generateTabIds(parentId: TabId, tabId: TabId) { | ||
return { | ||
tabPanelId: `${Classes.TAB_PANEL}_${parentId}_${tabId}`, | ||
tabTitleId: `${Classes.TAB}-title_${parentId}_${tabId}`, | ||
} satisfies TabIdProps; |
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.
nice!
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.
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' |
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.
✂️
@@ -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; |
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.
nit: no diff here please
/** | ||
* Used for setting `aria-hidden` prop. | ||
*/ |
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.
/** | |
* Used for setting `aria-hidden` prop. | |
*/ | |
/** | |
* If true, will set required accessibility roles and set `display` to `none`. | |
*/ |
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'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.
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.
sure
import { type TabProps } from "./tab"; | ||
import { generateTabIds, type TabTitleProps } from "./tabTitle"; | ||
|
||
export interface TabPanelProps extends Pick<TabProps, "className" | "id" | "panel">, Pick<TabTitleProps, "parentId"> { |
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.
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
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.
Yep. I had to remove TabPanel
from the isomorphic tests then. 8f202ae
import { type TabProps } from "./tab"; | ||
import { generateTabIds, type TabTitleProps } from "./tabTitle"; | ||
|
||
export interface TabPanelProps extends Pick<TabProps, "className" | "id" | "panel">, Pick<TabTitleProps, "parentId"> { |
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 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
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.
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.
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.
But it wouldn't hurt anything to add your reccomendation as a bonus 🤷
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.
done fc9d945
…t/blueprint into bvandercar/tabPanel
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'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} |
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.
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
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.
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.
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.
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
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.
Done 12a665b
Just got that done! |
id={this.state.navbarTabId} | ||
selectedTabId={this.state.navbarTabId} |
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.
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
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.
LGTM - thanks for the contribution and getting the docs updated! 🎉
Fixes #0000
Checklist
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 usingTabs
in controlled mode.