From 6b4e0fb8a0c674a65b89235252d6a81268f8f053 Mon Sep 17 00:00:00 2001 From: Anna Wen <54281166+annawen1@users.noreply.github.com> Date: Fri, 8 Dec 2023 12:56:27 -0500 Subject: [PATCH] feat(checkbox): checkbox group component and new checkbox states --- .../src/components/checkbox/checkbox-group.ts | 169 ++++++++++++++++++ .../components/checkbox/checkbox-story.mdx | 9 + .../src/components/checkbox/checkbox-story.ts | 88 +++++---- .../src/components/checkbox/checkbox.scss | 11 ++ .../src/components/checkbox/checkbox.ts | 72 ++++++++ .../src/components/checkbox/index.ts | 1 + 6 files changed, 320 insertions(+), 30 deletions(-) create mode 100644 packages/carbon-web-components/src/components/checkbox/checkbox-group.ts diff --git a/packages/carbon-web-components/src/components/checkbox/checkbox-group.ts b/packages/carbon-web-components/src/components/checkbox/checkbox-group.ts new file mode 100644 index 00000000000..7114022dff7 --- /dev/null +++ b/packages/carbon-web-components/src/components/checkbox/checkbox-group.ts @@ -0,0 +1,169 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 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 { classMap } from 'lit/directives/class-map.js'; +import { ifDefined } from 'lit/directives/if-defined.js'; +import { LitElement, html } from 'lit'; +import { property } from 'lit/decorators.js'; +import { prefix } from '../../globals/settings'; +import WarningFilled16 from '@carbon/icons/lib/warning--filled/16'; +import WarningAltFilled16 from '@carbon/icons/lib/warning--alt--filled/16'; +import styles from './checkbox.scss'; +import { carbonElement as customElement } from '../../globals/decorators/carbon-element'; + +/** + * Check box. + * + * @element cds-checkbox + * @fires cds-checkbox-changed - The custom event fired after this changebox changes its checked state. + * @csspart input The checkbox. + * @csspart label The label. + */ +@customElement(`${prefix}-checkbox-group`) +class CDSCheckboxGroup extends LitElement { + /** + * fieldset `aria-labelledby` + */ + @property({ type: String, reflect: true, attribute: 'aria-labelledby' }) + ariaLabelledBy; + + /** + * Specify whether the form group is currently disabled + */ + @property({ type: Boolean }) + disabled; + + /** + * Provide text for the form group for additional help + */ + @property({ type: String, reflect: true, attribute: 'helper-text' }) + helperText; + + /** + * Specify whether the form group is currently invalid + */ + @property({ type: Boolean, attribute: 'invalid' }) + invalid; + + /** + * Provide the text that is displayed when the form group is in an invalid state + */ + @property({ type: String, reflect: true, attribute: 'invalid-text' }) + invalidText; + + /** + * Provide id for the fieldset which corresponds to the fieldset + * `aria-labelledby` + */ + @property({ type: String, reflect: true, attribute: 'legend-id' }) + legendId; + + /** + * Provide the text to be rendered inside of the fieldset + */ + @property({ type: String, reflect: true, attribute: 'legend-text' }) + legendText; + + /** + * Whether the CheckboxGroup should be read-only + */ + @property({ type: Boolean, reflect: true }) + readonly = false; + + /** + * Specify whether the form group is currently in warning state + */ + @property({ type: Boolean, reflect: true }) + warn = false; + + /** + * Provide the text that is displayed when the form group is in warning state + */ + @property({ type: String, reflect: true, attribute: 'warn-text' }) + warnText = ''; + + render() { + const { + ariaLabelledBy, + disabled, + helperText, + invalid, + invalidText, + legendId, + legendText, + readonly, + warn, + warnText, + } = this; + + const showWarning = !readonly && !invalid && warn; + const showHelper = !invalid && !warn; + + const checkboxGroupInstanceId = Math.random().toString(16).slice(2); + + const helperId = !helperText + ? undefined + : `checkbox-group-helper-text-${checkboxGroupInstanceId}`; + + const helper = helperText + ? html`
+ ${helperText} +
` + : null; + + const fieldsetClasses = classMap({ + [`${prefix}--checkbox-group`]: true, + [`${prefix}--checkbox-group--readonly`]: readonly, + [`${prefix}--checkbox-group--invalid`]: !readonly && invalid, + [`${prefix}--checkbox-group--warning`]: showWarning, + }); + + return html` +
+ + ${legendText} + + +
+ ${!readonly && invalid + ? html` + ${WarningFilled16({ + class: `${prefix}--checkbox__invalid-icon`, + })} +
${invalidText}
+ ` + : undefined} + ${showWarning + ? html` + ${WarningAltFilled16({ + class: `${prefix}--checkbox__invalid-icon ${prefix}--checkbox__invalid-icon--warning`, + })} +
${warnText}
+ ` + : undefined} +
+ ${showHelper ? helper : undefined} +
+ `; + } + + static shadowRootOptions = { + ...LitElement.shadowRootOptions, + delegatesFocus: true, + }; + static styles = styles; // `styles` here is a `CSSResult` generated by custom WebPack loader +} + +export default CDSCheckboxGroup; diff --git a/packages/carbon-web-components/src/components/checkbox/checkbox-story.mdx b/packages/carbon-web-components/src/components/checkbox/checkbox-story.mdx index 4938f3cf1ed..c0b733db472 100644 --- a/packages/carbon-web-components/src/components/checkbox/checkbox-story.mdx +++ b/packages/carbon-web-components/src/components/checkbox/checkbox-story.mdx @@ -31,6 +31,15 @@ import '@carbon/web-components/es/components/checkbox/index.js'; ``` +Use `cds-checkbox-group` when handling multiple checkboxes + +```html + + + + +``` + ## `` attributes, properties and events Unlike regular checkbox, `` does _not_ fire `change` event. Please diff --git a/packages/carbon-web-components/src/components/checkbox/checkbox-story.ts b/packages/carbon-web-components/src/components/checkbox/checkbox-story.ts index efd804a1e04..5681f4ed5ab 100644 --- a/packages/carbon-web-components/src/components/checkbox/checkbox-story.ts +++ b/packages/carbon-web-components/src/components/checkbox/checkbox-story.ts @@ -20,11 +20,10 @@ const checkboxLabel = 'Checkbox label'; export const Default = () => { return html` -
- Group label + -
+ `; }; @@ -39,52 +38,81 @@ export const Skeleton = () => { `; }; +export const Single = () => { + return html` + +

+ +

+ +

+ + `; +}; + export const Playground = (args) => { const { - checked, disabled, - hideLabel, - indeterminate, - labelText = checkboxLabel, readonly, - title, onChange, + helperText, + invalid, + invalidText, + legendText, + warn, + warnText, } = args?.[`${prefix}-checkbox`] ?? {}; return html` -
- Group label + -
+ + `; }; Playground.parameters = { knobs: { [`${prefix}-checkbox`]: () => ({ - checked: boolean('Checked (checked)', false), + onChange: action(`${prefix}-checkbox-changed`), disabled: boolean('Disabled (disabled)', false), - hideLabel: boolean('Hide label (hide-label)', false), - indeterminate: boolean('Indeterminate (indeterminate)', false), + helperText: textNullable( + 'Helper text (helper-text)', + 'Helper text goes here' + ), + invalid: boolean('Invalid (invalid)', false), + invalidText: textNullable( + 'Invalid text (invalid-text)', + 'Invalid message goes here' + ), + legendText: textNullable('Legend text (legend-text)', 'Group label'), readonly: boolean('Read only (readonly)', false), - title: textNullable('Title (title)', ''), - onChange: action(`${prefix}-checkbox-changed`), + warn: boolean('Warn (warn)', false), + warnText: textNullable('Warn text (warn-text)', 'Warn message goes here'), }), }, }; diff --git a/packages/carbon-web-components/src/components/checkbox/checkbox.scss b/packages/carbon-web-components/src/components/checkbox/checkbox.scss index 0d24a158255..3c2478de9ab 100644 --- a/packages/carbon-web-components/src/components/checkbox/checkbox.scss +++ b/packages/carbon-web-components/src/components/checkbox/checkbox.scss @@ -23,6 +23,14 @@ $css--plex: true !default; @extend .#{$prefix}--checkbox-wrapper--readonly; } +:host(#{$prefix}-checkbox:not([readonly])[invalid]) { + @extend .#{$prefix}--checkbox-wrapper--invalid; +} + +:host(#{$prefix}-checkbox:not([readonly]):not([invalid])[warn]) { + @extend .#{$prefix}--checkbox-wrapper--warning; +} + :host(#{$prefix}-checkbox[data-table]) { margin: 0; } @@ -42,3 +50,6 @@ $css--plex: true !default; cursor: default; } } + +:host(#{$prefix}-checkbox-group[data-invalid]) { +} diff --git a/packages/carbon-web-components/src/components/checkbox/checkbox.ts b/packages/carbon-web-components/src/components/checkbox/checkbox.ts index 7be15b608fc..33c65774627 100644 --- a/packages/carbon-web-components/src/components/checkbox/checkbox.ts +++ b/packages/carbon-web-components/src/components/checkbox/checkbox.ts @@ -14,6 +14,8 @@ import { property, query } from 'lit/decorators.js'; import { prefix } from '../../globals/settings'; import FocusMixin from '../../globals/mixins/focus'; import FormMixin from '../../globals/mixins/form'; +import WarningFilled16 from '@carbon/icons/lib/warning--filled/16'; +import WarningAltFilled16 from '@carbon/icons/lib/warning--alt--filled/16'; import styles from './checkbox.scss'; import { carbonElement as customElement } from '../../globals/decorators/carbon-element'; @@ -85,6 +87,12 @@ class CDSCheckbox extends FocusMixin(FormMixin(LitElement)) { @property({ type: Boolean, reflect: true }) disabled = false; + /** + * Provide text for the form group for additional help + */ + @property({ type: String, reflect: true, attribute: 'helper-text' }) + helperText; + /** * Specify whether the checkbox should be present in the DOM, * but invisible and uninteractable. Used for data-table purposes. @@ -123,6 +131,18 @@ class CDSCheckbox extends FocusMixin(FormMixin(LitElement)) { @property({ type: Boolean, reflect: true }) readonly = false; + /** + * Specify whether the Checkbox is currently invalid + */ + @property({ type: Boolean }) + invalid = false; + + /** + * Provide the text that is displayed when the Checkbox is in an invalid state + */ + @property({ type: String, attribute: 'invalid-text' }) + invalidText; + /** * Specify a title for the node for the Checkbox */ @@ -135,20 +155,53 @@ class CDSCheckbox extends FocusMixin(FormMixin(LitElement)) { @property() value!: string; + /** + * Specify whether the Checkbox is in a warn state + */ + @property({ type: Boolean }) + warn = false; + + /** + * Provide the text that is displayed when the Checkbox is in a warn state + */ + @property({ type: String, attribute: 'warn-text' }) + warnText = false; + render() { const { checked, disabled, + helperText, hideLabel, indeterminate, + invalid, + invalidText, labelText, name, readonly, title, value, + warn, + warnText, _handleChange: handleChange, _handleClick: handleClick, } = this; + + const showWarning = !readonly && !invalid && warn; + const showHelper = !invalid && !warn; + + const checkboxGroupInstanceId = Math.random().toString(16).slice(2); + + const helperId = !helperText + ? undefined + : `checkbox-group-helper-text-${checkboxGroupInstanceId}`; + + const helper = helperText + ? html`
+ ${helperText} +
` + : null; + const labelClasses = classMap({ [`${prefix}--checkbox-label`]: true, }); @@ -178,6 +231,25 @@ class CDSCheckbox extends FocusMixin(FormMixin(LitElement)) { title="${ifDefined(title)}"> ${labelText} +
+ ${!readonly && invalid + ? html` + ${WarningFilled16({ + class: `${prefix}--checkbox__invalid-icon`, + })} +
${invalidText}
+ ` + : undefined} + ${showWarning + ? html` + ${WarningAltFilled16({ + class: `${prefix}--checkbox__invalid-icon ${prefix}--checkbox__invalid-icon--warning`, + })} +
${warnText}
+ ` + : undefined} +
+ ${showHelper ? helper : undefined} `; } diff --git a/packages/carbon-web-components/src/components/checkbox/index.ts b/packages/carbon-web-components/src/components/checkbox/index.ts index 22a69bf6a41..281b674ea3f 100644 --- a/packages/carbon-web-components/src/components/checkbox/index.ts +++ b/packages/carbon-web-components/src/components/checkbox/index.ts @@ -8,4 +8,5 @@ */ import './checkbox'; +import './checkbox-group'; import './checkbox-skeleton';