@vaadin/field-base 23.0.0-alpha1 → 23.0.0-alpha2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/field-base",
3
- "version": "23.0.0-alpha1",
3
+ "version": "23.0.0-alpha2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -31,7 +31,7 @@
31
31
  "dependencies": {
32
32
  "@open-wc/dedupe-mixin": "^1.3.0",
33
33
  "@polymer/polymer": "^3.0.0",
34
- "@vaadin/component-base": "23.0.0-alpha1",
34
+ "@vaadin/component-base": "23.0.0-alpha2",
35
35
  "lit": "^2.0.0"
36
36
  },
37
37
  "devDependencies": {
@@ -39,5 +39,5 @@
39
39
  "@vaadin/testing-helpers": "^0.3.2",
40
40
  "sinon": "^9.2.1"
41
41
  },
42
- "gitHead": "fbcb07328fdf88260e3b461088d207426b21c710"
42
+ "gitHead": "070f586dead02ca41b66717820c647f48bf1665f"
43
43
  }
@@ -3,6 +3,7 @@
3
3
  * Copyright (c) 2021 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { addValueToAttribute, removeValueFromAttribute } from './utils.js';
6
7
 
7
8
  /**
8
9
  * A controller for managing ARIA attributes for a field element:
@@ -160,16 +161,12 @@ export class FieldAriaController {
160
161
  return;
161
162
  }
162
163
 
163
- const value = this.__target.getAttribute(attr);
164
- const ids = value ? new Set(value.split(' ')) : new Set();
165
-
166
164
  if (oldId) {
167
- ids.delete(oldId);
165
+ removeValueFromAttribute(this.__target, attr, oldId);
168
166
  }
167
+
169
168
  if (newId) {
170
- ids.add(newId);
169
+ addValueToAttribute(this.__target, attr, newId);
171
170
  }
172
-
173
- this.__target.setAttribute(attr, [...ids].join(' '));
174
171
  }
175
172
  }
@@ -301,6 +301,7 @@ export const FieldMixin = (superclass) =>
301
301
  }
302
302
  const hasError = Boolean(invalid && errorMessage);
303
303
  error.textContent = hasError ? errorMessage : '';
304
+ error.hidden = !hasError;
304
305
  this.toggleAttribute('has-error-message', hasError);
305
306
 
306
307
  // Role alert will make the error message announce immediately
@@ -368,12 +369,18 @@ export const FieldMixin = (superclass) =>
368
369
  * @protected
369
370
  */
370
371
  _invalidChanged(invalid) {
371
- // Error message ID needs to be dynamically added / removed based on the validity
372
- // Otherwise assistive technologies would announce the error, even if we hide it.
373
- if (invalid) {
374
- this._fieldAriaController.setErrorId(this._errorId);
375
- } else {
376
- this._fieldAriaController.setErrorId(null);
377
- }
372
+ // This timeout is needed to prevent NVDA from announcing the error message twice:
373
+ // 1. Once adding the `[role=alert]` attribute by the `_updateErrorMessage` method (OK).
374
+ // 2. Once linking the error ID with the ARIA target here (unwanted).
375
+ // Related issue: https://github.com/vaadin/web-components/issues/3061.
376
+ setTimeout(() => {
377
+ // Error message ID needs to be dynamically added / removed based on the validity
378
+ // Otherwise assistive technologies would announce the error, even if we hide it.
379
+ if (invalid) {
380
+ this._fieldAriaController.setErrorId(this._errorId);
381
+ } else {
382
+ this._fieldAriaController.setErrorId(null);
383
+ }
384
+ });
378
385
  }
379
386
  };
@@ -49,9 +49,8 @@ export const InputConstraintsMixin = dedupingMixin(
49
49
  checkValidity() {
50
50
  if (this.inputElement && this._hasValidConstraints(this.constructor.constraints.map((c) => this[c]))) {
51
51
  return this.inputElement.checkValidity();
52
- } else {
53
- return !this.invalid;
54
52
  }
53
+ return !this.invalid;
55
54
  }
56
55
 
57
56
  /**
@@ -3,9 +3,9 @@
3
3
  * Copyright (c) 2021 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { SlotController } from './slot-controller.js';
6
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
7
7
 
8
8
  /**
9
9
  * A controller to create and initialize slotted `<input>` element.
10
10
  */
11
- export class InputController implements SlotController {}
11
+ export class InputController extends SlotController {}
@@ -3,14 +3,15 @@
3
3
  * Copyright (c) 2021 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { SlotController } from './slot-controller.js';
6
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
7
7
 
8
8
  /**
9
9
  * A controller to create and initialize slotted `<input>` element.
10
10
  */
11
11
  export class InputController extends SlotController {
12
12
  constructor(host, callback) {
13
- super(host, [
13
+ super(
14
+ host,
14
15
  'input',
15
16
  () => document.createElement('input'),
16
17
  (host, node) => {
@@ -30,6 +31,6 @@ export class InputController extends SlotController {
30
31
  callback(node);
31
32
  }
32
33
  }
33
- ]);
34
+ );
34
35
  }
35
36
  }
@@ -55,7 +55,6 @@ export const PatternMixin = (superclass) =>
55
55
  this._inputDebouncer = Debouncer.debounce(this._inputDebouncer, timeOut.after(200), () => {
56
56
  this.removeAttribute('input-prevented');
57
57
  });
58
- return;
59
58
  }
