mirror of
https://github.com/material-components/material-web.git
synced 2024-10-28 06:28:12 +03:00
feat(formfield): add nowrap class/prop to MDC/MWC
PiperOrigin-RevId: 307142349
This commit is contained in:
parent
bade5580b8
commit
dae382476b
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Added `activated` and `selected` states for ripple
|
- Added `activated` and `selected` states for ripple
|
||||||
- Added documentation for ripple
|
- Added documentation for ripple
|
||||||
- Prefix and suffix to mwc-textfield
|
- Prefix and suffix to mwc-textfield
|
||||||
|
- `mwc-formfield` now has a nowrap property
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Refactor `mwc-ripple`
|
- Refactor `mwc-ripple`
|
||||||
- Normalized API to `start${state}` `end${state}` naming
|
- Normalized API to `start${state}` `end${state}` naming
|
||||||
- **BREAKING:VISUAL:** mwc-list-item now internally uses mwc-ripple instead of styling ripple on host
|
- **BREAKING:VISUAL:** mwc-list-item now internally uses mwc-ripple instead of styling ripple on host
|
||||||
|
- `mwc-menu`'s `quick` variant now opens synchronously
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
@ -36,6 +36,26 @@ npm install @material/mwc-formfield
|
|||||||
|
|
||||||
```html
|
```html
|
||||||
<mwc-formfield label="Tomato">
|
<mwc-formfield label="Tomato">
|
||||||
|
<mwc-checkbox checked></mwc-checkbox>
|
||||||
|
</mwc-formfield>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import '@material/mwc-checkbox';
|
||||||
|
import '@material/mwc-formfield';
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### nowrap label with checkbox
|
||||||
|
|
||||||
|
<img src="images/nowrap.png" width="150px" height="40px">
|
||||||
|
|
||||||
|
```html
|
||||||
|
<style>
|
||||||
|
mwc-formfield[nowrap] {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<mwc-formfield label="really really long label" nowrap>
|
||||||
<mwc-checkbox></mwc-checkbox>
|
<mwc-checkbox></mwc-checkbox>
|
||||||
</mwc-formfield>
|
</mwc-formfield>
|
||||||
|
|
||||||
@ -98,9 +118,10 @@ Name | Description
|
|||||||
|
|
||||||
Name | Type | Description
|
Name | Type | Description
|
||||||
------- | -------- | ----------------------------------
|
------- | -------- | ----------------------------------
|
||||||
`label` | `string` | The text to display for the label.
|
`label` | `string` | The text to display for the label and sets a11y label on input. (visually overriden by slotted label)
|
||||||
`alignEnd` | `boolean` | Align the component at the end of the label.
|
`alignEnd` | `boolean` | Align the component at the end of the label.
|
||||||
`spaceBetween` | `boolean` | Add space between the component and the label as the formfield grows.
|
`spaceBetween` | `boolean` | Add space between the component and the label as the formfield grows.
|
||||||
|
`nowrap` | `boolean` | Prevents the label from wrapping and overflow text is ellipsed.
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
|
|
||||||
|
BIN
packages/formfield/images/nowrap.png
Normal file
BIN
packages/formfield/images/nowrap.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
@ -1,31 +1,35 @@
|
|||||||
/**
|
/**
|
||||||
@license
|
* @license
|
||||||
Copyright 2018 Google Inc. All Rights Reserved.
|
* Copyright 2018 Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// tslint:disable:no-new-decorators
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
import {MDCFormFieldAdapter} from '@material/form-field/adapter';
|
||||||
you may not use this file except in compliance with the License.
|
import MDCFormFieldFoundation from '@material/form-field/foundation';
|
||||||
You may obtain a copy of the License at
|
import {BaseElement, EventType, SpecificEventListener} from '@material/mwc-base/base-element';
|
||||||
|
import {FormElement} from '@material/mwc-base/form-element';
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
import {observer} from '@material/mwc-base/observer';
|
||||||
|
import {findAssignedElement} from '@material/mwc-base/utils';
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
import {MDCFormFieldAdapter} from '@material/form-field/adapter.js';
|
|
||||||
import MDCFormFieldFoundation from '@material/form-field/foundation.js';
|
|
||||||
import {BaseElement, EventType, SpecificEventListener} from '@material/mwc-base/base-element.js';
|
|
||||||
import {FormElement} from '@material/mwc-base/form-element.js';
|
|
||||||
import {observer} from '@material/mwc-base/observer.js';
|
|
||||||
import {findAssignedElement} from '@material/mwc-base/utils.js';
|
|
||||||
import {html, property, query} from 'lit-element';
|
import {html, property, query} from 'lit-element';
|
||||||
import {classMap} from 'lit-html/directives/class-map.js';
|
import {classMap} from 'lit-html/directives/class-map';
|
||||||
|
|
||||||
|
|
||||||
export class FormfieldBase extends BaseElement {
|
export class FormfieldBase extends BaseElement {
|
||||||
@property({type: Boolean}) alignEnd = false;
|
@property({type: Boolean}) alignEnd = false;
|
||||||
@property({type: Boolean}) spaceBetween = false;
|
@property({type: Boolean}) spaceBetween = false;
|
||||||
|
@property({type: Boolean}) nowrap = false;
|
||||||
|
|
||||||
@property({type: String})
|
@property({type: String})
|
||||||
@observer(async function(this: FormfieldBase, label: string) {
|
@observer(async function(this: FormfieldBase, label: string) {
|
||||||
@ -93,7 +97,8 @@ export class FormfieldBase extends BaseElement {
|
|||||||
protected render() {
|
protected render() {
|
||||||
const classes = {
|
const classes = {
|
||||||
'mdc-form-field--align-end': this.alignEnd,
|
'mdc-form-field--align-end': this.alignEnd,
|
||||||
'mdc-form-field--space-between': this.spaceBetween
|
'mdc-form-field--space-between': this.spaceBetween,
|
||||||
|
'mdc-form-field--nowrap': this.nowrap
|
||||||
};
|
};
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
@ -15,7 +15,12 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import '@material/form-field/mdc-form-field.scss';
|
@use '@material/form-field' as formfield;
|
||||||
|
@use '@material/typography';
|
||||||
|
@use '@material/theme';
|
||||||
|
@use '@material/rtl';
|
||||||
|
|
||||||
|
@include formfield.core-styles();
|
||||||
|
|
||||||
:host {
|
:host {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@ -26,13 +31,13 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
|
|
||||||
::slotted(*) {
|
::slotted(*) {
|
||||||
@include mdc-typography(body2);
|
@include typography.typography(body2);
|
||||||
@include mdc-theme-prop(color, text-primary-on-background);
|
@include theme.prop(color, text-primary-on-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
::slotted(mwc-switch) {
|
::slotted(mwc-switch) {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
@include mdc-rtl {
|
@include rtl.rtl {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,21 +15,258 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Formfield} from '@material/mwc-formfield';
|
import '@material/mwc-formfield';
|
||||||
|
import '@material/mwc-checkbox';
|
||||||
|
import '@material/mwc-radio';
|
||||||
|
import '@material/mwc-switch';
|
||||||
|
|
||||||
|
|
||||||
|
import {Checkbox} from '@material/mwc-checkbox';
|
||||||
|
import {Formfield} from '@material/mwc-formfield';
|
||||||
|
import {Radio} from '@material/mwc-radio';
|
||||||
|
import {Switch} from '@material/mwc-switch';
|
||||||
|
import {html} from 'lit-html';
|
||||||
|
|
||||||
|
import {fixture, TestFixture} from '../../../../test/src/util/helpers';
|
||||||
|
|
||||||
|
const defaultEl = html`<mwc-formfield></mwc-formfield>`;
|
||||||
|
|
||||||
|
const defaultFormfieldProps = {
|
||||||
|
alignEnd: false,
|
||||||
|
spaceBetween: false,
|
||||||
|
label: '',
|
||||||
|
content: html``,
|
||||||
|
};
|
||||||
|
|
||||||
|
type FormfieldProps = typeof defaultFormfieldProps;
|
||||||
|
|
||||||
|
const formfield = (propsInit: Partial<FormfieldProps>) => {
|
||||||
|
const props: FormfieldProps = {...defaultFormfieldProps, ...propsInit};
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<mwc-formfield
|
||||||
|
.alignEnd=${props.alignEnd}
|
||||||
|
.spaceBetween=${props.spaceBetween}
|
||||||
|
.label=${props.label}>
|
||||||
|
${props.content}
|
||||||
|
</mwc-formfield>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
suite('mwc-formfield', () => {
|
suite('mwc-formfield', () => {
|
||||||
|
let fixt: TestFixture;
|
||||||
let element: Formfield;
|
let element: Formfield;
|
||||||
setup(() => {
|
|
||||||
element = document.createElement('mwc-formfield');
|
|
||||||
document.body.appendChild(element);
|
|
||||||
});
|
|
||||||
|
|
||||||
teardown(() => {
|
teardown(() => {
|
||||||
document.body.removeChild(element);
|
fixt.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('initializes as an mwc-formfield', () => {
|
suite('basic', () => {
|
||||||
assert.instanceOf(element, Formfield);
|
setup(async () => {
|
||||||
|
fixt = await fixture(defaultEl);
|
||||||
|
element = fixt.root.querySelector('mwc-formfield')!;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('initializes as an mwc-formfield', () => {
|
||||||
|
assert.instanceOf(element, Formfield);
|
||||||
|
assert.isFalse(element.alignEnd);
|
||||||
|
assert.isFalse(element.spaceBetween);
|
||||||
|
assert.equal(element.label, '');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('with checkbox', () => {
|
||||||
|
let control: Checkbox;
|
||||||
|
|
||||||
|
suite('prop label', () => {
|
||||||
|
setup(async () => {
|
||||||
|
fixt = await fixture(formfield(
|
||||||
|
{label: 'label', content: html`<mwc-checkbox></mwc-checkbox>`}));
|
||||||
|
element = fixt.root.querySelector('mwc-formfield')!;
|
||||||
|
await element.updateComplete;
|
||||||
|
control = fixt.root.querySelector('mwc-checkbox')!;
|
||||||
|
await control.updateComplete;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sets the aria-label on the control', async () => {
|
||||||
|
const internalInput = control.shadowRoot!.querySelector('input')!;
|
||||||
|
assert.equal(internalInput.getAttribute('aria-label'), 'label');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('label click propagates click and focus to control', async () => {
|
||||||
|
const labelEl = element.shadowRoot!.querySelector('label')!;
|
||||||
|
let numClicks = 0;
|
||||||
|
const origClick = control.click;
|
||||||
|
|
||||||
|
control.click = () => {
|
||||||
|
numClicks += 1;
|
||||||
|
origClick.call(control);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.isFalse(control.checked);
|
||||||
|
assert.equal(fixt.shadowRoot!.activeElement, null);
|
||||||
|
assert.equal(numClicks, 0);
|
||||||
|
|
||||||
|
labelEl.click();
|
||||||
|
|
||||||
|
await element.updateComplete;
|
||||||
|
await control.updateComplete;
|
||||||
|
|
||||||
|
assert.isTrue(control.checked);
|
||||||
|
assert.equal(fixt.shadowRoot!.activeElement, control);
|
||||||
|
assert.equal(numClicks, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('formfield will not double click control', async () => {
|
||||||
|
let numClicks = 0;
|
||||||
|
const origClick = control.click;
|
||||||
|
|
||||||
|
control.click = () => {
|
||||||
|
numClicks += 1;
|
||||||
|
origClick.call(control);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.isFalse(control.checked);
|
||||||
|
assert.equal(numClicks, 0);
|
||||||
|
|
||||||
|
control.click();
|
||||||
|
|
||||||
|
await element.updateComplete;
|
||||||
|
await control.updateComplete;
|
||||||
|
|
||||||
|
assert.equal(numClicks, 1);
|
||||||
|
assert.isTrue(control.checked);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('with switch', () => {
|
||||||
|
let control: Switch;
|
||||||
|
|
||||||
|
suite('prop label', () => {
|
||||||
|
setup(async () => {
|
||||||
|
fixt = await fixture(formfield(
|
||||||
|
{label: 'label', content: html`<mwc-switch></mwc-switch>`}));
|
||||||
|
element = fixt.root.querySelector('mwc-formfield')!;
|
||||||
|
await element.updateComplete;
|
||||||
|
control = fixt.root.querySelector('mwc-switch')!;
|
||||||
|
await control.updateComplete;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sets the aria-label on the control', async () => {
|
||||||
|
const internalInput = control.shadowRoot!.querySelector('input')!;
|
||||||
|
assert.equal(internalInput.getAttribute('aria-label'), 'label');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('label click propagates click and focus to control', async () => {
|
||||||
|
const labelEl = element.shadowRoot!.querySelector('label')!;
|
||||||
|
let numClicks = 0;
|
||||||
|
const origClick = control.click;
|
||||||
|
|
||||||
|
control.click = () => {
|
||||||
|
numClicks += 1;
|
||||||
|
origClick.call(control);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.isFalse(control.checked);
|
||||||
|
assert.equal(fixt.shadowRoot!.activeElement, null);
|
||||||
|
assert.equal(numClicks, 0);
|
||||||
|
|
||||||
|
labelEl.click();
|
||||||
|
|
||||||
|
await element.updateComplete;
|
||||||
|
await control.updateComplete;
|
||||||
|
|
||||||
|
assert.isTrue(control.checked);
|
||||||
|
assert.equal(fixt.shadowRoot!.activeElement, control);
|
||||||
|
assert.equal(numClicks, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('formfield will not double click control', async () => {
|
||||||
|
let numClicks = 0;
|
||||||
|
const origClick = control.click;
|
||||||
|
|
||||||
|
control.click = () => {
|
||||||
|
numClicks += 1;
|
||||||
|
origClick.call(control);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.isFalse(control.checked);
|
||||||
|
assert.equal(numClicks, 0);
|
||||||
|
|
||||||
|
control.click();
|
||||||
|
|
||||||
|
await element.updateComplete;
|
||||||
|
await control.updateComplete;
|
||||||
|
|
||||||
|
assert.equal(numClicks, 1);
|
||||||
|
assert.isTrue(control.checked);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('with radio', () => {
|
||||||
|
let control: Radio;
|
||||||
|
|
||||||
|
suite('prop label', () => {
|
||||||
|
setup(async () => {
|
||||||
|
fixt = await fixture(formfield(
|
||||||
|
{label: 'label', content: html`<mwc-radio></mwc-radio>`}));
|
||||||
|
element = fixt.root.querySelector('mwc-formfield')!;
|
||||||
|
await element.updateComplete;
|
||||||
|
control = fixt.root.querySelector('mwc-radio')!;
|
||||||
|
await control.updateComplete;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sets the aria-label on the control', async () => {
|
||||||
|
const internalInput = control.shadowRoot!.querySelector('input')!;
|
||||||
|
assert.equal(internalInput.getAttribute('aria-label'), 'label');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('label click propagates click and focus to control', async () => {
|
||||||
|
const labelEl = element.shadowRoot!.querySelector('label')!;
|
||||||
|
let numClicks = 0;
|
||||||
|
const origClick = control.click;
|
||||||
|
|
||||||
|
control.click = () => {
|
||||||
|
numClicks += 1;
|
||||||
|
origClick.call(control);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.isFalse(control.checked);
|
||||||
|
assert.equal(fixt.shadowRoot!.activeElement, null);
|
||||||
|
assert.equal(numClicks, 0);
|
||||||
|
|
||||||
|
labelEl.click();
|
||||||
|
|
||||||
|
await element.updateComplete;
|
||||||
|
await control.updateComplete;
|
||||||
|
|
||||||
|
assert.isTrue(control.checked);
|
||||||
|
assert.equal(fixt.shadowRoot!.activeElement, control);
|
||||||
|
assert.equal(numClicks, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('formfield will not double click control', async () => {
|
||||||
|
let numClicks = 0;
|
||||||
|
const origClick = control.click;
|
||||||
|
|
||||||
|
control.click = () => {
|
||||||
|
numClicks += 1;
|
||||||
|
origClick.call(control);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert.isFalse(control.checked);
|
||||||
|
assert.equal(numClicks, 0);
|
||||||
|
|
||||||
|
control.click();
|
||||||
|
|
||||||
|
await element.updateComplete;
|
||||||
|
await control.updateComplete;
|
||||||
|
|
||||||
|
assert.equal(numClicks, 1);
|
||||||
|
assert.isTrue(control.checked);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user