Skip to content

Commit e708b6f

Browse files
[EuiAccordion] Migrate from class to function component (elastic#9558)
Co-authored-by: Weronika Olejniczak <32842468+weronikaolejniczak@users.noreply.github.com>
1 parent 3665907 commit e708b6f

1 file changed

Lines changed: 92 additions & 131 deletions

File tree

packages/eui/src/components/accordion/accordion.tsx

Lines changed: 92 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
* Side Public License, v 1.
77
*/
88

9-
import React, { Component, HTMLAttributes, ReactNode } from 'react';
9+
import React, {
10+
FunctionComponent,
11+
HTMLAttributes,
12+
ReactNode,
13+
useState,
14+
} from 'react';
1015
import classNames from 'classnames';
1116

12-
import {
13-
htmlIdGenerator,
14-
withEuiTheme,
15-
WithEuiThemeProps,
16-
} from '../../services';
17+
import { useEuiMemoizedStyles, useGeneratedHtmlId } from '../../services';
1718
import { CommonProps } from '../common';
1819
import { EuiLoadingSpinner } from '../loading';
1920
import type { EuiButtonIconProps } from '../button';
@@ -114,133 +115,93 @@ export type EuiAccordionProps = CommonProps &
114115
isDisabled?: boolean;
115116
};
116117

