From 7ec90dcb06505d2e47733fe7959026a3e8aebdee Mon Sep 17 00:00:00 2001 From: gerjanvangeest Date: Wed, 14 Jun 2023 16:24:34 +0200 Subject: [PATCH] chore: add checkboxIndeterminate test suite (#2013) --- .changeset/neat-poets-hope.md | 5 + docs/components/checkbox-group/overview.md | 15 +- docs/components/checkbox-group/use-cases.md | 252 +++++------ .../src/LionCheckboxIndeterminate.js | 4 +- .../CheckboxIndeterminate.suite.js} | 398 +++++++++--------- ...heckbox-indeterminate-integrations.test.js | 6 + .../ui/exports/checkbox-group-test-suites.js | 1 + 7 files changed, 341 insertions(+), 340 deletions(-) create mode 100644 .changeset/neat-poets-hope.md rename packages/ui/components/checkbox-group/{test/lion-checkbox-indeterminate.test.js => test-suites/CheckboxIndeterminate.suite.js} (65%) create mode 100644 packages/ui/exports/checkbox-group-test-suites.js diff --git a/.changeset/neat-poets-hope.md b/.changeset/neat-poets-hope.md new file mode 100644 index 000000000..65061aef1 --- /dev/null +++ b/.changeset/neat-poets-hope.md @@ -0,0 +1,5 @@ +--- +'@lion/ui': patch +--- + +add CheckboxIndeterminate test suite diff --git a/docs/components/checkbox-group/overview.md b/docs/components/checkbox-group/overview.md index 555dded8f..025213ebd 100644 --- a/docs/components/checkbox-group/overview.md +++ b/docs/components/checkbox-group/overview.md @@ -11,14 +11,12 @@ import '@lion/ui/define/lion-checkbox-group.js'; import '@lion/ui/define/lion-checkbox.js'; ``` -```js preview-story -export const main = () => html` - - - - - -`; +```html preview-story + + + + + ``` > Make sure that the checkbox-group also has a name attribute, this is necessary for our [form](../form/overview.md)'s serialization result. @@ -36,5 +34,6 @@ npm i --save @lion/ui ```js import '@lion/ui/define/lion-checkbox-group.js'; +import '@lion/ui/define/lion-checkbox-indeterminate.js'; import '@lion/ui/define/lion-checkbox.js'; ``` diff --git a/docs/components/checkbox-group/use-cases.md b/docs/components/checkbox-group/use-cases.md index ae161f14d..6496919df 100644 --- a/docs/components/checkbox-group/use-cases.md +++ b/docs/components/checkbox-group/use-cases.md @@ -45,86 +45,72 @@ Our recommendation would be to set the `name` attribute only on the `lion-checkb You can pre-select options by targeting the `modelValue` object of the option and setting the `checked` property to `true`. -```js preview-story -export const preselect = () => html` - - - - - -`; +```html preview-story + + + + + ``` ## Disabled You can disable the entire group by setting the `disabled` attribute on the ``. -```js preview-story -export const disabled = () => html` - - - - - -`; +```html preview-story + + + + + ``` ## Label You can use `slot="label"` instead of the `label` attribute for defining more complex labels, such as containing screen reader only text or an anchor. -```js preview-story -export const label = () => html` - - - - - - - - - - - -`; +```html preview-story + + + + + + + + + + + ``` ## Help text You can add help text on each checkbox with `help-text` attribute on the ``. -```js preview-story -export const helpText = () => html` - - - - - -`; +```html preview-story + + + + + ``` ## Event @@ -156,102 +142,84 @@ export const event = ({ shadowRoot }) => html` A `checkbox-indeterminate`'s value is neither true nor false, but is instead indeterminate, meaning that its state cannot be determined or stated in pure binary terms, based on it's `checkbox` children. -```js preview-story -export const indeterminateSiblings = () => html` - - - - - - - - - - - -`; +```html preview-story + + + + + + + + + + + ``` The `checkbox-indeterminate` can have another `checkbox-indeterminate` as a child. -```js preview-story -export const indeterminateChildren = () => html` - - +```html preview-story + + + + + + - - - - - - -`; + + ``` You can also use `mixed-state` attribute so your indeterminate checkbox toggles through three states (indeterminate, checked, unchecked), where for indeterminate state the old children states are restored when you toggle back into this. -```js preview-story -export const mixedState = () => html` - - +```html preview-story + + + + + + - - - - - - -`; + + ``` diff --git a/packages/ui/components/checkbox-group/src/LionCheckboxIndeterminate.js b/packages/ui/components/checkbox-group/src/LionCheckboxIndeterminate.js index 2e274935f..4f8699ca4 100644 --- a/packages/ui/components/checkbox-group/src/LionCheckboxIndeterminate.js +++ b/packages/ui/components/checkbox-group/src/LionCheckboxIndeterminate.js @@ -13,7 +13,7 @@ export class LionCheckboxIndeterminate extends LionCheckbox { :host .choice-field__nested-checkboxes { display: block; } - ::slotted([slot='checkbox']) { + ::slotted(*) { padding-left: 8px; } `, @@ -198,7 +198,7 @@ export class LionCheckboxIndeterminate extends LionCheckbox { _afterTemplate() { return html`
- +
`; } diff --git a/packages/ui/components/checkbox-group/test/lion-checkbox-indeterminate.test.js b/packages/ui/components/checkbox-group/test-suites/CheckboxIndeterminate.suite.js similarity index 65% rename from packages/ui/components/checkbox-group/test/lion-checkbox-indeterminate.test.js rename to packages/ui/components/checkbox-group/test-suites/CheckboxIndeterminate.suite.js index 7dce4a805..68691a1d4 100644 --- a/packages/ui/components/checkbox-group/test/lion-checkbox-indeterminate.test.js +++ b/packages/ui/components/checkbox-group/test-suites/CheckboxIndeterminate.suite.js @@ -1,9 +1,5 @@ -import { expect, fixture } from '@open-wc/testing'; -import { html } from 'lit/static-html.js'; +import { expect, fixture, html, unsafeStatic } from '@open-wc/testing'; import { getFormControlMembers } from '@lion/ui/form-core-test-helpers.js'; -import '@lion/ui/define/lion-checkbox.js'; -import '@lion/ui/define/lion-checkbox-group.js'; -import '@lion/ui/define/lion-checkbox-indeterminate.js'; /** * @typedef {import('../src/LionCheckbox.js').LionCheckbox} LionCheckbox @@ -25,54 +21,81 @@ function getCheckboxIndeterminateMembers(el) { }; } -describe('', () => { - it('should have type = checkbox', async () => { - // Arrange - const el = await fixture(html` - - `); +/** + * @param {{tagString?: string, groupTagString?: string, childTagString?: string}} [customConfig] + */ +export function runCheckboxIndeterminateSuite(customConfig) { + const cfg = { + tagString: null, + groupTagString: null, + childTagString: null, + ...customConfig, + }; - // Assert - expect(el.getAttribute('type')).to.equal('checkbox'); + /** @type {{_$litStatic$: any}} */ + let tag; + /** @type {{_$litStatic$: any}} */ + let groupTag; + /** @type {{_$litStatic$: any}} */ + let childTag; + + before(async () => { + tag = unsafeStatic(cfg.tagString); + groupTag = unsafeStatic(cfg.groupTagString); + childTag = unsafeStatic(cfg.childTagString); }); - it('should not be indeterminate by default if all children are unchecked', async () => { - // Arrange - const el = await fixture(html` - - - - - - - - `); - const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') - ); + describe('CheckboxIndeterminate', async () => { + /** @type {{_$litStatic$: any}} */ - // Assert - expect(elIndeterminate?.hasAttribute('indeterminate')).to.be.false; + it('should have type = checkbox', async () => { + // Arrange + const el = await fixture(html` + <${tag} + name="checkbox" + .choiceValue="${'male'}" + > + `); + + // Assert + expect(el.getAttribute('type')).to.equal('checkbox'); + }); + + it('should not be indeterminate by default if all children are unchecked', async () => { + // Arrange + const el = await fixture(html` + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes"> + <${childTag} label="Francis Bacon"> + <${childTag} label="Marie Curie"> + + + `); + const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( + el.querySelector(`${cfg.tagString}`) + ); + + // Assert + expect(elIndeterminate?.hasAttribute('indeterminate')).to.be.false; + }); }); it('should be indeterminate if one child is checked', async () => { // Arrange const el = /** @type {LionCheckboxGroup} */ await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes"> + <${childTag} label="Francis Bacon" checked> + <${childTag} label="Marie Curie"> + + `); - const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') - ); + const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( + el.querySelector(`${cfg.tagString}`) + ); // Assert expect(elIndeterminate?.hasAttribute('indeterminate')).to.be.true; }); @@ -81,17 +104,17 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes" checked> + <${childTag} label="Francis Bacon" checked> + <${childTag} label="Marie Curie" checked> + + `) ); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); // Assert @@ -103,17 +126,17 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes"> + <${childTag} label="Francis Bacon"> + <${childTag} label="Marie Curie"> + + `) ); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const { _subCheckboxes } = getCheckboxIndeterminateMembers(elIndeterminate); @@ -130,17 +153,17 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes"> + <${childTag} label="Francis Bacon"> + <${childTag} label="Marie Curie"> + + `) ); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const { _subCheckboxes } = getCheckboxIndeterminateMembers(elIndeterminate); @@ -159,17 +182,17 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes"> + <${childTag} label="Francis Bacon" disabled> + <${childTag} label="Marie Curie"> + + `) ); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const { _subCheckboxes } = getCheckboxIndeterminateMembers(elIndeterminate); @@ -187,17 +210,17 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes"> + <${childTag} label="Francis Bacon" checked> + <${childTag} label="Marie Curie"> + + `) ); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const { _subCheckboxes, _inputNode } = getCheckboxIndeterminateMembers(elIndeterminate); @@ -216,17 +239,17 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes"> + <${childTag} label="Francis Bacon" disabled> + <${childTag} label="Marie Curie"> + + `) ); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const { _subCheckboxes, _inputNode } = getCheckboxIndeterminateMembers(elIndeterminate); @@ -245,17 +268,17 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes" disabled> + <${childTag} label="Francis Bacon" disabled> + <${childTag} label="Marie Curie" disabled> + + `) ); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const { _subCheckboxes, _inputNode } = getCheckboxIndeterminateMembers(elIndeterminate); @@ -275,17 +298,17 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes" disabled checked> + <${childTag} label="Francis Bacon" disabled checked> + <${childTag} label="Marie Curie" disabled checked> + + `) ); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const { _subCheckboxes, _inputNode } = getCheckboxIndeterminateMembers(elIndeterminate); @@ -305,17 +328,17 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes"> + <${childTag} label="Francis Bacon"> + <${childTag} label="Marie Curie"> + + `) ); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const { _subCheckboxes, _inputNode } = getCheckboxIndeterminateMembers(elIndeterminate); @@ -334,17 +357,17 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} label="Favorite scientists"> + <${childTag} label="Archimedes" checked> + <${childTag} label="Francis Bacon" checked> + <${childTag} label="Marie Curie" checked> + + `) ); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const { _subCheckboxes, _inputNode } = getCheckboxIndeterminateMembers(elIndeterminate); @@ -363,39 +386,39 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - + <${tag} label="Old Greek scientists" id="first-checkbox-indeterminate" > - - - + <${childTag} label="Plato" .choiceValue=${'Plato'}> + <${childTag} slot="checkbox" label="Pythagoras" .choiceValue=${'Pythagoras'} - > - - + + <${tag} label="17th Century scientists" id="second-checkbox-indeterminate" > - - + <${childTag} slot="checkbox" label="Galileo Galilei" .choiceValue=${'Galileo Galilei'} - > - - + > + + `) ); const elFirstIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( @@ -429,37 +452,37 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - - - + <${tag} label="Scientists" id="parent-checkbox-indeterminate"> + <${childTag} slot="checkbox" label="Isaac Newton" .choiceValue=${'Isaac Newton'} - > - + <${childTag} slot="checkbox" label="Galileo Galilei" .choiceValue=${'Galileo Galilei'} - > - + <${tag} slot="checkbox" label="Old Greek scientists" id="nested-checkbox-indeterminate" > - - - + <${childTag} label="Plato" .choiceValue=${'Plato'}> + <${childTag} slot="checkbox" label="Pythagoras" .choiceValue=${'Pythagoras'} - > - - - + > + + + `) ); const elNestedIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( @@ -517,24 +540,24 @@ describe('', () => { // Arrange const el = /** @type {LionCheckboxGroup} */ ( await fixture(html` - + <${groupTag} name="scientists[]">
Let's have some fun
Hello I'm a div
- + <${tag} label="Favorite scientists">
useless div
- - + <${childTag} label="Archimedes"> + <${childTag} label="Francis Bacon">
absolutely useless
- -
+ <${childTag} label="Marie Curie"> +
Too much fun, stop it !
-
+ `) ); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const { _subCheckboxes } = getCheckboxIndeterminateMembers(elIndeterminate); @@ -553,16 +576,16 @@ describe('', () => { describe('mixed-state', () => { it('can have a mixed-state (using mixed-state attribute), none -> indeterminate -> all, cycling through', async () => { const el = await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} mixed-state label="Favorite scientists"> + <${childTag} label="Archimedes" checked> + <${childTag} label="Francis Bacon"> + <${childTag} label="Marie Curie"> + + `); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); expect(elIndeterminate.mixedState).to.be.true; @@ -590,19 +613,19 @@ describe('', () => { it('should reset to old child checkbox states when reaching indeterminate state', async () => { const el = await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} mixed-state label="Favorite scientists"> + <${childTag} label="Archimedes" checked> + <${childTag} label="Francis Bacon"> + <${childTag} label="Marie Curie"> + + `); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const checkboxEls = /** @type {LionCheckbox[]} */ ( - Array.from(el.querySelectorAll('lion-checkbox')) + Array.from(el.querySelectorAll(`${cfg.childTagString}`)) ); expect(checkboxEls.map(checkboxEl => checkboxEl.checked)).to.eql([true, false, false]); @@ -625,19 +648,19 @@ describe('', () => { it('should no longer reach indeterminate state if the child boxes are all checked or all unchecked during indeterminate state', async () => { const el = await fixture(html` - - - - - - - + <${groupTag} name="scientists[]"> + <${tag} mixed-state label="Favorite scientists"> + <${childTag} label="Archimedes" checked> + <${childTag} label="Francis Bacon"> + <${childTag} label="Marie Curie"> + + `); const elIndeterminate = /** @type {LionCheckboxIndeterminate} */ ( - el.querySelector('lion-checkbox-indeterminate') + el.querySelector(`${cfg.tagString}`) ); const checkboxEls = /** @type {LionCheckbox[]} */ ( - Array.from(el.querySelectorAll('lion-checkbox')) + Array.from(el.querySelectorAll(`${cfg.childTagString}`)) ); // Check when all child boxes in indeterminate state are unchecked @@ -671,7 +694,6 @@ describe('', () => { // @ts-ignore for testing purposes, we access this protected getter elIndeterminate._inputNode.click(); // unchecked await elIndeterminate.updateComplete; - for (const checkEl of checkboxEls) { // @ts-ignore for testing purposes, we access this protected getter checkEl._inputNode.click(); @@ -697,4 +719,4 @@ describe('', () => { expect(elIndeterminate.indeterminate).to.be.false; }); }); -}); +} diff --git a/packages/ui/components/checkbox-group/test/lion-checkbox-indeterminate-integrations.test.js b/packages/ui/components/checkbox-group/test/lion-checkbox-indeterminate-integrations.test.js index 0f01a6816..ec076ada7 100644 --- a/packages/ui/components/checkbox-group/test/lion-checkbox-indeterminate-integrations.test.js +++ b/packages/ui/components/checkbox-group/test/lion-checkbox-indeterminate-integrations.test.js @@ -1,4 +1,10 @@ import '@lion/ui/define/lion-checkbox-indeterminate.js'; import { runChoiceInputMixinSuite } from '@lion/ui/form-core-test-suites.js'; +import { runCheckboxIndeterminateSuite } from '../test-suites/CheckboxIndeterminate.suite.js'; runChoiceInputMixinSuite({ tagString: 'lion-checkbox-indeterminate' }); +runCheckboxIndeterminateSuite({ + tagString: 'lion-checkbox-indeterminate', + groupTagString: 'lion-checkbox-group', + childTagString: 'lion-checkbox', +}); diff --git a/packages/ui/exports/checkbox-group-test-suites.js b/packages/ui/exports/checkbox-group-test-suites.js new file mode 100644 index 000000000..e8a25382f --- /dev/null +++ b/packages/ui/exports/checkbox-group-test-suites.js @@ -0,0 +1 @@ +export { runCheckboxIndeterminateSuite } from '../components/checkbox-group/test-suites/CheckboxIndeterminate.suite.js';