add keyboard navigation for video actions (#2854)
authorCaroline Chuong <caroline.chuong@octo.com>
Thu, 11 Jun 2020 17:19:23 +0000 (19:19 +0200)
committerGitHub <noreply@github.com>
Thu, 11 Jun 2020 17:19:23 +0000 (19:19 +0200)
Co-authored-by: Rigel Kent <sendmemail@rigelk.eu>
13 files changed:
client/src/app/+admin/plugins/plugin-search/plugin-search.component.html
client/src/app/shared/buttons/action-dropdown.component.html
client/src/app/shared/channel/avatar.component.scss
client/src/app/shared/menu/top-menu-dropdown.component.html
client/src/app/shared/users/user-notifications.component.html
client/src/app/shared/video/abstract-video-list.html
client/src/app/shared/video/video-miniature.component.html
client/src/app/shared/video/video-miniature.component.scss
client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html
client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html
client/src/app/videos/+video-edit/video-add-components/video-upload.component.html
client/src/app/videos/+video-watch/video-watch.component.html
client/src/sass/application.scss

index 9f942c4b3a377dac37616eb073d6dc7e02fb4116..dc4d0046987627c059976f1cdc1ea3f09626c484 100644 (file)
@@ -12,7 +12,7 @@
 
 <div class="result-title" *ngIf="!isSearching">
   <ng-container *ngIf="!search">
-    <my-global-icon iconName="trending"></my-global-icon>
+    <my-global-icon iconName="trending" aria-hidden="true"></my-global-icon>
     <ng-container i18n>Popular</ng-container>
   </ng-container>
 
index 952b3b6f80b93769b02cd0ef72f0b358c51ae471..12933d4ca7145b94294064e8d009d4d7ca4792b7 100644 (file)
@@ -1,5 +1,5 @@
 <div class="dropdown-root" ngbDropdown [placement]="placement" [container]="container" *ngIf="areActionsDisplayed(actions, entry)">
-  <div
+  <button
     class="action-button" [ngClass]="{ small: buttonSize === 'small', grey: theme === 'grey', orange: theme === 'orange', 'button-styled': buttonStyled }"
     ngbDropdownToggle role="button"
   >
@@ -7,7 +7,7 @@
     <my-global-icon *ngIf="!label && buttonDirection === 'vertical'" class="more-icon" iconName="more-vertical"></my-global-icon>
 
     <span *ngIf="label" class="dropdown-toggle">{{ label }}</span>
-  </div>
+</button>
 
   <div ngbDropdownMenu class="dropdown-menu">
     <ng-container *ngFor="let actions of getActions()">
@@ -16,7 +16,7 @@
         <ng-container *ngIf="action.isDisplayed === undefined || action.isDisplayed(entry) === true">
 
           <ng-template #templateActionLabel let-action>
-            <my-global-icon *ngIf="action.iconName" [iconName]="action.iconName" [ngClass]="'icon-' + action.iconName"></my-global-icon>
+            <my-global-icon *ngIf="action.iconName" [iconName]="action.iconName" [ngClass]="'icon-' + action.iconName" aria-hidden="true"></my-global-icon>
 
             <div class="d-flex flex-column">
               <span i18n>{{ action.label }}</span>
 
           <span
             *ngIf="!action.linkBuilder && !action.isHeader" [ngClass]="{ 'with-icon': !!action.iconName }"
-            class="custom-action dropdown-item" role="button" [title]="action.title || ''" (click)="action.handler(entry)"
+            class="custom-action dropdown-item" tabindex="0" role="button" [title]="action.title || ''" (click)="action.handler(entry)" (keyup.enter)="action.handler(entry)"
           >
             <ng-container *ngTemplateOutlet="templateActionLabel; context:{ $implicit: action }"></ng-container>
           </span>
 
           <h6
             *ngIf="!action.linkBuilder && action.isHeader" [ngClass]="{ 'with-icon': !!action.iconName }"
-            class="dropdown-header" role="button" [title]="action.title || ''" (click)="action.handler(entry)"
+            class="dropdown-header" tabindex="0" role="button" [title]="action.title || ''" (click)="action.handler(entry)" (keyup.enter)="action.handler(entry)"
           >
             <ng-container *ngTemplateOutlet="templateActionLabel; context:{ $implicit: action }"></ng-container>
           </h6>
index 4bf8fcd2168be22a63bdd81671758d75b6cb1ee1..37709fce6ee7d11093ca2946300cc751dc1d407c 100644 (file)
@@ -7,10 +7,12 @@
   height: $avatar-size;
   position: relative;
   margin-right: 5px;
+  margin-bottom: 5px;
 
   &.avatar-sm {
     width: 28px;
     height: 28px;
+    margin-bottom: 3px;
   }
 
   a {
index 7b13eade4c355ee96be9a241e65b3bdb1cfc7433..aeaceb662207663aabc6bb2ccfc8053b17b7bb02 100644 (file)
@@ -24,7 +24,7 @@
 
       <div ngbDropdownMenu>
         <a *ngFor="let menuChild of menuEntry.children" class="dropdown-item" [ngClass]="{ icon: hasIcons }" [routerLink]="menuChild.routerLink">
-          <my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName"></my-global-icon>
+          <my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName" aria-hidden="true"></my-global-icon>
 
           {{ menuChild.label }}
         </a>
@@ -40,7 +40,7 @@
         <a *ngFor="let menuChild of menuEntry.children"
           [ngClass]="{ icon: hasIcons }"
           [routerLink]="menuChild.routerLink" routerLinkActive="active" (click)="dismissOtherModals()">
-          <my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName"></my-global-icon>
+          <my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName" aria-hidden="true"></my-global-icon>
 
           {{ menuChild.label }}
         </a>
index c6037f0a9654965270a4a47807ae8f80c7a85657..08771110d14badc920a26391daa4df20595c12df 100644 (file)
@@ -18,7 +18,7 @@
         </ng-template>
 
         <ng-template #noVideo>
-          <my-global-icon iconName="alert"></my-global-icon>
+          <my-global-icon iconName="alert" aria-hidden="true"></my-global-icon>
   
           <div class="message" i18n>
             The notification concerns a video now unavailable
@@ -27,7 +27,7 @@
       </ng-container>
 
       <ng-container *ngSwitchCase="UserNotificationType.UNBLACKLIST_ON_MY_VIDEO">
-        <my-global-icon iconName="undo"></my-global-icon>
+        <my-global-icon iconName="undo" aria-hidden="true"></my-global-icon>
 
         <div class="message" i18n>
           Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been unblocked
@@ -35,7 +35,7 @@
       </ng-container>
 
       <ng-container *ngSwitchCase="UserNotificationType.BLACKLIST_ON_MY_VIDEO">
-        <my-global-icon iconName="no"></my-global-icon>
+        <my-global-icon iconName="no" aria-hidden="true"></my-global-icon>
 
         <div class="message" i18n>
           Your video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoBlacklist.video.name }}</a> has been blocked
