Skip to content

Commit

Permalink
Merge branch 'release-cwc/v2.1.0' into feat/data-table-slug
Browse files Browse the repository at this point in the history
  • Loading branch information
annawen1 authored Dec 14, 2023
2 parents ee76135 + 279da1f commit 887b54d
Show file tree
Hide file tree
Showing 8 changed files with 527 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
/**
* @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 { 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 CDSCheckbox from './checkbox';
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 <legend> 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 <legend>
*/
@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 = '';

/*
* Handles `slotchange` event.
*/
protected _handleSlotChange({ target }: Event) {
const hasContent = (target as HTMLSlotElement)
.assignedNodes()
.filter((elem) =>
(elem as HTMLElement).matches !== undefined
? (elem as HTMLElement).matches(
(this.constructor as typeof CDSCheckboxGroup).slugItem
)
: false
);

this._hasSlug = Boolean(hasContent);
(hasContent[0] as HTMLElement).setAttribute('size', 'mini');
this.requestUpdate();
}

/**
* `true` if there is a slug.
*/
protected _hasSlug = false;

updated(changedProperties) {
const { selectorCheckbox } = this.constructor as typeof CDSCheckboxGroup;
const checkboxes = this.querySelectorAll(selectorCheckbox);
['disabled', 'readonly'].forEach((name) => {
if (changedProperties.has(name)) {
const { [name as keyof CDSCheckboxGroup]: value } = this;
// Propagate the property to descendants until `:host-context()` gets supported in all major browsers
checkboxes.forEach((elem) => {
(elem as CDSCheckbox)[name] = value;
});
}
});
if (changedProperties.has('invalid')) {
const { invalid } = this;
checkboxes.forEach((elem) => {
if (invalid) {
(elem as CDSCheckbox).setAttribute('invalid-group', '');
} else {
(elem as CDSCheckbox).removeAttribute('invalid-group');
}
});
}
}

render() {
const {
ariaLabelledBy,
disabled,
helperText,
invalid,
invalidText,
legendId,
legendText,
readonly,
warn,
warnText,
_hasSlug: hasSlug,
_handleSlotChange: handleSlotChange,
} = 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` <div id="${helperId}" class="${prefix}--form__helper-text">
${helperText}
</div>`
: null;

const fieldsetClasses = classMap({
[`${prefix}--checkbox-group`]: true,
[`${prefix}--checkbox-group--readonly`]: readonly,
[`${prefix}--checkbox-group--invalid`]: !readonly && invalid,
[`${prefix}--checkbox-group--warning`]: showWarning,
[`${prefix}--checkbox-group--slug`]: hasSlug,
});

return html`
<fieldset
class="${fieldsetClasses}"
?data-invalid=${invalid}
?disabled=${disabled}
aria-readonly=${readonly}
?aria-labelledby=${ariaLabelledBy || legendId}
?aria-describedby=${!invalid && !warn && helper ? helperId : undefined}>
<legend class="${prefix}--label" id=${legendId || ariaLabelledBy}>
${legendText}
<slot name="slug" @slotchange="${handleSlotChange}"></slot>
</legend>
<slot></slot>
<div class="${prefix}--checkbox-group__validation-msg">
${!readonly && invalid
? html`
${WarningFilled16({
class: `${prefix}--checkbox__invalid-icon`,
})}
<div class="${prefix}--form-requirement">${invalidText}</div>
`
: null}
${showWarning
? html`
${WarningAltFilled16({
class: `${prefix}--checkbox__invalid-icon ${prefix}--checkbox__invalid-icon--warning`,
})}
<div class="${prefix}--form-requirement">${warnText}</div>
`
: null}
</div>
${showHelper ? helper : null}
</fieldset>
`;
}

/**
* A selector that will return the checkboxes.
*/
static get selectorCheckbox() {
return `${prefix}-checkbox`;
}

/**
* A selector that will return the slug item.
*/
static get slugItem() {
return `${prefix}-slug`;
}

static shadowRootOptions = {
...LitElement.shadowRootOptions,
delegatesFocus: true,
};
static styles = styles; // `styles` here is a `CSSResult` generated by custom WebPack loader
}

export default CDSCheckboxGroup;
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ import '@carbon/web-components/es/components/checkbox/index.js';
<cds-checkbox label-text="Lorem Ipsum"></cds-checkbox>
```

Use `cds-checkbox-group` when handling multiple checkboxes

```html
<cds-checkbox-group legend-text="Group label">
<cds-checkbox label-text="Lorem Ipsum 0"></cds-checkbox>
<cds-checkbox label-text="Lorem Ipsum 1"></cds-checkbox>
</cds-checkbox-group>
```

## `<cds-checkbox>` attributes, properties and events

Unlike regular checkbox, `<cds-checkbox>` does _not_ fire `change` event. Please
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ const checkboxLabel = 'Checkbox label';

export const Default = () => {
return html`
<fieldset class="${prefix}--fieldset">
<legend class="${prefix}--label">Group label</legend>
<cds-checkbox-group legend-text="Group label">
<cds-checkbox label-text="${checkboxLabel}"></cds-checkbox>
<cds-checkbox label-text="${checkboxLabel}"></cds-checkbox>
</fieldset>
</cds-checkbox-group>
`;
};

Expand All @@ -39,52 +38,81 @@ export const Skeleton = () => {
`;
};

export const Single = () => {
return html`
<cds-checkbox
label-text="${checkboxLabel}"
helper-text="Helper text goes here"></cds-checkbox>
<br /><br />
<cds-checkbox
label-text="${checkboxLabel}"
invalid
invalid-text="Invalid test goes here"></cds-checkbox>
<br /><br />
<cds-checkbox
label-text="${checkboxLabel}"
warn
warn-text="Warning test goes here"></cds-checkbox>
<br /><br />
<cds-checkbox label-text="${checkboxLabel}" readonly></cds-checkbox>
`;
};

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`
<fieldset class="${prefix}--fieldset">
<legend class="${prefix}--label">Group label</legend>
<cds-checkbox-group
helper-text="${helperText}"
?disabled="${disabled}"
?invalid="${invalid}"
invalid-text="${invalidText}"
legend-text="${legendText}"
?readonly="${readonly}"
?warn="${warn}"
warn-text="${warnText}">
<cds-checkbox
?checked="${checked}"
?disabled="${disabled}"
?hide-label="${hideLabel}"
?indeterminate="${indeterminate}"
label-text="${ifDefined(labelText)}"
?readonly="${readonly}"
title="${ifDefined(title)}"
checked
label-text="Checkbox label"
@cds-checkbox-changed="${onChange}"></cds-checkbox>
<cds-checkbox
?checked="${checked}"
?disabled="${disabled}"
?hide-label="${hideLabel}"
?indeterminate="${indeterminate}"
label-text="${ifDefined(labelText)}"
?readonly="${readonly}"
title="${ifDefined(title)}"
label-text="Checkbox label"
@cds-checkbox-changed="${onChange}"></cds-checkbox>
</fieldset>
<cds-checkbox
disabled
label-text="Checkbox label"
@cds-checkbox-changed="${onChange}"></cds-checkbox>
</cds-checkbox-group>
`;
};

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'),
}),
},
};
Expand Down
Loading

0 comments on commit 887b54d

Please sign in to comment.