-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5f79664
commit be9d051
Showing
1 changed file
with
186 additions
and
0 deletions.
There are no files selected for viewing
186 changes: 186 additions & 0 deletions
186
packages/react/src/components/CheckboxGroup/CheckboxGroup.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
/** | ||
* 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, { ReactNode } from 'react'; | ||
import cx from 'classnames'; | ||
import { usePrefix } from '../../internal/usePrefix'; | ||
import { WarningFilled, WarningAltFilled } from '@carbon/icons-react'; | ||
import setupGetInstanceId from '../../tools/setupGetInstanceId'; | ||
|
||
const getInstanceId = setupGetInstanceId(); | ||
|
||
export interface CheckboxGroupProps { | ||
children?: ReactNode; | ||
className?: string; | ||
helperText?: ReactNode; | ||
invalid?: boolean; | ||
invalidText?: ReactNode; | ||
legendId?: ReactNode; | ||
orientation?: 'horizontal' | 'vertical'; | ||
legendText: ReactNode; | ||
readOnly?: boolean; | ||
slug?: ReactNode; | ||
warn?: boolean; | ||
warnText?: ReactNode; | ||
} | ||
|
||
export interface CustomType { | ||
size: string; | ||
kind: string; | ||
} | ||
|
||
const CheckboxGroup: React.FC<CheckboxGroupProps> = ({ | ||
children, | ||
className, | ||
helperText, | ||
invalid, | ||
invalidText, | ||
legendId, | ||
legendText, | ||
readOnly, | ||
warn, | ||
warnText, | ||
slug, | ||
orientation = 'vertical', | ||
...rest | ||
}) => { | ||
const prefix = usePrefix(); | ||
|
||
const showWarning = !readOnly && !invalid && warn; | ||
const showHelper = !invalid && !warn; | ||
|
||
const checkboxGroupInstanceId = getInstanceId(); | ||
|
||
const helperId = !helperText | ||
? undefined | ||
: `checkbox-group-helper-text-${checkboxGroupInstanceId}`; | ||
|
||
const helper = helperText ? ( | ||
<div id={helperId} className={`${prefix}--form__helper-text`}> | ||
{helperText} | ||
</div> | ||
) : null; | ||
|
||
const fieldsetClasses = cx(`${prefix}--checkbox-group`, className, { | ||
[`${prefix}--checkbox-group--${orientation}`]: orientation === 'horizontal', | ||
[`${prefix}--checkbox-group--readonly`]: readOnly, | ||
[`${prefix}--checkbox-group--invalid`]: !readOnly && invalid, | ||
[`${prefix}--checkbox-group--warning`]: showWarning, | ||
[`${prefix}--checkbox-group--slug`]: slug, | ||
}); | ||
|
||
// Slug is always size `mini` | ||
let normalizedSlug; | ||
if ( | ||
React.isValidElement(slug) && | ||
(slug['type'] as any)?.displayName === 'Slug' | ||
) { | ||
normalizedSlug = React.cloneElement(slug, { | ||
size: 'mini', | ||
kind: 'default', | ||
} as CustomType); | ||
} | ||
return ( | ||
<fieldset | ||
className={fieldsetClasses} | ||
data-invalid={invalid ? true : undefined} | ||
aria-labelledby={rest['aria-labelledby'] || legendId} | ||
aria-readonly={readOnly} | ||
aria-describedby={!invalid && !warn && helper ? helperId : undefined} | ||
{...rest}> | ||
<legend | ||
className={`${prefix}--label`} | ||
id={legendId || rest['aria-labelledby']}> | ||
{legendText} | ||
{normalizedSlug} | ||
</legend> | ||
{children} | ||
<div className={`${prefix}--checkbox-group__validation-msg`}> | ||
{!readOnly && invalid && ( | ||
<> | ||
<WarningFilled className={`${prefix}--checkbox__invalid-icon`} /> | ||
<div className={`${prefix}--form-requirement`}>{invalidText}</div> | ||
</> | ||
)} | ||
{showWarning && ( | ||
<> | ||
<WarningAltFilled | ||
className={`${prefix}--checkbox__invalid-icon ${prefix}--checkbox__invalid-icon--warning`} | ||
/> | ||
<div className={`${prefix}--form-requirement`}>{warnText}</div> | ||
</> | ||
)} | ||
</div> | ||
{showHelper && helper} | ||
</fieldset> | ||
); | ||
}; | ||
|
||
CheckboxGroup.propTypes = { | ||
/** | ||
* Provide the children form elements to be rendered inside of the <fieldset> | ||
*/ | ||
children: PropTypes.node, | ||
|
||
/** | ||
* Provide a custom className to be applied to the containing <fieldset> node | ||
*/ | ||
className: PropTypes.string, | ||
|
||
/** | ||
* Provide text for the form group for additional help | ||
*/ | ||
helperText: PropTypes.node, | ||
|
||
/** | ||
* Specify whether the form group is currently invalid | ||
*/ | ||
invalid: PropTypes.bool, | ||
|
||
/** | ||
* Provide the text that is displayed when the form group is in an invalid state | ||
*/ | ||
invalidText: PropTypes.node, | ||
|
||
/** | ||
* Provide id for the fieldset <legend> which corresponds to the fieldset | ||
* `aria-labelledby` | ||
*/ | ||
legendId: PropTypes.node, | ||
|
||
/** | ||
* Provide the text to be rendered inside of the fieldset <legend> | ||
*/ | ||
legendText: PropTypes.node.isRequired, | ||
|
||
/** | ||
* Provide the orientation for how the checkbox should be displayed | ||
*/ | ||
orientation: PropTypes.oneOf(['horizontal', 'vertical']), | ||
/** | ||
* Whether the CheckboxGroup should be read-only | ||
*/ | ||
readOnly: PropTypes.bool, | ||
|
||
/** | ||
* **Experimental**: Provide a `Slug` component to be rendered inside the `CheckboxGroup` component | ||
*/ | ||
slug: PropTypes.node, | ||
|
||
/** | ||
* Specify whether the form group is currently in warning state | ||
*/ | ||
warn: PropTypes.bool, | ||
|
||
/** | ||
* Provide the text that is displayed when the form group is in warning state | ||
*/ | ||
warnText: PropTypes.node, | ||
}; | ||
|
||
export default CheckboxGroup; |