Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions packages/compass-components/src/components/accordion.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ describe('Accordion Component', function () {
renderAccordion();

expect(screen.getByTestId('my-test-id')).to.exist;
const button = screen.getByText('Accordion Test');
userEvent.click(button);
expect(screen.getByText('Hello World')).to.be.visible;
const summary = screen.getByText('Accordion Test');
userEvent.click(summary);
expect(summary.closest('details')).to.have.attribute('open');
});

it('should close the accordion on click - default open', function () {
Expand All @@ -31,22 +31,22 @@ describe('Accordion Component', function () {
});

expect(screen.getByTestId('my-test-id')).to.exist;
const button = screen.getByText('Accordion Test');
const summary = screen.getByText('Accordion Test');
expect(screen.getByText('Hello World')).to.be.visible;
userEvent.click(button);
userEvent.click(summary);

expect(screen.queryByText('Hello World')).not.to.exist;
expect(summary.closest('details')).to.not.have.attribute('open');
});

it('should close the accordion after clicking to open then close', function () {
renderAccordion();

expect(screen.getByTestId('my-test-id')).to.exist;
const button = screen.getByText('Accordion Test');
userEvent.click(button);
expect(screen.getByText('Hello World')).to.be.visible;
userEvent.click(button);
expect(screen.queryByText('Hello World')).to.not.exist;
const summary = screen.getByText('Accordion Test');
userEvent.click(summary);
expect(summary.closest('details')).to.have.attribute('open');
userEvent.click(summary);
expect(summary.closest('details')).to.not.have.attribute('open');
});

