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 documentation for ripple
|
||||
- Prefix and suffix to mwc-textfield
|
||||
- `mwc-formfield` now has a nowrap property
|
||||
|
||||
### Changed
|
||||
|
||||
@ -27,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Refactor `mwc-ripple`
|
||||
- Normalized API to `start${state}` `end${state}` naming
|
||||
- **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
|
||||
|
||||
|
@ -36,6 +36,26 @@ npm install @material/mwc-formfield
|
||||
|
||||
```html
|
||||
<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-formfield>
|
||||
|
||||
@ -98,9 +118,10 @@ Name | 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.
|
||||
`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
|
||||
|
||||
|
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
|
||||
Copyright 2018 Google Inc. All Rights Reserved.
|
||||
* @license
|
||||
* 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");
|
||||
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.
|
||||
*/
|
||||
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 {MDCFormFieldAdapter} from '@material/form-field/adapter';
|
||||
import MDCFormFieldFoundation from '@material/form-field/foundation';
|
||||
import {BaseElement, EventType, SpecificEventListener} from '@material/mwc-base/base-element';
|
||||
import {FormElement} from '@material/mwc-base/form-element';
|
||||
import {observer} from '@material/mwc-base/observer';
|
||||
import {findAssignedElement} from '@material/mwc-base/utils';
|
||||
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 {
|
||||
@property({type: Boolean}) alignEnd = false;
|
||||
@property({type: Boolean}) spaceBetween = false;
|
||||
@property({type: Boolean}) nowrap = false;
|
||||
|
||||
@property({type: String})
|
||||
@observer(async function(this: FormfieldBase, label: string) {
|
||||
@ -93,7 +97,8 @@ export class FormfieldBase extends BaseElement {
|
||||
protected render() {
|
||||
const classes = {
|
||||
'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`
|
||||
|
@ -15,7 +15,12 @@ See the License for the specific language governing permissions and
|
||||
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 {
|
||||
display: inline-flex;
|
||||
@ -26,13 +31,13 @@ limitations under the License.
|
||||
}
|
||||
|
||||
::slotted(*) {
|
||||
@include mdc-typography(body2);
|
||||
@include mdc-theme-prop(color, text-primary-on-background);
|
||||
@include typography.typography(body2);
|
||||
@include theme.prop(color, text-primary-on-background);
|
||||
}
|
||||
|
||||
::slotted(mwc-switch) {
|
||||
margin-right: 10px;
|
||||
@include mdc-rtl {
|
||||
@include rtl.rtl {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
@ -15,21 +15,258 @@
|
||||
* 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', () => {
|
||||
let fixt: TestFixture;
|
||||
let element: Formfield;
|
||||
setup(() => {
|
||||
element = document.createElement('mwc-formfield');
|
||||
document.body.appendChild(element);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
document.body.removeChild(element);
|
||||
fixt.remove();
|
||||
});
|
||||
|
||||
suite('basic', () => {
|
||||
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