@@ -43,7 +43,7 @@
       </ng-container>
 
       <ng-container *ngSwitchCase="UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS">
-        <my-global-icon iconName="alert"></my-global-icon>
+        <my-global-icon iconName="alert" aria-hidden="true"></my-global-icon>
 
         <div class="message" i18n>
           <a (click)="markAsRead(notification)" [routerLink]="notification.videoAbuseUrl">A new video abuse</a> has been created on video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoAbuse.video.name }}</a>
@@ -51,7 +51,7 @@
       </ng-container>
 
       <ng-container *ngSwitchCase="UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS">
-        <my-global-icon iconName="no"></my-global-icon>
+        <my-global-icon iconName="no" aria-hidden="true"></my-global-icon>
 
         <div class="message" i18n>
           The recently added video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.videoBlacklist.video.name }}</a> has been <a (click)="markAsRead(notification)" [routerLink]="notification.videoAutoBlacklistUrl">automatically blocked</a>
@@ -72,7 +72,7 @@
         </ng-template>
 
         <ng-template #noComment>
-          <my-global-icon iconName="alert"></my-global-icon>
+          <my-global-icon iconName="alert" aria-hidden="true"></my-global-icon>
   
           <div class="message" i18n>
             The notification concerns a comment now unavailable
       </ng-container>
 
       <ng-container *ngSwitchCase="UserNotificationType.NEW_USER_REGISTRATION">
-        <my-global-icon iconName="user-add"></my-global-icon>
+        <my-global-icon iconName="user-add" aria-hidden="true"></my-global-icon>
 
         <div class="message" i18n>
           User <a (click)="markAsRead(notification)" [routerLink]="notification.accountUrl">{{ notification.account.name }}</a> registered on your instance
       </ng-container>
 
       <ng-container *ngSwitchCase="UserNotificationType.NEW_INSTANCE_FOLLOWER">
