Skip to content

Commit 250655c

Browse files
authored
fix(FormCheck): fix Form.Check.Label spacing (react-bootstrap#5983)
* Fix `Form.Check.Label` spacing Currently, when using a `Form.Check.Label` component to customize `Form.Check` rendering, there will be no space between the checkbox and the label. This is because `Form.Check` is currently relying on the presence of a `label` prop to apply the `form-check` class name to the wrapping `<div>`, because checkboxes without labels [don't need the wrapping element to have the `form-check` class name][1]. This commit adds a utility to check whether an element has a child of a certain type. It then uses that utility to check if a `Form.Check` element has a `Form.Check.Label` child and takes that into account when determining whether the checkbox has a label. Adding a special property (currently called `typeName`, but that can certainly change) to components for this utility is necessary because React minifies the `displayName` property in production. [1]: react-bootstrap#5938 (comment) * Add error handling to `includesType` * Support mixing auto and custom child components Currently, `Form.Check` doesn't allow users to use a custom `Form.Check.Input` with a label generated by the `label` prop (and requires that a custom `Form.Check.Input` be used if a custom `Form.Check.Label` is also used) because the presence of _any_ children in `Form.Check` prevents automatic inputs and labels (and feedback) from generating. This approach allows mixing and matching by extracting any custom input, label, and feedback components from `Form.Check`'s `children` prop, then separately for each, using the custom component if provided or an automatic component otherwise. * Simply check `child.type` The `type` property of React elements have reference equality with matching imported component variables (`JSXElementConstructor` objects, to be precise)! For some reason I was not expecting that, but it makes sense to me now, since these constructor objects are being imported from a singular source, so there's no reason React would create multiple instances of them. So `child.type === type` all we need to check in `getChildOfType`; the `typeName` prop I added earlier is not at all needed. * Add scripts for testing production build of docs Just as a convenience. I can revert this commit if these scripts aren't desired. * Just check immediate children for `FormCheckLabel` * Use correct variable * Revert empty `.husky/pre-commit` change No idea how that got into 0754b49. * Revert "Add scripts for testing production build of docs" This reverts commit 5343b0a.
1 parent b3f880a commit 250655c

File tree

2 files changed

+19
-3
lines changed

2 files changed

+19
-3
lines changed

src/ElementChildren.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,17 @@ function forEach<P = any>(
3535
});
3636
}
3737

38-
export { map, forEach };
38+
/**
39+
* Finds whether a component's `children` prop includes a React element of the
40+
* specified type.
41+
*/
42+
function hasChildOfType<P = any>(
43+
children: React.ReactNode,
44+
type: string | React.JSXElementConstructor<P>,
45+
): boolean {
46+
return React.Children.toArray(children).some(
47+
(child) => React.isValidElement(child) && child.type === type,
48+
);
49+
}
50+
51+
export { map, forEach, hasChildOfType };

src/FormCheck.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import FormCheckLabel from './FormCheckLabel';
88
import FormContext from './FormContext';
99
import { useBootstrapPrefix } from './ThemeProvider';
1010
import { BsPrefixProps, BsPrefixRefForwardingComponent } from './helpers';
11+
import { hasChildOfType } from './ElementChildren';
1112

1213
export type FormCheckType = 'checkbox' | 'radio' | 'switch';
1314

@@ -152,7 +153,9 @@ const FormCheck: BsPrefixRefForwardingComponent<'input', FormCheckProps> =
152153
[controlId, id],
153154
);
154155

155-
const hasLabel = label != null && label !== false && !children;
156+
const hasLabel =
157+
(!children && label != null && label !== false) ||
158+
hasChildOfType(children, FormCheckLabel);
156159

157160
const input = (
158161
<FormCheckInput
@@ -172,7 +175,7 @@ const FormCheck: BsPrefixRefForwardingComponent<'input', FormCheckProps> =
172175
style={style}
173176
className={classNames(
174177
className,
175-
label && bsPrefix,
178+
hasLabel && bsPrefix,
176179
inline && `${bsPrefix}-inline`,
177180
type === 'switch' && bsSwitchPrefix,
178181
)}

0 commit comments

Comments
 (0)