diff --git a/package-lock.json b/package-lock.json index 3664a42ba05..ad561477e33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38838,6 +38838,9 @@ "terra-theme-context": "^1.0.0", "uuid": "3.4.0" }, + "devDependencies": { + "terra-icon": "^3.60.0" + }, "peerDependencies": { "react": "^16.8.5", "react-dom": "^16.8.5", diff --git a/packages/terra-core-docs/CHANGELOG.md b/packages/terra-core-docs/CHANGELOG.md index 46e727da53d..abf845e7a91 100644 --- a/packages/terra-core-docs/CHANGELOG.md +++ b/packages/terra-core-docs/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Added + * Added examples and tests for `terra-dropdown-button` `SplitButton` with icons. + ## 1.70.0 - (April 4, 2024) * Changed diff --git a/packages/terra-core-docs/src/terra-dev-site/doc/dropdown-button/SplitButton.2.doc.mdx b/packages/terra-core-docs/src/terra-dev-site/doc/dropdown-button/SplitButton.2.doc.mdx index 5a1d726faed..04539ebfcf5 100644 --- a/packages/terra-core-docs/src/terra-dev-site/doc/dropdown-button/SplitButton.2.doc.mdx +++ b/packages/terra-core-docs/src/terra-dev-site/doc/dropdown-button/SplitButton.2.doc.mdx @@ -4,6 +4,7 @@ import DefaultSplitButton from './example/DefaultSplitButton?dev-site-example'; import GhostSplitButton from './example/GhostSplitButton?dev-site-example'; import DisabledSplitButton from './example/DisabledSplitButton?dev-site-example'; import BlockSplitButton from './example/BlockSplitButton?dev-site-example'; +import IconSplitButton from './example/IconSplitButton?dev-site-example'; import SplitButtonPropsTable from 'terra-dropdown-button/lib/SplitButton?dev-site-props-table'; import ItemPropsTable from 'terra-dropdown-button/lib/Item?dev-site-props-table'; @@ -46,6 +47,7 @@ import { Item, SplitButton } from 'terra-dropdown-button'; + ## Split Button Props diff --git a/packages/terra-core-docs/src/terra-dev-site/doc/dropdown-button/example/IconSplitButton.jsx b/packages/terra-core-docs/src/terra-dev-site/doc/dropdown-button/example/IconSplitButton.jsx new file mode 100644 index 00000000000..23abbdd9b2e --- /dev/null +++ b/packages/terra-core-docs/src/terra-dev-site/doc/dropdown-button/example/IconSplitButton.jsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { Item, SplitButton } from 'terra-dropdown-button'; +import { IconReply } from 'terra-icon'; + +import classNames from 'classnames/bind'; +import styles from './IconSplitButton.module.scss'; + +const cx = classNames.bind(styles); + +const Example = () => ( + <> + } + onSelect={() => {}} + buttonAttrs={{ + 'aria-label': 'icon split', + }} + className={cx('icon-button')} + > + {}} /> + {}} /> + {}} /> + {}} /> + + } + isReversed + onSelect={() => {}} + buttonAttrs={{ + 'aria-label': 'reverse icon split', + }} + className={cx('icon-button')} + > + {}} /> + {}} /> + {}} /> + {}} /> + + } + isIconOnly + onSelect={() => {}} + buttonAttrs={{ + 'aria-label': 'icon only split', + }} + className={cx('icon-button')} + > + {}} /> + {}} /> + {}} /> + {}} /> + + +); + +export default Example; diff --git a/packages/terra-core-docs/src/terra-dev-site/doc/dropdown-button/example/IconSplitButton.module.scss b/packages/terra-core-docs/src/terra-dev-site/doc/dropdown-button/example/IconSplitButton.module.scss new file mode 100644 index 00000000000..c83f805dbbf --- /dev/null +++ b/packages/terra-core-docs/src/terra-dev-site/doc/dropdown-button/example/IconSplitButton.module.scss @@ -0,0 +1,5 @@ +:local { + .icon-button { + margin: 5px; + } +} diff --git a/packages/terra-core-docs/src/terra-dev-site/test/dropdown-button/IconSplitButton.test.jsx b/packages/terra-core-docs/src/terra-dev-site/test/dropdown-button/IconSplitButton.test.jsx new file mode 100644 index 00000000000..5f75c5e7e71 --- /dev/null +++ b/packages/terra-core-docs/src/terra-dev-site/test/dropdown-button/IconSplitButton.test.jsx @@ -0,0 +1,58 @@ +import React from 'react'; +import classnames from 'classnames/bind'; +import { SplitButton, Item } from 'terra-dropdown-button'; +import { IconFeaturedOutlineYellow } from 'terra-icon'; +import styles from './ExtraSpacing.module.scss'; + +const cx = classnames.bind(styles); + +const RightIconSplitButton = () => ( +
+

Icon Left

+
+ } + metaData={{ key: 'primary-button' }} + onSelect={() => {}} + id="left-icon" + > + {}} /> + {}} /> + {}} /> + +
+