-        <my-global-icon iconName="users"></my-global-icon>
+        <my-global-icon iconName="users" aria-hidden="true"></my-global-icon>
 
         <div class="message" i18n>
           Your instance has <a (click)="markAsRead(notification)" [routerLink]="notification.instanceFollowUrl">a new follower</a> ({{ notification.actorFollow?.follower.host }})
       </ng-container>
 
       <ng-container *ngSwitchCase="UserNotificationType.AUTO_INSTANCE_FOLLOWING">
-        <my-global-icon iconName="users"></my-global-icon>
+        <my-global-icon iconName="users" aria-hidden="true"></my-global-icon>
 
         <div class="message" i18n>
           Your instance automatically followed <a (click)="markAsRead(notification)" [routerLink]="notification.instanceFollowUrl">{{ notification.actorFollow.following.host }}</a>
       </ng-container>
 
       <ng-container *ngSwitchDefault>
-        <my-global-icon iconName="alert"></my-global-icon>
+        <my-global-icon iconName="alert" aria-hidden="true"></my-global-icon>
 
         <div class="message" i18n>
           The notification points to a content now unavailable
index 9e551c1c85b488d7c1dbe94e370e2cae76da9262..54557df6b745458f8388eb0231b33bbc0b947bca 100644 (file)
@@ -10,7 +10,7 @@
     <div class="action-block" *ngIf="actions.length > 0">
       <a [routerLink]="action.routerLink" routerLinkActive="active" *ngFor="let action of actions">
         <button class="btn">
-          <my-global-icon [iconName]="action.iconName"></my-global-icon>
+          <my-global-icon [iconName]="action.iconName" aria-hidden="true"></my-global-icon>
           <span>{{ action.label }}</span>
         </button>
       </a>
index d37ab478dcd67ddbb7a829a87f78728abf5f7847..992c16585bc4031fee7d147ece975d17d94310e7 100644 (file)
@@ -9,7 +9,7 @@
 
   <div class="video-bottom">
     <div class="video-miniature-information">
-      <div class="d-inline-flex">
+      <div class="d-inline-flex video-miniature-meta">
         <div *ngIf="displayOptions.avatar" class="avatar">
           <img [src]="getAvatarUrl()" alt="Avatar" />
         </div>
index 4e7c9fb57835ab74fbca02652e25324336ebd9dc..d926c4dec26797e9af55f287b70d57da548f64be 100644 (file)
@@ -2,7 +2,7 @@
 @import '_mixins';
 @import '_miniature';
 
