diff --git a/src/components/checkbox/checkbox.spec.ts b/src/components/checkbox/checkbox.spec.ts index 208e50608..5ad691bbe 100644 --- a/src/components/checkbox/checkbox.spec.ts +++ b/src/components/checkbox/checkbox.spec.ts @@ -5,10 +5,14 @@ import { html, unsafeStatic, } from '@open-wc/testing'; +import type { TemplateResult } from 'lit'; import { spy } from 'sinon'; - -import { IgcCheckboxComponent, defineComponents } from '../../index.js'; -import { FormAssociatedTestBed } from '../common/utils.spec.js'; +import { defineComponents } from '../common/definitions/defineComponents.js'; +import { + FormAssociatedTestBed, + checkValidationSlots, +} from '../common/utils.spec.js'; +import IgcCheckboxComponent from './checkbox.js'; describe('Checkbox', () => { before(() => { @@ -16,7 +20,7 @@ describe('Checkbox', () => { }); const label = 'Label'; - let el: IgcCheckboxComponent; + let element: IgcCheckboxComponent; let input: HTMLInputElement; const DIFF_OPTIONS = { @@ -33,50 +37,50 @@ describe('Checkbox', () => { describe('', () => { beforeEach(async () => { - el = await createCheckboxComponent(); - input = el.renderRoot.querySelector('input') as HTMLInputElement; + element = await createCheckboxComponent(); + input = element.renderRoot.querySelector('input') as HTMLInputElement; }); it('should initialize checkbox component with default values', async () => { expect(input.id).to.equal('checkbox-1'); - expect(el.name).to.be.undefined; + expect(element.name).to.be.undefined; expect(input.name).to.equal(''); - expect(el.value).to.be.undefined; + expect(element.value).to.be.undefined; expect(input.value).to.equal('on'); - expect(el.checked).to.equal(false); + expect(element.checked).to.equal(false); expect(input.checked).to.equal(false); - expect(el.indeterminate).to.equal(false); + expect(element.indeterminate).to.equal(false); expect(input.indeterminate).to.equal(false); - expect(el.invalid).to.equal(false); - expect(el.disabled).to.equal(false); + expect(element.invalid).to.equal(false); + expect(element.disabled).to.equal(false); expect(input.disabled).to.equal(false); - expect(el.labelPosition).to.equal('after'); + expect(element.labelPosition).to.equal('after'); }); it('should render a checkbox component successfully', async () => { - await expect(el).shadowDom.to.be.accessible(); + await expect(element).shadowDom.to.be.accessible(); }); it('should set the checkbox name property correctly', async () => { const name = 'fruit'; - el.name = name; - expect(el.name).to.equal(name); + element.name = name; + expect(element.name).to.equal(name); - await elementUpdated(el); + await elementUpdated(element); expect(input).dom.to.equal(``, DIFF_OPTIONS); }); it('should set the checkbox label position correctly', async () => { - el.labelPosition = 'before'; - await elementUpdated(el); - expect(el).dom.to.equal( + element.labelPosition = 'before'; + await elementUpdated(element); + expect(element).dom.to.equal( `${label}` ); - el.labelPosition = 'after'; - await elementUpdated(el); - expect(el).dom.to.equal( + element.labelPosition = 'after'; + await elementUpdated(element); + expect(element).dom.to.equal( `${label}` ); }); @@ -84,42 +88,42 @@ describe('Checkbox', () => { it('should set the checkbox value property correctly', async () => { const value = 'apple'; - el.value = value; - expect(el.value).to.equal(value); + element.value = value; + expect(element.value).to.equal(value); - await elementUpdated(el); + await elementUpdated(element); expect(input).dom.to.equal(``, DIFF_OPTIONS); }); it('should set the checkbox invalid property correctly', async () => { - el.invalid = true; - await elementUpdated(el); - expect(el).dom.to.equal( + element.invalid = true; + await elementUpdated(element); + expect(element).dom.to.equal( `${label}` ); - el.invalid = false; - await elementUpdated(el); - expect(el).dom.to.equal( + element.invalid = false; + await elementUpdated(element); + expect(element).dom.to.equal( `${label}` ); }); it('should correctly report validity status', async () => { - el = await fixture( + element = await fixture( html`` ); - expect(el.reportValidity()).to.equals(false); + expect(element.reportValidity()).to.equals(false); - el = await fixture( + element = await fixture( html`` ); - expect(el.reportValidity()).to.equals(true); + expect(element.reportValidity()).to.equals(true); - el = await fixture( + element = await fixture( html`` ); - expect(el.reportValidity()).to.equals(true); + expect(element.reportValidity()).to.equals(true); }); it('should set the checkbox checked property correctly', async () => { @@ -134,14 +138,14 @@ describe('Checkbox', () => { ], }; - el.checked = true; - expect(el.checked).to.be.true; - await elementUpdated(el); + element.checked = true; + expect(element.checked).to.be.true; + await elementUpdated(element); expect(input).dom.to.equal(``, DIFF_OPTIONS); - el.checked = false; - expect(el.checked).to.be.false; - await elementUpdated(el); + element.checked = false; + expect(element.checked).to.be.false; + await elementUpdated(element); expect(input).dom.to.equal( ``, @@ -161,27 +165,27 @@ describe('Checkbox', () => { ], }; - el.indeterminate = true; - expect(el.indeterminate).to.be.true; - await elementUpdated(el); + element.indeterminate = true; + expect(element.indeterminate).to.be.true; + await elementUpdated(element); expect(input).dom.to.equal( ``, DIFF_OPTIONS ); - el.indeterminate = false; - expect(el.indeterminate).to.be.false; - await elementUpdated(el); + element.indeterminate = false; + expect(element.indeterminate).to.be.false; + await elementUpdated(element); expect(input).dom.to.equal( ``, DIFF_OPTIONS ); - el.indeterminate = true; - expect(el.indeterminate).to.be.true; - el.checked = true; - expect(el.checked).to.be.true; - await elementUpdated(el); + element.indeterminate = true; + expect(element.indeterminate).to.be.true; + element.checked = true; + expect(element.checked).to.be.true; + await elementUpdated(element); expect(input).dom.to.equal(``, DIFF_OPTIONS); }); @@ -197,17 +201,17 @@ describe('Checkbox', () => { ], }; - el.disabled = true; - expect(el.disabled).to.be.true; - await elementUpdated(el); + element.disabled = true; + expect(element.disabled).to.be.true; + await elementUpdated(element); expect(input).dom.to.equal( ``, DIFF_OPTIONS ); - el.disabled = false; - expect(el.disabled).to.be.false; - await elementUpdated(el); + element.disabled = false; + expect(element.disabled).to.be.false; + await elementUpdated(element); expect(input).dom.to.equal( ``, @@ -216,24 +220,24 @@ describe('Checkbox', () => { }); it('should emit igcFocus/igcBlur events when the checkbox gains/loses focus', () => { - const eventSpy = spy(el, 'emitEvent'); - el.focus(); + const eventSpy = spy(element, 'emitEvent'); + element.focus(); - expect(el.shadowRoot?.activeElement).to.equal(input); + expect(element.shadowRoot?.activeElement).to.equal(input); expect(eventSpy).calledOnceWithExactly('igcFocus'); - el.blur(); + element.blur(); - expect(el.shadowRoot?.activeElement).to.be.null; + expect(element.shadowRoot?.activeElement).to.be.null; expect(eventSpy).calledTwice; expect(eventSpy).calledWithExactly('igcBlur'); }); it('should emit igcChange event when the checkbox checked state changes', async () => { - const eventSpy = spy(el, 'emitEvent'); - el.click(); + const eventSpy = spy(element, 'emitEvent'); + element.click(); - await elementUpdated(el); + await elementUpdated(element); expect(eventSpy).calledWithExactly('igcChange', { detail: true }); }); @@ -377,4 +381,41 @@ describe('Checkbox', () => { ); }); }); + + describe('Validation message slots', () => { + async function createFixture(template: TemplateResult) { + element = await fixture(template); + } + + it('renders value-missing slot', async () => { + await createFixture(html` + +
+
+ `); + + await checkValidationSlots(element, 'valueMissing'); + }); + + it('renders custom-error slot', async () => { + await createFixture(html` + +
+
+ `); + + element.setCustomValidity('invalid'); + await checkValidationSlots(element, 'customError'); + }); + + it('renders invalid slot', async () => { + await createFixture(html` + +
+
+ `); + + await checkValidationSlots(element, 'invalid'); + }); + }); }); diff --git a/src/components/checkbox/checkbox.ts b/src/components/checkbox/checkbox.ts index 702094ce9..f5df6d029 100644 --- a/src/components/checkbox/checkbox.ts +++ b/src/components/checkbox/checkbox.ts @@ -1,4 +1,4 @@ -import { html } from 'lit'; +import { type TemplateResult, html } from 'lit'; import { property } from 'lit/decorators.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { live } from 'lit/directives/live.js'; @@ -6,6 +6,7 @@ import { live } from 'lit/directives/live.js'; import { themes } from '../../theming/theming-decorator.js'; import { registerComponent } from '../common/definitions/register.js'; import { createCounter, partNameMap } from '../common/util.js'; +import IgcValidationContainerComponent from '../validation-container/validation-container.js'; import { IgcCheckboxBaseComponent } from './checkbox-base.js'; import { all } from './themes/checkbox-themes.js'; import { styles } from './themes/checkbox.base.css.js'; @@ -17,6 +18,10 @@ import { styles as shared } from './themes/shared/checkbox/checkbox.common.css.j * @element igc-checkbox * * @slot - The checkbox label. + * @slot helper-text - Renders content below the input. + * @slot value-missing - Renders content when the required validation fails. + * @slot custom-error - Renders content when setCustomValidity(message) is set. + * @slot invalid - Renders content when the component is in invalid state (validity.valid = false). * * @fires igcChange - Emitted when the control's checked state changes. * @fires igcFocus - Emitted when the control gains focus. @@ -34,7 +39,7 @@ export default class IgcCheckboxComponent extends IgcCheckboxBaseComponent { /* blazorSuppress */ public static register() { - registerComponent(IgcCheckboxComponent); + registerComponent(IgcCheckboxComponent, IgcValidationContainerComponent); } private static readonly increment = createCounter(); @@ -53,14 +58,19 @@ export default class IgcCheckboxComponent extends IgcCheckboxBaseComponent { super.handleClick(); } + protected renderValidatorContainer(): TemplateResult { + return IgcValidationContainerComponent.create(this); + } + protected override render() { const labelledBy = this.getAttribute('aria-labelledby'); + const checked = this.checked; return html` + + ${this.renderValidatorContainer()} `; } } diff --git a/src/components/checkbox/switch.spec.ts b/src/components/checkbox/switch.spec.ts index 7a07870bf..5cd20fb25 100644 --- a/src/components/checkbox/switch.spec.ts +++ b/src/components/checkbox/switch.spec.ts @@ -6,9 +6,9 @@ import { unsafeStatic, } from '@open-wc/testing'; import { spy } from 'sinon'; - -import { IgcSwitchComponent, defineComponents } from '../../index.js'; +import { defineComponents } from '../common/definitions/defineComponents.js'; import { FormAssociatedTestBed } from '../common/utils.spec.js'; +import IgcSwitchComponent from './switch.js'; describe('Switch', () => { before(() => { diff --git a/src/components/checkbox/switch.ts b/src/components/checkbox/switch.ts index d55c5296d..2c90dd26e 100644 --- a/src/components/checkbox/switch.ts +++ b/src/components/checkbox/switch.ts @@ -43,10 +43,11 @@ export default class IgcSwitchComponent extends IgcCheckboxBaseComponent { protected override render() { const labelledBy = this.getAttribute('aria-labelledby'); + const checked = this.checked; return html`