refactor form control parts

pull/721/head
Cory LaViska 2022-03-18 17:33:23 -04:00
rodzic fdeb7689d7
commit c26b1335f5
16 zmienionych plików z 134 dodań i 81 usunięć

Wyświetl plik

@ -262,4 +262,31 @@ const App = () => (
);
```
### Customizing Label Position
Use parts to to customize the label's position.
```html preview
<sl-input class="label-on-left" label="Name"></sl-input><br />
<sl-input class="label-on-left" label="Email" type="email"></sl-input>
<style>
.label-on-left::part(form-control) {
display: flex;
align-items: center;
gap: 1rem;
}
.label-on-left::part(form-control-label) {
flex: 0 0 auto;
width: 60px;
text-align: right;
}
.label-on-left::part(form-control-input) {
flex: 1 1 auto;
}
</style>
```
[component-metadata:sl-input]

Wyświetl plik

@ -36,6 +36,54 @@ const App = () => (
## Examples
### Labels
Use the `label` attribute to give the select an accessible label. For labels that contain HTML, use the `label` slot instead.
```html preview
<sl-select label="Select one">
<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>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect label="Select one">
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</SlSelect>
);
```
### Help Text
Add descriptive help text to a select with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
```html preview
<sl-select label="Experience" help-text="Please tell us your skill level.">
<sl-menu-item value="1">Novice</sl-menu-item>
<sl-menu-item value="2">Intermediate</sl-menu-item>
<sl-menu-item value="3">Advanced</sl-menu-item>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect label="Experience" help-text="Please tell us your skill level.">
<SlMenuItem value="1">Novice</SlMenuItem>
<SlMenuItem value="2">Intermediate</SlMenuItem>
<SlMenuItem value="3">Advanced</SlMenuItem>
</SlSelect>
);
```
### Placeholders
Use the `placeholder` attribute to add a placeholder.
@ -364,54 +412,6 @@ const App = () => (
);
```
### Labels
Use the `label` attribute to give the select an accessible label. For labels that contain HTML, use the `label` slot instead.
```html preview
<sl-select label="Select one">
<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>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect label="Select one">
<SlMenuItem value="option-1">Option 1</SlMenuItem>
<SlMenuItem value="option-2">Option 2</SlMenuItem>
<SlMenuItem value="option-3">Option 3</SlMenuItem>
</SlSelect>
);
```
### Help Text
Add descriptive help text to a select with the `help-text` attribute. For help texts that contain HTML, use the `help-text` slot instead.
```html preview
<sl-select label="Experience" help-text="Please tell us your skill level.">
<sl-menu-item value="1">Novice</sl-menu-item>
<sl-menu-item value="2">Intermediate</sl-menu-item>
<sl-menu-item value="3">Advanced</sl-menu-item>
</sl-select>
```
```jsx react
import { SlMenuItem, SlSelect } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlSelect label="Experience" help-text="Please tell us your skill level.">
<SlMenuItem value="1">Novice</SlMenuItem>
<SlMenuItem value="2">Intermediate</SlMenuItem>
<SlMenuItem value="3">Advanced</SlMenuItem>
</SlSelect>
);
```
### Placement
The preferred placement of the select's menu can be set with the `placement` attribute. Note that the actual position may vary to ensure the panel remains in the viewport. Valid placements are `top` and `bottom`.

Wyświetl plik

@ -8,6 +8,10 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
## Next
- 🚨 BREAKING: refactored parts in `<sl-input>`, `<sl-range>`, `<sl-select>`, and `<sl-textarea>` to allow you to customize the label and help text position
- Added `form-control-input` part
- Renamed `label` to `form-control-label`
- Renamed `help-text` to `form-control-help-text`
- 🚨 BREAKING: removed status from the `sl-error` event payload in `<sl-icon>`
- Added the experimental `<sl-radio-button>` component
- Added `button-group` and `button-group__base` parts to `<sl-radio-group>`

Wyświetl plik

@ -5,7 +5,7 @@ import { ifDefined } from 'lit/directives/if-defined.js';
import { html, literal } from 'lit/static-html.js';
import '~/components/spinner/spinner';
import { emit } from '~/internal/event';
import { FormSubmitController } from '~/internal/form-control';
import { FormSubmitController } from '~/internal/form';
import { HasSlotController } from '~/internal/slot';
import styles from './button.styles';

Wyświetl plik

