Merge pull request #1791 from crazyserver/MOBILE-2819

MOBILE-2819 a11y: Apply aria attributes to components
main
Juan Leyva 2019-03-06 11:48:17 +01:00 committed by GitHub
commit c11cfa336b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 144 additions and 74 deletions

View File

@ -7,7 +7,6 @@ $addon-mod-wiki-toc-background-color: $gray-light !default;
ion-app.app-root addon-mod-wiki-index {
background-color: $white;
.core-tabs-content-container,
.addon-mod_wiki-page-content {
background-color: $white;
}

View File

@ -28,7 +28,7 @@ import { Component, Input, OnChanges, OnDestroy, Output, EventEmitter, SimpleCha
*/
@Component({
selector: 'core-chrono',
template: '<span>{{ time / 1000 | coreSecondsToHMS }}</span>'
template: '<span role="timer">{{ time / 1000 | coreSecondsToHMS }}</span>'
})
export class CoreChronoComponent implements OnChanges, OnDestroy {
@Input() running: boolean; // Set it to true to start the chrono. Set it to false to stop it.

View File

@ -26,12 +26,14 @@ import { CoreLoggerProvider } from '@providers/logger';
})
export class CoreContextMenuPopoverComponent {
title: string;
uniqueId: string;
items: CoreContextMenuItemComponent[];
protected logger: any;
constructor(navParams: NavParams, private viewCtrl: ViewController, logger: CoreLoggerProvider) {
this.title = navParams.get('title');
this.items = navParams.get('items') || [];
this.uniqueId = navParams.get('id');
this.logger = logger.getInstance('CoreContextMenuPopoverComponent');
}

View File

@ -16,6 +16,7 @@ import { Component, Input, OnInit, OnDestroy, ElementRef, Optional } from '@angu
import { PopoverController } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreContextMenuItemComponent } from './context-menu-item';
import { CoreContextMenuPopoverComponent } from './context-menu-popover';
import { CoreTabComponent } from '@components/tabs/tab';
@ -34,14 +35,16 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy {
hideMenu = true; // It will be unhidden when items are added.
ariaLabel: string;
expanded = false;
protected items: CoreContextMenuItemComponent[] = [];
protected itemsMovedToParent: CoreContextMenuItemComponent[] = [];
protected itemsChangedStream: Subject<void>; // Stream to update the hideMenu boolean when items change.
protected instanceId: string;
protected parentContextMenu: CoreContextMenuComponent;
protected uniqueId: string;
constructor(private translate: TranslateService, private popoverCtrl: PopoverController, elementRef: ElementRef,
private domUtils: CoreDomUtilsProvider, @Optional() public coreTab: CoreTabComponent) {
private domUtils: CoreDomUtilsProvider, @Optional() public coreTab: CoreTabComponent, utils: CoreUtilsProvider) {
// Create the stream and subscribe to it. We ignore successive changes during 250ms.
this.itemsChangedStream = new Subject<void>();
this.itemsChangedStream.auditTime(250).subscribe(() => {
@ -56,6 +59,9 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy {
});
});
// Calculate the unique ID.
this.uniqueId = 'core-context-menu-' + utils.getUniqueId('CoreContextMenuComponent');
this.instanceId = this.domUtils.storeInstanceByElement(elementRef.nativeElement, this);
}
@ -170,10 +176,19 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy {
* @param {MouseEvent} event Event.
*/
showContextMenu(event: MouseEvent): void {
const popover = this.popoverCtrl.create(CoreContextMenuPopoverComponent, { title: this.title, items: this.items });
popover.present({
ev: event
});
if (!this.expanded) {
const popover = this.popoverCtrl.create(CoreContextMenuPopoverComponent,
{ title: this.title, items: this.items, id: this.uniqueId });
popover.onDidDismiss(() => {
this.expanded = false;
});
popover.present({
ev: event
});
this.expanded = true;
}
}
/**

View File

@ -1,6 +1,6 @@
<ion-list>
<ion-list [id]="uniqueId" role="menu">
<ion-list-header *ngIf="title">{{title}}</ion-list-header>
<a ion-item text-wrap *ngFor="let item of items" core-link [capture]="item.captureLink" [autoLogin]="item.autoLogin" [href]="item.href" (click)="itemClicked($event, item)" [attr.aria-label]="item.ariaAction" [hidden]="item.hidden" [attr.detail-none]="!item.href || item.iconAction">
<a ion-item text-wrap *ngFor="let item of items" core-link [capture]="item.captureLink" [autoLogin]="item.autoLogin" [href]="item.href" (click)="itemClicked($event, item)" [attr.aria-label]="item.ariaAction" [hidden]="item.hidden" [attr.detail-none]="!item.href || item.iconAction" role="menuitem" [attr.aria-controls]="uniqueId">
<core-icon *ngIf="item.iconDescription" [name]="item.iconDescription" [attr.aria-label]="item.ariaDescription" item-start></core-icon>
<core-format-text [clean]="true" [text]="item.content"></core-format-text>
<core-icon *ngIf="(item.href || item.action) && item.iconAction && item.iconAction != 'spinner'" [name]="item.iconAction" item-end></core-icon>

View File

@ -1,4 +1,4 @@
<button [hidden]="hideMenu" ion-button clear icon-only [attr.aria-label]="ariaLabel" (click)="showContextMenu($event)" class="bar-button">
<button [hidden]="hideMenu" ion-button clear icon-only [attr.aria-label]="ariaLabel" (click)="showContextMenu($event)" class="bar-button" aria-haspopup="true" [attr.aria-expanded]="expanded" [attr.aria-controls]="uniqueId">
<core-icon [name]="icon"></core-icon>
</button>
<ng-content></ng-content>

View File

@ -1 +1 @@
<ion-icon [name]="name" [isActive]="isActive" [md]="md" [ios]="ios" [color]="color"></ion-icon>
<ion-icon [name]="name" [isActive]="isActive" [md]="md" [ios]="ios" [color]="color" aria-hidden="true" role="presentation"></ion-icon>

View File

@ -54,6 +54,8 @@ export class CoreIconComponent implements OnInit, OnDestroy {
this.newElement.classList.add('icon');
this.newElement.classList.add('fa');
this.newElement.classList.add(this.name);
this.newElement.setAttribute('aria-hidden', 'true');
this.newElement.setAttribute('role', 'img');
if (this.isTrueProperty(this.fixedWidth)) {
this.newElement.classList.add('fa-fw');
}

View File

@ -12,5 +12,5 @@
</div>
</ng-container>
</ng-container>
<div *ngIf="errorText" class="core-input-error">{{ errorText }}</div>
<div *ngIf="errorText" class="core-input-error" aria-live="assertive">{{ errorText }}</div>
</div>

View File

@ -1,5 +1,5 @@
<div class="tabbar" role="tablist" #tabbar [hidden]="hidden">
<a [hidden]="_loaded === false" *ngFor="let t of _tabs" [tab]="t" class="tab-button" role="tab" href="#" (ionSelect)="select(t)"></a>
<div class="tabbar" role="tablist" #tabbar>
<a [hidden]="_loaded === false" *ngFor="let t of _tabs" [tab]="t" class="tab-button" role="tab" href="#" (ionSelect)="select(t)" [attr.aria-hidden]="!t.show" [attr.aria-label]="t.tabTitle || ''"></a>
<div class="tab-highlight"></div>
<div *ngIf="_loaded === false" class="core-ion-tabs-loading">
<span class="core-ion-tabs-loading-spinner">

View File

@ -1,10 +1,10 @@
<div [@coreShowHideAnimation] class="core-loading-container" *ngIf="!hideUntil">
<div [@coreShowHideAnimation] class="core-loading-container" *ngIf="!hideUntil" role="status">
<span class="core-loading-spinner">
<ion-spinner></ion-spinner>
<p class="core-loading-message" *ngIf="message">{{message}}</p>
<p class="core-loading-message" *ngIf="message" role="status">{{message}}</p>
</span>
</div>
<div #content class="core-loading-content" [id]="uniqueId">
<div #content class="core-loading-content" [id]="uniqueId" [attr.aria-busy]="hideUntil">
<ng-content [@coreShowHideAnimation] *ngIf="hideUntil">
</ng-content>
</div>

View File

@ -1,5 +1,5 @@
<div *ngIf="progress >= 0">
<progress max="100" [value]="progress">
<progress max="100" [value]="progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" [attr.aria-valuenow]="progress">
<div class="progress-bar-fallback" role="progressbar" aria-valuemin="0" aria-valuemax="100" [attr.aria-valuenow]="progress">
<span [style.width]="width"></span>
</div>

View File

@ -1,5 +1,5 @@
<div [hidden]="!rteEnabled">
<div #editor contenteditable="true" class="core-rte-editor" tappable [attr.data-placeholder-text]="placeholder">
<div #editor contenteditable="true" class="core-rte-editor" tappable [attr.data-placeholder-text]="placeholder" role="textbox">
</div>
<!-- https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand -->
@ -22,7 +22,7 @@
</div>
<div [hidden]="rteEnabled">
<ion-textarea #textarea class="core-textarea" [placeholder]="placeholder" [attr.name]="name" ngControl="control" (ionChange)="onChange($event)"></ion-textarea>
<ion-textarea #textarea class="core-textarea" [placeholder]="placeholder" [attr.name]="name" ngControl="control" (ionChange)="onChange($event)" role="textbox"></ion-textarea>
<div class="core-rte-toolbar" [hidden]="!editorSupported">
<div #decorate class="core-rte-buttons">
<button tappable [core-suppress-events] (onClick)="toggleEditor($event)"><core-icon name="fa-pencil-square-o"></core-icon> {{ 'core.vieweditor' | translate }}</button>

View File

@ -1,7 +1,7 @@
<ion-card>
<form #f="ngForm" (ngSubmit)="submitForm($event)">
<form #f="ngForm" (ngSubmit)="submitForm($event)" role="search">
<ion-item>
<ion-input type="text" name="search" [(ngModel)]="searchText" [placeholder]="placeholder" [autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus" [disabled]="disabled"></ion-input>
<ion-input type="search" name="search" [(ngModel)]="searchText" [placeholder]="placeholder" [autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus" [disabled]="disabled" role="searchbox"></ion-input>
<button item-end ion-button clear icon-only type="submit" class="button-small" [attr.aria-label]="searchLabel" [disabled]="!searchText || (searchText.length < lengthCheck)" [disabled]="disabled">
<ion-icon name="search"></ion-icon>
</button>

View File

@ -1,5 +1,5 @@
<form>
<textarea class="core-send-message-input" [core-auto-focus]="showKeyboard" [placeholder]="placeholder" rows="1" core-auto-rows [(ngModel)]="message" name="message" (onResize)="textareaResized()" (keydown.enter)="enterClicked($event)" (keydown.control.enter)="enterClicked($event, 'control')" (keydown.meta.enter)="enterClicked($event, 'meta')"></textarea>
<textarea class="core-send-message-input" [core-auto-focus]="showKeyboard" [placeholder]="placeholder" rows="1" core-auto-rows [(ngModel)]="message" name="message" (onResize)="textareaResized()" (keydown.enter)="enterClicked($event)" (keydown.control.enter)="enterClicked($event, 'control')" (keydown.meta.enter)="enterClicked($event, 'meta')" aria-multiline="true"></textarea>
<ion-buttons end>
<button ion-button icon-only clear="true" type="submit" [disabled]="!message" [attr.aria-label]="'core.send' | translate" [core-suppress-events] (onClick)="submitForm($event)">
<ion-icon name="send" color="dark"></ion-icon>

View File

@ -1,23 +1,21 @@
<core-loading [hideUntil]="hideUntil" class="core-loading-center">
<div class="core-tabs-bar" #topTabs [hidden]="!tabs || numTabsShown < 2">
<div class="core-tabs-bar" #topTabs [hidden]="!tabs || numTabsShown < 2" id="core-tabs-bar">
<ion-row>
<ion-col class="col-with-arrow" (click)="slidePrev()" no-padding col-1>
<ion-col class="col-with-arrow" (click)="slidePrev()" no-padding col-1 aria-hidden="true">
<ion-icon *ngIf="showPrevButton" name="arrow-back" md="ios-arrow-back"></ion-icon>
</ion-col>
<ion-col no-padding col-10>
<ion-slides (ionSlideDidChange)="slideChanged()" [slidesPerView]="slidesShown" [dir]="direction">
<ion-slides (ionSlideDidChange)="slideChanged()" [slidesPerView]="slidesShown" [dir]="direction" role="tablist" [attr.aria-label]="description" aria-hidden="false">
<ng-container *ngFor="let tab of tabs; let idx = index">
<ion-slide *ngIf="tab.show">
<a [attr.aria-selected]="selected == idx" (click)="selectTab(idx)" class="tab-slide">
<core-icon *ngIf="tab.icon" [name]="tab.icon"></core-icon>
<span *ngIf="tab.title">{{ tab.title }}</span>
<ion-badge *ngIf="tab.badge" [color]="tab.badgeStyle" class="tab-badge">{{tab.badge}}</ion-badge>
</a>
<ion-slide *ngIf="tab.show" [attr.aria-selected]="selected == idx" (click)="selectTab(idx)" class="tab-slide" [attr.aria-label]="tab.title || ''" role="tab" [attr.aria-controls]="tab.id" [id]="tab.id + '-tab'" [tabindex]="selected == idx ? null : -1">
<core-icon *ngIf="tab.icon" [name]="tab.icon"></core-icon>
<div *ngIf="tab.title">{{ tab.title }}</div>
<ion-badge *ngIf="tab.badge" [color]="tab.badgeStyle" class="tab-badge">{{tab.badge}}</ion-badge>
</ion-slide>
</ng-container>
</ion-slides>
</ion-col>
<ion-col class="col-with-arrow" (click)="slideNext()" no-padding col-1>
<ion-col class="col-with-arrow" (click)="slideNext()" no-padding col-1 aria-hidden="true">
<ion-icon *ngIf="showNextButton" name="arrow-forward" md="ios-arrow-forward"></ion-icon>
</ion-col>
</ion-row>

View File

@ -16,6 +16,7 @@ import { Component, Input, Output, OnInit, OnDestroy, ElementRef, EventEmitter,
import { CoreTabsComponent } from './tabs';
import { Content } from 'ionic-angular';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreNavBarButtonsComponent } from '../navbar-buttons/navbar-buttons';
/**
@ -54,6 +55,9 @@ export class CoreTabComponent implements OnInit, OnDestroy {
if (this.initialized && hasChanged) {
this.tabs.tabVisibilityChanged();
this.tabElement = document.getElementById(this.id + '-tab');
this.tabElement && this.tabElement.setAttribute('aria-hidden', !this._show);
}
}
}
@ -70,17 +74,31 @@ export class CoreTabComponent implements OnInit, OnDestroy {
loaded = false;
initialized = false;
_show = true;
tabElement: any;
constructor(protected tabs: CoreTabsComponent, element: ElementRef, protected domUtils: CoreDomUtilsProvider) {
constructor(protected tabs: CoreTabsComponent, element: ElementRef, protected domUtils: CoreDomUtilsProvider,
utils: CoreUtilsProvider) {
this.element = element.nativeElement;
this.element.setAttribute('role', 'tabpanel');
this.element.setAttribute('tabindex', '0');
this.id = this.id || 'core-tab-' + utils.getUniqueId('CoreTabComponent');
}
/**
* Component being initialized.
*/
ngOnInit(): void {
this.element.setAttribute('aria-labelledby', this.id + '-tab');
this.element.setAttribute('id', this.id);
this.tabs.addTab(this);
this.initialized = true;
setTimeout(() => {
this.tabElement = document.getElementById(this.id + '-tab');
this.tabElement && this.tabElement.setAttribute('aria-hidden', !this._show);
}, 1000);
}
/**
@ -96,6 +114,11 @@ export class CoreTabComponent implements OnInit, OnDestroy {
selectTab(): void {
this.element.classList.add('selected');
this.tabElement = this.tabElement || document.getElementById(this.id + '-tab');
this.tabElement && this.tabElement.setAttribute('aria-selected', true);
this.tabElement && this.tabElement.setAttribute('aria-hidden', !this._show);
this.loaded = true;
this.ionSelect.emit(this);
this.showHideNavBarButtons(true);
@ -114,6 +137,9 @@ export class CoreTabComponent implements OnInit, OnDestroy {
* Unselect tab.
*/
unselectTab(): void {
this.tabElement && this.tabElement.setAttribute('aria-selected', false);
this.tabElement && this.tabElement.setAttribute('aria-hidden', true);
this.element.classList.remove('selected');
this.showHideNavBarButtons(false);
}

View File

@ -10,19 +10,20 @@ ion-app.app-root .core-tabs-bar {
width: 100%;
}
a.tab-slide {
ion-slide.tab-slide {
@extend .tab-button;
background: $core-top-tabs-background;
color: $core-top-tabs-color !important;
font-size: 1.6rem;
border: 0;
padding: 0 5px 0 5px !important;
border-bottom: 2px solid transparent !important;
padding: 0 2px 0 2px !important;
margin: 0 auto !important;
display: flex;
flex-direction: row;
flex: none;
min-width: 100px;
span {
div {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
@ -44,9 +45,12 @@ ion-app.app-root .core-tabs-bar {
&[aria-selected=true] {
color: $core-top-tabs-color-active !important;
border: 0 !important;
border-bottom: 2px solid $core-top-tabs-border-active !important;
padding: 0 !important;
border-bottom-color: $core-top-tabs-border-active !important;
}
.slide-zoom {
display: flex;
flex-direction: column;
}
}
@ -64,7 +68,7 @@ ion-app.app-root .core-tabs-bar {
}
}
ion-app.app-root.md .core-tabs-bar a.tab-slide {
ion-app.app-root.md .core-tabs-bar ion-slide.tab-slide {
// @extend .tabs-md .tab-button;
min-height: $tabs-md-tab-min-height;
@ -72,7 +76,7 @@ ion-app.app-root.md .core-tabs-bar a.tab-slide {
color: $tabs-md-tab-text-color;
}
ion-app.app-root.ios .core-tabs-bar a.tab-slide {
ion-app.app-root.ios .core-tabs-bar ion-slide.tab-slide {
// @extend .tabs-ios .tab-button;
max-width: $tabs-ios-tab-max-width;
min-height: $tabs-ios-tab-min-height;
@ -82,7 +86,7 @@ ion-app.app-root.ios .core-tabs-bar a.tab-slide {
color: $tabs-ios-tab-text-color;
}
ion-app.app-root.wp .core-tabs-bar a.tab-slide {
ion-app.app-root.wp .core-tabs-bar ion-slide.tab-slide {
//@extend .tabs-wp .tab-button;
@include border-radius(0);
@ -124,6 +128,7 @@ ion-app.app-root core-tabs {
display: none;
height: 100%;
position: relative;
z-index: 1;
&.selected {
display: block;

View File

@ -62,6 +62,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges, OnDe
slidesShown = this.maxSlides;
numTabsShown = 0;
direction = 'ltr';
description = '';
protected originalTabsContainer: HTMLElement; // The container of the original tabs. It will include each tab's content.
protected initialized = false;
@ -238,8 +239,8 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges, OnDe
/**
* Get the index of tab.
*
* @param {any} tab [description]
* @return {number} [description]
* @param {any} tab Tab object to check.
* @return {number} Index number on the tabs array or -1 if not found.
*/
getIndex(tab: any): number {
for (let i = 0; i < this.tabs.length; i++) {

View File

@ -1,5 +1,5 @@
<ion-item #container class="core-timer" [attr.text-center]="align == 'center' ? true : null" [attr.text-end]="align == 'right' ? true : null">
<ion-icon name="timer" item-start></ion-icon>
<ion-item #container class="core-timer" [attr.text-center]="align == 'center' ? true : null" [attr.text-end]="align == 'right' ? true : null" role="timer">
<ion-icon name="timer" item-start role="presentation"></ion-icon>
<label *ngIf="timeLeft > 0 && timerText">{{ timerText }}</label>
<span *ngIf="timeLeft > 0">{{ timeLeft | coreSecondsToHMS }}</span>
<span class="core-timesup" *ngIf="timeLeft <= 0">{{ 'core.timesup' | translate }}</span>

View File

@ -11,7 +11,7 @@
<!-- Section selector. -->
<core-dynamic-component [component]="sectionSelectorComponent" [data]="data">
<div text-wrap *ngIf="displaySectionSelector && sections && sections.length" padding class="clearfix" ion-row justify-content-between class="safe-padding-horizontal">
<button float-start ion-button icon-start icon-end (click)="showSectionSelector($event)" color="light" class="core-button-select button-no-uppercase" ion-col>
<button float-start ion-button icon-start icon-end (click)="showSectionSelector($event)" color="light" class="core-button-select button-no-uppercase" ion-col [attr.aria-label]="('core.course.sections' | translate) + ': ' + (selectedSection && (selectedSection.formattedName || selectedSection.name))" aria-haspopup="true" [attr.aria-expanded]="sectionSelectorExpanded" aria-controls="core-course-section-selector" id="core-course-section-button">
<core-icon name="fa-folder"></core-icon>
<span class="core-section-selector-text">{{selectedSection && (selectedSection.formattedName || selectedSection.name) || 'core.course.sections' | translate }}</span>
<ion-icon name="arrow-dropdown" ios="md-arrow-dropdown"></ion-icon>

View File

@ -61,6 +61,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
allSectionsComponent: any;
canLoadMore = false;
showSectionId = 0;
sectionSelectorExpanded = false;
// Data to pass to the components.
data: any = {};
@ -243,16 +244,27 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
/**
* Display the section selector modal.
*
* @param {MouseEvent} event Event.
*/
showSectionSelector(): void {
const modal = this.modalCtrl.create('CoreCourseSectionSelectorPage',
{course: this.course, sections: this.sections, selected: this.selectedSection});
modal.onDidDismiss((newSection) => {
if (newSection) {
this.sectionChanged(newSection);
}
});
modal.present();
showSectionSelector(event: MouseEvent): void {
if (!this.sectionSelectorExpanded) {
const modal = this.modalCtrl.create('CoreCourseSectionSelectorPage',
{course: this.course, sections: this.sections, selected: this.selectedSection});
modal.onDidDismiss((newSection) => {
if (newSection) {
this.sectionChanged(newSection);
}
this.sectionSelectorExpanded = false;
});
modal.present({
ev: event
});
this.sectionSelectorExpanded = true;
}
}
/**

View File

@ -1,6 +1,6 @@
<a *ngIf="module && module.visibleoncoursepage !== 0" ion-item text-wrap id="core-course-module-{{module.id}}" class="core-course-module-handler {{module.handlerData.class}}" (click)="moduleClicked($event)" [ngClass]="{'item-media': module.handlerData.icon, 'core-not-clickable': !module.handlerData.action || !module.uservisible === false, 'item-dimmed': module.visible === 0 || module.uservisible === false}" [title]="module.handlerData.a11yTitle" detail-none>
<img item-start *ngIf="module.handlerData.icon" [src]="module.handlerData.icon" alt="" role="presentation" class="core-module-icon">
<img item-start *ngIf="module.handlerData.icon" [src]="module.handlerData.icon" [alt]="module.modnametranslated" class="core-module-icon">
<div class="core-module-title">
<core-format-text [text]="module.handlerData.title"></core-format-text>

View File

@ -18,6 +18,7 @@ import { CoreEventsProvider } from '@providers/events';
import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreCourseHelperProvider } from '../../providers/helper';
import { CoreCourseProvider } from '../../providers/course';
import { CoreCourseModuleHandlerButton } from '../../providers/module-delegate';
import { CoreCourseModulePrefetchDelegate, CoreCourseModulePrefetchHandler } from '../../providers/module-prefetch-delegate';
import { CoreConstants } from '../../../constants';
@ -63,7 +64,8 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
constructor(@Optional() protected navCtrl: NavController, protected prefetchDelegate: CoreCourseModulePrefetchDelegate,
protected domUtils: CoreDomUtilsProvider, protected courseHelper: CoreCourseHelperProvider,
protected eventsProvider: CoreEventsProvider, protected sitesProvider: CoreSitesProvider) {
protected eventsProvider: CoreEventsProvider, protected sitesProvider: CoreSitesProvider,
protected courseProvider: CoreCourseProvider) {
this.completionChanged = new EventEmitter();
}
@ -97,6 +99,8 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
this.module.handlerData.a11yTitle = typeof this.module.handlerData.a11yTitle != 'undefined' ?
this.module.handlerData.a11yTitle : this.module.handlerData.title;
this.module.modnametranslated = this.courseProvider.translateModuleName(this.module.modname) || '';
}
/**

View File

@ -9,13 +9,15 @@
</ion-navbar>
</ion-header>
<ion-content>
<ng-container *ngFor="let section of sections">
<a ion-item *ngIf="!section.hiddenbynumsections && section.id != stealthModulesSectionId" text-wrap (click)="selectSection(section)" [class.core-primary-selected-item]="selected.id == section.id" [class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail-none>
<core-icon name="fa-folder" item-start></core-icon>
<h2><core-format-text [text]="section.formattedName || section.name"></core-format-text></h2>
<core-progress-bar *ngIf="section.progress >= 0" [progress]="section.progress"></core-progress-bar>
<ion-badge color="secondary" *ngIf="section.visible === 0 && section.uservisible !== false">{{ 'core.course.hiddenfromstudents' | translate }}</ion-badge>
<ion-badge color="secondary" *ngIf="section.availabilityinfo"><core-format-text [text]=" section.availabilityinfo"></core-format-text></ion-badge>
</a>
</ng-container>
<ion-list id="core-course-section-selector" role="menu" aria-labelledby="core-course-section-button">
<ng-container *ngFor="let section of sections">
<a ion-item *ngIf="!section.hiddenbynumsections && section.id != stealthModulesSectionId" text-wrap (click)="selectSection(section)" [class.core-primary-selected-item]="selected.id == section.id" [class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail-none role="menuitem" [attr.aria-hidden]="section.uservisible === false" [attr.aria-label]="section.formattedName || section.name">
<core-icon name="fa-folder" item-start></core-icon>
<h2><core-format-text [text]="section.formattedName || section.name"></core-format-text></h2>
<core-progress-bar *ngIf="section.progress >= 0" [progress]="section.progress"></core-progress-bar>
<ion-badge color="secondary" *ngIf="section.visible === 0 && section.uservisible !== false" text-wrap>{{ 'core.course.hiddenfromstudents' | translate }}</ion-badge>
<ion-badge color="secondary" *ngIf="section.availabilityinfo" text-wrap><core-format-text [text]=" section.availabilityinfo"></core-format-text></ion-badge>
</a>
</ng-container>
</ion-list>
</ion-content>

View File

@ -8,4 +8,8 @@ ion-app.app-root page-core-course-section-selector {
margin: 8px 0 4px 0;
}
}
ion-badge {
text-align: left;
}
}

View File

@ -30,19 +30,19 @@
</a>
</div>
<a *ngIf="showWeb" ion-item [href]="siteInfo.siteurl" core-link autoLogin="yes" title="{{ 'core.mainmenu.website' | translate }}">
<ion-icon name="globe" item-start></ion-icon>
<ion-icon name="globe" item-start aria-hidden="true"></ion-icon>
<p>{{ 'core.mainmenu.website' | translate }}</p>
</a>
<a *ngIf="showHelp" ion-item [href]="docsUrl" core-link autoLogin="no" title="{{ 'core.mainmenu.help' | translate }}">
<ion-icon name="help-buoy" item-start></ion-icon>
<ion-icon name="help-buoy" item-start aria-hidden="true"></ion-icon>
<p>{{ 'core.mainmenu.help' | translate }}</p>
</a>
<a ion-item (click)="openSettings()" title="{{ 'core.mainmenu.appsettings' | translate }}">
<ion-icon name="cog" item-start></ion-icon>
<ion-icon name="cog" item-start aria-hidden="true"></ion-icon>
<p>{{ 'core.mainmenu.appsettings' | translate }}</p>
</a>
<a ion-item (click)="logout()" title="{{ logoutLabel | translate }}">
<ion-icon name="log-out" item-start></ion-icon>
<ion-icon name="log-out" item-start aria-hidden="true"></ion-icon>
<p>{{ logoutLabel | translate }}</p>
</a>
</ion-list>