Skip to content

Commit 2ba1a10

Browse files
feat(content-sidebar): Option to render Box AI nav button disabled (#3831)
* feat(content-sidebar): Option to render Box AI nav button disabled * feat(content-sidebar): Option to render Box AI nav button disabled * feat(content-sidebar): Option to render Box AI nav button disabled * feat(content-sidebar): Option to render Box AI nav button disabled
1 parent 5201382 commit 2ba1a10

File tree

8 files changed

+174
-33
lines changed

8 files changed

+174
-33
lines changed

src/elements/common/nav-button/NavButton.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type Props = {
1818
component?: React.ComponentType<any>,
1919
exact?: boolean,
2020
isActive?: (match: Match, location: Location) => ?boolean,
21+
isDisabled?: boolean,
2122
onClick?: (event: SyntheticEvent<>) => void,
2223
replace?: boolean,
2324
strict?: boolean,
@@ -32,6 +33,7 @@ const NavButton = React.forwardRef<Props, React.Ref<any>>((props: Props, ref: Re
3233
component: Component = PlainButton,
3334
exact,
3435
isActive,
36+
isDisabled,
3537
onClick,
3638
replace,
3739
strict,
@@ -40,14 +42,20 @@ const NavButton = React.forwardRef<Props, React.Ref<any>>((props: Props, ref: Re
4042
} = props;
4143
const path = typeof to === 'object' ? to.pathname : to;
4244

45+
const disabledClassName = 'bdl-is-disabled';
46+
4347
return (
4448
<Route exact={exact} path={path} strict={strict}>
4549
{({ history, location, match }) => {
4650
const isActiveValue = !!(isActive ? isActive(match, location) : match);
4751

4852
return (
4953
<Component
50-
className={classNames(className, { [activeClassName]: isActiveValue })}
54+
className={classNames(className, {
55+
[activeClassName]: isActiveValue,
56+
[disabledClassName]: isDisabled,
57+
})}
58+
isDisabled={isDisabled}
5159
onClick={event => {
5260
if (onClick) {
5361
onClick(event);

src/elements/common/nav-button/__tests__/NavButton.test.js

+18
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import * as React from 'react';
22
import { mount, render } from 'enzyme';
33
import { MemoryRouter, Router } from 'react-router-dom';
4+
import { render as rtlRender, screen } from '../../../../test-utils/testing-library';
45
import NavButton from '..';
56

67
describe('elements/common/nav-button/NavButton', () => {
8+
const getNavButton = (content, { path = '/activity', ...props }) => (
9+
<MemoryRouter initialEntries={[path]}>
10+
<NavButton to={path} {...props}>
11+
{content}
12+
</NavButton>
13+
</MemoryRouter>
14+
);
15+
716
describe('when active', () => {
817
test('applies its default activeClassName', () => {
918
const button = render(
@@ -54,6 +63,15 @@ describe('elements/common/nav-button/NavButton', () => {
5463
});
5564
});
5665

66+
describe('when disabled', () => {
67+
test('applies bdl-is-disabled class name', () => {
68+
const content = 'Activity';
69+
rtlRender(getNavButton(content, { isDisabled: true }));
70+
71+
expect(screen.getByText(content, { selector: '.bdl-is-disabled' })).toBeInTheDocument();
72+
});
73+
});
74+
5775
describe('exact', () => {
5876
test('does not do exact matching by default', () => {
5977
const button = render(

src/elements/content-sidebar/SidebarNav.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ const SidebarNav = ({
6868
onPanelChange = noop,
6969
}: Props) => {
7070
const { enabled: hasBoxSign } = useFeatureConfig('boxSign');
71+
const { disabledTooltip: boxAIDisabledTooltip, showOnlyNavButton: showOnlyBoxAINavButton } =
72+
useFeatureConfig('boxai.sidebar');
7173

7274
const handleSidebarNavButtonClick = (sidebarview: string) => {
7375
onPanelChange(sidebarview, false);
@@ -82,9 +84,14 @@ const SidebarNav = ({
8284
data-resin-target={SIDEBAR_NAV_TARGETS.BOXAI}
8385
data-target-id="SidebarNavButton-boxAI"
8486
data-testid="sidebarboxai"
87+
isDisabled={showOnlyBoxAINavButton}
8588
onClick={handleSidebarNavButtonClick}
8689
sidebarView={SIDEBAR_VIEW_BOXAI}
87-
tooltip={intl.formatMessage(messages.sidebarBoxAITitle)}
90+
tooltip={
91+
showOnlyBoxAINavButton
92+
? boxAIDisabledTooltip
93+
: intl.formatMessage(messages.sidebarBoxAITitle)
94+
}
8895
>
8996
<BoxAiLogo height={Size5} width={Size5} />
9097
</SidebarNavButton>

src/elements/content-sidebar/SidebarNavButton.js

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type Props = {
1616
'data-testid'?: string,
1717
children: React.Node,
1818
elementId?: string,
19+
isDisabled?: boolean,
1920
isOpen?: boolean,
2021
onClick?: (sidebarView: string) => void,
2122
sidebarView: string,
@@ -28,6 +29,7 @@ const SidebarNavButton = React.forwardRef<Props, React.Ref<any>>((props: Props,
2829
'data-testid': dataTestId,
2930
children,
3031
elementId = '',
32+
isDisabled,
3133
isOpen,
3234
onClick = noop,
3335
sidebarView,
@@ -61,6 +63,7 @@ const SidebarNavButton = React.forwardRef<Props, React.Ref<any>>((props: Props,
6163
getDOMRef={ref}
6264
id={id}
6365
isActive={isActive}
66+
isDisabled={isDisabled}
6467
onClick={handleNavButtonClick}
6568
replace={isExactMatch}
6669
role="tab"

src/elements/content-sidebar/SidebarPanels.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import SidebarUtils from './SidebarUtils';
1313
import withSidebarAnnotations from './withSidebarAnnotations';
1414
import { withAnnotatorContext } from '../common/annotator-context';
1515
import { withAPIContext } from '../common/api-context';
16-
import { withFeatureConsumer, isFeatureEnabled } from '../common/feature-checking';
16+
import { getFeatureConfig, withFeatureConsumer, isFeatureEnabled } from '../common/feature-checking';
1717
import { withRouterAndRef } from '../common/routing';
1818
import {
1919
ORIGIN_ACTIVITY_SIDEBAR,
@@ -237,8 +237,12 @@ class SidebarPanels extends React.Component<Props, State> {
237237
const isMetadataSidebarRedesignEnabled = isFeatureEnabled(features, 'metadata.redesign.enabled');
238238
const isMetadataAiSuggestionsEnabled = isFeatureEnabled(features, 'metadata.aiSuggestions.enabled');
239239

240+
const { showOnlyNavButton: showOnlyBoxAINavButton } = getFeatureConfig(features, 'boxai.sidebar');
241+
242+
const canShowBoxAISidebarPanel = hasBoxAI && !showOnlyBoxAINavButton;
243+
240244
const panelsEligibility = {
241-
[SIDEBAR_VIEW_BOXAI]: hasBoxAI,
245+
[SIDEBAR_VIEW_BOXAI]: canShowBoxAISidebarPanel,
242246
[SIDEBAR_VIEW_DOCGEN]: hasDocGen,
243247
[SIDEBAR_VIEW_SKILLS]: hasSkills,
244248
[SIDEBAR_VIEW_ACTIVITY]: hasActivity,
@@ -254,7 +258,7 @@ class SidebarPanels extends React.Component<Props, State> {
254258

255259
return (
256260
<Switch>
257-
{hasBoxAI && (
261+
{canShowBoxAISidebarPanel && (
258262
<Route
259263
exact
260264
path={`/${SIDEBAR_VIEW_BOXAI}`}
@@ -437,7 +441,7 @@ class SidebarPanels extends React.Component<Props, State> {
437441

438442
if (showDefaultPanel) {
439443
redirect = defaultPanel;
440-
} else if (hasBoxAI) {
444+
} else if (canShowBoxAISidebarPanel) {
441445
redirect = SIDEBAR_VIEW_BOXAI;
442446
} else if (hasDocGen) {
443447
redirect = SIDEBAR_VIEW_DOCGEN;

src/elements/content-sidebar/__tests__/SidebarNav.test.js

+51
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as React from 'react';
22
import { MemoryRouter } from 'react-router-dom';
3+
import userEvent from '@testing-library/user-event';
34
import { mount } from 'enzyme';
45
import { BoxAiLogo } from '@box/blueprint-web-assets/icons/Logo';
56
import AdditionalTabPlaceholder from '../additional-tabs/AdditionalTabPlaceholder';
@@ -14,6 +15,7 @@ import IconMetadataThick from '../../../icons/general/IconMetadataThick';
1415
import SidebarNav from '../SidebarNav';
1516
import SidebarNavButton from '../SidebarNavButton';
1617
import SidebarNavSignButton from '../SidebarNavSignButton';
18+
import { render, screen } from '../../../test-utils/testing-library';
1719

1820
describe('elements/content-sidebar/SidebarNav', () => {
1921
const getWrapper = (props = {}, active = '', features = {}) =>
@@ -27,6 +29,14 @@ describe('elements/content-sidebar/SidebarNav', () => {
2729
.find('SidebarNav')
2830
.at(1);
2931

32+
const getSidebarNav = ({ path = '/', props, features }) => (
33+
<MemoryRouter initialEntries={[path]}>
34+
<FeatureProvider features={features}>
35+
<SidebarNav {...props} />
36+
</FeatureProvider>
37+
</MemoryRouter>
38+
);
39+
3040
test('should render skills tab', () => {
3141
const props = {
3242
hasSkills: true,
@@ -87,6 +97,47 @@ describe('elements/content-sidebar/SidebarNav', () => {
8797
expect(wrapper.find(IconChatRound)).toHaveLength(0);
8898
});
8999

100+
describe('should render box ai tab with correct disabled state and tooltip', () => {
101+
test.each`
102+
disabledTooltip | expectedTooltip
103+
${'tooltip msg'} | ${'tooltip msg'}
104+
${'another tooltip msg'} | ${'another tooltip msg'}
105+
`(
106+
'given feature boxai.sidebar.showOnlyNavButton = true and boxai.sidebar.disabledTooltip = $disabledTooltip, should render box ai tab with disabled state and tooltip = $expectedTooltip',
107+
async ({ disabledTooltip, expectedTooltip }) => {
108+
render(
109+
getSidebarNav({
110+
features: { boxai: { sidebar: { disabledTooltip, showOnlyNavButton: true } } },
111+
props: { hasBoxAI: true },
112+
}),
113+
);
114+
115+
const button = screen.getByTestId('sidebarboxai');
116+
117+
await userEvent.hover(button);
118+
119+
expect(button).toHaveAttribute('aria-disabled', 'true');
120+
expect(screen.getByText(expectedTooltip)).toBeInTheDocument();
121+
},
122+
);
123+
124+
test('given feature boxai.sidebar.showOnlyNavButton = false, should render box ai tab with default tooltip', async () => {
125+
render(
126+
getSidebarNav({
127+
features: { boxai: { sidebar: { showOnlyNavButton: false } } },
128+
props: { hasBoxAI: true },
129+
}),
130+
);
131+
132+
const button = screen.getByTestId('sidebarboxai');
133+
134+
await userEvent.hover(button);
135+
136+
expect(button).not.toHaveAttribute('aria-disabled');
137+
expect(screen.getByText('Box AI')).toBeInTheDocument();
138+
});
139+
});
140+
90141
test('should have multiple tabs', () => {
91142
const props = {
92143
hasActivity: true,

0 commit comments

Comments
 (0)