diff --git a/packages/react/src/components/TileGroup/TileGroup.js b/packages/react/src/components/TileGroup/TileGroup.js deleted file mode 100644 index 2d1242780ee6..000000000000 --- a/packages/react/src/components/TileGroup/TileGroup.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2023 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import PropTypes from 'prop-types'; -import React from 'react'; -import RadioTile from '../RadioTile'; -import { warning } from '../../internal/warning'; -import { PrefixContext } from '../../internal/usePrefix'; -import { noopFn } from '../../internal/noopFn'; - -export default class TileGroup extends React.Component { - state = { - selected: this.props.valueSelected || this.props.defaultSelected || null, - prevValueSelected: this.props.valueSelected, - }; - - static contextType = PrefixContext; - - static propTypes = { - /** - * Provide a collection of components to render in the group - */ - children: PropTypes.node, - - /** - * Provide an optional className to be applied to the container node - */ - className: PropTypes.string, - - /** - * Specify the the value of to be selected by default - */ - defaultSelected: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - - /** - * Specify whether the group is disabled - */ - disabled: PropTypes.bool, - - /** - * Provide an optional legend for this group - */ - legend: PropTypes.string, - - /** - * Specify the name of the underlying `` nodes - */ - name: PropTypes.string.isRequired, - - /** - * Provide an optional `onChange` hook that is called whenever the value of - * the group changes - */ - onChange: PropTypes.func, - - /** - * Specify the value that is currently selected in the group - */ - valueSelected: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - }; - - static getDerivedStateFromProps({ valueSelected, defaultSelected }, state) { - const { prevValueSelected } = state; - return prevValueSelected === valueSelected - ? null - : { - selected: valueSelected || defaultSelected || null, - prevValueSelected: valueSelected, - }; - } - - getRadioTiles = () => { - const childrenArray = React.Children.toArray(this.props.children); - const children = childrenArray.map((tileRadio) => { - const { value, ...other } = tileRadio.props; - /* istanbul ignore if */ - if (typeof tileRadio.props.checked !== 'undefined') { - warning( - false, - `Instead of using the checked property on the RadioTile, set - the defaultSelected property or valueSelected property on the TileGroup.` - ); - } - - return ( - - ); - }); - - return children; - }; - - handleChange = (newSelection, value, evt) => { - const { onChange = noopFn } = this.props; - if (newSelection !== this.state.selected) { - this.setState({ selected: newSelection }); - onChange(newSelection, this.props.name, evt); - } - }; - - renderLegend = (legend) => { - if (legend) { - return {legend}; - } - }; - - render() { - const { context: prefix } = this; - const { - disabled, - className = `${prefix}--tile-group`, - legend, - } = this.props; - - return ( -
- {this.renderLegend(legend)} -
{this.getRadioTiles()}
-
- ); - } -} diff --git a/packages/react/src/components/TileGroup/TileGroup.tsx b/packages/react/src/components/TileGroup/TileGroup.tsx new file mode 100644 index 000000000000..09a78bac9df7 --- /dev/null +++ b/packages/react/src/components/TileGroup/TileGroup.tsx @@ -0,0 +1,183 @@ +/** + * Copyright IBM Corp. 2016, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import PropTypes, { ReactElementLike, ReactNodeLike } from 'prop-types'; +import React, { useState } from 'react'; +import RadioTile from '../RadioTile'; +import { warning } from '../../internal/warning'; +import { usePrefix } from '../../internal/usePrefix'; +import { ReactAttr } from '../../types/common'; +import { noopFn } from '../../internal/noopFn'; + +type ExcludedAttributes = 'onChange'; + +export interface TileGroupProps + extends Omit, ExcludedAttributes> { + /** + * Provide a collection of components to render in the group + */ + children?: ReactNodeLike; + + /** + * Provide an optional className to be applied to the container node + */ + className?: string; + + /** + * Specify the the value of to be selected by default + */ + defaultSelected?: string | number; + + /** + * Specify whether the group is disabled + */ + disabled?: boolean; + + /** + * Provide an optional legend for this group + */ + legend?: string; + + /** + * Specify the name of the underlying `` nodes + */ + name: string; + + /** + * Provide an optional `onChange` hook that is called whenever the value of the group changes + */ + onChange?: (selection: unknown, name: string, evt: unknown) => void; + + /** + * Specify the value that is currently selected in the group + */ + valueSelected?: string | number; +} + +const TileGroup = (props) => { + const { + children, + className, + defaultSelected, + disabled, + legend, + name, + onChange = noopFn, + valueSelected, + } = props; + const prefix = usePrefix(); + + const [selected, setSelected] = useState(valueSelected ?? defaultSelected); + const [prevValueSelected, setPrevValueSelected] = useState(valueSelected); + + /** + * prop + state alignment - getDerivedStateFromProps + * only update if selected prop changes + */ + if (valueSelected !== prevValueSelected) { + setSelected(valueSelected); + setPrevValueSelected(valueSelected); + } + + const getRadioTiles = () => { + const childrenArray = React.Children.toArray(children); + const radioTiles = childrenArray.map((tileRadio) => { + const tileRadioProps = (tileRadio as ReactElementLike).props ?? undefined; + const { value, ...other } = tileRadioProps; + /* istanbul ignore if */ + if (typeof tileRadioProps.checked !== 'undefined') { + warning( + false, + `Instead of using the checked property on the RadioTile, set + the defaultSelected property or valueSelected property on the TileGroup.` + ); + } + + return ( + + ); + }); + + return radioTiles; + }; + + const handleChange = (newSelection, value, evt) => { + if (newSelection !== selected) { + setSelected(newSelection); + onChange(newSelection, name, evt); + } + }; + + const renderLegend = (legend) => { + if (legend) { + return {legend}; + } + }; + + return ( +
+ {renderLegend(legend)} +
{getRadioTiles()}
+
+ ); +}; + +TileGroup.propTypes = { + /** + * Provide a collection of components to render in the group + */ + children: PropTypes.node, + + /** + * Provide an optional className to be applied to the container node + */ + className: PropTypes.string, + + /** + * Specify the the value of to be selected by default + */ + defaultSelected: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + + /** + * Specify whether the group is disabled + */ + disabled: PropTypes.bool, + + /** + * Provide an optional legend for this group + */ + legend: PropTypes.string, + + /** + * Specify the name of the underlying `` nodes + */ + name: PropTypes.string.isRequired, + + /** + * Provide an optional `onChange` hook that is called whenever the value of + * the group changes + */ + onChange: PropTypes.func, + + /** + * Specify the value that is currently selected in the group + */ + valueSelected: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +}; + +TileGroup.displayName = 'TileGroup'; + +export default TileGroup;