117-
type EuiAccordionState = {
118-
isOpen: boolean;
119-
};
120-
121-
export class EuiAccordionClass extends Component<
122-
WithEuiThemeProps & EuiAccordionProps,
123-
EuiAccordionState
124-
> {
125-
static defaultProps = {
126-
initialIsOpen: false,
127-
borders: 'none' as const,
128-
paddingSize: 'none' as const,
129-
arrowDisplay: 'left' as const,
130-
isLoading: false,
131-
isDisabled: false,
132-
isLoadingMessage: false,
133-
element: 'div' as const,
134-
buttonElement: 'button' as const,
135-
role: 'group' as const,
136-
};
137-
138-
state = {
139-
isOpen: this.props.forceState
140-
? this.props.forceState === 'open'
141-
: this.props.initialIsOpen!,
142-
};
143-
144-
get isOpen() {
145-
return this.props.forceState
146-
? this.props.forceState === 'open'
147-
: this.state.isOpen;
148-
}
149-
150-
onToggle = () => {
151-
const { forceState } = this.props;
118+
export const EuiAccordion: FunctionComponent<EuiAccordionProps> = ({
119+
children,
120+
className,
121+
id,
122+
role = 'group',
123+
element: Element = 'div',
124+
buttonElement = 'button',
125+
buttonProps,
126+
buttonClassName,
127+
buttonContentClassName,
128+
buttonContent,
129+
arrowDisplay = 'left',
130+
arrowProps,
131+
extraAction,
132+
paddingSize = 'none',
133+
borders = 'none',
134+
initialIsOpen = false,
135+
forceState,
136+
isLoading = false,
137+
isLoadingMessage = false,
138+
isDisabled = false,
139+
onToggle,
140+
...rest
141+
}) => {
142+
const [isOpenState, setIsOpenState] = useState(
143+
forceState ? forceState === 'open' : initialIsOpen
144+
);
145+
146+
const isOpen = forceState ? forceState === 'open' : isOpenState;
147+
148+
const onAccordionToggle = () => {
152149
if (forceState) {
153-
const nextState = !this.isOpen;
154-
this.props.onToggle?.(nextState);
150+
onToggle?.(!isOpen);
155151
} else {
156-
this.setState(
157-
(prevState) => ({
158-
isOpen: !prevState.isOpen,
159-
}),
160-
() => {
161-
this.props.onToggle?.(this.state.isOpen);
162-
}
163-
);
152+
const nextState = !isOpenState;
153+
setIsOpenState(nextState);
154+
onToggle?.(nextState);
164155
}
165156
};
166157

167-
generatedId = htmlIdGenerator()();
168-
169-
render() {
170-
const {
171-
children,
172-
className,
173-
id,
174-
role,
175-
element: Element = 'div',
176-
buttonElement,
177-
buttonProps,
178-
buttonClassName,
179-
buttonContentClassName,
180-
buttonContent,
181-
arrowDisplay,
182-
arrowProps,
183-
extraAction,
184-
paddingSize,
185-
borders,
186-
initialIsOpen,
187-
forceState,
188-
isLoading,
189-
isLoadingMessage,
190-
isDisabled,
191-
theme,
192-
...rest
193-
} = this.props;
194-
195-
const classes = classNames(
196-
'euiAccordion',
197-
{ 'euiAccordion-isOpen': this.isOpen },
198-
className
199-
);
200-
201-
const styles = euiAccordionStyles(theme);
202-
const cssStyles = [
203-
styles.euiAccordion,
204-
borders !== 'none' && styles.borders.borders,
205-
borders !== 'none' && styles.borders[borders!],
206-
];
207-
208-
const buttonId = buttonProps?.id ?? this.generatedId;
209-
210-
return (
211-
<Element className={classes} css={cssStyles} {...rest}>
212-
<EuiAccordionTrigger
213-
ariaControlsId={id}
214-
buttonId={buttonId}
215-
// Force button element to be a legend if the element is a fieldset
216-
buttonElement={Element === 'fieldset' ? 'legend' : buttonElement}
217-
buttonClassName={buttonClassName}
218-
buttonContent={buttonContent}
219-
buttonContentClassName={buttonContentClassName}
220-
buttonProps={buttonProps}
221-
arrowProps={arrowProps}
222-
arrowDisplay={arrowDisplay}
223-
isDisabled={isDisabled}
224-
isOpen={this.isOpen}
225-
onToggle={this.onToggle}
226-
extraAction={isLoading ? <EuiLoadingSpinner /> : extraAction}
227-
/>
228-
229-
<EuiAccordionChildren
230-
role={role}
231-
id={id}
232-
aria-labelledby={buttonId}
233-
paddingSize={paddingSize}
234-
isLoading={isLoading}
235-
isLoadingMessage={isLoadingMessage}
236-
isOpen={this.isOpen}
237-
initialIsOpen={initialIsOpen}
238-
>
239-
{children}
240-
</EuiAccordionChildren>
241-
</Element>
242-
);
243-
}
244-
}
245-
246-
export const EuiAccordion = withEuiTheme<EuiAccordionProps>(EuiAccordionClass);
158+
const generatedId = useGeneratedHtmlId();
159+
const buttonId = buttonProps?.id ?? generatedId;
160+
161+
const classes = classNames(
162+
'euiAccordion',
163+
{ 'euiAccordion-isOpen': isOpen },
164+
className
165+
);
166+
167+
const styles = useEuiMemoizedStyles(euiAccordionStyles);
168+
const cssStyles = [
169+
styles.euiAccordion,
170+
borders !== 'none' && styles.borders.borders,
171+
borders !== 'none' && styles.borders[borders],
172+
];
173+
174+
return (
175+
<Element className={classes} css={cssStyles} {...rest}>
176+
<EuiAccordionTrigger
177+
ariaControlsId={id}
178+
buttonId={buttonId}
179+
// Force button element to be a legend if the element is a fieldset
180+
buttonElement={Element === 'fieldset' ? 'legend' : buttonElement}
181+
buttonClassName={buttonClassName}
182+
buttonContent={buttonContent}
183+
buttonContentClassName={buttonContentClassName}
184+
buttonProps={buttonProps}
185+
arrowProps={arrowProps}
186+
arrowDisplay={arrowDisplay}
187+
isDisabled={isDisabled}
188+
isOpen={isOpen}
189+
onToggle={onAccordionToggle}
190+
extraAction={isLoading ? <EuiLoadingSpinner /> : extraAction}
191+
/>
192+
193+
<EuiAccordionChildren
194+
role={role}
195+
id={id}
196+
aria-labelledby={buttonId}
197+
paddingSize={paddingSize}
198+
isLoading={isLoading}
199+
isLoadingMessage={isLoadingMessage}
200+
isOpen={isOpen}
201+
initialIsOpen={initialIsOpen}
202+
>
203+
{children}
204+
</EuiAccordionChildren>
205+
</Element>
206+
);
207+
};

0 commit comments

Comments
 (0)