Skip to content

Commit

Permalink
feat(sidebar): disable clicking on the performance tab when unsupport…
Browse files Browse the repository at this point in the history
…ed in Atlas (#5235)

* feat(sidebar): disallow clicking on the performance tab when unsupported

* chore(sidebar): use the same behavior for adf; fix tests
  • Loading branch information
gribnoysup authored Dec 14, 2023
1 parent e85db71 commit 4bf2666
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 52 deletions.
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/compass-home/src/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const createDataService = () => ({
configuredKMSProviders() {
return [];
},
currentOp() {},
top() {},
on() {},
off() {},
removeListener() {},
Expand Down
1 change: 1 addition & 0 deletions packages/compass-sidebar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
"eslint": "^7.25.0",
"lodash": "^4.17.21",
"mocha": "^10.2.0",
"mongodb": "^6.3.0",
"mongodb-ns": "^2.4.0",
"nyc": "^15.1.0",
"prettier": "^2.7.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ function renderNavigationItems(
onAction={() => {
/* noop */
}}
showPerformanceItem={false}
showCreateDatabaseAction={true}
isPerformanceTabSupported={true}
onFilterChange={() => {
/* noop */
}}
Expand Down Expand Up @@ -59,4 +59,17 @@ describe('NavigationItems [Component]', function () {
expect(screen.queryByLabelText(createDatabaseText)).to.not.exist;
});
});

describe('when performance tab is not supported', function () {
it('renders disabled "Performance" navigation item', function () {
renderNavigationItems({
isPerformanceTabSupported: false,
});

expect(screen.getByRole('button', { name: 'Performance' })).to.exist;
expect(
screen.getByRole('button', { name: 'Performance' })
).to.have.attribute('aria-disabled', 'true');
});
});
});
152 changes: 102 additions & 50 deletions packages/compass-sidebar/src/components/navigation-items.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import { connect } from 'react-redux';
import {
useHoverState,
Expand All @@ -14,6 +14,9 @@ import {
PerformanceSignals,
Placeholder,
ContentWithFallback,
palette,
useDarkMode,
Tooltip,
} from '@mongodb-js/compass-components';
import { usePreference, withPreferences } from 'compass-preferences-model';
import type { ItemAction } from '@mongodb-js/compass-components';
Expand Down Expand Up @@ -117,17 +120,31 @@ const navigationItemLabel = css({
marginLeft: spacing[2],
});

const navigationItemDisabledDarkModeStyles = css({
'--item-color': palette.gray.dark1,
'--item-color-active': palette.gray.dark1,
'--item-bg-color-hover': 'var(--item-bg-color)',
});

const navigationItemDisabledLightModeStyles = css({
'--item-color': palette.gray.base,
'--item-color-active': palette.gray.base,
'--item-bg-color-hover': 'var(--item-bg-color)',
});

const navigationItemActionIcons = css({ color: 'inherit' });

export function NavigationItem<Actions extends string>({
isExpanded,
onAction,
onClick,
onClick: onButtonClick,
glyph,
label,
actions,
isActive,
showTooManyCollectionsInsight,
disabled: isButtonDisabled = false,
disabledMessage: buttonDisabledMessage,
}: {
isExpanded?: boolean;
onAction(actionName: Actions, ...rest: any[]): void;
Expand All @@ -137,17 +154,35 @@ export function NavigationItem<Actions extends string>({
actions?: ItemAction<Actions>[];
isActive: boolean;
showTooManyCollectionsInsight?: boolean;
disabled?: boolean;
disabledMessage?: string;
}) {
const darkMode = useDarkMode();
const showInsights = usePreference('showInsights', React);
const onClick = useCallback(() => {
if (isButtonDisabled) {
return;
}
onButtonClick();
}, [isButtonDisabled, onButtonClick]);
const [hoverProps] = useHoverState();
const focusRingProps = useFocusRing();
const defaultActionProps = useDefaultAction(onClick);

const navigationItemProps = mergeProps(
{
className: cx(navigationItem, isActive && activeNavigationItem),
className: cx(
navigationItem,
isActive && activeNavigationItem,
isButtonDisabled &&
(darkMode
? navigationItemDisabledDarkModeStyles
: navigationItemDisabledLightModeStyles)
),
role: 'button',
['aria-label']: label,
['aria-current']: isActive,
['aria-disabled']: isButtonDisabled,
tabIndex: 0,
},
hoverProps,
Expand All @@ -156,37 +191,52 @@ export function NavigationItem<Actions extends string>({
) as React.HTMLProps<HTMLDivElement>;

return (
<div {...navigationItemProps}>
<div className={itemWrapper}>
<div className={itemButtonWrapper}>
<Icon glyph={glyph} size="small"></Icon>
{isExpanded && <span className={navigationItemLabel}>{label}</span>}
</div>
{showInsights && isExpanded && showTooManyCollectionsInsight && (
<div className={signalContainerStyles}>
<SignalPopover
signals={PerformanceSignals.get('too-many-collections')}
></SignalPopover>
<Tooltip
align="right"
spacing={spacing[3]}
isDisabled={!isButtonDisabled || !buttonDisabledMessage}
trigger={({ children: tooltip, ...triggerProps }) => {
const props = mergeProps(triggerProps, navigationItemProps);
return (
<div {...props}>
<div className={itemWrapper}>
<div className={itemButtonWrapper}>
<Icon glyph={glyph} size="small"></Icon>
{isExpanded && (
<span className={navigationItemLabel}>{label}</span>
)}
{tooltip}
</div>
{showInsights && isExpanded && showTooManyCollectionsInsight && (
<div className={signalContainerStyles}>
<SignalPopover
signals={PerformanceSignals.get('too-many-collections')}
></SignalPopover>
</div>
)}
{!isButtonDisabled && isExpanded && actions && (
<ItemActionControls<Actions>
iconSize="small"
onAction={onAction}
data-testid="sidebar-navigation-item-actions"
actions={actions}
// This is what renders the "create database" action,
// the icons here should always be clearly visible,
// so we let the icon to inherit the foreground color of
// the text
isVisible={true}
iconClassName={navigationItemActionIcons}
collapseToMenuThreshold={3}
></ItemActionControls>
)}
<div className={cx('item-background', itemBackground)} />
</div>
</div>
)}
{isExpanded && actions && (
<ItemActionControls<Actions>
iconSize="small"
onAction={onAction}
data-testid="sidebar-navigation-item-actions"
actions={actions}
// This is what renders the "create database" action,
// the icons here should always be clearly visible,
// so we let the icon to inherit the foreground color of
// the text
isVisible={true}
iconClassName={navigationItemActionIcons}
collapseToMenuThreshold={3}
></ItemActionControls>
)}
<div className={cx('item-background', itemBackground)} />
</div>
</div>
);
}}
>
{buttonDisabledMessage}
</Tooltip>
);
}

Expand All @@ -201,8 +251,8 @@ const PlaceholderItem = ({ forLabel }: { forLabel: string }) => {
export function NavigationItems({
isReady,
isExpanded,
showCreateDatabaseAction = false,
showPerformanceItem = false,
showCreateDatabaseAction,
isPerformanceTabSupported,
onFilterChange,
onAction,
currentLocation,
Expand All @@ -211,8 +261,8 @@ export function NavigationItems({
}: {
isReady?: boolean;
isExpanded?: boolean;
showCreateDatabaseAction?: boolean;
showPerformanceItem?: boolean;
showCreateDatabaseAction: boolean;
isPerformanceTabSupported: boolean;
onFilterChange(regex: RegExp | null): void;
onAction(actionName: string, ...rest: any[]): void;
currentLocation: string | null;
Expand Down Expand Up @@ -272,16 +322,16 @@ export function NavigationItems({
label="My Queries"
isActive={currentLocation === 'My Queries'}
/>
{showPerformanceItem && (
<NavigationItem<''>
isExpanded={isExpanded}
onAction={onAction}
onClick={openPerformanceWorkspace}
glyph="Gauge"
label="Performance"
isActive={currentLocation === 'Performance'}
/>
)}
<NavigationItem<''>
isExpanded={isExpanded}
onAction={onAction}
onClick={openPerformanceWorkspace}
glyph="Gauge"
label="Performance"
isActive={currentLocation === 'Performance'}
disabled={!isPerformanceTabSupported}
disabledMessage="Performance metrics are not available for your deployment or to your database user"
/>
<NavigationItem<DatabasesActions>
isExpanded={isExpanded}
onAction={onAction}
Expand Down Expand Up @@ -321,9 +371,10 @@ const mapStateToProps = (
0
);

const isReady = ['ready', 'refreshing'].includes(
state.instance?.status ?? ''
);
const isReady =
['ready', 'refreshing'].includes(state.instance?.status ?? '') &&
state.isPerformanceTabSupported !== null;

const isDataLake = state.instance?.dataLake.isDataLake ?? false;
const isWritable = state.instance?.isWritable ?? false;

Expand All @@ -332,6 +383,7 @@ const mapStateToProps = (
showPerformanceItem: !isDataLake,
showCreateDatabaseAction: !isDataLake && isWritable && !preferencesReadOnly,
showTooManyCollectionsInsight: totalCollectionsCount > 10_000,
isPerformanceTabSupported: !isDataLake && !!state.isPerformanceTabSupported,
};
};

Expand Down
7 changes: 7 additions & 0 deletions packages/compass-sidebar/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { mongoDBInstanceLocator } from '@mongodb-js/compass-app-stores/provider'
import { dataServiceLocator } from 'mongodb-data-service/provider';
import type { DataService } from 'mongodb-data-service';
import type { MongoDBInstance } from 'mongodb-instance-model';
import type { LoggerAndTelemetry } from '@mongodb-js/compass-logging/provider';
import { createLoggerAndTelemetryLocator } from '@mongodb-js/compass-logging/provider';

function activate() {
// noop
Expand All @@ -21,6 +23,7 @@ export const CompassSidebarPlugin = registerHadronPlugin<
{
instance: () => MongoDBInstance;
dataService: () => DataService;
logger: () => LoggerAndTelemetry;
}
>(
{
Expand All @@ -32,10 +35,12 @@ export const CompassSidebarPlugin = registerHadronPlugin<
globalAppRegistry,
instance,
dataService,
logger,
}: {
globalAppRegistry: AppRegistry;
instance: MongoDBInstance;
dataService: DataService;
logger: LoggerAndTelemetry;
},
helpers: ActivateHelpers
) {
Expand All @@ -45,6 +50,7 @@ export const CompassSidebarPlugin = registerHadronPlugin<
instance,
dataService,
connectionInfo: initialConnectionInfo,
logger,
},
helpers
);
Expand All @@ -57,6 +63,7 @@ export const CompassSidebarPlugin = registerHadronPlugin<
{
instance: mongoDBInstanceLocator,
dataService: dataServiceLocator,
logger: createLoggerAndTelemetryLocator('COMPASS-SIDEBAR-UI'),
}
);

Expand Down
10 changes: 9 additions & 1 deletion packages/compass-sidebar/src/modules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ import isExpanded from './is-expanded';
import type { AppRegistry } from 'hadron-app-registry';
import type { DataServiceAction, DataServiceState } from './data-service';
import dataService from './data-service';
import type {
IsPerformanceTabSupportedState,
SetIsPerformanceTabSupportedAction,
} from './is-performance-tab-supported';
import isPerformanceTabSupported from './is-performance-tab-supported';

export interface RootState {
appRegistry: {
Expand All @@ -44,6 +49,7 @@ export interface RootState {
isDetailsExpanded: IsDetailsExpandedState;
isGenuineMongoDBVisible: IsGenuineMongoDBVisibleState;
isExpanded: IsExpandedState;
isPerformanceTabSupported: IsPerformanceTabSupportedState;
}

export type RootAction =
Expand All @@ -54,7 +60,8 @@ export type RootAction =
| ToggleIsDetailsExpandedAction
| IsGenuineMongoDBVisibleAction
| IsExpandedAction
| DataServiceAction;
| DataServiceAction
| SetIsPerformanceTabSupportedAction;

/**
* The reducer.
Expand All @@ -69,6 +76,7 @@ const reducer = combineReducers<RootState, RootAction>({
isDetailsExpanded,
isGenuineMongoDBVisible,
isExpanded,
isPerformanceTabSupported,
});

export default reducer;
Loading

0 comments on commit 4bf2666

Please sign in to comment.