it('should show a hint', function () {
Expand Down
89 changes: 38 additions & 51 deletions packages/compass-components/src/components/accordion.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import React, { useCallback, useState } from 'react';
import React, { useCallback } from 'react';
import { spacing } from '@leafygreen-ui/tokens';
import { css, cx } from '@leafygreen-ui/emotion';
import { palette } from '@leafygreen-ui/palette';
import { useId } from '@react-aria/utils';
import { useDarkMode } from '../hooks/use-theme';

import { Description, Icon } from './leafygreen';
import { useCurrentValueRef } from '../hooks/use-current-value-ref';

const buttonStyles = css({
const summaryStyles = css({
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
Expand All @@ -28,7 +27,7 @@ const buttonStyles = css({
},
});

const buttonVariantStyles = {
const summaryVariantStyles = {
default: css({
fontSize: 14,
lineHeight: `${spacing[500]}px`,
Expand All @@ -44,105 +43,93 @@ const iconVariantSizes = {
small: 14,
};

const buttonLightThemeStyles = css({
const summaryLightThemeStyles = css({
color: palette.gray.dark2,
});

const buttonDarkThemeStyles = css({
const summaryDarkThemeStyles = css({
color: palette.white,
});

const buttonIconContainerStyles = css({
const summaryIconContainerStyles = css({
fontSize: 0,
lineHeight: 0,
padding: 0,
paddingRight: spacing[150],
'details[open] > summary & svg': {
transform: 'rotate(90deg)',
},
});

const buttonTextStyles = css({
const summaryTextStyles = css({
textAlign: 'left',
});

const buttonHintStyles = css({
const summaryHintStyles = css({
margin: 0,
marginLeft: spacing[100],
padding: 0,
display: 'inline',
});

interface AccordionProps
extends Omit<React.HTMLProps<HTMLButtonElement>, 'size'> {
extends Omit<React.HTMLProps<HTMLDetailsElement>, 'size'> {
text: string | React.ReactNode;
hintText?: string;
textClassName?: string;
buttonTextClassName?: string;
open?: boolean;
summaryTextClassName?: string;
defaultOpen?: boolean;
setOpen?: (newValue: boolean) => void;
onOpenToggle?: (newValue: boolean) => void;
size?: 'default' | 'small';
}

function Accordion({
text,
hintText,
textClassName,
buttonTextClassName,
open: _open,
setOpen: _setOpen,
summaryTextClassName,
defaultOpen = false,
onOpenToggle,
size = 'default',
...props
}: React.PropsWithChildren<AccordionProps>): React.ReactElement {
const darkMode = useDarkMode();
const [localOpen, setLocalOpen] = useState(_open ?? defaultOpen);
const setOpenRef = useCurrentValueRef(_setOpen);
const onOpenChange = useCallback(() => {
setLocalOpen((prevValue) => {
const newValue = !prevValue;
setOpenRef.current?.(newValue);
return newValue;
});
}, [setOpenRef]);
const regionId = useId();
const labelId = useId();
const open = typeof _open !== 'undefined' ? _open : localOpen;
const handleToggle = useCallback(
(event: React.SyntheticEvent<HTMLElement>) => {
const isOpen = (event.target as HTMLDetailsElement).open;
onOpenToggle?.(isOpen);
},
[onOpenToggle]
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Aren't we basically reimplementing the behavior that details + summary have in plain HTML here? Not a bad thing, just wondering if we need this

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is allowing for a controlled version. I'm not sure if we actually need the controlled version though, I'll go through the use cases and see if this could be simplified.

return (
<>
<button
{...props}
<details open={defaultOpen} onToggle={handleToggle}>
<summary
className={cx(
darkMode ? buttonDarkThemeStyles : buttonLightThemeStyles,
buttonStyles,
buttonVariantStyles[size],
darkMode ? summaryDarkThemeStyles : summaryLightThemeStyles,
summaryStyles,
summaryVariantStyles[size],
textClassName
)}
id={labelId}
type="button"
aria-expanded={open ? 'true' : 'false'}
aria-controls={regionId}
onClick={onOpenChange}
{...props}
>
<span className={buttonIconContainerStyles}>
<Icon
glyph={open ? 'ChevronDown' : 'ChevronRight'}
size={iconVariantSizes[size]}
/>
<span className={summaryIconContainerStyles}>
<Icon glyph={'ChevronRight'} size={iconVariantSizes[size]} />
</span>

<div className={cx(buttonTextStyles, buttonTextClassName)}>
<div className={cx(summaryTextStyles, summaryTextClassName)}>
{text}
{hintText && (
<Description className={buttonHintStyles}>{hintText}</Description>
<Description className={summaryHintStyles}>{hintText}</Description>
)}
</div>
</button>
</summary>

{open && (
<div role="region" aria-labelledby={labelId} id={regionId}>
{props.children}
</div>
)}
</>
<div role="region" aria-labelledby={labelId}>
{props.children}
</div>
</details>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const DMDrawerSection: React.FC<{
text={label}
defaultOpen={true}
textClassName={accordionTitleStyles}
buttonTextClassName={buttonStyles}
summaryTextClassName={buttonStyles}
size="small"
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ export function CreateShardKeyForm({
isCancellingSharding,
onCreateShardKey,
}: CreateShardKeyFormProps) {
const [isAdvancedOptionsOpen, setIsAdvancedOptionsOpen] = useState(false);
const [selectedAdvancedOption, setSelectedAdvancedOption] =
useState<ShardingAdvancedOption>('default');
const fields = useAutocompleteFields(namespace);
Expand Down Expand Up @@ -221,8 +220,6 @@ export function CreateShardKeyForm({
<Accordion
data-testid="advanced-shard-key-configuration"
text="Advanced Shard Key Configuration"
open={isAdvancedOptionsOpen}
setOpen={setIsAdvancedOptionsOpen}
className={accordionStyles}
>
<RadioGroup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function CreateIndexForm({
<Accordion
data-testid="create-index-modal-toggle-options"
text={'Options'}
setOpen={() => {
onOpenToggle={() => {
track('Options Clicked', {
context: 'Create Index Modal',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ function AdvancedConnectionOptions({
errors,
updateConnectionFormField,
connectionOptions,
open,
setOpen,
onOpenToggle,
openSettingsModal,
}: {
errors: ConnectionFormError[];
Expand All @@ -44,14 +43,13 @@ function AdvancedConnectionOptions({
openSettingsModal?: (tab?: string) => void;
} & Pick<
React.ComponentProps<typeof Accordion>,
'open' | 'setOpen'
'onOpenToggle'
>): React.ReactElement {
return (
<Accordion
data-testid="advanced-connection-options"
text="Advanced Connection Options"
open={open}
setOpen={setOpen}
onOpenToggle={onOpenToggle}
>
<div className={connectionTabsContainer}>
{disabled && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,9 @@ function CSFLETab({
return (
<div className={accordionContainerStyles} key={kmsProviderType}>
<Accordion
setOpen={(open) => onOpenAccordion(kmsProviderType, open)}
onOpenToggle={(open) =>
onOpenAccordion(kmsProviderType, open)
}
data-testid={`csfle-kms-provider-${kmsProviderType}`}
text={accordionTitle}
>
Expand Down
7 changes: 2 additions & 5 deletions packages/connection-form/src/components/connection-form.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useMemo } from 'react';
import type {
ConnectionInfo,
ConnectionFavoriteOptions,
Expand Down Expand Up @@ -357,12 +357,10 @@ function ConnectionForm({
onAdvancedOptionsToggle,
openSettingsModal,
}: ConnectionFormPropsWithoutSettings): React.ReactElement {
const [advancedOpen, setAdvancedOpen] = useState(false);
const isDarkMode = useDarkMode();

const onAdvancedChange = useCallback(
(newState: boolean) => {
setAdvancedOpen(newState);
onAdvancedOptionsToggle?.(newState);
},
[onAdvancedOptionsToggle]
Expand Down Expand Up @@ -594,8 +592,7 @@ function ConnectionForm({
protectConnectionStrings || disableEditingConnectedConnection
) && (
<AdvancedConnectionOptions
open={advancedOpen}
setOpen={onAdvancedChange}
onOpenToggle={onAdvancedChange}
errors={connectionStringInvalidError ? [] : errors}
disabled={
!!(
Expand Down
Loading