From db072c3adc2a3ea11d589a1ae33fa73e570e1c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Wed, 19 Apr 2023 17:22:36 +0200 Subject: [PATCH 1/4] feat: add a11y `autocomplete-valid` Part of #820 --- src/compiler/compile/compiler_warnings.ts | 4 + src/compiler/compile/nodes/Element.ts | 14 +- src/compiler/compile/utils/a11y.ts | 192 ++++++++++++++++++ .../a11y-autocomplete-valid/input.svelte | 22 ++ .../a11y-autocomplete-valid/warnings.json | 98 +++++++++ 5 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 test/validator/samples/a11y-autocomplete-valid/input.svelte create mode 100644 test/validator/samples/a11y-autocomplete-valid/warnings.json diff --git a/src/compiler/compile/compiler_warnings.ts b/src/compiler/compile/compiler_warnings.ts index a851bc24c2c8..2f8ade43e2e9 100644 --- a/src/compiler/compile/compiler_warnings.ts +++ b/src/compiler/compile/compiler_warnings.ts @@ -162,6 +162,10 @@ export default { code: 'a11y-missing-attribute', message: `A11y: <${name}> element should have ${article} ${sequence} attribute` }), + a11y_autocomplete_valid: (type: null | true | string, value: null | true | string) => ({ + code: 'a11y-autocomplete-valid', + message: `A11y: The value '${value}' is not supported by the attribute 'autocomplete' on element ` + }), a11y_img_redundant_alt: { code: 'a11y-img-redundant-alt', message: 'A11y: Screenreaders already announce elements as an image.' diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 2410904d6301..3aee4b14c27b 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -25,7 +25,7 @@ import { Literal } from 'estree'; import compiler_warnings from '../compiler_warnings'; import compiler_errors from '../compiler_errors'; import { ARIARoleDefinitionKey, roles, aria, ARIAPropertyDefinition, ARIAProperty } from 'aria-query'; -import { is_interactive_element, is_non_interactive_element, is_non_interactive_roles, is_presentation_role, is_interactive_roles, is_hidden_from_screen_reader, is_semantic_role_element, is_abstract_role, is_static_element, has_disabled_attribute } from '../utils/a11y'; +import { is_interactive_element, is_non_interactive_element, is_non_interactive_roles, is_presentation_role, is_interactive_roles, is_hidden_from_screen_reader, is_semantic_role_element, is_abstract_role, is_static_element, has_disabled_attribute, is_valid_autocomplete } from '../utils/a11y'; const aria_attributes = 'activedescendant atomic autocomplete busy checked colcount colindex colspan controls current describedby description details disabled dropeffect errormessage expanded flowto grabbed haspopup hidden invalid keyshortcuts label labelledby level live modal multiline multiselectable orientation owns placeholder posinset pressed readonly relevant required roledescription rowcount rowindex rowspan selected setsize sort valuemax valuemin valuenow valuetext'.split(' '); const aria_attribute_set = new Set(aria_attributes); @@ -848,6 +848,18 @@ export default class Element extends Node { should_have_attribute(this, required_attributes, 'input type="image"'); } } + + // autocomplete-valid + const autocomplete = attribute_map.get('autocomplete'); + + if (type && autocomplete) { + const type_value: null | true | string = type.get_static_value(); + const autocomplete_value: null | true | string = autocomplete.get_static_value(); + + if (!is_valid_autocomplete(type_value, autocomplete_value)) { + component.warn(autocomplete, compiler_warnings.a11y_autocomplete_valid(type_value, autocomplete_value)); + } + } } if (this.name === 'img') { diff --git a/src/compiler/compile/utils/a11y.ts b/src/compiler/compile/utils/a11y.ts index 4409f802623d..fbdf9e74df05 100644 --- a/src/compiler/compile/utils/a11y.ts +++ b/src/compiler/compile/utils/a11y.ts @@ -6,6 +6,7 @@ import { } from 'aria-query'; import { AXObjects, AXObjectRoles, elementAXObjects } from 'axobject-query'; import Attribute from '../nodes/Attribute'; +import { regex_whitespaces } from '../../utils/patterns'; const aria_roles = roles_map.keys(); const abstract_roles = new Set(aria_roles.filter(role => roles_map.get(role).abstract)); @@ -223,3 +224,194 @@ export function is_semantic_role_element(role: ARIARoleDefinitionKey, tag_name: } return false; } + +// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute +const address_type_tokens = new Set(['shipping', 'billing']); +const autofill_field_name_tokens = new Set([ + 'name', + 'honorific-prefix', + 'given-name', + 'additional-name', + 'family-name', + 'honorific-suffix', + 'nickname', + 'username', + 'new-password', + 'current-password', + 'one-time-code', + 'organization-title', + 'organization', + 'street-address', + 'address-line1', + 'address-line2', + 'address-line3', + 'address-level4', + 'address-level3', + 'address-level2', + 'address-level1', + 'country', + 'country-name', + 'postal-code', + 'cc-name', + 'cc-given-name', + 'cc-additional-name', + 'cc-family-name', + 'cc-number', + 'cc-exp', + 'cc-exp-month', + 'cc-exp-year', + 'cc-csc', + 'cc-type', + 'transaction-currency', + 'transaction-amount', + 'language', + 'bday', + 'bday-day', + 'bday-month', + 'bday-year', + 'sex', + 'url', + 'photo' +]); +const contact_type_tokens = new Set(['home', 'work', 'mobile', 'fax', 'pager']); +const autofill_contact_field_name_tokens = new Set([ + 'tel', + 'tel-country-code', + 'tel-national', + 'tel-area-code', + 'tel-local', + 'tel-local-prefix', + 'tel-local-suffix', + 'tel-extension', + 'email', + 'impp' +]); + +const control_group_text_types = new Set(['hidden', 'text', 'search']); +const control_group_multiline_types = new Set(['hidden']); +const control_group_password_types = new Set(['hidden', 'text', 'search', 'password']); +const control_group_url_types = new Set(['hidden', 'text', 'search', 'url']); +const control_group_username_types = new Set(['hidden', 'text', 'search', 'email']); +const control_group_telephone_types = new Set(['hidden', 'text', 'search', 'tel']); +const control_group_numeric_types = new Set(['hidden', 'text', 'search', 'number']); +const control_group_month_types = new Set(['hidden', 'text', 'search', 'month']); +const control_group_date_types = new Set(['hidden', 'text', 'search', 'date']); + +const appropriate_types_for_field_names = new Map([ + ['name', control_group_text_types], + ['honorific-prefix', control_group_text_types], + ['given-name', control_group_text_types], + ['additional-name', control_group_text_types], + ['family-name', control_group_text_types], + ['honorific-suffix', control_group_text_types], + ['nickname', control_group_text_types], + ['organization-title', control_group_text_types], + ['username', control_group_username_types], + ['new-password', control_group_password_types], + ['current-password', control_group_password_types], + ['one-time-code', control_group_password_types], + ['organization', control_group_text_types], + ['street-address', control_group_multiline_types], + ['address-line1', control_group_text_types], + ['address-line2', control_group_text_types], + ['address-line3', control_group_text_types], + ['address-level4', control_group_text_types], + ['address-level3', control_group_text_types], + ['address-level2', control_group_text_types], + ['address-level1', control_group_text_types], + ['country', control_group_text_types], + ['country-name', control_group_text_types], + ['postal-code', control_group_text_types], + ['cc-name', control_group_text_types], + ['cc-given-name', control_group_text_types], + ['cc-additional-name', control_group_text_types], + ['cc-family-name', control_group_text_types], + ['cc-number', control_group_text_types], + ['cc-exp', control_group_month_types], + ['cc-exp-month', control_group_numeric_types], + ['cc-exp-year', control_group_numeric_types], + ['cc-csc', control_group_text_types], + ['cc-type', control_group_text_types], + ['transaction-currency', control_group_text_types], + ['transaction-amount', control_group_numeric_types], + ['language', control_group_text_types], + ['bday', control_group_date_types], + ['bday-day', control_group_numeric_types], + ['bday-month', control_group_numeric_types], + ['bday-year', control_group_numeric_types], + ['sex', control_group_text_types], + ['url', control_group_url_types], + ['photo', control_group_url_types], + ['tel', control_group_telephone_types], + ['tel-country-code', control_group_text_types], + ['tel-national', control_group_text_types], + ['tel-area-code', control_group_text_types], + ['tel-local', control_group_text_types], + ['tel-local-prefix', control_group_text_types], + ['tel-local-suffix', control_group_text_types], + ['tel-extension', control_group_text_types], + ['email', control_group_username_types], + ['impp', control_group_url_types] +]); + +function is_appropriate_type_for_field_name(type: string, field_name: string) { + if (autofill_field_name_tokens.has(field_name)) { + return appropriate_types_for_field_names.get(field_name)?.has(type); + } + + return false; +} + +function is_appropriate_type_for_contact_field_name(type: string, field_name: string) { + if (autofill_contact_field_name_tokens.has(field_name)) { + return appropriate_types_for_field_names.get(field_name)?.has(type); + } + + return false; +} + +export function is_valid_autocomplete(type: null | true | string, autocomplete: null | true | string) { + if (typeof autocomplete !== 'string' || typeof type !== 'string') { + return false; + } + + const tokens = autocomplete.trim().toLowerCase().split(regex_whitespaces); + const normalized_type = type.toLowerCase(); + + const input_wears_autofill_anchor_mantle = normalized_type === 'hidden'; + const input_wears_autofill_expectation_mantle = !input_wears_autofill_anchor_mantle; + + if (input_wears_autofill_expectation_mantle) { + if (tokens[0] === 'on' || tokens[0] === 'off') { + return tokens.length === 1; + } + } + + if (typeof tokens[0] === 'string' && tokens[0].startsWith('section-')) { + tokens.shift(); + } + + if (address_type_tokens.has(tokens[0])) { + tokens.shift(); + } + + if (is_appropriate_type_for_field_name(normalized_type, tokens[0])) { + tokens.shift(); + } else { + if (contact_type_tokens.has(tokens[0])) { + tokens.shift(); + } + + if (is_appropriate_type_for_contact_field_name(normalized_type, tokens[0])) { + tokens.shift(); + } else { + return false; + } + } + + if (tokens[0] === 'webauthn') { + tokens.shift(); + } + + return tokens.length === 0; +} diff --git a/test/validator/samples/a11y-autocomplete-valid/input.svelte b/test/validator/samples/a11y-autocomplete-valid/input.svelte new file mode 100644 index 000000000000..f6b01b66407c --- /dev/null +++ b/test/validator/samples/a11y-autocomplete-valid/input.svelte @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/validator/samples/a11y-autocomplete-valid/warnings.json b/test/validator/samples/a11y-autocomplete-valid/warnings.json new file mode 100644 index 000000000000..8256c57bc7cf --- /dev/null +++ b/test/validator/samples/a11y-autocomplete-valid/warnings.json @@ -0,0 +1,98 @@ +[ + { + "code": "a11y-autocomplete-valid", + "end": { + "column": 31, + "line": 14 + }, + "message": "A11y: The value 'true' is not supported by the attribute 'autocomplete' on element ", + "start": { + "column": 19, + "line": 14 + } + }, + { + "code": "a11y-autocomplete-valid", + "end": { + "column": 39, + "line": 15 + }, + "message": "A11y: The value 'off' is not supported by the attribute 'autocomplete' on element ", + "start": { + "column": 21, + "line": 15 + } + }, + { + "code": "a11y-autocomplete-valid", + "message": "A11y: The value 'on' is not supported by the attribute 'autocomplete' on element ", + "end": { + "column": 38, + "line": 16 + }, + "start": { + "column": 21, + "line": 16 + } + }, + { + "code": "a11y-autocomplete-valid", + "message": "A11y: The value '' is not supported by the attribute 'autocomplete' on element ", + "end": { + "column": 34, + "line": 17 + }, + "start": { + "column": 19, + "line": 17 + } + }, + { + "code": "a11y-autocomplete-valid", + "end": { + "column": 43, + "line": 18 + }, + "message": "A11y: The value 'incorrect' is not supported by the attribute 'autocomplete' on element ", + "start": { + "column": 19, + "line": 18 + } + }, + { + "code": "a11y-autocomplete-valid", + "end": { + "column": 38, + "line": 19 + }, + "message": "A11y: The value 'url' is not supported by the attribute 'autocomplete' on element ", + "start": { + "column": 20, + "line": 19 + } + }, + { + "code": "a11y-autocomplete-valid", + "end": { + "column": 70, + "line": 21 + }, + "message": "A11y: The value 'section-blue shipping street-address' is not supported by the attribute 'autocomplete' on element ", + "start": { + "column": 19, + "line": 21 + } + }, + { + "code": "a11y-autocomplete-valid", + "end": { + "column": 42, + "line": 22 + }, + "message": "A11y: The value 'webauthn' is not supported by the attribute 'autocomplete' on element ", + "start": { + "column": 19, + "line": 22 + } + } +] From c0b9cc1a339f05c48d544cba1498078a06db9585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Sat, 22 Apr 2023 11:48:22 +0200 Subject: [PATCH 2/4] feat: remove checks for appropriate field types --- src/compiler/compile/utils/a11y.ts | 87 +------------------ .../a11y-autocomplete-valid/input.svelte | 5 +- .../a11y-autocomplete-valid/warnings.json | 48 +++------- 3 files changed, 16 insertions(+), 124 deletions(-) diff --git a/src/compiler/compile/utils/a11y.ts b/src/compiler/compile/utils/a11y.ts index fbdf9e74df05..7f9a529a279d 100644 --- a/src/compiler/compile/utils/a11y.ts +++ b/src/compiler/compile/utils/a11y.ts @@ -287,89 +287,6 @@ const autofill_contact_field_name_tokens = new Set([ 'impp' ]); -const control_group_text_types = new Set(['hidden', 'text', 'search']); -const control_group_multiline_types = new Set(['hidden']); -const control_group_password_types = new Set(['hidden', 'text', 'search', 'password']); -const control_group_url_types = new Set(['hidden', 'text', 'search', 'url']); -const control_group_username_types = new Set(['hidden', 'text', 'search', 'email']); -const control_group_telephone_types = new Set(['hidden', 'text', 'search', 'tel']); -const control_group_numeric_types = new Set(['hidden', 'text', 'search', 'number']); -const control_group_month_types = new Set(['hidden', 'text', 'search', 'month']); -const control_group_date_types = new Set(['hidden', 'text', 'search', 'date']); - -const appropriate_types_for_field_names = new Map([ - ['name', control_group_text_types], - ['honorific-prefix', control_group_text_types], - ['given-name', control_group_text_types], - ['additional-name', control_group_text_types], - ['family-name', control_group_text_types], - ['honorific-suffix', control_group_text_types], - ['nickname', control_group_text_types], - ['organization-title', control_group_text_types], - ['username', control_group_username_types], - ['new-password', control_group_password_types], - ['current-password', control_group_password_types], - ['one-time-code', control_group_password_types], - ['organization', control_group_text_types], - ['street-address', control_group_multiline_types], - ['address-line1', control_group_text_types], - ['address-line2', control_group_text_types], - ['address-line3', control_group_text_types], - ['address-level4', control_group_text_types], - ['address-level3', control_group_text_types], - ['address-level2', control_group_text_types], - ['address-level1', control_group_text_types], - ['country', control_group_text_types], - ['country-name', control_group_text_types], - ['postal-code', control_group_text_types], - ['cc-name', control_group_text_types], - ['cc-given-name', control_group_text_types], - ['cc-additional-name', control_group_text_types], - ['cc-family-name', control_group_text_types], - ['cc-number', control_group_text_types], - ['cc-exp', control_group_month_types], - ['cc-exp-month', control_group_numeric_types], - ['cc-exp-year', control_group_numeric_types], - ['cc-csc', control_group_text_types], - ['cc-type', control_group_text_types], - ['transaction-currency', control_group_text_types], - ['transaction-amount', control_group_numeric_types], - ['language', control_group_text_types], - ['bday', control_group_date_types], - ['bday-day', control_group_numeric_types], - ['bday-month', control_group_numeric_types], - ['bday-year', control_group_numeric_types], - ['sex', control_group_text_types], - ['url', control_group_url_types], - ['photo', control_group_url_types], - ['tel', control_group_telephone_types], - ['tel-country-code', control_group_text_types], - ['tel-national', control_group_text_types], - ['tel-area-code', control_group_text_types], - ['tel-local', control_group_text_types], - ['tel-local-prefix', control_group_text_types], - ['tel-local-suffix', control_group_text_types], - ['tel-extension', control_group_text_types], - ['email', control_group_username_types], - ['impp', control_group_url_types] -]); - -function is_appropriate_type_for_field_name(type: string, field_name: string) { - if (autofill_field_name_tokens.has(field_name)) { - return appropriate_types_for_field_names.get(field_name)?.has(type); - } - - return false; -} - -function is_appropriate_type_for_contact_field_name(type: string, field_name: string) { - if (autofill_contact_field_name_tokens.has(field_name)) { - return appropriate_types_for_field_names.get(field_name)?.has(type); - } - - return false; -} - export function is_valid_autocomplete(type: null | true | string, autocomplete: null | true | string) { if (typeof autocomplete !== 'string' || typeof type !== 'string') { return false; @@ -395,14 +312,14 @@ export function is_valid_autocomplete(type: null | true | string, autocomplete: tokens.shift(); } - if (is_appropriate_type_for_field_name(normalized_type, tokens[0])) { + if (autofill_field_name_tokens.has(tokens[0])) { tokens.shift(); } else { if (contact_type_tokens.has(tokens[0])) { tokens.shift(); } - if (is_appropriate_type_for_contact_field_name(normalized_type, tokens[0])) { + if (autofill_contact_field_name_tokens.has(tokens[0])) { tokens.shift(); } else { return false; diff --git a/test/validator/samples/a11y-autocomplete-valid/input.svelte b/test/validator/samples/a11y-autocomplete-valid/input.svelte index f6b01b66407c..28de32bb8645 100644 --- a/test/validator/samples/a11y-autocomplete-valid/input.svelte +++ b/test/validator/samples/a11y-autocomplete-valid/input.svelte @@ -9,6 +9,8 @@ + + @@ -16,7 +18,4 @@ - - - diff --git a/test/validator/samples/a11y-autocomplete-valid/warnings.json b/test/validator/samples/a11y-autocomplete-valid/warnings.json index 8256c57bc7cf..c7e5ab9490fe 100644 --- a/test/validator/samples/a11y-autocomplete-valid/warnings.json +++ b/test/validator/samples/a11y-autocomplete-valid/warnings.json @@ -3,24 +3,24 @@ "code": "a11y-autocomplete-valid", "end": { "column": 31, - "line": 14 + "line": 16 }, "message": "A11y: The value 'true' is not supported by the attribute 'autocomplete' on element ", "start": { "column": 19, - "line": 14 + "line": 16 } }, { "code": "a11y-autocomplete-valid", "end": { "column": 39, - "line": 15 + "line": 17 }, "message": "A11y: The value 'off' is not supported by the attribute 'autocomplete' on element ", "start": { "column": 21, - "line": 15 + "line": 17 } }, { @@ -28,11 +28,11 @@ "message": "A11y: The value 'on' is not supported by the attribute 'autocomplete' on element ", "end": { "column": 38, - "line": 16 + "line": 18 }, "start": { "column": 21, - "line": 16 + "line": 18 } }, { @@ -40,59 +40,35 @@ "message": "A11y: The value '' is not supported by the attribute 'autocomplete' on element ", "end": { "column": 34, - "line": 17 + "line": 19 }, "start": { "column": 19, - "line": 17 + "line": 19 } }, { "code": "a11y-autocomplete-valid", "end": { "column": 43, - "line": 18 + "line": 20 }, "message": "A11y: The value 'incorrect' is not supported by the attribute 'autocomplete' on element ", "start": { "column": 19, - "line": 18 - } - }, - { - "code": "a11y-autocomplete-valid", - "end": { - "column": 38, - "line": 19 - }, - "message": "A11y: The value 'url' is not supported by the attribute 'autocomplete' on element ", - "start": { - "column": 20, - "line": 19 - } - }, - { - "code": "a11y-autocomplete-valid", - "end": { - "column": 70, - "line": 21 - }, - "message": "A11y: The value 'section-blue shipping street-address' is not supported by the attribute 'autocomplete' on element ", - "start": { - "column": 19, - "line": 21 + "line": 20 } }, { "code": "a11y-autocomplete-valid", "end": { "column": 42, - "line": 22 + "line": 21 }, "message": "A11y: The value 'webauthn' is not supported by the attribute 'autocomplete' on element ", "start": { "column": 19, - "line": 22 + "line": 21 } } ] From 1ab4085c9f99206001a637123bfc4bafb169308f Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 5 May 2023 12:44:12 +0200 Subject: [PATCH 3/4] relax a little --- src/compiler/compile/nodes/Element.ts | 4 +- src/compiler/compile/utils/a11y.ts | 13 ++----- .../a11y-autocomplete-valid/input.svelte | 6 +-- .../a11y-autocomplete-valid/warnings.json | 38 +------------------ 4 files changed, 9 insertions(+), 52 deletions(-) diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 3aee4b14c27b..81e61ef86b52 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -853,8 +853,8 @@ export default class Element extends Node { const autocomplete = attribute_map.get('autocomplete'); if (type && autocomplete) { - const type_value: null | true | string = type.get_static_value(); - const autocomplete_value: null | true | string = autocomplete.get_static_value(); + const type_value = type.get_static_value(); + const autocomplete_value = autocomplete.get_static_value(); if (!is_valid_autocomplete(type_value, autocomplete_value)) { component.warn(autocomplete, compiler_warnings.a11y_autocomplete_valid(type_value, autocomplete_value)); diff --git a/src/compiler/compile/utils/a11y.ts b/src/compiler/compile/utils/a11y.ts index 7f9a529a279d..0d24f5dbda1d 100644 --- a/src/compiler/compile/utils/a11y.ts +++ b/src/compiler/compile/utils/a11y.ts @@ -228,6 +228,9 @@ export function is_semantic_role_element(role: ARIARoleDefinitionKey, tag_name: // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute const address_type_tokens = new Set(['shipping', 'billing']); const autofill_field_name_tokens = new Set([ + '', + 'on', + 'off', 'name', 'honorific-prefix', 'given-name', @@ -293,16 +296,6 @@ export function is_valid_autocomplete(type: null | true | string, autocomplete: } const tokens = autocomplete.trim().toLowerCase().split(regex_whitespaces); - const normalized_type = type.toLowerCase(); - - const input_wears_autofill_anchor_mantle = normalized_type === 'hidden'; - const input_wears_autofill_expectation_mantle = !input_wears_autofill_anchor_mantle; - - if (input_wears_autofill_expectation_mantle) { - if (tokens[0] === 'on' || tokens[0] === 'off') { - return tokens.length === 1; - } - } if (typeof tokens[0] === 'string' && tokens[0].startsWith('section-')) { tokens.shift(); diff --git a/test/validator/samples/a11y-autocomplete-valid/input.svelte b/test/validator/samples/a11y-autocomplete-valid/input.svelte index 28de32bb8645..1f47878e3789 100644 --- a/test/validator/samples/a11y-autocomplete-valid/input.svelte +++ b/test/validator/samples/a11y-autocomplete-valid/input.svelte @@ -11,11 +11,11 @@ - - - + + + diff --git a/test/validator/samples/a11y-autocomplete-valid/warnings.json b/test/validator/samples/a11y-autocomplete-valid/warnings.json index c7e5ab9490fe..dfde14a51f96 100644 --- a/test/validator/samples/a11y-autocomplete-valid/warnings.json +++ b/test/validator/samples/a11y-autocomplete-valid/warnings.json @@ -3,45 +3,9 @@ "code": "a11y-autocomplete-valid", "end": { "column": 31, - "line": 16 - }, - "message": "A11y: The value 'true' is not supported by the attribute 'autocomplete' on element ", - "start": { - "column": 19, - "line": 16 - } - }, - { - "code": "a11y-autocomplete-valid", - "end": { - "column": 39, - "line": 17 - }, - "message": "A11y: The value 'off' is not supported by the attribute 'autocomplete' on element ", - "start": { - "column": 21, - "line": 17 - } - }, - { - "code": "a11y-autocomplete-valid", - "message": "A11y: The value 'on' is not supported by the attribute 'autocomplete' on element ", - "end": { - "column": 38, - "line": 18 - }, - "start": { - "column": 21, - "line": 18 - } - }, - { - "code": "a11y-autocomplete-valid", - "message": "A11y: The value '' is not supported by the attribute 'autocomplete' on element ", - "end": { - "column": 34, "line": 19 }, + "message": "A11y: The value 'true' is not supported by the attribute 'autocomplete' on element ", "start": { "column": 19, "line": 19 From ee6e889305cf46423a55b116640e77714330a9d3 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 5 May 2023 12:49:47 +0200 Subject: [PATCH 4/4] changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc75b301a774..730a5b8cae72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ * Update interpolated style directive properly when using spread ([#8438](https://github.com/sveltejs/svelte/issues/8438)) * Remove style directive property when value is `undefined` ([#8462](https://github.com/sveltejs/svelte/issues/8462)) * Ensure version is typed as `string` instead of the literal `__VERSION__` ([#8498](https://github.com/sveltejs/svelte/issues/8498)) +* Add `a11y-autocomplete-valid` warning ([#8520](https://github.com/sveltejs/svelte/pull/8520)) +* Handle nested array rest destructuring ([#8554](https://github.com/sveltejs/svelte/issues/8554), [#8552](https://github.com/sveltejs/svelte/issues/8552)) +* Add `fullscreenElement` and `visibilityState` bindings for `` ([#8507](https://github.com/sveltejs/svelte/pull/8507)) +* Add `devicePixelRatio` binding for `` ([#8285](https://github.com/sveltejs/svelte/issues/8285)) +* Relax `a11y-no-redundant-roles` warning ([#8536](https://github.com/sveltejs/svelte/pull/8536)) ## 3.58.0