Icon Right

+
+ } + isReversed + metaData={{ key: 'primary-button' }} + onSelect={() => {}} + id="right-icon" + > + {}} /> + {}} /> + {}} /> + +
+

Icon Only

+
+ } + isIconOnly + metaData={{ key: 'primary-button' }} + onSelect={() => {}} + id="icon-only" + > + {}} /> + {}} /> + {}} /> + +
+
+); + +export default RightIconSplitButton; diff --git a/packages/terra-dropdown-button/CHANGELOG.md b/packages/terra-dropdown-button/CHANGELOG.md index 11e6021bcc2..4687e03fd45 100644 --- a/packages/terra-dropdown-button/CHANGELOG.md +++ b/packages/terra-dropdown-button/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Added + * Added support for icons in `SplitButton`. + ## 1.40.0 - (February 15, 2024) * Changed diff --git a/packages/terra-dropdown-button/package.json b/packages/terra-dropdown-button/package.json index d33ed711611..741e3474557 100644 --- a/packages/terra-dropdown-button/package.json +++ b/packages/terra-dropdown-button/package.json @@ -57,5 +57,8 @@ "LICENSE", "NOTICE", "README.md" - ] + ], + "devDependencies": { + "terra-icon": "^3.60.0" + } } diff --git a/packages/terra-dropdown-button/src/SplitButton.jsx b/packages/terra-dropdown-button/src/SplitButton.jsx index b47ece58a8a..c14a9050d4f 100644 --- a/packages/terra-dropdown-button/src/SplitButton.jsx +++ b/packages/terra-dropdown-button/src/SplitButton.jsx @@ -23,6 +23,10 @@ const propTypes = { * The options to display in the dropdown. Should be comprised of the subcomponent `Item`. */ children: PropTypes.node.isRequired, + /** + * An optional icon. Nested inline with the text when provided. + */ + icon: PropTypes.element, /** * Determines whether the component should have block styles applied. The dropdown will match the component's width. */ @@ -35,6 +39,14 @@ const propTypes = { * Determines whether the primary button and expanding the dropdown should be disabled. */ isDisabled: PropTypes.bool, + /** + * Whether or not the button should only display as an icon. + */ + isIconOnly: PropTypes.bool, + /** + * Reverses the position of the icon and text. + */ + isReversed: PropTypes.bool, /** * Sets the text that will be shown on the primary button which is outside the dropdown. */ @@ -190,9 +202,12 @@ class SplitButton extends React.Component { render() { const { children, + isReversed, + icon, isBlock, isCompact, isDisabled, + isIconOnly, primaryOptionLabel, onSelect, variant, @@ -235,6 +250,30 @@ class SplitButton extends React.Component { theme.className, ); + const buttonTextClassnames = (icon && isReversed) ? cx([ + 'text-first', + ]) : undefined; + + const iconClassnames = (!isIconOnly) && !isReversed ? cx([ + 'icon-first', + ]) : undefined; + + const buttonText = !isIconOnly ? {primaryOptionLabel} : null; + + let buttonIcon = null; + if (icon) { + const iconSvgClasses = icon.props.className ? `${icon.props.className} ${cx('icon-svg')}` : cx('icon-svg'); + const cloneIcon = React.cloneElement(icon, { className: iconSvgClasses }); + buttonIcon = {cloneIcon}; + } + + const buttonLabel = ( + <> + {isReversed ? buttonText : buttonIcon} + {isReversed ? buttonIcon : buttonText} + + ); + let buttonAriaLabel = ''; const modifiedButtonAttrs = { ...buttonAttrs }; if (modifiedButtonAttrs && modifiedButtonAttrs['aria-label']) { @@ -270,8 +309,9 @@ class SplitButton extends React.Component { disabled={isDisabled} tabIndex={isDisabled ? '-1' : undefined} aria-disabled={isDisabled} + aria-label={isIconOnly ? primaryOptionLabel : undefined} > - {primaryOptionLabel} + {buttonLabel}