|
6 | 6 | * Side Public License, v 1. |
7 | 7 | */ |
8 | 8 |
|
9 | | -import React, { Component, HTMLAttributes, ReactNode } from 'react'; |
| 9 | +import React, { |
| 10 | + FunctionComponent, |
| 11 | + HTMLAttributes, |
| 12 | + ReactNode, |
| 13 | + useState, |
| 14 | +} from 'react'; |
10 | 15 | import classNames from 'classnames'; |
11 | 16 |
|
12 | | -import { |
13 | | - htmlIdGenerator, |
14 | | - withEuiTheme, |
15 | | - WithEuiThemeProps, |
16 | | -} from '../../services'; |
| 17 | +import { useEuiMemoizedStyles, useGeneratedHtmlId } from '../../services'; |
17 | 18 | import { CommonProps } from '../common'; |
18 | 19 | import { EuiLoadingSpinner } from '../loading'; |
19 | 20 | import type { EuiButtonIconProps } from '../button'; |
@@ -114,133 +115,93 @@ export type EuiAccordionProps = CommonProps & |
114 | 115 | isDisabled?: boolean; |
115 | 116 | }; |
116 | 117 |
|
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 = () => { |
152 | 149 | if (forceState) { |
153 | | - const nextState = !this.isOpen; |
154 | | - this.props.onToggle?.(nextState); |
| 150 | + onToggle?.(!isOpen); |
155 | 151 | } 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); |
164 | 155 | } |
165 | 156 | }; |
166 | 157 |
|
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