60
59
  }
61
60
  }
@@ -3,9 +3,9 @@
3
3
  * Copyright (c) 2021 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { SlotController } from './slot-controller.js';
6
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
7
7
 
8
8
  /**
9
9
  * A controller to create and initialize slotted `<textarea>` element.
10
10
  */
11
- export class TextAreaController implements SlotController {}
11
+ export class TextAreaController extends SlotController {}
@@ -3,14 +3,15 @@
3
3
  * Copyright (c) 2021 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { SlotController } from './slot-controller.js';
6
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
7
7
 
8
8
  /**
9
9
  * A controller to create and initialize slotted `<textarea>` element.
10
10
  */
11
11
  export class TextAreaController extends SlotController {
12
12
  constructor(host, callback) {
13
- super(host, [
13
+ super(
14
+ host,
14
15
  'textarea',
15
16
  () => document.createElement('textarea'),
16
17
  (host, node) => {
@@ -33,6 +34,6 @@ export class TextAreaController extends SlotController {
33
34
  callback(node);
34
35
  }
35
36
  }
36
- ]);
37
+ );
37
38
  }
38
39
  }
package/src/utils.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+
7
+ /**
8
+ * Adds a value to an attribute containing space-delimited values.
9
+ */
10
+ export declare function addValueToAttribute(element: HTMLElement, attr: string, value: string): void;
11
+
12
+ /**
13
+ * Removes a value from an attribute containing space-delimited values.
14
+ * If the value is the last one, the whole attribute is removed.
15
+ */
16
+ export declare function removeValueFromAttribute(element: HTMLElement, attr: string, value: string): void;
package/src/utils.js ADDED
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+
7
+ /**
8
+ * @param {string} value
9
+ * @return {Set<string>}
10
+ */
11
+ function deserializeAttributeValue(value) {
12
+ if (!value) {
13
+ return new Set();
14
+ }
15
+
16
+ return new Set(value.split(' '));
17
+ }
18
+
19
+ /**
20
+ * @param {Set<string>} values
21
+ * @return {string}
22
+ */
23
+ function serializeAttributeValue(values) {
24
+ return [...values].join(' ');
25
+ }
26
+
27
+ /**
28
+ * Adds a value to an attribute containing space-delimited values.
29
+ *
30
+ * @param {HTMLElement} element
31
+ * @param {string} attr
32
+ * @param {string} value
33
+ */
34
+ export function addValueToAttribute(element, attr, value) {
35
+ const values = deserializeAttributeValue(element.getAttribute(attr));
36
+ values.add(value);
37
+ element.setAttribute(attr, serializeAttributeValue(values));
38
+ }
39
+
40
+ /**
41
+ * Removes a value from an attribute containing space-delimited values.
42
+ * If the value is the last one, the whole attribute is removed.
43
+ *
44
+ * @param {HTMLElement} element
45
+ * @param {string} attr
46
+ * @param {string} value
47
+ */
48
+ export function removeValueFromAttribute(element, attr, value) {
49
+ const values = deserializeAttributeValue(element.getAttribute(attr));
50
+ values.delete(value);
51
+ if (values.size === 0) {
52
+ element.removeAttribute(attr);
53
+ return;
54
+ }
55
+ element.setAttribute(attr, serializeAttributeValue(values));
56
+ }
@@ -1,8 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright (c) 2021 Vaadin Ltd.
4
- * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
- */
6
- import { ReactiveController } from 'lit';
7
-
8
- export class SlotController implements ReactiveController {}
@@ -1,36 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright (c) 2021 Vaadin Ltd.
4
- * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
- */
6
- export class SlotController {
7
- constructor(host, [slotName, slotFactory, slotInitializer]) {
8
- this.host = host;
9
- this.slotName = slotName;
10
- this.slotFactory = slotFactory;
11
- this.slotInitializer = slotInitializer;
12
- }
13
-
14
- hostConnected() {
15
- if (!this.__initialized) {
16
- const { host, slotName, slotFactory } = this;
17
-
18
- const slotted = host.querySelector(`[slot=${slotName}]`);
19
-
20
- if (!slotted) {
21
- const slotContent = slotFactory(host);
22
- if (slotContent instanceof Element) {
23
- slotContent.setAttribute('slot', slotName);
24
- host.appendChild(slotContent);
25
- this.__slotContent = slotContent;
26
- }
27
- } else {
28
- this.__slotContent = slotted;
29
- }
30
-
31
- this.slotInitializer(host, this.__slotContent);
32
-
33
- this.__initialized = true;
34
- }
35
- }
36
- }