diff --git a/packages/react/src/components/CodeSnippet/CodeSnippet.js b/packages/react/src/components/CodeSnippet/CodeSnippet.js index 51abd03c773a..3dd581309e00 100644 --- a/packages/react/src/components/CodeSnippet/CodeSnippet.js +++ b/packages/react/src/components/CodeSnippet/CodeSnippet.js @@ -6,223 +6,170 @@ */ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { useState, useRef, useLayoutEffect } from 'react'; import classNames from 'classnames'; import { ChevronDown16 } from '@carbon/icons-react'; import { settings } from 'carbon-components'; import Copy from '../Copy'; +import Button from '../Button'; import CopyButton from '../CopyButton'; -import uid from '../../tools/uniqueId'; +import getUniqueId from '../../tools/uniqueId'; const { prefix } = settings; -export default class CodeSnippet extends Component { - static propTypes = { - /** - * Provide the type of Code Snippet - */ - type: PropTypes.oneOf(['single', 'inline', 'multi']), - - /** - * Specify an optional className to be applied to the container node - */ - className: PropTypes.string, - - /** - * Provide the content of your CodeSnippet as a string - */ - children: PropTypes.string, - - /** - * Specify the string displayed when the snippet is copied - */ - feedback: PropTypes.string, - - /** - * Specify the description for the Copy Button - */ - copyButtonDescription: PropTypes.string, - - /** - * An optional handler to listen to the `onClick` even fired by the Copy - * Button - */ - onClick: PropTypes.func, - - /** - * Specify a label to be read by screen readers on the containing - * node - */ - copyLabel: PropTypes.string, - - /** - * Specify a label to be read by screen readers on the containing - * node - */ - ariaLabel: PropTypes.string, - - /** - * Specify a string that is displayed when the Code Snippet text is more - * than 15 lines - */ - showMoreText: PropTypes.string, - - /** - * Specify a string that is displayed when the Code Snippet has been - * interacted with to show more lines - */ - showLessText: PropTypes.string, - - /** - * Specify whether you are using the light variant of the Code Snippet, - * typically used for inline snippet to display an alternate color - */ - light: PropTypes.bool, - }; - - static defaultProps = { - type: 'single', - showMoreText: 'Show more', - showLessText: 'Show less', - }; - - state = { - shouldShowMoreLessBtn: false, - expandedCode: false, - }; - - componentDidMount() { - if (this.codeContent) { - if (this.codeContent.getBoundingClientRect().height > 255) { - this.setState({ shouldShowMoreLessBtn: true }); - } +function CodeSnippet({ + className, + type, + children, + feedback, + onClick, + ariaLabel, + copyLabel, //TODO: Merge this prop to `ariaLabel` in `v11` + copyButtonDescription, + light, + showMoreText, + showLessText, + ...rest +}) { + const [expandedCode, setExpandedCode] = useState(false); + const [shouldShowMoreLessBtn, setShouldShowMoreLessBtn] = useState(false); + const { current: uid } = useRef(getUniqueId()); + const codeContentRef = useRef(); + + useLayoutEffect(() => { + if (codeContentRef.current) { + const { height } = codeContentRef.current.getBoundingClientRect(); + setShouldShowMoreLessBtn(type === 'multi' && height > 255); } - } + }, [children, type]); - componentDidUpdate(prevProps) { - if (this.props.children !== prevProps.children && this.codeContent) { - if (this.codeContent.getBoundingClientRect().height > 255) { - this.setState({ shouldShowMoreLessBtn: true }); - } - } - } + const codeSnippetClasses = classNames(className, { + [`${prefix}--snippet`]: true, + [`${prefix}--snippet--${type}`]: type, + [`${prefix}--snippet--expand`]: expandedCode, + [`${prefix}--snippet--light`]: light, + }); - expandCode = () => { - this.setState({ expandedCode: !this.state.expandedCode }); - }; - - render() { - const { - className, - type, - children, - feedback, - onClick, - ariaLabel, - copyLabel, //TODO: Merge this prop to `ariaLabel` in `v11` - copyButtonDescription, - light, - showMoreText, - showLessText, - ...other - } = this.props; - - // a unique id generated for aria-describedby - this.uid = uid(); - - const codeSnippetClasses = classNames(className, { - [`${prefix}--snippet`]: true, - [`${prefix}--snippet--${type}`]: type, - [`${prefix}--snippet--expand`]: this.state.expandedCode, - [`${prefix}--snippet--light`]: light, - }); - - const expandCodeBtnText = this.state.expandedCode - ? showLessText - : showMoreText; - - const moreLessBtn = ( - + const expandCodeBtnText = expandedCode ? showLessText : showMoreText; + + if (type === 'inline') { + return ( + + {children} + ); + } - const code = ( + return ( +
-
 {
-              this.codeContent = codeContent;
-            }}>
-            {children}
-          
+
{children}
- ); - - const copy = ( - ); - - if (type === 'inline') { - return ( - - {children} - - ); - } - - if (type === 'single') { - return ( -
- {code} - {copy} -
- ); - } - - if (!this.state.shouldShowMoreLessBtn && type === 'multi') { - return ( -
- {code} - {copy} -
- ); - } - - if (this.state.shouldShowMoreLessBtn && type === 'multi') { - return ( -
- {code} - {copy} - {moreLessBtn} -
- ); - } - } + {shouldShowMoreLessBtn && ( + + )} +
+ ); } + +CodeSnippet.propTypes = { + /** + * Provide the type of Code Snippet + */ + type: PropTypes.oneOf(['single', 'inline', 'multi']), + + /** + * Specify an optional className to be applied to the container node + */ + className: PropTypes.string, + + /** + * Provide the content of your CodeSnippet as a string + */ + children: PropTypes.string, + + /** + * Specify the string displayed when the snippet is copied + */ + feedback: PropTypes.string, + + /** + * Specify the description for the Copy Button + */ + copyButtonDescription: PropTypes.string, + + /** + * An optional handler to listen to the `onClick` even fired by the Copy + * Button + */ + onClick: PropTypes.func, + + /** + * Specify a label to be read by screen readers on the containing + * node + */ + copyLabel: PropTypes.string, + + /** + * Specify a label to be read by screen readers on the containing + * node + */ + ariaLabel: PropTypes.string, + + /** + * Specify a string that is displayed when the Code Snippet text is more + * than 15 lines + */ + showMoreText: PropTypes.string, + + /** + * Specify a string that is displayed when the Code Snippet has been + * interacted with to show more lines + */ + showLessText: PropTypes.string, + + /** + * Specify whether you are using the light variant of the Code Snippet, + * typically used for inline snippet to display an alternate color + */ + light: PropTypes.bool, +}; + +CodeSnippet.defaultProps = { + type: 'single', + showMoreText: 'Show more', + showLessText: 'Show less', +}; + +export default CodeSnippet;