Merge pull request #373 from material-components/radio-checked-bug

Fix broken checked property in <mwc-radio>
This commit is contained in:
Alexander Marks 2019-08-15 13:51:39 -07:00 committed by GitHub
commit 40f5e43947
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 95 additions and 12 deletions

View File

@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Improve README for `<mwc-button>`
- Improve README for `<mwc-icon-button>`
- Split toggling icon button out into `@material/mwc-icon-button-toggle` with tag name `<mwc-icon-button-toggle>`
- Fix bug where setting the `checked` property on an `<mwc-radio>` did not
result in the other radios in the group becoming unchecked.
## [0.6.0] - 2019-06-05
- Upgrade lerna to 3.x

View File

@ -24,11 +24,46 @@ export class RadioBase extends FormElement {
@query('input') protected formElement!: HTMLInputElement;
@property({type: Boolean})
@observer(function(this: RadioBase, checked: boolean) {
this.formElement.checked = checked;
})
checked = false;
private _checked = false;
@property({type: Boolean, reflect: true})
get checked() {
return this._checked;
}
/**
* We define our own getter/setter for `checked` because we need to track
* changes to it synchronously.
*
* The order in which the `checked` property is set across radio buttons
* within the same group is very important. However, we can't rely on
* UpdatingElement's `updated` callback to observe these changes (which is
* also what the `@observer` decorator uses), because it batches changes to
* all properties.
*
* Consider:
*
* radio1.disabled = true;
* radio2.checked = true;
* radio1.checked = true;
*
* In this case we'd first see all changes for radio1, and then for radio2,
* and we couldn't tell that radio1 was the most recently checked.
*/
set checked(checked: boolean) {
const oldValue = this._checked;
if (!!checked === !!oldValue) {
return;
}
this._checked = checked;
if (this.formElement) {
this.formElement.checked = checked;
}
if (this._selectionController) {
this._selectionController.update(this);
}
this.requestUpdate('checked', oldValue);
}
@property({type: Boolean})
@observer(function(this: RadioBase, disabled: boolean) {
@ -127,6 +162,9 @@ export class RadioBase extends FormElement {
firstUpdated() {
super.firstUpdated();
// We might not have been able to synchronize this from the checked setter
// earlier, if checked was set before the input was stamped.
this.formElement.checked = this.checked;
if (this._selectionController) {
this._selectionController.update(this);
}

View File

@ -14,22 +14,65 @@
* limitations under the License.
*/
import {assert} from 'chai';
import {Radio} from '@material/mwc-radio';
import {assert} from 'chai';
import {html, render} from 'lit-html';
let element;
let container;
suite('mwc-radio');
beforeEach(() => {
element = document.createElement('mwc-radio');
document.body.appendChild(element);
before(() => {
container = document.createElement('main');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(element);
render(html``, container);
});
after(() => {
document.body.removeChild(container);
});
test('initializes as an mwc-radio', () => {
assert.instanceOf(element, Radio);
const radio = document.createElement('mwc-radio');
container.appendChild(radio);
assert.instanceOf(radio, Radio);
});
test('radio group', async () => {
render(
html`
<mwc-radio name="a"></mwc-group>
<mwc-radio name="a"></mwc-group>
<mwc-radio name="b"></mwc-group>
`,
container);
const [a1, a2, b1] = [...container.querySelectorAll('mwc-radio')];
assert.isFalse(a1.checked);
assert.isFalse(a2.checked);
assert.isFalse(b1.checked);
a1.checked = true;
assert.isTrue(a1.checked);
assert.isFalse(a2.checked);
assert.isFalse(b1.checked);
a2.checked = true;
assert.isFalse(a1.checked);
assert.isTrue(a2.checked);
assert.isFalse(b1.checked);
b1.checked = true;
assert.isFalse(a1.checked);
assert.isTrue(a2.checked);
assert.isTrue(b1.checked);
a2.checked = false;
b1.checked = false;
assert.isFalse(a1.checked);
assert.isFalse(a2.checked);
assert.isFalse(b1.checked);
});