feat(select-rich): add has no default selection feature
This commit is contained in:
parent
29e1252560
commit
975a01aca9
4 changed files with 127 additions and 3 deletions
|
|
@ -68,7 +68,15 @@ export class LionSelectInvoker extends LionButton {
|
|||
}
|
||||
return this.selectedElement.textContent;
|
||||
}
|
||||
return ``;
|
||||
return this._noSelectionTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overriden for a placeholder, used when `hasNoDefaultSelected` is true on the select rich
|
||||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
_noSelectionTemplate() {
|
||||
return html``;
|
||||
}
|
||||
|
||||
_beforeTemplate() {
|
||||
|
|
|
|||
|
|
@ -79,6 +79,17 @@ export class LionSelectRich extends ScopedElementsMixin(
|
|||
type: String,
|
||||
attribute: 'interaction-mode',
|
||||
},
|
||||
|
||||
/**
|
||||
* When setting this to true, on initial render, no option will be selected.
|
||||
* It it advisable to override `_noSelectionTemplate` method in the select-invoker
|
||||
* to render some kind of placeholder initially
|
||||
*/
|
||||
hasNoDefaultSelected: {
|
||||
type: Boolean,
|
||||
reflect: true,
|
||||
attribute: 'has-no-default-selected',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -184,6 +195,7 @@ export class LionSelectRich extends ScopedElementsMixin(
|
|||
// for interaction states
|
||||
this._listboxActiveDescendant = null;
|
||||
this.__hasInitialSelectedFormElement = false;
|
||||
this.hasNoDefaultSelected = false;
|
||||
this._repropagationRole = 'choice-group'; // configures FormControlMixin
|
||||
this.__setupEventListeners();
|
||||
this.__initInteractionStates();
|
||||
|
|
@ -335,7 +347,11 @@ export class LionSelectRich extends ScopedElementsMixin(
|
|||
}
|
||||
|
||||
// the first elements checked by default
|
||||
if (!this.__hasInitialSelectedFormElement && (!child.disabled || this.disabled)) {
|
||||
if (
|
||||
!this.hasNoDefaultSelected &&
|
||||
!this.__hasInitialSelectedFormElement &&
|
||||
(!child.disabled || this.disabled)
|
||||
) {
|
||||
child.active = true;
|
||||
child.checked = true;
|
||||
this.__hasInitialSelectedFormElement = true;
|
||||
|
|
|
|||
|
|
@ -462,6 +462,51 @@ console.log(`checkedIndex: ${selectEl.checkedIndex}`); // 0
|
|||
console.log(`checkedValue: ${selectEl.checkedValue}`); // 'red'
|
||||
```
|
||||
|
||||
### No default selection
|
||||
|
||||
If you want to set a placeholder option with something like 'Please select', you can of course do this, the same way you would do it in a native select.
|
||||
|
||||
Simply put an option with a modelValue that is `null`.
|
||||
|
||||
```html
|
||||
<lion-option .choiceValue=${null}>select a color</lion-option>
|
||||
```
|
||||
|
||||
However, this allows the user to explicitly select this option.
|
||||
|
||||
Often, you may want a placeholder that appears initially, but cannot be selected explicitly by the user.
|
||||
For this you can use `has-no-default-selected` attribute.
|
||||
|
||||
Both methods work with the `Required` validator.
|
||||
|
||||
<Story name="No default selected">
|
||||
{html`
|
||||
<lion-select-rich name="favoriteColor" label="Favorite color" has-no-default-selected>
|
||||
<lion-options slot="input">
|
||||
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
||||
<lion-option .choiceValue=${'hotpink'}>Hotpink</lion-option>
|
||||
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
||||
</lion-options>
|
||||
</lion-select-rich>
|
||||
`}
|
||||
</Story>
|
||||
|
||||
|
||||
```html
|
||||
<lion-select-rich name="favoriteColor" label="Favorite color" has-no-default-selected>
|
||||
<lion-options slot="input">
|
||||
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
||||
<lion-option .choiceValue=${'hotpink'}>Hotpink</lion-option>
|
||||
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
||||
</lion-options>
|
||||
</lion-select-rich>
|
||||
```
|
||||
|
||||
> By default, the placeholder is completely empty in the `LionSelectInvoker`,
|
||||
> but subclassers can easily override this in their extension, by the overriding `_noSelectionTemplate()` method.
|
||||
|
||||
|
||||
|
||||
### Custom Invoker
|
||||
|
||||
You can provide a custom invoker using the invoker slot.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { formFixture as fixture } from '@lion/field/test-helpers.js';
|
|||
import { OverlayController } from '@lion/overlays';
|
||||
import { Required } from '@lion/validate';
|
||||
import { aTimeout, defineCE, expect, html, nextFrame, unsafeStatic } from '@open-wc/testing';
|
||||
import { LionSelectRich } from '../index.js';
|
||||
import { LionSelectInvoker, LionSelectRich } from '../index.js';
|
||||
import '../lion-option.js';
|
||||
import '../lion-options.js';
|
||||
import '../lion-select-rich.js';
|
||||
|
|
@ -204,6 +204,21 @@ describe('lion-select-rich', () => {
|
|||
expect(el.showsFeedbackFor.includes('error')).to.be.true;
|
||||
});
|
||||
|
||||
it('supports having no default selection initially', async () => {
|
||||
const el = await fixture(html`
|
||||
<lion-select-rich id="color" name="color" label="Favorite color" has-no-default-selected>
|
||||
<lion-options slot="input">
|
||||
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
||||
<lion-option .choiceValue=${'hotpink'} disabled>Hotpink</lion-option>
|
||||
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
||||
</lion-options>
|
||||
</lion-select-rich>
|
||||
`);
|
||||
|
||||
expect(el.selectedElement).to.be.undefined;
|
||||
expect(el.modelValue).to.equal('');
|
||||
});
|
||||
|
||||
describe('Invoker', () => {
|
||||
it('generates an lion-select-invoker if no invoker is provided', async () => {
|
||||
const el = await fixture(html`
|
||||
|
|
@ -711,5 +726,45 @@ describe('lion-select-rich', () => {
|
|||
el.dispatchEvent(new Event('switch'));
|
||||
expect(el._overlayCtrl.placementMode).to.equal('local');
|
||||
});
|
||||
|
||||
it('supports putting a placeholder template when there is no default selection initially', async () => {
|
||||
const invokerTagName = defineCE(
|
||||
class extends LionSelectInvoker {
|
||||
_noSelectionTemplate() {
|
||||
return html`
|
||||
Please select an option..
|
||||
`;
|
||||
}
|
||||
},
|
||||
);
|
||||
const invokerTag = unsafeStatic(invokerTagName);
|
||||
|
||||
const selectTagName = defineCE(
|
||||
class extends LionSelectRich {
|
||||
get slots() {
|
||||
return {
|
||||
...super.slots,
|
||||
invoker: () => document.createElement(invokerTag.d),
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
const selectTag = unsafeStatic(selectTagName);
|
||||
|
||||
const el = await fixture(html`
|
||||
<${selectTag} id="color" name="color" label="Favorite color" has-no-default-selected>
|
||||
<lion-options slot="input">
|
||||
<lion-option .choiceValue=${'red'}>Red</lion-option>
|
||||
<lion-option .choiceValue=${'hotpink'} disabled>Hotpink</lion-option>
|
||||
<lion-option .choiceValue=${'teal'}>Teal</lion-option>
|
||||
</lion-options>
|
||||
</${selectTag}>
|
||||
`);
|
||||
|
||||
expect(el._invokerNode.shadowRoot.getElementById('content-wrapper')).dom.to.equal(
|
||||
`<div id="content-wrapper">Please select an option..</div>`,
|
||||
);
|
||||
expect(el.modelValue).to.equal('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue