diff --git a/src/ElementChildren.tsx b/src/ElementChildren.tsx index 72079467fb..db3ce7520a 100644 --- a/src/ElementChildren.tsx +++ b/src/ElementChildren.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { BsComponent } from './helpers'; /** * Iterates through children that are typically specified as `props.children`, @@ -35,4 +36,22 @@ function forEach

( }); } -export { map, forEach }; +/** + * Checks that at least one child is of the specified type (either a string for + * an HTML element or a component for a React element). + */ +function includesType( + children: React.ReactNode, + type: string | BsComponent, +) { + const childrenList = React.Children.toArray(children); + return childrenList.some( + (child) => + React.isValidElement

(child) && + (child.type === type || + (child.type as BsComponent)?.typeName === + (type as BsComponent).typeName), + ); +} + +export { map, forEach, includesType }; diff --git a/src/FormCheck.tsx b/src/FormCheck.tsx index 4f70d96779..d608323e9c 100644 --- a/src/FormCheck.tsx +++ b/src/FormCheck.tsx @@ -8,6 +8,7 @@ import FormCheckLabel from './FormCheckLabel'; import FormContext from './FormContext'; import { useBootstrapPrefix } from './ThemeProvider'; import { BsPrefixProps, BsPrefixRefForwardingComponent } from './helpers'; +import { includesType } from './ElementChildren'; export type FormCheckType = 'checkbox' | 'radio' | 'switch'; @@ -150,7 +151,9 @@ const FormCheck: BsPrefixRefForwardingComponent<'input', FormCheckProps> = [controlId, id], ); - const hasLabel = label != null && label !== false && !children; + const hasLabel = + (label != null && label !== false) || + (children && includesType(children, FormCheckLabel)); const input = ( = style={style} className={classNames( className, - label && bsPrefix, + hasLabel && bsPrefix, inline && `${bsPrefix}-inline`, type === 'switch' && bsSwitchPrefix, )} diff --git a/src/FormCheckLabel.tsx b/src/FormCheckLabel.tsx index cd061fd88c..2c8da953ba 100644 --- a/src/FormCheckLabel.tsx +++ b/src/FormCheckLabel.tsx @@ -5,7 +5,7 @@ import { useContext } from 'react'; import FormContext from './FormContext'; import { useBootstrapPrefix } from './ThemeProvider'; -import { BsPrefixProps } from './helpers'; +import { BsPrefixProps, BsPrefixRefForwardingComponent } from './helpers'; export interface FormCheckLabelProps extends React.LabelHTMLAttributes, @@ -21,7 +21,10 @@ const propTypes = { htmlFor: PropTypes.string, }; -const FormCheckLabel = React.forwardRef( +const FormCheckLabel: BsPrefixRefForwardingComponent< + 'label', + FormCheckLabelProps +> = React.forwardRef( ({ bsPrefix, className, htmlFor, ...props }, ref) => { const { controlId } = useContext(FormContext); @@ -39,6 +42,7 @@ const FormCheckLabel = React.forwardRef( ); FormCheckLabel.displayName = 'FormCheckLabel'; +FormCheckLabel.typeName = 'FormCheckLabel'; FormCheckLabel.propTypes = propTypes; export default FormCheckLabel; diff --git a/src/helpers.ts b/src/helpers.ts index d514b24abd..c8c83917b4 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -33,18 +33,28 @@ export interface BsPrefixRefForwardingComponent< contextTypes?: any; defaultProps?: Partial

; displayName?: string; + typeName?: string; } export class BsPrefixComponent< As extends React.ElementType, P = unknown, -> extends React.Component & P>> {} +> extends React.Component & P>> { + typeName?: string; +} // Need to use this instead of typeof Component to get proper type checking. export type BsPrefixComponentClass< As extends React.ElementType, P = unknown, -> = React.ComponentClass & P>>; +> = React.ComponentClass & P>> & { + typeName?: string; +}; + +export type BsComponent = + | BsPrefixRefForwardingComponent + | BsPrefixComponentClass + | BsPrefixComponentClass; export type TransitionType = boolean | TransitionComponent;