@ -4,7 +4,7 @@ import { classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import { emit } from '~/internal/event';
import { FormSubmitController } from '~/internal/form-control';
import { FormSubmitController } from '~/internal/form';
import { watch } from '~/internal/watch';
import styles from './checkbox.styles';

Wyświetl plik

@ -15,7 +15,7 @@ import type SlInput from '~/components/input/input';
import '~/components/visually-hidden/visually-hidden';
import { drag } from '~/internal/drag';
import { emit } from '~/internal/event';
import { FormSubmitController } from '~/internal/form-control';
import { FormSubmitController } from '~/internal/form';
import { clamp } from '~/internal/math';
import { watch } from '~/internal/watch';
import { LocalizeController } from '~/utilities/localize';

Wyświetl plik

@ -42,7 +42,7 @@ describe('<sl-input>', () => {
it('should focus the input when clicking on the label', async () => {
const el = await fixture<SlInput>(html` <sl-input label="Name"></sl-input> `);
const label = el.shadowRoot!.querySelector('[part="label"]')!;
const label = el.shadowRoot!.querySelector('[part="form-control-label"]')!;
const submitHandler = sinon.spy();
el.addEventListener('sl-focus', submitHandler);

Wyświetl plik

@ -5,7 +5,7 @@ import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import '~/components/icon/icon';
import { emit } from '~/internal/event';
import { FormSubmitController } from '~/internal/form-control';
import { FormSubmitController } from '~/internal/form';
import { HasSlotController } from '~/internal/slot';
import { watch } from '~/internal/watch';
import styles from './input.styles';
@ -30,15 +30,16 @@ import styles from './input.styles';
* @event sl-focus - Emitted when the control gains focus.
* @event sl-blur - Emitted when the control loses focus.
*
* @csspart base - The component's internal wrapper.
* @csspart form-control - The form control that wraps the label, input, and help-text.
* @csspart label - The input label.
* @csspart form-control-label - The label's wrapper.
* @csspart form-control-input - The input's wrapper.
* @csspart form-control-help-text - The help text's wrapper.
* @csspart base - The component's internal wrapper.
* @csspart input - The input control.
* @csspart prefix - The input prefix container.
* @csspart clear-button - The clear button.
* @csspart password-toggle-button - The password toggle button.
* @csspart suffix - The input suffix container.
* @csspart help-text - The input help text.
*/
@customElement('sl-input')
export default class SlInput extends LitElement {
@ -293,11 +294,16 @@ export default class SlInput extends LitElement {
'form-control--has-help-text': hasHelpText
})}
>
<label part="label" class="form-control__label" for="input" aria-hidden=${hasLabel ? 'false' : 'true'}>
<label
part="form-control-label"
class="form-control__label"
for="input"
aria-hidden=${hasLabel ? 'false' : 'true'}
>
<slot name="label">${this.label}</slot>
</label>
<div class="form-control__input">
<div part="form-control-input" class="form-control-input">
<div
part="base"
class=${classMap({
@ -403,7 +409,7 @@ export default class SlInput extends LitElement {
</div>
<div
part="help-text"
part="form-control-help-text"
id="help-text"
class="form-control__help-text"
aria-hidden=${hasHelpText ? 'false' : 'true'}

Wyświetl plik

@ -4,7 +4,7 @@ import { classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import { emit } from '~/internal/event';
import { FormSubmitController } from '~/internal/form-control';
import { FormSubmitController } from '~/internal/form';
import { HasSlotController } from '~/internal/slot';
import { watch } from '~/internal/watch';
import styles from './range.styles';
@ -20,6 +20,10 @@ import styles from './range.styles';
* @event sl-blur - Emitted when the control loses focus.
* @event sl-focus - Emitted when the control gains focus.
*
* @csspart form-control - The form control that wraps the label, input, and help-text.
* @csspart form-control-label - The label's wrapper.
* @csspart form-control-input - The range's wrapper.
* @csspart form-control-help-text - The help text's wrapper.
* @csspart base - The component's internal wrapper.
* @csspart input - The native range input.
* @csspart tooltip - The range tooltip.
@ -213,11 +217,16 @@ export default class SlRange extends LitElement {
'form-control--has-help-text': hasHelpText
})}
>
<label part="label" class="form-control__label" for="input" aria-hidden=${hasLabel ? 'false' : 'true'}>
<label
part="form-control-label"
class="form-control__label"
for="input"
aria-hidden=${hasLabel ? 'false' : 'true'}
>
<slot name="label">${this.label}</slot>
</label>
<div class="form-control__input">
<div part="form-control-input" class="form-control-input">
<div
part="base"
class=${classMap({
@ -260,7 +269,7 @@ export default class SlRange extends LitElement {
</div>
<div
part="help-text"
part="form-control-help-text"
id="help-text"
class="form-control__help-text"
aria-hidden=${hasHelpText ? 'false' : 'true'}

Wyświetl plik

@ -34,7 +34,7 @@ describe('<sl-select>', () => {
<sl-menu-item value="option-3">Option 3</sl-menu-item>
</sl-select>
`);
const label = el.shadowRoot!.querySelector('[part="label"]')!;
const label = el.shadowRoot!.querySelector('[part="form-control-label"]')!;
const submitHandler = sinon.spy();
el.addEventListener('sl-focus', submitHandler);

Wyświetl plik

@ -11,7 +11,7 @@ import type SlMenu from '~/components/menu/menu';
import type { MenuSelectEventDetail } from '~/components/menu/menu';
import '~/components/tag/tag';
import { emit } from '~/internal/event';
import { FormSubmitController } from '~/internal/form-control';
import { FormSubmitController } from '~/internal/form';
import { getTextContent, HasSlotController } from '~/internal/slot';
import { watch } from '~/internal/watch';
import styles from './select.styles';
@ -39,15 +39,16 @@ import type { TemplateResult } from 'lit';
* @event sl-focus - Emitted when the control gains focus.
* @event sl-blur - Emitted when the control loses focus.
*
* @csspart form-control - The form control that wraps the label, input, and help-text.
* @csspart form-control-label - The label's wrapper.
* @csspart form-control-input - The select's wrapper.
* @csspart form-control-help-text - The help text's wrapper.
* @csspart base - The component's internal wrapper.
* @csspart clear-button - The clear button.
* @csspart control - The container that holds the prefix, label, and suffix.
* @csspart display-label - The label that displays the current selection. Not available when used with `multiple`.
* @csspart form-control - The form control that wraps the label, input, and help text.
* @csspart help-text - The select's help text.
* @csspart icon - The select's icon.
* @csspart prefix - The select's prefix.
* @csspart label - The select's label.
* @csspart suffix - The select's suffix.
* @csspart menu - The select menu, an `<sl-menu>` element.
* @csspart tag - The multi select option, an `<sl-tag>` element.
@ -464,7 +465,7 @@ export default class SlSelect extends LitElement {
})}
>
<label
part="label"
part="form-control-label"
class="form-control__label"
for="input"
aria-hidden=${hasLabel ? 'false' : 'true'}
@ -473,7 +474,7 @@ export default class SlSelect extends LitElement {
<slot name="label">${this.label}</slot>
</label>
<div class="form-control__input">
<div part="form-control-input" class="form-control-input">
<sl-dropdown
part="base"
.hoist=${this.hoist}
@ -568,7 +569,7 @@ export default class SlSelect extends LitElement {
</div>
<div
part="help-text"
part="form-control-help-text"
id="help-text"
class="form-control__help-text"
aria-hidden=${hasHelpText ? 'false' : 'true'}

Wyświetl plik

@ -4,7 +4,7 @@ import { classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import { emit } from '~/internal/event';
import { FormSubmitController } from '~/internal/form-control';
import { FormSubmitController } from '~/internal/form';
import { watch } from '~/internal/watch';
import styles from './switch.styles';

Wyświetl plik

@ -19,7 +19,7 @@ describe('<sl-textarea>', () => {
it('should focus the textarea when clicking on the label', async () => {
const el = await fixture<SlTextarea>(html` <sl-textarea label="Name"></sl-textarea> `);
const label = el.shadowRoot!.querySelector('[part="label"]')!;
const label = el.shadowRoot!.querySelector('[part="form-control-label"]')!;
const submitHandler = sinon.spy();
el.addEventListener('sl-focus', submitHandler);

Wyświetl plik

@ -4,7 +4,7 @@ import { classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { live } from 'lit/directives/live.js';
import { emit } from '~/internal/event';
import { FormSubmitController } from '~/internal/form-control';
import { FormSubmitController } from '~/internal/form';
import { HasSlotController } from '~/internal/slot';
import { watch } from '~/internal/watch';
import styles from './textarea.styles';
@ -21,11 +21,12 @@ import styles from './textarea.styles';
* @event sl-focus - Emitted when the control gains focus.
* @event sl-blur - Emitted when the control loses focus.
*
* @csspart form-control - The form control that wraps the label, input, and help-text.
* @csspart form-control-label - The label's wrapper.
* @csspart form-control-input - The input's wrapper.
* @csspart form-control-help-text - The help text's wrapper.
* @csspart base - The component's internal wrapper.
* @csspart form-control - The form control that wraps the label, textarea, and help text.
* @csspart label - The textarea label.
* @csspart textarea - The textarea control.
* @csspart help-text - The textarea help text.
*/
@customElement('sl-textarea')
export default class SlTextarea extends LitElement {
@ -263,11 +264,16 @@ export default class SlTextarea extends LitElement {
'form-control--has-help-text': hasHelpText
})}
>
<label part="label" class="form-control__label" for="input" aria-hidden=${hasLabel ? 'false' : 'true'}>
<label
part="form-control-label"
class="form-control__label"
for="input"
aria-hidden=${hasLabel ? 'false' : 'true'}
>
<slot name="label">${this.label}</slot>
</label>
<div class="form-control__input">
<div part="form-control-input" class="form-control-input">
<div
part="base"
class=${classMap({
@ -314,7 +320,7 @@ export default class SlTextarea extends LitElement {
</div>
<div
part="help-text"
part="form-control-help-text"
id="help-text"
class="form-control__help-text"
aria-hidden=${hasHelpText ? 'false' : 'true'}

Wyświetl plik

@ -1,7 +1,7 @@
import { LitElement } from 'lit';
import { property, query, state } from 'lit/decorators.js';
import { emit } from '~/internal/event';
import { FormSubmitController } from '~/internal/form-control';
import { FormSubmitController } from '~/internal/form';
import { watch } from '~/internal/watch';
/**