This commit is contained in:
Cory LaViska
2020-07-14 09:22:30 -04:00
parent c317d0f534
commit ea6ac8da8d
7 changed files with 120 additions and 29 deletions

View File

@@ -137,9 +137,4 @@ Show a valid or invalid state by setting the `valid` and `invalid` attributes, r
</sl-input>
```
The easiest way to handle validation in Shoelace is with the `valid` and `invalid` props shown above. This is designed to work well with client-side and server-side validation.
[HTML form validation](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#Using_built-in_form_validation) is also possible with limitations. For example, providing validation styles to all the component's internals (e.g. prefix, suffix, labels, and help text) is not a trivial task with this approach. If you only care about styling the underlying _native input_, you can target the component's `input` part with the `:valid` and `:invalid` selector.
[component-metadata:sl-input]

View File

@@ -32,6 +32,20 @@ Use the `label` attribute to give the select an accessible label.
</sl-select>
```
### Help Text
Add descriptive help text to an input with the `help-text` slot.
```html preview
<sl-select label="Experience">
<sl-menu-item value="option-1">Novice</sl-menu-item>
<sl-menu-item value="option-2">Intermediate</sl-menu-item>
<sl-menu-item value="option-3">Advanced</sl-menu-item>
<div slot="help-text">Please tell us your skill level.</div>
</sl-select>
```
### Multiple
To allow multiple options to be selected, use the `multiple` attribute.
@@ -118,4 +132,28 @@ Use the `disabled` prop to disable a select.
</sl-select>
```
### Validation
Show a valid or invalid state by setting the `valid` and `invalid` attributes, respectively. Help text can be used to provide feedback for validation and will be styled accordingly.
```html preview
<sl-select placeholder="Valid" valid>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<div slot="help-text">This is a valid selection!</div>
</sl-select>
<br>
<sl-select placeholder="Invalid" invalid>
<sl-menu-item value="option-1">Option 1</sl-menu-item>
<sl-menu-item value="option-2">Option 2</sl-menu-item>
<sl-menu-item value="option-3">Option 3</sl-menu-item>
<div slot="help-text">This is not a valid selection!</div>
</sl-select>
```
[component-metadata:sl-select]

16
src/components.d.ts vendored
View File

@@ -568,6 +568,10 @@ export namespace Components {
* Set to true to disable the select control.
*/
"disabled": boolean;
/**
* Set to true to indicate that the user input is invalid.
*/
"invalid": boolean;
/**
* The select's label.
*/
@@ -596,6 +600,10 @@ export namespace Components {
* The select's size.
*/
"size": 'small' | 'medium' | 'large';
/**
* Set to true to indicate that the user input is valid.
*/
"valid": boolean;
/**
* The value of the control. This will be a string or an array depending on `multiple`.
*/
@@ -1682,6 +1690,10 @@ declare namespace LocalJSX {
* Set to true to disable the select control.
*/
"disabled"?: boolean;
/**
* Set to true to indicate that the user input is invalid.
*/
"invalid"?: boolean;
/**
* The select's label.
*/
@@ -1722,6 +1734,10 @@ declare namespace LocalJSX {
* The select's size.
*/
"size"?: 'small' | 'medium' | 'large';
/**
* Set to true to indicate that the user input is valid.
*/
"valid"?: boolean;
/**
* The value of the control. This will be a string or an array depending on `multiple`.
*/

View File

@@ -6,7 +6,6 @@ let id = 0;
* @since 1.0
* @status stable
*
* @slot label - The input's label. Alternatively, you can use the label prop.
* @slot prefix - Used to prepend an icon or similar element to the input.
* @slot suffix - Used to append an icon or similar element to the input.
* @slot clear-icon - An icon to use in lieu of the default clear icon.
@@ -242,7 +241,7 @@ export class Input {
}}
htmlFor={this.inputId}
>
<slot name="label">{this.label}</slot>
{this.label}
</label>
<div

View File

@@ -57,3 +57,35 @@
.select--open .select__icon sl-icon {
transform: rotate(-180deg);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Help text
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
.help-text {
color: var(--sl-input-help-text-color);
&.help-text--small {
font-size: var(--sl-input-help-text-font-size-small);
}
&.help-text--medium {
font-size: var(--sl-input-help-text-font-size-medium);
}
&.help-text--large {
font-size: var(--sl-input-help-text-font-size-large);
}
&.help-text--valid {
color: var(--sl-input-help-text-color-valid);
}
&.help-text--invalid {
color: var(--sl-input-help-text-color-invalid);
}
::slotted(*) {
margin-top: var(--sl-spacing-xxx-small);
}
}

View File

@@ -9,14 +9,13 @@ let id = 0;
* @status stable
*
* @slot - The select's options in the form of menu items.
* @slot label - The select's label. Alternatively, you can use the label prop.
* @slot help-text - Help text that describes how to use the select.
*
* @part base - The component's base wrapper.
* @part form-control - The form control that wraps the label and the select.
* @part label - The select label.
* @part input - The select input.
* @part tags - The container in which multiselect options are rendered.
* @part icon - The select icon.
* @part help-text - The select help text.
*/
@Component({
@@ -39,8 +38,9 @@ export class Select {
dropdown: HTMLSlDropdownElement;
input: HTMLSlInputElement;
inputId = `input-${++id}`;
labelId = `input-label-${id}`;
inputId = `select-${++id}`;
labelId = `select-label-${id}`;
helpTextId = `select-help-text-${id}`;
menu: HTMLSlMenuElement;
resizeObserver: any;
@@ -82,6 +82,12 @@ export class Select {
/** The select's label. */
@Prop() label = '';
/** Set to true to indicate that the user input is valid. */
@Prop() valid = false;
/** Set to true to indicate that the user input is invalid. */
@Prop() invalid = false;
@Watch('multiple')
handleMultipleChange() {
// Cast to array | string based on `this.multiple`
@@ -282,22 +288,11 @@ export class Select {
part="form-control"
class={{
'form-control': true,
'form-control--has-label': this.label.length > 0
'form-control--has-label': this.label.length > 0,
'form-control--valid': this.valid,
'form-control--invalid': this.invalid
}}
>
<label
part="label"
class={{
label: true,
'label--small': this.size === 'small',
'label--medium': this.size === 'medium',
'label--large': this.size === 'large'
}}
htmlFor={this.inputId}
onClick={this.handleLabelClick}
>
<slot name="label">{this.label}</slot>
</label>
<sl-dropdown
part="base"
ref={el => (this.dropdown = el)}
@@ -327,9 +322,12 @@ export class Select {
value={this.displayLabel}
disabled={this.disabled}
pill={this.pill}
label={this.label}
placeholder={this.displayLabel === '' && this.displayTags.length === 0 ? this.placeholder : null}
readonly={true}
size={this.size}
valid={this.valid}
invalid={this.invalid}
aria-labelledby={this.labelId}
onSlFocus={this.handleFocus}
onSlBlur={this.handleBlur}
@@ -355,6 +353,21 @@ export class Select {
<slot />
</sl-menu>
</sl-dropdown>
<div
part="help-text"
id={this.helpTextId}
class={{
'help-text': true,
'help-text--small': this.size === 'small',
'help-text--medium': this.size === 'medium',
'help-text--large': this.size === 'large',
'help-text--valid': this.valid,
'help-text--invalid': this.invalid
}}
>
<slot name="help-text" />
</div>
</div>
);
}

View File

@@ -7,8 +7,6 @@ let id = 0;
* @since 1.0
* @status stable
*
* @slot label - The textarea's label. Alternatively, you can use the label prop.
*
* @part base - The component's base wrapper.
* @part form-control - The form control that wraps the textarea and label.
* @part label - The textarea label.
@@ -197,7 +195,7 @@ export class Textarea {
}}
htmlFor={this.textareaId}
>
<slot name="label">{this.label}</slot>
{this.label}
</label>
<div
part="base"