Skip to content

Commit 33c1afe

Browse files
asynclizcopybara-github
authored andcommitted
refactor(select): use shared validation mixins
PiperOrigin-RevId: 585751913
1 parent bedcd65 commit 33c1afe

File tree

6 files changed

+131
-175
lines changed

6 files changed

+131
-175
lines changed

labs/behaviors/validators/checkbox-validator.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ export interface CheckboxState {
1313
/**
1414
* Whether the checkbox is checked.
1515
*/
16-
checked: boolean;
16+
readonly checked: boolean;
1717

1818
/**
1919
* Whether the checkbox is required.
2020
*/
21-
required: boolean;
21+
readonly required: boolean;
2222
}
2323

2424
/**
@@ -46,8 +46,4 @@ export class CheckboxValidator extends Validator<CheckboxState> {
4646
protected override equals(prev: CheckboxState, next: CheckboxState) {
4747
return prev.checked === next.checked && prev.required === next.required;
4848
}
49-
50-
protected override copy({checked, required}: CheckboxState): CheckboxState {
51-
return {checked, required};
52-
}
5349
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* @license
3+
* Copyright 2023 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {html, render} from 'lit';
8+
9+
import {Validator} from './validator.js';
10+
11+
/**
12+
* Constraint validation properties for a select dropdown.
13+
*/
14+
export interface SelectState {
15+
/**
16+
* The current selected value.
17+
*/
18+
readonly value: string;
19+
20+
/**
21+
* Whether the select is required.
22+
*/
23+
readonly required: boolean;
24+
}
25+
26+
/**
27+
* A validator that provides constraint validation that emulates `<select>`
28+
* validation.
29+
*/
30+
export class SelectValidator extends Validator<SelectState> {
31+
private selectControl?: HTMLSelectElement;
32+
33+
protected override computeValidity(state: SelectState) {
34+
if (!this.selectControl) {
35+
// Lazily create the platform select
36+
this.selectControl = document.createElement('select');
37+
}
38+
39+
render(html`<option value=${state.value}></option>`, this.selectControl);
40+
41+
this.selectControl.value = state.value;
42+
this.selectControl.required = state.required;
43+
return {
44+
validity: this.selectControl.validity,
45+
validationMessage: this.selectControl.validationMessage,
46+
};
47+
}
48+
49+
protected override equals(prev: SelectState, next: SelectState) {
50+
return prev.value === next.value && prev.required === next.required;
51+
}
52+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @license
3+
* Copyright 2023 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
// import 'jasmine'; (google3-only)
8+
9+
import {SelectValidator} from './select-validator.js';
10+
11+
describe('SelectValidator', () => {
12+
it('is invalid when required and value is empty', () => {
13+
const state = {
14+
required: true,
15+
value: '',
16+
};
17+
18+
const validator = new SelectValidator(() => state);
19+
const {validity, validationMessage} = validator.getValidity();
20+
expect(validity.valueMissing).withContext('valueMissing').toBeTrue();
21+
expect(validationMessage).withContext('validationMessage').not.toBe('');
22+
});
23+
24+
it('is valid when required and value is provided', () => {
25+
const state = {
26+
required: true,
27+
value: 'Foo',
28+
};
29+
30+
const validator = new SelectValidator(() => state);
31+
const {validity, validationMessage} = validator.getValidity();
32+
expect(validity.valueMissing).withContext('valueMissing').toBeFalse();
33+
expect(validationMessage).withContext('validationMessage').toBe('');
34+
});
35+
36+
it('is valid when not required', () => {
37+
const state = {
38+
required: false,
39+
value: '',
40+
};
41+
42+
const validator = new SelectValidator(() => state);
43+
const {validity, validationMessage} = validator.getValidity();
44+
expect(validity.valueMissing).withContext('valueMissing').toBeFalse();
45+
expect(validationMessage).withContext('validationMessage').toBe('');
46+
});
47+
});

labs/behaviors/validators/validator.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ export abstract class Validator<State> {
111111
* @param state The state to copy.
112112
* @return A copy of the state.
113113
*/
114-
protected abstract copy(state: State): State;
114+
protected copy(state: State): State {
115+
return {...state};
116+
}
115117
}
116118

117119
/**

labs/behaviors/validators/validator_test.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ describe('Validator', () => {
2626
equals(prev: CustomState, next: CustomState) {
2727
return prev.value === next.value && prev.required === next.required;
2828
}
29-
30-
copy({value, required}: CustomState) {
31-
return {value, required};
32-
}
3329
}
3430

3531
describe('getValidity()', () => {

0 commit comments

Comments
 (0)