-$more-button-width: 41px;
+$more-button-width: 40px;
 $more-margin-right: 15px;
 
 .video-miniature {
@@ -11,7 +11,6 @@ $more-margin-right: 15px;
   padding-bottom: $video-miniature-margin-bottom;
   height: 195px;
   vertical-align: top;
-  overflow: hidden;
 
   .video-bottom {
     display: flex;
@@ -32,6 +31,11 @@ $more-margin-right: 15px;
         @include miniature-name;
       }
 
+      .video-miniature-meta {
+        width: calc(100% + #{$more-button-width});
+        overflow: hidden;
+      }
+
       .video-miniature-created-at-views {
         display: block;
         font-size: 13px;
@@ -122,7 +126,7 @@ $more-margin-right: 15px;
       width: 100% !important;
 
       .video-miniature-information {
-        width: calc(100% - 40px) !important;
+        width: calc(100% - #{$more-button-width}) !important;
       }
     }
 
index 89948dac85dec41809eae823476de9af678781ea..7287f799d0681da91d2f55b9bcc5ff49d2d3f6c3 100644 (file)
@@ -1,6 +1,6 @@
 <div *ngIf="!hasImportedVideo" class="upload-video-container" dragDrop (fileDropped)="setTorrentFile($event)">
   <div class="first-step-block">
-    <my-global-icon class="upload-icon" iconName="upload"></my-global-icon>
+    <my-global-icon class="upload-icon" iconName="upload" aria-hidden="true"></my-global-icon>
 
     <div class="button-file form-control" [ngbTooltip]="'(extensions: .torrent)'">
       <span i18n>Select the torrent to import</span>
index 54bbe8123d38f4b3d3b2cbe2d048f9759a7db253..1910da403b3e6c0fa06a369e5b704c169c6662a8 100644 (file)
@@ -1,6 +1,6 @@
 <div *ngIf="!hasImportedVideo" class="upload-video-container">
   <div class="first-step-block">
-    <my-global-icon class="upload-icon" iconName="upload"></my-global-icon>
+    <my-global-icon class="upload-icon" iconName="upload" aria-hidden="true"></my-global-icon>
 
     <div class="form-group">
       <label i18n for="targetUrl">URL</label>
index a2c8af2ae74ac8125f177bb1ce200275e5becadf..dad88a661ae4de84614a8dad766554a2e02fba56 100644 (file)
@@ -1,6 +1,6 @@
 <div *ngIf="!isUploadingVideo" class="upload-video-container" dragDrop (fileDropped)="setVideoFile($event)">
   <div class="first-step-block">
-    <my-global-icon class="upload-icon" iconName="upload"></my-global-icon>
+    <my-global-icon class="upload-icon" iconName="upload" aria-hidden="true"></my-global-icon>
 
     <div class="button-file form-control" [ngbTooltip]="'(extensions: ' + videoExtensions + ')'">
       <span i18n>Select the file to upload</span>
index 63103e2e7e34856e9a0d9bbdac32d15b0ea81314..589aba603b74198d3aaa782401ad86a0cc2dcab5 100644 (file)
 
               <div class="video-actions-rates">
                 <div class="video-actions fullWidth justify-content-end">
-                  <div
-                    [ngbPopover]="getRatePopoverText()" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()"
-                    class="action-button action-button-like" role="button" [attr.aria-pressed]="userRating === 'like'"
+                  <button
+                    [ngbPopover]="getRatePopoverText()" [ngClass]="{ 'activated': userRating === 'like' }" (click)="setLike()" (keyup.enter)="setLike()"
+                    class="action-button action-button-like" [attr.aria-pressed]="userRating === 'like'" [attr.aria-label]="tooltipLike"
                     [ngbTooltip]="tooltipLike"
                     placement="bottom auto"
                   >
                     <my-global-icon iconName="like"></my-global-icon>
                     <span *ngIf="video.likes" class="count">{{ video.likes }}</span>
-                  </div>
+                </button>
 
-                  <div
-                    [ngbPopover]="getRatePopoverText()" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()"
-                    class="action-button action-button-dislike" role="button" [attr.aria-pressed]="userRating === 'dislike'"
+                  <button
+                    [ngbPopover]="getRatePopoverText()" [ngClass]="{ 'activated': userRating === 'dislike' }" (click)="setDislike()" (keyup.enter)="setDislike()"
+                    class="action-button action-button-dislike" [attr.aria-pressed]="userRating === 'dislike'" [attr.aria-label]="tooltipDislike"
                     [ngbTooltip]="tooltipDislike"
                     placement="bottom auto"
                   >
                     <my-global-icon iconName="dislike"></my-global-icon>
                     <span *ngIf="video.dislikes" class="count">{{ video.dislikes }}</span>
-                  </div>
+                  </button>
 
-                  <div *ngIf="video.support" (click)="showSupportModal()" class="action-button action-button-support"
+                  <button *ngIf="video.support" (click)="showSupportModal()" (keyup.enter)="showSupportModal()" class="action-button action-button-support" [attr.aria-label]="tooltipSupport"
                     [ngbTooltip]="tooltipSupport"
                     placement="bottom auto"
                   >
-                    <my-global-icon iconName="support"></my-global-icon>
+                    <my-global-icon iconName="support" aria-hidden="true"></my-global-icon>
                     <span class="icon-text" i18n>SUPPORT</span>
-                  </div>
+                  </button>
 
-                  <div (click)="showShareModal()" class="action-button" role="button">
+                  <button (click)="showShareModal()" (keyup.enter)="showShareModal()" class="action-button">
                     <my-global-icon iconName="share" aria-hidden="true"></my-global-icon>
                     <span class="icon-text" i18n>SHARE</span>
-                  </div>
+                  </button>
 
                   <div
                     class="action-dropdown" ngbDropdown placement="top" role="button" autoClose="outside"
                      [ngbTooltip]="tooltipSaveToPlaylist"
                      placement="bottom auto"
                   >
-                    <div class="action-button action-button-save" ngbDropdownToggle role="button">
+                    <button class="action-button action-button-save" ngbDropdownToggle>
                       <my-global-icon iconName="playlist-add" aria-hidden="true"></my-global-icon>
                       <span class="icon-text" i18n>SAVE</span>
-                    </div>
+                    </button>
 
                     <div ngbDropdownMenu>
                       <my-video-add-to-playlist #addContent [video]="video"></my-video-add-to-playlist>
index d5763527e9fa3065eb914cda1c8477d5155f7118..aaa1c05bdd4965094342f270d6d71c4485537206 100644 (file)
@@ -86,6 +86,11 @@ input, textarea {
   color: pvar(--mainForegroundColor);
 }
 
+button {
+  background: unset;
+  @include disable-outline;
+}
+
 label {
   font-weight: $font-bold;
   font-size: 15px;