From 4b844ef1c35576e9ee854954f9f7505dc596af77 Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Sun, 22 Jul 2018 12:02:51 +0200 Subject: [PATCH] fix(all): accesibility and global styles for hidden nodes --- core/package.json | 1 - core/src/components/app/app.scss | 6 ++---- core/src/components/content/content.scss | 10 ++++----- .../components/menu-toggle/menu-toggle.scss | 4 ++++ .../components/menu-toggle/menu-toggle.tsx | 11 +++++++++- core/src/components/nav/nav.tsx | 7 ++++--- core/src/components/nav/view-controller.ts | 2 +- .../components/router-outlet/route-outlet.tsx | 2 +- .../src/components/tab-button/tab-button.scss | 3 ++- core/src/components/tab/tab.scss | 4 ++++ core/src/components/tab/tab.tsx | 16 ++++++++++---- core/src/components/tabbar/tabbar.scss | 3 ++- core/src/components/tabbar/tabbar.tsx | 21 +++++++++++-------- core/src/css/structure.scss | 5 ----- core/src/utils/animations/ios.transition.ts | 2 +- core/src/utils/animations/md.transition.ts | 2 +- core/src/utils/transition.ts | 18 ++++++++++++---- 17 files changed, 75 insertions(+), 42 deletions(-) create mode 100644 core/src/components/menu-toggle/menu-toggle.scss create mode 100644 core/src/components/tab/tab.scss diff --git a/core/package.json b/core/package.json index 513d357e91..dfb0e88504 100644 --- a/core/package.json +++ b/core/package.json @@ -65,7 +65,6 @@ "lint.ts": "tslint --project .", "lint.ts.fix": "tslint --project . --fix", "prerelease": "npm run validate && np prerelease --yolo --any-branch --tag next", - "set.version": "node scripts/set-version.js", "snapshot": "node ./scripts/e2e --snapshot", "test": "jest", "test.watch": "jest --watch --no-cache", diff --git a/core/src/components/app/app.scss b/core/src/components/app/app.scss index 0e972e6ba8..91066cf17f 100644 --- a/core/src/components/app/app.scss +++ b/core/src/components/app/app.scss @@ -26,13 +26,11 @@ ion-app, justify-content: space-between; } -.hide-page { +.ion-page-invisible { opacity: 0; } -[hidden] { - // TODO can we remove important here or remove this entirely - // since it is also in structure.scss +.ion-page-hidden { /* stylelint-disable-next-line declaration-no-important */ display: none !important; } diff --git a/core/src/components/content/content.scss b/core/src/components/content/content.scss index 48e63cf2c9..e083d40035 100644 --- a/core/src/components/content/content.scss +++ b/core/src/components/content/content.scss @@ -6,11 +6,11 @@ :host { --ion-color-base: #{$background-color}; --ion-color-contrast: #{$text-color}; - --padding-top: 0; - --padding-bottom: 0; - --padding-start: 0; - --padding-end: 0; - --keyboard-offset: 0; + --padding-top: 0px; + --padding-bottom: 0px; + --padding-start: 0px; + --padding-end: 0px; + --keyboard-offset: 0px; display: block; position: relative; diff --git a/core/src/components/menu-toggle/menu-toggle.scss b/core/src/components/menu-toggle/menu-toggle.scss new file mode 100644 index 0000000000..8891835921 --- /dev/null +++ b/core/src/components/menu-toggle/menu-toggle.scss @@ -0,0 +1,4 @@ +:host(.menu-toggle-hidden) { + /* stylelint-disable-next-line declaration-no-important */ + display: none; +} diff --git a/core/src/components/menu-toggle/menu-toggle.tsx b/core/src/components/menu-toggle/menu-toggle.tsx index 373fd900e2..acb19a2dff 100644 --- a/core/src/components/menu-toggle/menu-toggle.tsx +++ b/core/src/components/menu-toggle/menu-toggle.tsx @@ -2,6 +2,8 @@ import { Component, Listen, Prop, State } from '@stencil/core'; @Component({ tag: 'ion-menu-toggle', + styleUrl: 'menu-toggle.scss', + shadow: true }) export class MenuToggle { @Prop({ context: 'document' }) @@ -54,9 +56,16 @@ export class MenuToggle { hostData() { const hidden = this.autoHide && !this.visible; return { - 'hidden': hidden + 'aria-hidden': hidden ? 'true' : null, + class: { + 'menu-toggle-hidden': hidden, + } }; } + + render() { + return ; + } } function getMenuController(doc: Document): Promise { diff --git a/core/src/components/nav/nav.tsx b/core/src/components/nav/nav.tsx index 44e610d50f..cbd0474672 100644 --- a/core/src/components/nav/nav.tsx +++ b/core/src/components/nav/nav.tsx @@ -2,7 +2,7 @@ import { Build, Component, Element, Event, EventEmitter, Method, Prop, QueueApi, import { ViewLifecycle } from '../..'; import { Animation, ComponentProps, Config, FrameworkDelegate, GestureDetail, Mode, NavComponent, NavOptions, NavOutlet, NavResult, RouteID, RouteWrite, TransitionDoneFn, TransitionInstruction, ViewController } from '../../interface'; import { assert } from '../../utils/helpers'; -import { TransitionOptions, lifecycle, transition } from '../../utils/transition'; +import { TransitionOptions, lifecycle, setPageHidden, transition } from '../../utils/transition'; import { ViewState, convertToViews, matches } from './view-controller'; @Component({ @@ -850,15 +850,16 @@ export class Nav implements NavOutlet { for (let i = views.length - 1; i >= 0; i--) { const view = views[i]; + const element = view.element; if (i > activeViewIndex) { // this view comes after the active view // let's unload it - lifecycle(this.win, view.element, ViewLifecycle.WillUnload); + lifecycle(this.win, element, ViewLifecycle.WillUnload); this.destroyView(view); } else if (i < activeViewIndex) { // this view comes before the active view // and it is not a portal then ensure it is hidden - view.element!.hidden = true; + setPageHidden(element!, true); } } } diff --git a/core/src/components/nav/view-controller.ts b/core/src/components/nav/view-controller.ts index 82391e5880..f9c7b11bb2 100644 --- a/core/src/components/nav/view-controller.ts +++ b/core/src/components/nav/view-controller.ts @@ -29,7 +29,7 @@ export class ViewController { if (!this.element) { const component = this.component; - this.element = await attachComponent(this.delegate, container, component, ['ion-page', 'hide-page'], this.params); + this.element = await attachComponent(this.delegate, container, component, ['ion-page', 'ion-page-invisible'], this.params); } } diff --git a/core/src/components/router-outlet/route-outlet.tsx b/core/src/components/router-outlet/route-outlet.tsx index c191118a28..728e7d95d7 100644 --- a/core/src/components/router-outlet/route-outlet.tsx +++ b/core/src/components/router-outlet/route-outlet.tsx @@ -55,7 +55,7 @@ export class RouterOutlet implements NavOutlet { this.activeComponent = component; // attach entering view to DOM - const enteringEl = await attachComponent(this.delegate, this.el, component, ['ion-page', 'hide-page'], params); + const enteringEl = await attachComponent(this.delegate, this.el, component, ['ion-page', 'ion-page-invisible'], params); const leavingEl = this.activeEl; // commit animation diff --git a/core/src/components/tab-button/tab-button.scss b/core/src/components/tab-button/tab-button.scss index 1965a79213..77d46cf76e 100644 --- a/core/src/components/tab-button/tab-button.scss +++ b/core/src/components/tab-button/tab-button.scss @@ -36,7 +36,8 @@ } :host(.tab-hidden) { - display: none; + /* stylelint-disable-next-line declaration-no-important */ + display: none !important; } a { diff --git a/core/src/components/tab/tab.scss b/core/src/components/tab/tab.scss new file mode 100644 index 0000000000..cb5be6d823 --- /dev/null +++ b/core/src/components/tab/tab.scss @@ -0,0 +1,4 @@ +:host(.tab-hidden) { + /* stylelint-disable-next-line declaration-no-important */ + display: none !important; +} diff --git a/core/src/components/tab/tab.tsx b/core/src/components/tab/tab.tsx index 518e8fb437..013dc24f22 100644 --- a/core/src/components/tab/tab.tsx +++ b/core/src/components/tab/tab.tsx @@ -3,7 +3,9 @@ import { Color, ComponentRef, FrameworkDelegate } from '../../interface'; import { attachComponent } from '../../utils/framework-delegate'; @Component({ - tag: 'ion-tab' + tag: 'ion-tab', + styleUrl: 'tab.scss', + shadow: true }) export class Tab { @@ -136,13 +138,19 @@ export class Tab { } hostData() { + const { btnId, active, component } = this; return { - 'aria-labelledby': this.btnId, + 'aria-labelledby': btnId, + 'aria-hidden': !active ? 'true' : null, 'role': 'tabpanel', - 'hidden': !this.active, 'class': { - 'ion-page': !this.component + 'ion-page': !component, + 'tab-hidden': !active } }; } + + render() { + return ; + } } diff --git a/core/src/components/tabbar/tabbar.scss b/core/src/components/tabbar/tabbar.scss index e8b69ecc86..6dad6756bb 100644 --- a/core/src/components/tabbar/tabbar.scss +++ b/core/src/components/tabbar/tabbar.scss @@ -24,7 +24,8 @@ } :host(.tabbar-hidden) { - display: none; + /* stylelint-disable-next-line declaration-no-important */ + display: none !important; } :host(.placement-top) { diff --git a/core/src/components/tabbar/tabbar.tsx b/core/src/components/tabbar/tabbar.tsx index 25fe4bed68..867742efd2 100644 --- a/core/src/components/tabbar/tabbar.tsx +++ b/core/src/components/tabbar/tabbar.tsx @@ -24,7 +24,7 @@ export class Tabbar { @State() canScrollLeft = false; @State() canScrollRight = false; - @State() hidden = false; + @State() keyboardVisible = false; /** * Set the layout of the text and icon in the tabbar. Available options: `"icon-top"`, `"icon-start"`, `"icon-end"`, `"icon-bottom"`, `"icon-hide"`, `"label-hide"`. @@ -68,13 +68,13 @@ export class Tabbar { @Listen('body:keyboardWillHide') protected onKeyboardWillHide() { - setTimeout(() => this.hidden = false, 50); + setTimeout(() => this.keyboardVisible = false, 50); } @Listen('body:keyboardWillShow') protected onKeyboardWillShow() { if (this.placement === 'bottom') { - this.hidden = true; + this.keyboardVisible = true; } } @@ -182,15 +182,17 @@ export class Tabbar { } hostData() { + const { color, translucent, layout, placement, keyboardVisible, scrollable } = this; return { role: 'tablist', + 'aria-hidden': keyboardVisible ? 'true' : null, class: { - ...createColorClasses(this.color), - 'tabbar-translucent': this.translucent, - [`layout-${this.layout}`]: true, - [`placement-${this.placement}`]: true, - 'tabbar-hidden': this.hidden, - 'scrollable': this.scrollable + ...createColorClasses(color), + 'tabbar-translucent': translucent, + [`layout-${layout}`]: true, + [`placement-${placement}`]: true, + 'tabbar-hidden': keyboardVisible, + 'scrollable': scrollable } }; } @@ -209,6 +211,7 @@ export class Tabbar { selected={selectedTab === tab} mode={this.mode} color={this.color} + aria-hidden={ !tab.show ? 'true' : null } class={{ 'tab-hidden': !tab.show }} onClick={(ev) => { if (!tab.disabled) { diff --git a/core/src/css/structure.scss b/core/src/css/structure.scss index b95c229161..50a5bf2847 100644 --- a/core/src/css/structure.scss +++ b/core/src/css/structure.scss @@ -50,8 +50,3 @@ body { text-size-adjust: none; } - -[hidden] { - /* stylelint-disable-next-line declaration-no-important */ - display: none !important; -} diff --git a/core/src/utils/animations/ios.transition.ts b/core/src/utils/animations/ios.transition.ts index e96d555346..2eef283a29 100644 --- a/core/src/utils/animations/ios.transition.ts +++ b/core/src/utils/animations/ios.transition.ts @@ -27,7 +27,7 @@ export function iosTransitionAnimation(Animation: Animation, navEl: HTMLElement, .addElement(enteringEl) .duration(opts.duration || DURATION) .easing(opts.easing || EASING) - .beforeRemoveClass('hide-page'); + .beforeRemoveClass('ion-page-invisible'); if (leavingEl && navEl) { const navDecor = new Animation(); diff --git a/core/src/utils/animations/md.transition.ts b/core/src/utils/animations/md.transition.ts index d8992f979a..46810e19f7 100644 --- a/core/src/utils/animations/md.transition.ts +++ b/core/src/utils/animations/md.transition.ts @@ -14,7 +14,7 @@ export function mdTransitionAnimation(Animation: Animation, _: HTMLElement, opts const rootTransition = new Animation(); rootTransition .addElement(ionPageElement) - .beforeRemoveClass('hide-page'); + .beforeRemoveClass('ion-page-invisible'); const backDirection = (opts.direction === 'back'); if (enteringEl) { diff --git a/core/src/utils/transition.ts b/core/src/utils/transition.ts index 7f5ce99f8c..cee3342044 100644 --- a/core/src/utils/transition.ts +++ b/core/src/utils/transition.ts @@ -45,9 +45,19 @@ function beforeTransition(opts: TransitionOptions) { } else { enteringEl.classList.remove('can-go-back'); } - enteringEl.hidden = false; + setPageHidden(enteringEl, false); if (leavingEl) { - leavingEl.hidden = false; + setPageHidden(leavingEl, false); + } +} + +export function setPageHidden(el: HTMLElement, hidden: boolean) { + if (hidden) { + el.setAttribute('aria-hidden', 'true'); + el.classList.add('ion-page-hidden'); + } else { + el.removeAttribute('aria-hidden'); + el.classList.remove('ion-page-hidden'); } } @@ -68,10 +78,10 @@ async function noAnimation(opts: TransitionOptions): Promise { const enteringEl = opts.enteringEl; const leavingEl = opts.leavingEl; if (enteringEl) { - enteringEl.classList.remove('hide-page'); + enteringEl.classList.remove('ion-page-invisible'); } if (leavingEl) { - leavingEl.classList.remove('hide-page'); + leavingEl.classList.remove('ion-page-invisible'); } await waitForReady(opts, false);