Skip to content

Commit 951e56d

Browse files
authored
chore(compass-components): move options dropdown from compass-aggregations to compass-components (#3106)
1 parent 5f1e7cd commit 951e56d

File tree

4 files changed

+147
-34
lines changed

4 files changed

+147
-34
lines changed

packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-header/pipeline-actions.tsx

Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ import React from 'react';
22
import { connect } from 'react-redux';
33
import {
44
Button,
5+
MoreOptionsToggle,
56
css,
67
spacing,
7-
Link,
8-
Icon,
9-
uiColors,
108
} from '@mongodb-js/compass-components';
119
import type { RootState } from '../../../modules';
1210
import {
@@ -23,22 +21,6 @@ const containerStyles = css({
2321
alignItems: 'center',
2422
});
2523

26-
const optionsButtonStyles = css({
27-
backgroundColor: 'transparent',
28-
border: 'none',
29-
display: 'inline',
30-
height: spacing[4] + spacing[1],
31-
':focus': {
32-
outline: `${spacing[1]}px auto ${uiColors.focus}`,
33-
},
34-
});
35-
36-
const optionStyles = css({
37-
display: 'flex',
38-
alignItems: 'center',
39-
minWidth: '100px',
40-
});
41-
4224
type PipelineActionsProps = {
4325
showRunButton?: boolean;
4426
isRunButtonDisabled?: boolean;
@@ -74,13 +56,11 @@ export const PipelineActions: React.FunctionComponent<PipelineActionsProps> = ({
7456
onExportAggregationResults,
7557
onExplainAggregation,
7658
}) => {
77-
const optionsIcon = isOptionsVisible ? 'CaretDown' : 'CaretRight';
7859
const showExportButton =
7960
process?.env?.COMPASS_ENABLE_AGGREGATION_EXPORT === 'true' &&
8061
_showExportButton;
8162
const showExplainButton =
8263
process?.env?.COMPASS_ENABLE_AGGREGATION_EXPLAIN === 'true';
83-
const optionsLabel = isOptionsVisible ? 'Less Options' : 'More Options';
8464
return (
8565
<div className={containerStyles}>
8666
{showUpdateViewButton && (
@@ -131,22 +111,13 @@ export const PipelineActions: React.FunctionComponent<PipelineActionsProps> = ({
131111
Run
132112
</Button>
133113
)}
134-
<Link
135-
aria-label={optionsLabel}
136-
aria-expanded={isOptionsVisible}
114+
<MoreOptionsToggle
115+
isExpanded={!!isOptionsVisible}
137116
aria-controls="pipeline-options"
138117
id="pipeline-toolbar-options"
139-
as="button"
140-
className={optionsButtonStyles}
141118
data-testid="pipeline-toolbar-options-button"
142-
hideExternalIcon={true}
143-
onClick={onToggleOptions}
144-
>
145-
<div className={optionStyles}>
146-
{optionsLabel}
147-
<Icon glyph={optionsIcon} />
148-
</div>
149-
</Link>
119+
onToggleOptions={onToggleOptions}
120+
/>
150121
</div>
151122
);
152123
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import React from 'react';
2+
import { expect } from 'chai';
3+
import sinon from 'sinon';
4+
5+
import { fireEvent, render, screen, cleanup } from '@testing-library/react';
6+
7+
import { MoreOptionsToggle } from './more-options-toggle';
8+
9+
function renderMoreOptionsToggle(
10+
props?: Partial<React.ComponentProps<typeof MoreOptionsToggle>>
11+
) {
12+
return render(
13+
<MoreOptionsToggle
14+
isExpanded={false}
15+
aria-controls="test"
16+
onToggleOptions={() => {}}
17+
{...props}
18+
/>
19+
);
20+
}
21+
22+
describe('MoreOptionsToggle Component', function () {
23+
afterEach(function () {
24+
cleanup();
25+
});
26+
27+
it('should call to open the options dropdown on click', function () {
28+
const onToggleOptionsSpy = sinon.spy();
29+
renderMoreOptionsToggle({
30+
isExpanded: false,
31+
onToggleOptions: onToggleOptionsSpy,
32+
});
33+
34+
expect(onToggleOptionsSpy.calledOnce).to.be.false;
35+
const button = screen.getByText('More Options');
36+
fireEvent.click(button);
37+
expect(onToggleOptionsSpy.calledOnce).to.be.true;
38+
});
39+
40+
it('should call to close the options dropdown on click', function () {
41+
const onToggleOptionsSpy = sinon.spy();
42+
renderMoreOptionsToggle({
43+
isExpanded: true,
44+
onToggleOptions: onToggleOptionsSpy,
45+
});
46+
47+
expect(onToggleOptionsSpy.calledOnce).to.be.false;
48+
const button = screen.getByText('Less Options');
49+
fireEvent.click(button);
50+
expect(onToggleOptionsSpy.calledOnce).to.be.true;
51+
});
52+
53+
it('should the test id', function () {
54+
renderMoreOptionsToggle({
55+
'data-testid': 'pineapple',
56+
});
57+
58+
expect(screen.getByTestId('pineapple')).to.be.visible;
59+
});
60+
});
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React, { useMemo } from 'react';
2+
import { css } from '@leafygreen-ui/emotion';
3+
import { spacing } from '@leafygreen-ui/tokens';
4+
5+
import { useFocusRing } from '../hooks/use-focus-ring';
6+
import { Link, Icon } from './leafygreen';
7+
import { mergeProps } from '../utils/merge-props';
8+
9+
const optionContainerStyles = css({
10+
textAlign: 'center',
11+
minWidth: spacing[4] * 5,
12+
});
13+
14+
const optionsButtonStyles = css({
15+
// Reset button styles.
16+
backgroundColor: 'transparent',
17+
border: 'none',
18+
19+
padding: `${spacing[1]}px ${spacing[2]}px`,
20+
});
21+
22+
const optionStyles = css({
23+
display: 'flex',
24+
alignItems: 'center',
25+
});
26+
27+
type MoreOptionsToggleProps = {
28+
'aria-controls': string;
29+
'data-testid'?: string;
30+
isExpanded: boolean;
31+
onToggleOptions: () => void;
32+
id?: string;
33+
};
34+
35+
export const MoreOptionsToggle: React.FunctionComponent<MoreOptionsToggleProps> =
36+
({
37+
'aria-controls': ariaControls,
38+
isExpanded,
39+
id,
40+
'data-testid': dataTestId,
41+
onToggleOptions,
42+
}) => {
43+
const optionsIcon = useMemo(
44+
() => (isExpanded ? 'CaretDown' : 'CaretRight'),
45+
[isExpanded]
46+
);
47+
const optionsLabel = useMemo(
48+
() => (isExpanded ? 'Less Options' : 'More Options'),
49+
[isExpanded]
50+
);
51+
const focusRingProps = useFocusRing();
52+
const buttonProps = mergeProps(
53+
{
54+
className: optionsButtonStyles,
55+
},
56+
focusRingProps
57+
// We cast here so that the `as` prop of link can be properly typed.
58+
) as Partial<React.ComponentType<React.ComponentProps<typeof Link>>>;
59+
60+
return (
61+
<div className={optionContainerStyles}>
62+
<Link
63+
aria-label={optionsLabel}
64+
aria-expanded={isExpanded}
65+
aria-controls={ariaControls}
66+
as="button"
67+
hideExternalIcon={true}
68+
data-testid={dataTestId}
69+
id={id}
70+
onClick={onToggleOptions}
71+
{...buttonProps}
72+
>
73+
<div className={optionStyles}>
74+
{optionsLabel}
75+
<Icon glyph={optionsIcon} />
76+
</div>
77+
</Link>
78+
</div>
79+
);
80+
};

packages/compass-components/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import FileInput from './components/file-input';
1919
import { Modal } from './components/modal';
2020
import { ModalTitle } from './components/modal-title';
2121
import { Toolbar } from './components/toolbar';
22+
import { MoreOptionsToggle } from './components/more-options-toggle';
2223
import {
2324
ErrorSummary,
2425
WarningSummary,
@@ -67,6 +68,7 @@ export {
6768
FileInput,
6869
Modal,
6970
ModalTitle,
71+
MoreOptionsToggle,
7072
RadioBoxGroup,
7173
SpinLoader,
7274
ResizeHandle,

0 commit comments

Comments
 (0)