From f7f225d69990877784cf7e15d7500edb5a8ec65b Mon Sep 17 00:00:00 2001 From: Matt King Date: Sun, 28 Jul 2024 17:28:40 -0700 Subject: [PATCH 01/22] Draft about section for tabs that illustrate aria-actions. --- .../coverage-and-quality-report.html | 25 +- .../coverage-and-quality/prop-coverage.csv | 6 +- .../coverage-and-quality/role-coverage.csv | 6 +- content/index/index.html | 9 +- .../tabs/examples/tabs-removable.html | 369 ++++++++++++++++++ 5 files changed, 404 insertions(+), 11 deletions(-) create mode 100644 content/patterns/tabs/examples/tabs-removable.html diff --git a/content/about/coverage-and-quality/coverage-and-quality-report.html b/content/about/coverage-and-quality/coverage-and-quality-report.html index 80325c249..2eb555a09 100644 --- a/content/about/coverage-and-quality/coverage-and-quality-report.html +++ b/content/about/coverage-and-quality/coverage-and-quality-report.html @@ -649,6 +649,7 @@

Roles with More than One Guidance or Exa
  • Auto-Rotating Image Carousel with Tabs for Slide Control (HC)
  • Tabs with Automatic Activation (HC)
  • Tabs with Manual Activation (HC)
  • +
  • Experimental Tabs with Remove Buttons (HC)
  • @@ -669,6 +670,7 @@

    Roles with More than One Guidance or Exa
  • Auto-Rotating Image Carousel with Tabs for Slide Control (HC)
  • Tabs with Automatic Activation (HC)
  • Tabs with Manual Activation (HC)
  • +
  • Experimental Tabs with Remove Buttons (HC)
  • @@ -679,6 +681,7 @@

    Roles with More than One Guidance or Exa
  • Auto-Rotating Image Carousel with Tabs for Slide Control (HC)
  • Tabs with Automatic Activation (HC)
  • Tabs with Manual Activation (HC)
  • +
  • Experimental Tabs with Remove Buttons (HC)
  • @@ -895,6 +898,7 @@

    Properties and States with More than One
  • Navigation Menu Button (HC)
  • Tabs with Automatic Activation (HC)
  • Tabs with Manual Activation (HC)
  • +
  • Experimental Tabs with Remove Buttons (HC)
  • Toolbar
  • @@ -1065,6 +1069,7 @@

    Properties and States with More than One
  • Switch Using HTML Button (HC)
  • Tabs with Automatic Activation (HC)
  • Tabs with Manual Activation (HC)
  • +
  • Experimental Tabs with Remove Buttons (HC)
  • File Directory Treeview Using Computed Properties
  • File Directory Treeview Using Declared Properties
  • Navigation Treeview (HC)
  • @@ -1176,6 +1181,7 @@

    Properties and States with More than One
  • Scrollable Listbox
  • Tabs with Automatic Activation (HC)
  • Tabs with Manual Activation (HC)
  • +
  • Experimental Tabs with Remove Buttons (HC)
  • File Directory Treeview Using Computed Properties
  • File Directory Treeview Using Declared Properties
  • @@ -1272,11 +1278,11 @@

    Coding Summary

    Total Examples - 60 + 61 High Contrast Documentation - 31 + 32 Uses SVG @@ -1306,7 +1312,7 @@

    Coding Summary

    Use Class - 36 + 37 Use Prototype @@ -2043,6 +2049,19 @@

    Coding Practices

    3 + + Experimental Tabs with Remove Buttons + class + + + Yes + ex1 + 3 + 3 + 3 + 3 + + Toolbar diff --git a/content/about/coverage-and-quality/prop-coverage.csv b/content/about/coverage-and-quality/prop-coverage.csv index 7effc0b04..2415b912e 100644 --- a/content/about/coverage-and-quality/prop-coverage.csv +++ b/content/about/coverage-and-quality/prop-coverage.csv @@ -7,7 +7,7 @@ "aria-colcount","1","1","Guidance: Using aria-colcount and aria-colindex","Example: Data Grid" "aria-colindex","3","1","Guidance: Using aria-colcount and aria-colindex","Guidance: Using aria-colindex When Column Indices Are Contiguous","Guidance: Using aria-colindex When Column Indices Are Not Contiguous","Example: Data Grid" "aria-colspan","1","0","Guidance: Defining cell spans using aria-colspan and aria-rowspan" -"aria-controls","0","20","Example: Accordion","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Checkbox (Mixed-State)","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Disclosure (Show/Hide) for Answers to Frequently Asked Questions","Example: Disclosure (Show/Hide) for Image Description","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Toolbar" +"aria-controls","0","21","Example: Accordion","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Checkbox (Mixed-State)","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Disclosure (Show/Hide) for Answers to Frequently Asked Questions","Example: Disclosure (Show/Hide) for Image Description","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons","Example: Toolbar" "aria-current","0","5","Example: Breadcrumb","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Navigation Menubar","Example: Navigation Treeview" "aria-describedby","1","6","Guidance: Describing by referencing content with aria-describedby","Example: Alert Dialog","Example: Date Picker Combobox","Example: Date Picker Dialog","Example: Modal Dialog","Example: Infinite Scrolling Feed","Example: Table" "aria-details","0","0" @@ -22,7 +22,7 @@ "aria-invalid","0","0" "aria-keyshortcuts","0","0" "aria-label","1","18","Guidance: Naming with a String Attribute Via aria-label","Example: Breadcrumb","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Date Picker Dialog","Example: Link","Example: Editor Menubar","Example: Navigation Menubar","Example: Rating Radio Group","Example: Horizontal Multi-Thumb Slider","Example: Date Picker Spin Button","Example: Table","Example: Toolbar","Example: Treegrid Email Inbox","Example: Navigation Treeview" -"aria-labelledby","1","40","Guidance: Naming with Referenced Content Via aria-labelledby","Example: Accordion","Example: Alert Dialog","Example: Checkbox (Two State)","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: Modal Dialog","Example: Infinite Scrolling Feed","Example: Data Grid","Example: Layout Grid","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Navigation Menubar","Example: Meter","Example: Radio Group Using aria-activedescendant","Example: Rating Radio Group","Example: Radio Group Using Roving tabindex","Example: Color Viewer Slider","Example: Rating Slider","Example: Media Seek Slider","Example: Vertical Temperature Slider","Example: Date Picker Spin Button","Example: Switch Using HTML Button","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties","Example: Navigation Treeview","Example: Complementary Landmark","Example: Form Landmark","Example: Main Landmark","Example: Navigation Landmark","Example: Region Landmark","Example: Search Landmark" +"aria-labelledby","1","41","Guidance: Naming with Referenced Content Via aria-labelledby","Example: Accordion","Example: Alert Dialog","Example: Checkbox (Two State)","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: Modal Dialog","Example: Infinite Scrolling Feed","Example: Data Grid","Example: Layout Grid","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Navigation Menubar","Example: Meter","Example: Radio Group Using aria-activedescendant","Example: Rating Radio Group","Example: Radio Group Using Roving tabindex","Example: Color Viewer Slider","Example: Rating Slider","Example: Media Seek Slider","Example: Vertical Temperature Slider","Example: Date Picker Spin Button","Example: Switch Using HTML Button","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties","Example: Navigation Treeview","Example: Complementary Landmark","Example: Form Landmark","Example: Main Landmark","Example: Navigation Landmark","Example: Region Landmark","Example: Search Landmark" "aria-level","0","2","Example: Treegrid Email Inbox","Example: File Directory Treeview Using Declared Properties" "aria-live","0","5","Example: Alert","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Date Picker Combobox","Example: Date Picker Dialog" "aria-modal","0","4","Example: Alert Dialog","Example: Date Picker Combobox","Example: Date Picker Dialog","Example: Modal Dialog" @@ -40,7 +40,7 @@ "aria-rowcount","1","2","Guidance: Using aria-rowcount and aria-rowindex","Example: Data Grid","Example: Layout Grid" "aria-rowindex","1","2","Guidance: Using aria-rowcount and aria-rowindex","Example: Data Grid","Example: Layout Grid" "aria-rowspan","1","0","Guidance: Defining cell spans using aria-colspan and aria-rowspan" -"aria-selected","0","16","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties" +"aria-selected","0","17","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties" "aria-setsize","0","3","Example: Infinite Scrolling Feed","Example: Treegrid Email Inbox","Example: File Directory Treeview Using Declared Properties" "aria-sort","1","2","Guidance: Indicating sort order with aria-sort","Example: Data Grid","Example: Sortable Table" "aria-valuemax","1","8","Guidance: Using aria-valuemin, aria-valuemax and aria-valuenow","Example: Meter","Example: Horizontal Multi-Thumb Slider","Example: Color Viewer Slider","Example: Rating Slider","Example: Media Seek Slider","Example: Vertical Temperature Slider","Example: Date Picker Spin Button","Example: Toolbar" diff --git a/content/about/coverage-and-quality/role-coverage.csv b/content/about/coverage-and-quality/role-coverage.csv index cbebe3fb3..923be3f7a 100644 --- a/content/about/coverage-and-quality/role-coverage.csv +++ b/content/about/coverage-and-quality/role-coverage.csv @@ -65,10 +65,10 @@ "spinbutton","1","2","Guidance: Spinbutton Pattern","Example: Date Picker Spin Button","Example: Toolbar" "status","0","0" "switch","1","3","Guidance: Switch Pattern","Example: Switch Using HTML Button","Example: Switch Using HTML Checkbox Input","Example: Switch" -"tab","1","3","Guidance: Keyboard Navigation Between Components (The Tab Sequence)","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation" +"tab","1","4","Guidance: Keyboard Navigation Between Components (The Tab Sequence)","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons" "table","2","1","Guidance: Table Pattern","Guidance: Grid and Table Properties","Example: Table" -"tablist","0","3","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation" -"tabpanel","0","3","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation" +"tablist","0","4","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons" +"tabpanel","0","4","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons" "term","0","0" "textbox","0","0" "timer","0","0" diff --git a/content/index/index.html b/content/index/index.html index 983d2a2aa..cbfcc0ad4 100644 --- a/content/index/index.html +++ b/content/index/index.html @@ -21,7 +21,7 @@

    About the Index

    Examples by Role

    @@ -904,7 +904,12 @@

    Examples By Properties and States

    - +
    +

    Experimental Examples

    +
    NOTE: The HC abbreviation means example has High Contrast support.
    + +
    diff --git a/content/patterns/tabs/examples/tabs-removable.html b/content/patterns/tabs/examples/tabs-removable.html new file mode 100644 index 000000000..9cfede565 --- /dev/null +++ b/content/patterns/tabs/examples/tabs-removable.html @@ -0,0 +1,369 @@ + + + + + + Experimental Example of Tabs with Remove Buttons + + + + + + + + + + + + + + + +
    +

    Experimental Example of Tabs with Remove Buttons

    + +
    +

    About This Experimental Example

    + +

    + This is an experimental implementation of the draft specification of the aria-actions attribute. + The aria-actions property enables an element to reference another interactive element that can be activated to perform an action on the referencing element. + In this example, a tab element is associated with a button that removes a tab from the tablist. + This association enables an assistive technology to both communicate the availability of the action button and provide a command for activating the button while focus is on the tab. +

    +

    + The below example demonstrates a tabs widget that implements the Tabs Pattern. + In this example, a panel is displayed when users activate its tab with either Space, Enter, or a mouse click. + So, for keyboard users, activating a tab requires two steps: 1) focus the tab, and 2) activate the tab. + This two-step process is referred to as manual activation. + Manual activation of tabs is recommended unless panels can be displayed instantly, i.e., all the panel content is present in the DOM. + For additional guidance, see Deciding When to Make Selection Automatically Follow Focus. +

    +

    + This example also demonstrates how to provide buttons that provide contextual actions for the tab. + Each tab has a remove icon. + The remove icon is referenced by aria-actions, which enables it to be activated by an assistive technology user while focus is on the tab. +

    +

    Similar examples include:

    + +
    + +
    +
    +

    Experimental Example

    +
    + +
    +
    +

    Danish Composers

    +
    + + + + +
    + +
    +

    + Maria Theresia Ahlefeldt + (16 January 1755 – 20 December 1810) was a Danish, (originally German), composer. + She is known as the first female composer in Denmark. + Maria Theresia composed music for several ballets, operas, and plays of the royal theatre. + She was given good critic as a composer and described as a “virkelig Tonekunstnerinde” ('a True Artist of Music'). +

    +
    + + + +
    +
    + +
    + +
    +

    Accessibility Features

    +
      +
    • + To ensure people who rely on browser or operating system high contrast settings can both distinguish the active (selected) tab from other tabs and perceive keyboard focus: +
        +
      • + The active tab has a 2 pixel border on its left and right sides and a 4 pixel border on top, while the names of inactive tabs have 1 pixel borders. + The active tab is also 4 pixels higher than the inactive tabs. +
      • +
      • + The focus ring is drawn with a CSS border on a child span element of the tab element. + This focus span is separated from the tab border by 2 pixels of space to ensure focus and selection are separately perceivable. + Note that when a tab element is focused, the outline of the tab element itself is set to 0 so that only one focus ring is displayed. +
      • +
      • + Because transparent borders are visible on some systems when high contrast settings are enabled, only the focused span element has a visible border. + When span elements are not indicating focus, they have a 0-width border and additional padding equal in width to the border that is used to indicate focus. +
      • +
      +
    • +
    • + Note that since the first element in every tabpanel is a focusable element (i.e., a link), the tabpanel is not included in the page Tab sequence. + To make it easy for screen reader users to navigate from a tab to the beginning of content in the active tabpanel, it is recommended that all tabpanel elements in a tab set are focusable if there are any panels in the set that contain content where the first element in the panel is not focusable. +
    • +
    • + To ensure the tab content remains visible when the screen is magnified, the width of the tabs and tab panels are defined using a percentage of the screen width. As the page is magnified the height of the tab increases and the tab content re-flows to the new dimensions of the tab. +
    • +
    +
    + +
    +

    Keyboard Support

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    KeyFunction
    Tab +
      +
    • When focus moves into the tab list, places focus on the active tab element.
    • +
    • When the tab list contains the focus, moves focus to the next element in the tab sequence, which is the a element in tabpanel.
    • +
    +
    Enter
    Space
    When a tab has focus, activates the tab, causing its associated panel to be displayed.
    Right Arrow + When a tab has focus: +
      +
    • Moves focus to the next tab.
    • +
    • If focus is on the last tab, moves focus to the first tab.
    • +
    +
    Left Arrow + When a tab has focus: +
      +
    • Moves focus to the previous tab.
    • +
    • If focus is on the first tab, moves focus to the last tab.
    • +
    +
    HomeWhen a tab has focus, moves focus to the first tab.
    EndWhen a tab has focus, moves focus to the last tab.
    +
    + +
    +

    Role, Property, State, and Tabindex Attributes

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    RoleAttributeElementUsage
    + tablist + + div + Indicates that the element serves as a container for a set of tabs.
    + aria-labelledby="ID_REFERENCE" + + div + Provides a label that describes the purpose of the set of tabs.
    + tab + + button + +
      +
    • Indicates the element serves as a tab control.
    • +
    • Provides a title for its associated tabpanel.
    • +
    +
    + aria-selected="true" + + button + +
      +
    • Indicates the tab control is activated and its associated panel is displayed.
    • +
    • Set when a user activates a tab.
    • +
    • Does not change when focus moves in the tablist.
    • +
    +
    + aria-selected="false" + + button + +
      +
    • Indicates the tab control is not active and its associated panel is NOT displayed.
    • +
    • Set for all tab elements in the tab set except the active tab; the one associated with the currently displayed panel.
    • +
    +
    + tabindex="-1" + + button + +
      +
    • Removes the element from the page Tab sequence.
    • +
    • Set when a tab is not selected so that only the selected (active) tab is in the page Tab sequence.
    • +
    • Since an HTML button element is used for the tab, it is not necessary to set tabindex="0" on the selected (active) tab element.
    • +
    • + This approach to managing focus is described in the section on + Managing Focus Within Components Using a Roving tabindex. +
    • +
    +
    + aria-controls="ID_REFERENCE" + + button + Refers to the tabpanel element associated with the tab.
    + tabpanel + + div + +
      +
    • Indicates the element serves as a container for tab panel content.
    • +
    • Is hidden unless its associated tab control is activated.
    • +
    +
    + aria-labelledby="ID_REFERENCE" + + div + +
      +
    • Refers to the tab element that controls the panel.
    • +
    • Provides an accessible name for the tab panel.
    • +
    +
    +
    + +
    +

    JavaScript and CSS Source Code

    + +
    + +
    +

    HTML Source Code

    +

    To copy the following HTML code, please open it in CodePen.

    + +
    + + +
    +
    + + From 5cb10e181e75c9b0444885fa23e99bc07f967389 Mon Sep 17 00:00:00 2001 From: Matt King Date: Mon, 29 Jul 2024 23:17:20 -0700 Subject: [PATCH 02/22] Draft about section for new experimental example --- .../coverage-and-quality-report.html | 18 +++++++++--------- .../coverage-and-quality/prop-coverage.csv | 6 +++--- .../coverage-and-quality/role-coverage.csv | 6 +++--- content/index/index.html | 2 +- .../{tabs-removable.html => tabs-actions.html} | 12 ++++++------ 5 files changed, 22 insertions(+), 22 deletions(-) rename content/patterns/tabs/examples/{tabs-removable.html => tabs-actions.html} (96%) diff --git a/content/about/coverage-and-quality/coverage-and-quality-report.html b/content/about/coverage-and-quality/coverage-and-quality-report.html index 2eb555a09..b3b7711f8 100644 --- a/content/about/coverage-and-quality/coverage-and-quality-report.html +++ b/content/about/coverage-and-quality/coverage-and-quality-report.html @@ -647,9 +647,9 @@

    Roles with More than One Guidance or Exa @@ -668,9 +668,9 @@

    Roles with More than One Guidance or Exa @@ -679,9 +679,9 @@

    Roles with More than One Guidance or Exa @@ -896,9 +896,9 @@

    Properties and States with More than One
  • Actions Menu Button Using aria-activedescendant (HC)
  • Actions Menu Button Using element.focus() (HC)
  • Navigation Menu Button (HC)
  • +
  • Experimental Tabs with Action Buttons (HC)
  • Tabs with Automatic Activation (HC)
  • Tabs with Manual Activation (HC)
  • -
  • Experimental Tabs with Remove Buttons (HC)
  • Toolbar
  • @@ -1067,9 +1067,9 @@

    Properties and States with More than One
  • Vertical Temperature Slider (HC)
  • Date Picker Spin Button
  • Switch Using HTML Button (HC)
  • +
  • Experimental Tabs with Action Buttons (HC)
  • Tabs with Automatic Activation (HC)
  • Tabs with Manual Activation (HC)
  • -
  • Experimental Tabs with Remove Buttons (HC)
  • File Directory Treeview Using Computed Properties
  • File Directory Treeview Using Declared Properties
  • Navigation Treeview (HC)
  • @@ -1179,9 +1179,9 @@

    Properties and States with More than One
  • Listbox with Grouped Options
  • Listboxes with Rearrangeable Options
  • Scrollable Listbox
  • +
  • Experimental Tabs with Action Buttons (HC)
  • Tabs with Automatic Activation (HC)
  • Tabs with Manual Activation (HC)
  • -
  • Experimental Tabs with Remove Buttons (HC)
  • File Directory Treeview Using Computed Properties
  • File Directory Treeview Using Declared Properties
  • @@ -2024,7 +2024,7 @@

    Coding Practices

    - Tabs with Automatic Activation + Experimental Tabs with Action Buttons class @@ -2037,7 +2037,7 @@

    Coding Practices

    - Tabs with Manual Activation + Tabs with Automatic Activation class @@ -2050,7 +2050,7 @@

    Coding Practices

    - Experimental Tabs with Remove Buttons + Tabs with Manual Activation class diff --git a/content/about/coverage-and-quality/prop-coverage.csv b/content/about/coverage-and-quality/prop-coverage.csv index 2415b912e..f0f597ea1 100644 --- a/content/about/coverage-and-quality/prop-coverage.csv +++ b/content/about/coverage-and-quality/prop-coverage.csv @@ -7,7 +7,7 @@ "aria-colcount","1","1","Guidance: Using aria-colcount and aria-colindex","Example: Data Grid" "aria-colindex","3","1","Guidance: Using aria-colcount and aria-colindex","Guidance: Using aria-colindex When Column Indices Are Contiguous","Guidance: Using aria-colindex When Column Indices Are Not Contiguous","Example: Data Grid" "aria-colspan","1","0","Guidance: Defining cell spans using aria-colspan and aria-rowspan" -"aria-controls","0","21","Example: Accordion","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Checkbox (Mixed-State)","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Disclosure (Show/Hide) for Answers to Frequently Asked Questions","Example: Disclosure (Show/Hide) for Image Description","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons","Example: Toolbar" +"aria-controls","0","21","Example: Accordion","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Checkbox (Mixed-State)","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Disclosure (Show/Hide) for Answers to Frequently Asked Questions","Example: Disclosure (Show/Hide) for Image Description","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Toolbar" "aria-current","0","5","Example: Breadcrumb","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Navigation Menubar","Example: Navigation Treeview" "aria-describedby","1","6","Guidance: Describing by referencing content with aria-describedby","Example: Alert Dialog","Example: Date Picker Combobox","Example: Date Picker Dialog","Example: Modal Dialog","Example: Infinite Scrolling Feed","Example: Table" "aria-details","0","0" @@ -22,7 +22,7 @@ "aria-invalid","0","0" "aria-keyshortcuts","0","0" "aria-label","1","18","Guidance: Naming with a String Attribute Via aria-label","Example: Breadcrumb","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Date Picker Dialog","Example: Link","Example: Editor Menubar","Example: Navigation Menubar","Example: Rating Radio Group","Example: Horizontal Multi-Thumb Slider","Example: Date Picker Spin Button","Example: Table","Example: Toolbar","Example: Treegrid Email Inbox","Example: Navigation Treeview" -"aria-labelledby","1","41","Guidance: Naming with Referenced Content Via aria-labelledby","Example: Accordion","Example: Alert Dialog","Example: Checkbox (Two State)","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: Modal Dialog","Example: Infinite Scrolling Feed","Example: Data Grid","Example: Layout Grid","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Navigation Menubar","Example: Meter","Example: Radio Group Using aria-activedescendant","Example: Rating Radio Group","Example: Radio Group Using Roving tabindex","Example: Color Viewer Slider","Example: Rating Slider","Example: Media Seek Slider","Example: Vertical Temperature Slider","Example: Date Picker Spin Button","Example: Switch Using HTML Button","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties","Example: Navigation Treeview","Example: Complementary Landmark","Example: Form Landmark","Example: Main Landmark","Example: Navigation Landmark","Example: Region Landmark","Example: Search Landmark" +"aria-labelledby","1","41","Guidance: Naming with Referenced Content Via aria-labelledby","Example: Accordion","Example: Alert Dialog","Example: Checkbox (Two State)","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: Modal Dialog","Example: Infinite Scrolling Feed","Example: Data Grid","Example: Layout Grid","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Navigation Menubar","Example: Meter","Example: Radio Group Using aria-activedescendant","Example: Rating Radio Group","Example: Radio Group Using Roving tabindex","Example: Color Viewer Slider","Example: Rating Slider","Example: Media Seek Slider","Example: Vertical Temperature Slider","Example: Date Picker Spin Button","Example: Switch Using HTML Button","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties","Example: Navigation Treeview","Example: Complementary Landmark","Example: Form Landmark","Example: Main Landmark","Example: Navigation Landmark","Example: Region Landmark","Example: Search Landmark" "aria-level","0","2","Example: Treegrid Email Inbox","Example: File Directory Treeview Using Declared Properties" "aria-live","0","5","Example: Alert","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Date Picker Combobox","Example: Date Picker Dialog" "aria-modal","0","4","Example: Alert Dialog","Example: Date Picker Combobox","Example: Date Picker Dialog","Example: Modal Dialog" @@ -40,7 +40,7 @@ "aria-rowcount","1","2","Guidance: Using aria-rowcount and aria-rowindex","Example: Data Grid","Example: Layout Grid" "aria-rowindex","1","2","Guidance: Using aria-rowcount and aria-rowindex","Example: Data Grid","Example: Layout Grid" "aria-rowspan","1","0","Guidance: Defining cell spans using aria-colspan and aria-rowspan" -"aria-selected","0","17","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties" +"aria-selected","0","17","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Date Picker Dialog","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Listbox with Grouped Options","Example: Listboxes with Rearrangeable Options","Example: Scrollable Listbox","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties" "aria-setsize","0","3","Example: Infinite Scrolling Feed","Example: Treegrid Email Inbox","Example: File Directory Treeview Using Declared Properties" "aria-sort","1","2","Guidance: Indicating sort order with aria-sort","Example: Data Grid","Example: Sortable Table" "aria-valuemax","1","8","Guidance: Using aria-valuemin, aria-valuemax and aria-valuenow","Example: Meter","Example: Horizontal Multi-Thumb Slider","Example: Color Viewer Slider","Example: Rating Slider","Example: Media Seek Slider","Example: Vertical Temperature Slider","Example: Date Picker Spin Button","Example: Toolbar" diff --git a/content/about/coverage-and-quality/role-coverage.csv b/content/about/coverage-and-quality/role-coverage.csv index 923be3f7a..d3d229644 100644 --- a/content/about/coverage-and-quality/role-coverage.csv +++ b/content/about/coverage-and-quality/role-coverage.csv @@ -65,10 +65,10 @@ "spinbutton","1","2","Guidance: Spinbutton Pattern","Example: Date Picker Spin Button","Example: Toolbar" "status","0","0" "switch","1","3","Guidance: Switch Pattern","Example: Switch Using HTML Button","Example: Switch Using HTML Checkbox Input","Example: Switch" -"tab","1","4","Guidance: Keyboard Navigation Between Components (The Tab Sequence)","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons" +"tab","1","4","Guidance: Keyboard Navigation Between Components (The Tab Sequence)","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation" "table","2","1","Guidance: Table Pattern","Guidance: Grid and Table Properties","Example: Table" -"tablist","0","4","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons" -"tabpanel","0","4","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Experimental Tabs with Remove Buttons" +"tablist","0","4","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation" +"tabpanel","0","4","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation" "term","0","0" "textbox","0","0" "timer","0","0" diff --git a/content/index/index.html b/content/index/index.html index cbfcc0ad4..43027a6d1 100644 --- a/content/index/index.html +++ b/content/index/index.html @@ -908,7 +908,7 @@

    Examples By Properties and States

    Experimental Examples

    NOTE: The HC abbreviation means example has High Contrast support.
    +
  • Experimental Tabs with Action Buttons (HC)
  • diff --git a/content/patterns/tabs/examples/tabs-removable.html b/content/patterns/tabs/examples/tabs-actions.html similarity index 96% rename from content/patterns/tabs/examples/tabs-removable.html rename to content/patterns/tabs/examples/tabs-actions.html index 9cfede565..df9759218 100644 --- a/content/patterns/tabs/examples/tabs-removable.html +++ b/content/patterns/tabs/examples/tabs-actions.html @@ -3,7 +3,7 @@ - Experimental Example of Tabs with Remove Buttons + Experimental Example of Tabs with Action Buttons @@ -25,7 +25,7 @@
    -

    Experimental Example of Tabs with Remove Buttons

    +

    Experimental Example of Tabs with Action Buttons

    About This Experimental Example

    @@ -33,8 +33,8 @@

    About This Experimental Example

    This is an experimental implementation of the draft specification of the aria-actions attribute. The aria-actions property enables an element to reference another interactive element that can be activated to perform an action on the referencing element. - In this example, a tab element is associated with a button that removes a tab from the tablist. - This association enables an assistive technology to both communicate the availability of the action button and provide a command for activating the button while focus is on the tab. + In this example, each tab element references a menu button that opens a menu of actions that apply to the referencing tab. + The relationship provided by aria-actions enables an assistive technology to both communicate the availability of the action button and provide a command for activating the button while focus is on the tab.

    The below example demonstrates a tabs widget that implements the Tabs Pattern. @@ -46,8 +46,8 @@

    About This Experimental Example

    This example also demonstrates how to provide buttons that provide contextual actions for the tab. - Each tab has a remove icon. - The remove icon is referenced by aria-actions, which enables it to be activated by an assistive technology user while focus is on the tab. + Each tab has an associated context actions menu button. + The menu button is referenced by aria-actions, which enables it to be activated by an assistive technology user while focus is on the tab.

    Similar examples include:

      From 1c2d09c7a3553efcdd8a54f2c2a7ea8c2eb50daf Mon Sep 17 00:00:00 2001 From: Adam Page Date: Mon, 19 Aug 2024 18:49:04 -0700 Subject: [PATCH 03/22] initial tabs with aria-actions implementation --- .../coverage-and-quality-report.html | 25 +- .../tabs/examples/css/tabs-with-actions.css | 214 ++++++++++++++++++ .../patterns/tabs/examples/js/tabs-actions.js | 33 +++ .../patterns/tabs/examples/js/tabs-manual.js | 52 +++++ .../patterns/tabs/examples/tabs-actions.html | 136 ++++++++++- 5 files changed, 444 insertions(+), 16 deletions(-) create mode 100644 content/patterns/tabs/examples/css/tabs-with-actions.css create mode 100644 content/patterns/tabs/examples/js/tabs-actions.js diff --git a/content/about/coverage-and-quality/coverage-and-quality-report.html b/content/about/coverage-and-quality/coverage-and-quality-report.html index b3b7711f8..19b95c319 100644 --- a/content/about/coverage-and-quality/coverage-and-quality-report.html +++ b/content/about/coverage-and-quality/coverage-and-quality-report.html @@ -1286,7 +1286,7 @@

      Coding Summary

      Uses SVG - 34 + 35 Uses forced-colors media query @@ -1294,7 +1294,7 @@

      Coding Summary

      Uses currentColor value - 27 + 28 - + + +
    -
    +
    @@ -106,7 +106,7 @@

    Interesting Sharks

    -
    +
    @@ -126,7 +126,7 @@

    Interesting Sharks

    -
    +
    @@ -219,151 +219,6 @@

    Interesting Sharks

    - -
    -

    Tropical Fruit

    -
    -
    - - - -
    -
    - - - -
    -
    - - - -
    -
    - - - -
    -
    -
    -

    - Mangosteen, - also known as the purple mangosteen, is a tropical evergreen - tree with edible fruit native to Island Southeast Asia, from the - Malay Peninsula to Borneo. It has been cultivated extensively in - tropical Asia since ancient times. -

    -
    - - - - - -
    - - -
    -

    Danish Composers

    -
    - - - - -
    -
    -

    - Maria Theresia Ahlefeldt - (16 January 1755 – 20 December 1810) was a Danish, (originally German), composer. - She is known as the first female composer in Denmark. - Maria Theresia composed music for several ballets, operas, and plays of the royal theatre. - She was given good critic as a composer and described as a “virkelig Tonekunstnerinde” ('a True Artist of Music'). -

    -
    - - - -
    From 09262fa1ee8e9f0fcd056b5a493a7ee044d64fd1 Mon Sep 17 00:00:00 2001 From: Adam Page Date: Tue, 10 Sep 2024 11:40:39 -0700 Subject: [PATCH 15/22] resolve linting errors --- .vnurc | 2 ++ cspell.json | 29 +++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.vnurc b/.vnurc index e4fd70beb..0a6175d14 100644 --- a/.vnurc +++ b/.vnurc @@ -25,3 +25,5 @@ Bad value “none” for attribute “role” on element “svg”. Bad value “presentation” for attribute “role” on element “svg”. # https://github.com/validator/validator/issues/1122 Element “input” is missing required attribute “aria-checked”. +# https://github.com/w3c/aria-practices/issues/3070 +Attribute “aria-actions” not allowed on element “button” at this point. diff --git a/cspell.json b/cspell.json index 5bd99d9d1..3841ca274 100644 --- a/cspell.json +++ b/cspell.json @@ -15,6 +15,7 @@ "AXAPI", "Balusani", "Bellew", + "benthopelagic", "Bijl", "Bocoup", "Bogdan", @@ -30,14 +31,18 @@ "canvastext", "Capitan", "Carron", + "Cetorhinus", "checkmark", "Chjat", + "Chondrichthyes", + "cirratum", "citystate", "Claremont", "Codepen", "codepenbutton", "colheader", "Colom", + "colour", "combobox's", "comboboxes", "commenters", @@ -70,6 +75,7 @@ "dropup", "Dubnium", "Dušek", + "Elasmobranchii", "entrypoints", "EXPANDO", "Fairchild", @@ -89,6 +95,8 @@ "geckodriver", "genstedt", "Geppy", + "Ginglymostoma", + "Ginglymostomatidae", "gridbox", "gridcells", "GUIs", @@ -108,6 +116,7 @@ "imgs", "Iskandar", "issie", + "IUCN", "Jaeun", "jarosewli", "Jemma", @@ -133,19 +142,23 @@ "Malo", "Manish", "Marek", + "maximus", + "megamouth", "Meitnerium", "menubars", "Menubutton", "menubuttons", "menuitems", - "Minard", "Minard's", + "Minard", + "Mitsukurina", + "Mitsukurinidae", "Mobilow", "Mojaisk", "Moloderno", + "Montalvo", "moreaccessible", "Moscovium", - "Montalvo", "MSAA", "Müller", "multithumb", @@ -165,6 +178,7 @@ "onlosecapture", "Orscha", "outdent", + "owstoni", "Paciello", "PAGEDOWN", "PAGEUP", @@ -178,12 +192,17 @@ "Popout", "portlet", "Presentational", + "protrusible", "Pseudocereal", "radiogroups", "rawgit", "Rearrangeable", "refreshable", "respec", + "Rhincodon", + "Rhincodontidae", + "Rhiniodon", + "Rhinodontidae", "roadmap", "Roentgenium", "rowgroups", @@ -215,13 +234,13 @@ "Studienska", "stylelint", "subfolders", - "submenu", "submenu's", + "submenu", "submenus", "Sulaiman", "tabbable", - "tablist", "tablist’s", + "tablist", "tablists", "tabpanels", "Tatiana", @@ -241,9 +260,11 @@ "treeitems", "Treeview", "Tryens", + "typus", "uncheck", "Unchecking", "unchecks", + "Unfocusable", "unimp", "unmanaged", "Vasily", From 4b29ea07b26a5522db29643023b5c0cfc9db5e5f Mon Sep 17 00:00:00 2001 From: Adam Page Date: Tue, 10 Sep 2024 11:44:08 -0700 Subject: [PATCH 16/22] clone menu button js to support codepen integration --- .../tabs/examples/js/menu-button-actions.js | 351 ++++++++++++++++++ .../patterns/tabs/examples/tabs-actions.html | 2 +- 2 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 content/patterns/tabs/examples/js/menu-button-actions.js diff --git a/content/patterns/tabs/examples/js/menu-button-actions.js b/content/patterns/tabs/examples/js/menu-button-actions.js new file mode 100644 index 000000000..bde2c8a25 --- /dev/null +++ b/content/patterns/tabs/examples/js/menu-button-actions.js @@ -0,0 +1,351 @@ +/* + * This content is licensed according to the W3C Software License at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document + * + * File: menu-button-actions.js + * + * Desc: Creates a menu button that opens a menu of actions + */ + +'use strict'; + +class MenuButtonActions { + constructor(domNode, performMenuAction) { + this.domNode = domNode; + this.performMenuAction = performMenuAction; + this.buttonNode = domNode.querySelector('button'); + this.menuNode = domNode.querySelector('[role="menu"]'); + this.menuitemNodes = []; + this.firstMenuitem = false; + this.lastMenuitem = false; + this.firstChars = []; + + this.buttonNode.addEventListener( + 'keydown', + this.onButtonKeydown.bind(this) + ); + this.buttonNode.addEventListener('click', this.onButtonClick.bind(this)); + + var nodes = domNode.querySelectorAll('[role="menuitem"]'); + + for (var i = 0; i < nodes.length; i++) { + var menuitem = nodes[i]; + this.menuitemNodes.push(menuitem); + menuitem.tabIndex = -1; + this.firstChars.push(menuitem.textContent.trim()[0].toLowerCase()); + + menuitem.addEventListener('keydown', this.onMenuitemKeydown.bind(this)); + + menuitem.addEventListener('click', this.onMenuitemClick.bind(this)); + + menuitem.addEventListener( + 'mouseover', + this.onMenuitemMouseover.bind(this) + ); + + if (!this.firstMenuitem) { + this.firstMenuitem = menuitem; + } + this.lastMenuitem = menuitem; + } + + domNode.addEventListener('focusin', this.onFocusin.bind(this)); + domNode.addEventListener('focusout', this.onFocusout.bind(this)); + + window.addEventListener( + 'mousedown', + this.onBackgroundMousedown.bind(this), + true + ); + } + + setFocusToMenuitem(newMenuitem) { + this.menuitemNodes.forEach(function (item) { + if (item === newMenuitem) { + item.tabIndex = 0; + newMenuitem.focus(); + } else { + item.tabIndex = -1; + } + }); + } + + setFocusToFirstMenuitem() { + this.setFocusToMenuitem(this.firstMenuitem); + } + + setFocusToLastMenuitem() { + this.setFocusToMenuitem(this.lastMenuitem); + } + + setFocusToPreviousMenuitem(currentMenuitem) { + var newMenuitem, index; + + if (currentMenuitem === this.firstMenuitem) { + newMenuitem = this.lastMenuitem; + } else { + index = this.menuitemNodes.indexOf(currentMenuitem); + newMenuitem = this.menuitemNodes[index - 1]; + } + + this.setFocusToMenuitem(newMenuitem); + + return newMenuitem; + } + + setFocusToNextMenuitem(currentMenuitem) { + var newMenuitem, index; + + if (currentMenuitem === this.lastMenuitem) { + newMenuitem = this.firstMenuitem; + } else { + index = this.menuitemNodes.indexOf(currentMenuitem); + newMenuitem = this.menuitemNodes[index + 1]; + } + this.setFocusToMenuitem(newMenuitem); + + return newMenuitem; + } + + setFocusByFirstCharacter(currentMenuitem, char) { + var start, index; + + if (char.length > 1) { + return; + } + + char = char.toLowerCase(); + + // Get start index for search based on position of currentItem + start = this.menuitemNodes.indexOf(currentMenuitem) + 1; + if (start >= this.menuitemNodes.length) { + start = 0; + } + + // Check remaining slots in the menu + index = this.firstChars.indexOf(char, start); + + // If not found in remaining slots, check from beginning + if (index === -1) { + index = this.firstChars.indexOf(char, 0); + } + + // If match was found... + if (index > -1) { + this.setFocusToMenuitem(this.menuitemNodes[index]); + } + } + + // Utilities + + getIndexFirstChars(startIndex, char) { + for (var i = startIndex; i < this.firstChars.length; i++) { + if (char === this.firstChars[i]) { + return i; + } + } + return -1; + } + + // Popup menu methods + + openPopup() { + this.menuNode.style.display = 'block'; + this.buttonNode.setAttribute('aria-expanded', 'true'); + } + + closePopup() { + if (this.isOpen()) { + this.buttonNode.setAttribute('aria-expanded', 'false'); + this.menuNode.style.display = 'none'; + } + } + + isOpen() { + return this.buttonNode.getAttribute('aria-expanded') === 'true'; + } + + // Menu event handlers + + onFocusin() { + this.domNode.classList.add('focus'); + } + + onFocusout() { + this.domNode.classList.remove('focus'); + } + + onButtonKeydown(event) { + var key = event.key, + flag = false; + + switch (key) { + case ' ': + case 'Enter': + case 'ArrowDown': + case 'Down': + this.openPopup(); + this.setFocusToFirstMenuitem(); + flag = true; + break; + + case 'Esc': + case 'Escape': + this.closePopup(); + flag = true; + break; + + case 'Up': + case 'ArrowUp': + this.openPopup(); + this.setFocusToLastMenuitem(); + flag = true; + break; + + default: + break; + } + + if (flag) { + event.stopPropagation(); + event.preventDefault(); + } + } + + onButtonClick(event) { + if (this.isOpen()) { + this.closePopup(); + this.buttonNode.focus(); + } else { + this.openPopup(); + this.setFocusToFirstMenuitem(); + } + + event.stopPropagation(); + event.preventDefault(); + } + + onMenuitemKeydown(event) { + var tgt = event.currentTarget, + key = event.key, + flag = false; + + function isPrintableCharacter(str) { + return str.length === 1 && str.match(/\S/); + } + + if (event.ctrlKey || event.altKey || event.metaKey) { + return; + } + + if (event.shiftKey) { + if (isPrintableCharacter(key)) { + this.setFocusByFirstCharacter(tgt, key); + flag = true; + } + + if (event.key === 'Tab') { + this.buttonNode.focus(); + this.closePopup(); + flag = true; + } + } else { + switch (key) { + case ' ': + case 'Enter': + this.closePopup(); + this.performMenuAction(tgt); + this.buttonNode.focus(); + flag = true; + break; + + case 'Esc': + case 'Escape': + this.closePopup(); + this.buttonNode.focus(); + flag = true; + break; + + case 'Up': + case 'ArrowUp': + this.setFocusToPreviousMenuitem(tgt); + flag = true; + break; + + case 'ArrowDown': + case 'Down': + this.setFocusToNextMenuitem(tgt); + flag = true; + break; + + case 'Home': + case 'PageUp': + this.setFocusToFirstMenuitem(); + flag = true; + break; + + case 'End': + case 'PageDown': + this.setFocusToLastMenuitem(); + flag = true; + break; + + case 'Tab': + this.closePopup(); + break; + + default: + if (isPrintableCharacter(key)) { + this.setFocusByFirstCharacter(tgt, key); + flag = true; + } + break; + } + } + + if (flag) { + event.stopPropagation(); + event.preventDefault(); + } + } + + onMenuitemClick(event) { + var tgt = event.currentTarget; + this.closePopup(); + this.buttonNode.focus(); + this.performMenuAction(tgt); + } + + onMenuitemMouseover(event) { + var tgt = event.currentTarget; + tgt.focus(); + } + + onBackgroundMousedown(event) { + if (!this.domNode.contains(event.target)) { + if (this.isOpen()) { + this.closePopup(); + this.buttonNode.focus(); + } + } + } +} + +// Initialize menu buttons +window.addEventListener('load', function () { + const output = document.getElementById('action_output'); + if (output) { + output.value = 'none'; + } + + function performMenuAction(node) { + if (output) { + output.value = node.textContent.trim(); + } + } + + var menuButtons = document.querySelectorAll('.menu-button-actions'); + for (var i = 0; i < menuButtons.length; i++) { + new MenuButtonActions(menuButtons[i], performMenuAction); + } +}); diff --git a/content/patterns/tabs/examples/tabs-actions.html b/content/patterns/tabs/examples/tabs-actions.html index bd81535cc..d39408bcc 100644 --- a/content/patterns/tabs/examples/tabs-actions.html +++ b/content/patterns/tabs/examples/tabs-actions.html @@ -16,7 +16,7 @@ - +