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';