From 0f64e54ebba0ab7c09a271c72dcdb82a859330a3 Mon Sep 17 00:00:00 2001 From: Geoff Rich <4992896+geoffrich@users.noreply.github.com> Date: Fri, 17 Mar 2023 08:58:51 -0700 Subject: [PATCH 1/5] chore: update changelog (#8390) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 097cfd51ba..771adf2100 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Prevent derived store callbacks after store is unsubscribed from ([#8364](https://github.com/sveltejs/svelte/issues/8364)) * Account for `bind:group` members being spread across multiple control flow blocks ([#8372](https://github.com/sveltejs/svelte/issues/8372)) * Revert buggy reactive statement optimization ([#8374](https://github.com/sveltejs/svelte/issues/8374)) +* Support CSS units in the `fly` and `blur` transitions ([#7623](https://github.com/sveltejs/svelte/pull/7623)) ## 3.56.0 From 33065aa1e629da315942859a06f5161e3244d8f4 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 17 Mar 2023 17:55:50 +0100 Subject: [PATCH 2/5] chore: update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 771adf2100..b78413123a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Svelte changelog +## Unreleased + +* Add `bind:innerText` for `contenteditable` elements ([#3311](https://github.com/sveltejs/svelte/issues/3311)) + ## 3.57.0 * Add `` ([#3310](https://github.com/sveltejs/svelte/issues/3310)) From 7e9e78b37c0c57a02c015196984e18cc47827345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cavit=20Ertu=C4=9Frul=20S=C4=B1rt?= <100495175+ertucode@users.noreply.github.com> Date: Sun, 19 Mar 2023 09:20:20 +0300 Subject: [PATCH 3/5] docs: Tutorial body had even though was the subject (#8396) --- .../16-special-elements/06-svelte-document/app-a/App.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/tutorial/16-special-elements/06-svelte-document/app-a/App.svelte b/site/content/tutorial/16-special-elements/06-svelte-document/app-a/App.svelte index cea052ec88..09cc596e8c 100644 --- a/site/content/tutorial/16-special-elements/06-svelte-document/app-a/App.svelte +++ b/site/content/tutorial/16-special-elements/06-svelte-document/app-a/App.svelte @@ -4,7 +4,7 @@ const handleSelectionChange = (e) => selection = document.getSelection(); - +

Select this text to fire events

Selection: {selection}

From fed93ab9e1da9b54852c17ebe1dc721e52bbb27b Mon Sep 17 00:00:00 2001 From: Nguyen Tran <88808276+ngtr6788@users.noreply.github.com> Date: Mon, 20 Mar 2023 08:18:54 -0400 Subject: [PATCH 4/5] feat: add a11y `interactive-supports-focus` (#8392) #820 --- .../content/docs/06-accessibility-warnings.md | 11 + src/compiler/compile/compiler_warnings.ts | 4 + src/compiler/compile/nodes/Element.ts | 44 ++- src/compiler/compile/utils/a11y.ts | 18 ++ .../input.svelte | 32 ++ .../warnings.json | 278 ++++++++++++++++++ 6 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 test/validator/samples/a11y-interactive-supports-focus/input.svelte create mode 100644 test/validator/samples/a11y-interactive-supports-focus/warnings.json diff --git a/site/content/docs/06-accessibility-warnings.md b/site/content/docs/06-accessibility-warnings.md index bc793d80e7..0d025d797d 100644 --- a/site/content/docs/06-accessibility-warnings.md +++ b/site/content/docs/06-accessibility-warnings.md @@ -137,6 +137,17 @@ Enforce that attributes important for accessibility have a valid value. For exam --- +### `a11y-interactive-supports-focus` + +Enforce that elements with an interactive role and interactive handlers (mouse or key press) must be focusable or tabbable. + +```sv + +
{}} /> +``` + +--- + ### `a11y-label-has-associated-control` Enforce that a label tag has a text label and an associated control. diff --git a/src/compiler/compile/compiler_warnings.ts b/src/compiler/compile/compiler_warnings.ts index 710377374f..a851bc24c2 100644 --- a/src/compiler/compile/compiler_warnings.ts +++ b/src/compiler/compile/compiler_warnings.ts @@ -166,6 +166,10 @@ export default { code: 'a11y-img-redundant-alt', message: 'A11y: Screenreaders already announce elements as an image.' }, + a11y_interactive_supports_focus: (role: string) => ({ + code: 'a11y-interactive-supports-focus', + message: `A11y: Elements with the '${role}' interactive role must have a tabindex value.` + }), a11y_label_has_associated_control: { code: 'a11y-label-has-associated-control', message: 'A11y: A form label must be associated with a control.' diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 2fd91ac495..7fcb586b6a 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 } 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 } 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); @@ -75,6 +75,33 @@ const a11y_labelable = new Set([ 'textarea' ]); +const a11y_interactive_handlers = new Set([ + // Keyboard events + 'keypress', + 'keydown', + 'keyup', + + // Click events + 'click', + 'contextmenu', + 'dblclick', + 'drag', + 'dragend', + 'dragenter', + 'dragexit', + 'dragleave', + 'dragover', + 'dragstart', + 'drop', + 'mousedown', + 'mouseenter', + 'mouseleave', + 'mousemove', + 'mouseout', + 'mouseover', + 'mouseup' +]); + const a11y_nested_implicit_semantics = new Map([ ['header', 'banner'], ['footer', 'contentinfo'] @@ -603,6 +630,21 @@ export default class Element extends Node { } } + // interactive-supports-focus + if ( + !has_disabled_attribute(attribute_map) && + !is_hidden_from_screen_reader(this.name, attribute_map) && + !is_presentation_role(current_role) && + is_interactive_roles(current_role) && + is_static_element(this.name, attribute_map) && + !attribute_map.get('tabindex') + ) { + const has_interactive_handlers = handlers.some((handler) => a11y_interactive_handlers.has(handler.name)); + if (has_interactive_handlers) { + component.warn(this, compiler_warnings.a11y_interactive_supports_focus(current_role)); + } + } + // no-interactive-element-to-noninteractive-role if (is_interactive_element(this.name, attribute_map) && (is_non_interactive_roles(current_role) || is_presentation_role(current_role))) { component.warn(this, compiler_warnings.a11y_no_interactive_element_to_noninteractive_role(current_role, this.name)); diff --git a/src/compiler/compile/utils/a11y.ts b/src/compiler/compile/utils/a11y.ts index d0564b419e..4409f80262 100644 --- a/src/compiler/compile/utils/a11y.ts +++ b/src/compiler/compile/utils/a11y.ts @@ -68,6 +68,24 @@ export function is_hidden_from_screen_reader(tag_name: string, attribute_map: Ma return aria_hidden_value === true || aria_hidden_value === 'true'; } +export function has_disabled_attribute(attribute_map: Map) { + const disabled_attr = attribute_map.get('disabled'); + const disabled_attr_value = disabled_attr && disabled_attr.get_static_value(); + if (disabled_attr_value) { + return true; + } + + const aria_disabled_attr = attribute_map.get('aria-disabled'); + if (aria_disabled_attr) { + const aria_disabled_attr_value = aria_disabled_attr.get_static_value(); + if (aria_disabled_attr_value === true) { + return true; + } + } + + return false; +} + const non_interactive_element_role_schemas: ARIARoleRelationConcept[] = []; elementRoles.entries().forEach(([schema, roles]) => { diff --git a/test/validator/samples/a11y-interactive-supports-focus/input.svelte b/test/validator/samples/a11y-interactive-supports-focus/input.svelte new file mode 100644 index 0000000000..9caa9ccfaf --- /dev/null +++ b/test/validator/samples/a11y-interactive-supports-focus/input.svelte @@ -0,0 +1,32 @@ + +
{}} /> +
{}} /> +
{}} /> +
{}} /> +