From 9cf6f5519c93e2508b5ac58790caf4f98e33d94d Mon Sep 17 00:00:00 2001 From: Cilibiu Bogdan Date: Wed, 29 May 2019 18:17:13 +0300 Subject: [PATCH] [ADF-4579] Layout - RTL support (#4741) * single contentAnimation state * fine tune contentAnimation state based on direction and position * tets * fix mock component inputs * use native direction attribute * update docs * pass direction to calculate sidenav layout * update test * fix unit test * remove dialogs directionality workaround * set document directionality service * remove context menu direction workaround * remove unneeded dependencies * remove direction style workaround * remove permission icon direction * remove sidenav layout direction attribute * update docs * update sidenav layout direction * test * remove document type * update test dependencies * clear storage before test * test * fix dl gap * try to fix Uncaught QuotaExceededError * fix QuotaExceededError * fix tests * fix tests --- .../app-layout/app-layout.component.html | 2 +- .../app-layout/app-layout.component.ts | 5 - .../components/sidenav-layout.component.md | 1 - .../components/document-list.component.html | 2 +- .../components/document-list.component.scss | 1 - .../context-menu-overlay.service.spec.ts | 39 +--- .../context-menu-overlay.service.ts | 15 +- lib/core/core.module.ts | 8 +- .../layout-container.component.html | 2 +- .../layout-container.component.scss | 9 +- .../layout-container.component.spec.ts | 210 ++++++++++++++++++ .../layout-container.component.ts | 90 +++++--- .../sidenav-layout.component.html | 3 +- .../sidenav-layout.component.spec.ts | 7 +- .../sidenav-layout.component.ts | 20 +- lib/core/layout/helpers/animations.ts | 18 +- lib/core/material.module.ts | 9 +- .../services/authentication.service.spec.ts | 3 + lib/core/services/dialog-config.service.ts | 52 ----- ...ry.ts => directionality-config-factory.ts} | 11 +- ... => directionality-config.service.spec.ts} | 39 +--- .../services/directionality-config.service.ts | 42 ++++ lib/core/services/storage.service.spec.ts | 11 +- .../services/user-preferences.service.spec.ts | 4 + lib/core/testing/core.testing.module.ts | 4 +- 25 files changed, 386 insertions(+), 221 deletions(-) create mode 100644 lib/core/layout/components/layout-container/layout-container.component.spec.ts delete mode 100644 lib/core/services/dialog-config.service.ts rename lib/core/services/{dialog-config-factory.ts => directionality-config-factory.ts} (70%) rename lib/core/services/{dialog-config.service.spec.ts => directionality-config.service.spec.ts} (51%) create mode 100644 lib/core/services/directionality-config.service.ts diff --git a/demo-shell/src/app/components/app-layout/app-layout.component.html b/demo-shell/src/app/components/app-layout/app-layout.component.html index 5157b2218a..1cc1e35967 100644 --- a/demo-shell/src/app/components/app-layout/app-layout.component.html +++ b/demo-shell/src/app/components/app-layout/app-layout.component.html @@ -1,4 +1,4 @@ - diff --git a/demo-shell/src/app/components/app-layout/app-layout.component.ts b/demo-shell/src/app/components/app-layout/app-layout.component.ts index e66720212a..3c4e449b95 100644 --- a/demo-shell/src/app/components/app-layout/app-layout.component.ts +++ b/demo-shell/src/app/components/app-layout/app-layout.component.ts @@ -115,17 +115,12 @@ export class AppLayoutComponent implements OnInit { this.headerService.tooltip.subscribe((tooltip) => this.tooltip = tooltip); this.headerService.position.subscribe((position) => this.position = position); this.headerService.hideSidenav.subscribe((hideSidenav) => this.hideSidenav = hideSidenav); - - this.userPreferencesService.select('textOrientation').subscribe((textOrientation) => { - this.direction = textOrientation; - }); } constructor( private userPreferences: UserPreferencesService, private config: AppConfigService, private alfrescoApiService: AlfrescoApiService, - private userPreferencesService: UserPreferencesService, private headerService: HeaderDataService) { if (this.alfrescoApiService.getInstance().isOauthConfiguration()) { this.enableRedirect = false; diff --git a/docs/core/components/sidenav-layout.component.md b/docs/core/components/sidenav-layout.component.md index b5a0b87028..12c571a1e2 100644 --- a/docs/core/components/sidenav-layout.component.md +++ b/docs/core/components/sidenav-layout.component.md @@ -69,7 +69,6 @@ sub-components (note the use of `` in the sub-components' body sect | Name | Type | Default value | Description | | ---- | ---- | ------------- | ----------- | -| direction | `string` | "ltr" | The direction of the layout. 'ltr' or 'rtl' | | expandedSidenav | `boolean` | true | Should the navigation region be expanded initially? | | hideSidenav | `boolean` | false | Toggles showing/hiding the navigation region. | | position | `string` | "start" | The side that the drawer is attached to. Possible values are 'start' and 'end'. | diff --git a/lib/content-services/document-list/components/document-list.component.html b/lib/content-services/document-list/components/document-list.component.html index f006522f5d..326a53d1fd 100644 --- a/lib/content-services/document-list/components/document-list.component.html +++ b/lib/content-services/document-list/components/document-list.component.html @@ -41,7 +41,7 @@
- ic_error + error

{{ 'ADF-DOCUMENT-LIST.NO_PERMISSION' | translate }}

diff --git a/lib/content-services/document-list/components/document-list.component.scss b/lib/content-services/document-list/components/document-list.component.scss index e7ef29c265..356b195391 100644 --- a/lib/content-services/document-list/components/document-list.component.scss +++ b/lib/content-services/document-list/components/document-list.component.scss @@ -41,7 +41,6 @@ font-size: 52px; height: 52px; width: 52px; - direction: rtl; } &--text { diff --git a/lib/core/context-menu/context-menu-overlay.service.spec.ts b/lib/core/context-menu/context-menu-overlay.service.spec.ts index abc33c37ea..13df9279a1 100644 --- a/lib/core/context-menu/context-menu-overlay.service.spec.ts +++ b/lib/core/context-menu/context-menu-overlay.service.spec.ts @@ -20,12 +20,9 @@ import { Overlay } from '@angular/cdk/overlay'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { CoreTestingModule } from '../testing/core.testing.module'; import { ContextMenuOverlayService } from './context-menu-overlay.service'; -import { UserPreferencesService } from '../services/user-preferences.service'; import { Injector } from '@angular/core'; -import { of } from 'rxjs'; describe('ContextMenuService', () => { - let userPreferencesService: UserPreferencesService; let contextMenuOverlayService: ContextMenuOverlayService; let overlay: Overlay; let injector: Injector; @@ -40,13 +37,9 @@ describe('ContextMenuService', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [NoopAnimationsModule, CoreTestingModule], - providers: [ - Overlay, - UserPreferencesService - ] + providers: [ Overlay ] }); - userPreferencesService = TestBed.get(UserPreferencesService); overlay = TestBed.get(Overlay); injector = TestBed.get(Injector); }); @@ -55,8 +48,7 @@ describe('ContextMenuService', () => { beforeEach(() => { contextMenuOverlayService = new ContextMenuOverlayService( injector, - overlay, - userPreferencesService + overlay ); }); @@ -71,32 +63,5 @@ describe('ContextMenuService', () => { expect(document.querySelector('adf-context-menu')).not.toBe(null); }); - - }); - - describe('Overlay direction', () => { - it('should have default LTR direction value', () => { - contextMenuOverlayService = new ContextMenuOverlayService( - injector, - overlay, - userPreferencesService - ); - contextMenuOverlayService.open(overlayConfig); - - expect(document.body.querySelector('div[dir="ltr"]')).not.toBe(null); - }); - - it('should be created with textOrientation event value', () => { - spyOn(userPreferencesService, 'select').and.returnValue(of('rtl')); - - contextMenuOverlayService = new ContextMenuOverlayService( - injector, - overlay, - userPreferencesService - ); - contextMenuOverlayService.open(overlayConfig); - - expect(document.body.querySelector('div[dir="rtl"]')).not.toBe(null); - }); }); }); diff --git a/lib/core/context-menu/context-menu-overlay.service.ts b/lib/core/context-menu/context-menu-overlay.service.ts index 117c987eaa..bf92b4d51a 100644 --- a/lib/core/context-menu/context-menu-overlay.service.ts +++ b/lib/core/context-menu/context-menu-overlay.service.ts @@ -22,8 +22,6 @@ import { ContextMenuOverlayRef } from './context-menu-overlay'; import { ContextMenuOverlayConfig } from './interfaces'; import { CONTEXT_MENU_DATA } from './context-menu.tokens'; import { ContextMenuListComponent } from './context-menu-list.component'; -import { UserPreferencesService } from '../services/user-preferences.service'; -import { Direction } from '@angular/cdk/bidi'; const DEFAULT_CONFIG: ContextMenuOverlayConfig = { panelClass: 'cdk-overlay-pane', @@ -35,17 +33,11 @@ const DEFAULT_CONFIG: ContextMenuOverlayConfig = { providedIn: 'root' }) export class ContextMenuOverlayService { - private direction: Direction; constructor( private injector: Injector, - private overlay: Overlay, - private userPreferencesService: UserPreferencesService - ) { - this.userPreferencesService.select('textOrientation').subscribe((textOrientation) => { - this.direction = textOrientation; - }); - } + private overlay: Overlay + ) {} open(config: ContextMenuOverlayConfig): ContextMenuOverlayRef { const overlayConfig = { ...DEFAULT_CONFIG, ...config }; @@ -134,8 +126,7 @@ export class ContextMenuOverlayService { backdropClass: config.backdropClass, panelClass: config.panelClass, scrollStrategy: this.overlay.scrollStrategies.close(), - positionStrategy, - direction: this.direction + positionStrategy }); return overlayConfig; diff --git a/lib/core/core.module.ts b/lib/core/core.module.ts index 2df3c8ade1..2978a6fbef 100644 --- a/lib/core/core.module.ts +++ b/lib/core/core.module.ts @@ -53,8 +53,8 @@ import { SortingPickerModule } from './sorting-picker/sorting-picker.module'; import { IconModule } from './icon/icon.module'; import { TranslateLoaderService } from './services/translate-loader.service'; import { ExtensionsModule } from '@alfresco/adf-extensions'; -import { dialogConfigFactory } from './services/dialog-config-factory'; -import { DialogConfigService } from './services/dialog-config.service'; +import { directionalityConfigFactory } from './services/directionality-config-factory'; +import { DirectionalityConfigService } from './services/directionality-config.service'; @NgModule({ imports: [ @@ -140,8 +140,8 @@ export class CoreModule { }, { provide: APP_INITIALIZER, - useFactory: dialogConfigFactory, - deps: [ DialogConfigService], + useFactory: directionalityConfigFactory, + deps: [ DirectionalityConfigService ], multi: true } ] diff --git a/lib/core/layout/components/layout-container/layout-container.component.html b/lib/core/layout/components/layout-container/layout-container.component.html index f6bb4c6dc3..7486c25018 100644 --- a/lib/core/layout/components/layout-container/layout-container.component.html +++ b/lib/core/layout/components/layout-container/layout-container.component.html @@ -10,7 +10,7 @@
-
+
diff --git a/lib/core/layout/components/layout-container/layout-container.component.scss b/lib/core/layout/components/layout-container/layout-container.component.scss index 9b10e9d1ab..0356eee3d2 100644 --- a/lib/core/layout/components/layout-container/layout-container.component.scss +++ b/lib/core/layout/components/layout-container/layout-container.component.scss @@ -9,14 +9,9 @@ overflow: hidden; } - [dir='rtl'] .adf-rtl-container-alignment { - margin-left: 10px!important; - } - .adf-container-full-width { width: inherit; } - /* query for Microsoft IE 11*/ @media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: none) { .adf-container-full-width { @@ -24,6 +19,10 @@ } } + mat-sidenav-content.mat-drawer-content.mat-sidenav-content { + margin: 0!important; + } + .adf-sidenav--hidden { visibility: hidden !important; width: 0 !important; diff --git a/lib/core/layout/components/layout-container/layout-container.component.spec.ts b/lib/core/layout/components/layout-container/layout-container.component.spec.ts new file mode 100644 index 0000000000..f403e12c58 --- /dev/null +++ b/lib/core/layout/components/layout-container/layout-container.component.spec.ts @@ -0,0 +1,210 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { LayoutContainerComponent } from './layout-container.component'; +import { SimpleChange } from '@angular/core'; + +describe('LayoutContainerComponent', () => { + let layoutContainerComponent: LayoutContainerComponent; + + beforeEach(() => { + layoutContainerComponent = new LayoutContainerComponent(); + layoutContainerComponent.sidenavMin = 70; + layoutContainerComponent.sidenavMax = 200; + layoutContainerComponent.mediaQueryList = { + matches: false, + addListener: () => {}, + removeListener: () => {} + }; + }); + + describe('OnInit', () => { + describe('Sidenav expanded', () => { + beforeEach(() => { + layoutContainerComponent.expandedSidenav = true; + }); + + describe('Sidenav [start] position', () => { + beforeEach(() => { + layoutContainerComponent.position = 'start'; + }); + + it('should set `margin-left` equal to sidenavMax when direction is `ltr`', () => { + layoutContainerComponent.direction = 'ltr'; + layoutContainerComponent.ngOnInit(); + + expect(layoutContainerComponent.contentAnimationState).toEqual({ + value: 'compact', params: { 'margin-left': layoutContainerComponent.sidenavMax} + }); + }); + + it('should set `margin-right` equal to sidenavMax when direction is `rtl`', () => { + layoutContainerComponent.direction = 'rtl'; + layoutContainerComponent.ngOnInit(); + + expect(layoutContainerComponent.contentAnimationState).toEqual({ + value: 'compact', params: { 'margin-right': layoutContainerComponent.sidenavMax} + }); + }); + }); + + describe('Sidenav [end] position', () => { + beforeEach(() => { + layoutContainerComponent.position = 'end'; + }); + + it('should set `margin-right` equal to sidenavMax when direction is `ltr`', () => { + layoutContainerComponent.direction = 'ltr'; + layoutContainerComponent.ngOnInit(); + + expect(layoutContainerComponent.contentAnimationState).toEqual({ + value: 'compact', params: { 'margin-right': layoutContainerComponent.sidenavMax} + }); + }); + + it('should set `margin-left` equal to sidenavMax when direction is `rtl`', () => { + layoutContainerComponent.direction = 'rtl'; + layoutContainerComponent.ngOnInit(); + + expect(layoutContainerComponent.contentAnimationState).toEqual({ + value: 'compact', params: { 'margin-left': layoutContainerComponent.sidenavMax} + }); + }); + }); + }); + + describe('Sidenav compact', () => { + beforeEach(() => { + layoutContainerComponent.expandedSidenav = false; + }); + + describe('Sidenav [start] position', () => { + beforeEach(() => { + layoutContainerComponent.position = 'start'; + }); + + it('should set `margin-left` equal to sidenavMin when direction is `ltr`', () => { + layoutContainerComponent.direction = 'ltr'; + layoutContainerComponent.ngOnInit(); + + expect(layoutContainerComponent.contentAnimationState).toEqual({ + value: 'expanded', params: { 'margin-left': layoutContainerComponent.sidenavMin} + }); + }); + + it('should set `margin-right` equal to sidenavMin when direction is `rtl`', () => { + layoutContainerComponent.direction = 'rtl'; + layoutContainerComponent.ngOnInit(); + + expect(layoutContainerComponent.contentAnimationState).toEqual({ + value: 'expanded', params: { 'margin-right': layoutContainerComponent.sidenavMin} + }); + }); + }); + + describe('Sidenav [end] position', () => { + beforeEach(() => { + layoutContainerComponent.position = 'end'; + }); + + it('should set `margin-right` equal to sidenavMin when direction is `ltr`', () => { + layoutContainerComponent.direction = 'ltr'; + layoutContainerComponent.ngOnInit(); + + expect(layoutContainerComponent.contentAnimationState).toEqual({ + value: 'expanded', params: { 'margin-right': layoutContainerComponent.sidenavMin} + }); + }); + + it('should set `margin-left` equal to sidenavMin when direction is `rtl`', () => { + layoutContainerComponent.direction = 'rtl'; + layoutContainerComponent.ngOnInit(); + + expect(layoutContainerComponent.contentAnimationState).toEqual({ + value: 'expanded', params: { 'margin-left': layoutContainerComponent.sidenavMin} + }); + }); + }); + }); + }); + + describe('OnChange direction', () => { + describe('Sidenav [start] position', () => { + beforeEach(() => { + layoutContainerComponent.position = 'start'; + }); + + it('should set `margin-left` when current direction is `ltr`', () => { + layoutContainerComponent.direction = 'ltr'; + layoutContainerComponent.ngOnChanges({ direction: new SimpleChange('', '', false) }); + + expect(layoutContainerComponent.contentAnimationState.params['margin-left']).not.toBeNull(); + }); + + it('should set `margin-right` when current direction is `rtl`', () => { + layoutContainerComponent.direction = 'rtl'; + layoutContainerComponent.ngOnChanges({ direction: new SimpleChange('', '', false) }); + + expect(layoutContainerComponent.contentAnimationState.params['margin-right']).not.toBeNull(); + }); + }); + + describe('Sidenav [end] position', () => { + beforeEach(() => { + layoutContainerComponent.position = 'end'; + }); + + it('should set `margin-right` when current direction is `ltr`', () => { + layoutContainerComponent.direction = 'ltr'; + layoutContainerComponent.ngOnChanges({ direction: new SimpleChange('', '', false) }); + + expect(layoutContainerComponent.contentAnimationState.params['margin-right']).not.toBeNull(); + }); + + it('should set `margin-left` when current direction is `rtl`', () => { + layoutContainerComponent.direction = 'rtl'; + layoutContainerComponent.ngOnChanges({ direction: new SimpleChange('', '', false) }); + + expect(layoutContainerComponent.contentAnimationState.params['margin-left']).not.toBeNull(); + }); + }); + }); + + describe('toggleMenu()', () => { + it('should switch to sidenav to compact state', () => { + layoutContainerComponent.expandedSidenav = true; + layoutContainerComponent.ngOnInit(); + + layoutContainerComponent.toggleMenu(); + + expect(layoutContainerComponent.sidenavAnimationState).toEqual({ + value: 'compact', params: { width: layoutContainerComponent.sidenavMin } + }); + }); + + it('should switch to sidenav to expanded state', () => { + layoutContainerComponent.expandedSidenav = false; + layoutContainerComponent.ngOnInit(); + + layoutContainerComponent.toggleMenu(); + + expect(layoutContainerComponent.sidenavAnimationState).toEqual({ + value: 'expanded', params: { width: layoutContainerComponent.sidenavMax } + }); + }); + }); +}); diff --git a/lib/core/layout/components/layout-container/layout-container.component.ts b/lib/core/layout/components/layout-container/layout-container.component.ts index d8bd8af132..cd5acbf578 100644 --- a/lib/core/layout/components/layout-container/layout-container.component.ts +++ b/lib/core/layout/components/layout-container/layout-container.component.ts @@ -15,18 +15,19 @@ * limitations under the License. */ -import { Component, Input, ViewChild, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'; +import { Component, Input, ViewChild, OnInit, OnDestroy, ViewEncapsulation, OnChanges } from '@angular/core'; import { MatSidenav } from '@angular/material'; -import { sidenavAnimation, contentAnimationLeft, contentAnimationRight } from '../../helpers/animations'; +import { sidenavAnimation, contentAnimation } from '../../helpers/animations'; +import { Direction } from '@angular/cdk/bidi'; @Component({ selector: 'adf-layout-container', templateUrl: './layout-container.component.html', styleUrls: ['./layout-container.component.scss'], encapsulation: ViewEncapsulation.None, - animations: [sidenavAnimation, contentAnimationLeft, contentAnimationRight] + animations: [sidenavAnimation, contentAnimation] }) -export class LayoutContainerComponent implements OnInit, OnDestroy { +export class LayoutContainerComponent implements OnInit, OnDestroy, OnChanges { @Input() sidenavMin: number; @Input() sidenavMax: number; @@ -39,6 +40,9 @@ export class LayoutContainerComponent implements OnInit, OnDestroy { /** The side that the drawer is attached to 'start' | 'end' page */ @Input() position = 'start'; + /** Layout text orientation 'ltr' | 'rtl' */ + @Input() direction: Direction = 'ltr'; + @ViewChild(MatSidenav) sidenav: MatSidenav; sidenavAnimationState: any; @@ -56,9 +60,7 @@ export class LayoutContainerComponent implements OnInit, OnDestroy { this.SIDENAV_STATES.EXPANDED = { value: 'expanded', params: { width: this.sidenavMax } }; this.SIDENAV_STATES.COMPACT = { value: 'compact', params: { width: this.sidenavMin } }; - this.CONTENT_STATES.MOBILE = { value: 'expanded', params: { margin: 0 } }; - this.CONTENT_STATES.EXPANDED = { value: 'expanded', params: { margin: this.sidenavMin } }; - this.CONTENT_STATES.COMPACT = { value: 'compact', params: { margin: this.sidenavMax } }; + this.CONTENT_STATES.MOBILE = { value: 'expanded' }; this.mediaQueryList.addListener(this.onMediaQueryChange); @@ -67,10 +69,10 @@ export class LayoutContainerComponent implements OnInit, OnDestroy { this.contentAnimationState = this.CONTENT_STATES.MOBILE; } else if (this.expandedSidenav) { this.sidenavAnimationState = this.SIDENAV_STATES.EXPANDED; - this.contentAnimationState = this.CONTENT_STATES.COMPACT; + this.contentAnimationState = this.toggledContentAnimation; } else { this.sidenavAnimationState = this.SIDENAV_STATES.COMPACT; - this.contentAnimationState = this.CONTENT_STATES.EXPANDED; + this.contentAnimationState = this.toggledContentAnimation; } } @@ -78,6 +80,12 @@ export class LayoutContainerComponent implements OnInit, OnDestroy { this.mediaQueryList.removeListener(this.onMediaQueryChange); } + ngOnChanges(changes) { + if (changes && changes.direction) { + this.contentAnimationState = this.toggledContentAnimation; + } + } + toggleMenu(): void { if (this.isMobileScreenSize) { this.sidenav.toggle(); @@ -87,6 +95,14 @@ export class LayoutContainerComponent implements OnInit, OnDestroy { } } + get isMobileScreenSize(): boolean { + return this.mediaQueryList.matches; + } + + getContentAnimationState() { + return this.contentAnimationState; + } + private get toggledSidenavAnimation() { return this.sidenavAnimationState === this.SIDENAV_STATES.EXPANDED ? this.SIDENAV_STATES.COMPACT @@ -99,35 +115,43 @@ export class LayoutContainerComponent implements OnInit, OnDestroy { } if (this.sidenavAnimationState === this.SIDENAV_STATES.EXPANDED) { - return this.CONTENT_STATES.COMPACT; - } else { - return this.CONTENT_STATES.EXPANDED; - } - } + if (this.position === 'start' && this.direction === 'ltr') { + return { value: 'compact', params: { 'margin-left': this.sidenavMax } }; + } - get isMobileScreenSize(): boolean { - return this.mediaQueryList.matches; + if (this.position === 'start' && this.direction === 'rtl') { + return { value: 'compact', params: { 'margin-right': this.sidenavMax } }; + } + + if (this.position === 'end' && this.direction === 'ltr') { + return { value: 'compact', params: { 'margin-right': this.sidenavMax } }; + } + + if (this.position === 'end' && this.direction === 'rtl') { + return { value: 'compact', params: { 'margin-left': this.sidenavMax } }; + } + + } else { + if (this.position === 'start' && this.direction === 'ltr') { + return { value: 'expanded', params: { 'margin-left': this.sidenavMin } }; + } + + if (this.position === 'start' && this.direction === 'rtl') { + return { value: 'expanded', params: { 'margin-right': this.sidenavMin } }; + } + + if (this.position === 'end' && this.direction === 'ltr') { + return { value: 'expanded', params: { 'margin-right': this.sidenavMin } }; + } + + if (this.position === 'end' && this.direction === 'rtl') { + return { value: 'expanded', params: { 'margin-left': this.sidenavMin } }; + } + } } private onMediaQueryChange() { this.sidenavAnimationState = this.SIDENAV_STATES.EXPANDED; this.contentAnimationState = this.toggledContentAnimation; } - - getContentAnimationStateLeft() { - if (this.position === 'start') { - return this.contentAnimationState; - } else { - return { value: 'compact', params: { width: this.sidenavMin } }; - } - } - - getContentAnimationStateRight() { - if (this.position === 'end') { - return this.contentAnimationState; - - } else { - return { value: 'compact', params: { width: this.sidenavMin } }; - } - } } diff --git a/lib/core/layout/components/sidenav-layout/sidenav-layout.component.html b/lib/core/layout/components/sidenav-layout/sidenav-layout.component.html index 2155d9b30b..e0ed2a3b09 100644 --- a/lib/core/layout/components/sidenav-layout/sidenav-layout.component.html +++ b/lib/core/layout/components/sidenav-layout/sidenav-layout.component.html @@ -1,10 +1,11 @@ -
+
{ SidenavLayoutNavigationDirective ], providers: [ - MediaMatcher + MediaMatcher, + { provide: UserPreferencesService, useValue: { select: () => of()} } ] }); })); diff --git a/lib/core/layout/components/sidenav-layout/sidenav-layout.component.ts b/lib/core/layout/components/sidenav-layout/sidenav-layout.component.ts index 7ba0cff6b1..f3bf6593ce 100644 --- a/lib/core/layout/components/sidenav-layout/sidenav-layout.component.ts +++ b/lib/core/layout/components/sidenav-layout/sidenav-layout.component.ts @@ -29,10 +29,13 @@ import { ViewEncapsulation } from '@angular/core'; import { MediaMatcher } from '@angular/cdk/layout'; +import { UserPreferencesService } from '../../../services/user-preferences.service'; import { SidenavLayoutContentDirective } from '../../directives/sidenav-layout-content.directive'; import { SidenavLayoutHeaderDirective } from '../../directives/sidenav-layout-header.directive'; import { SidenavLayoutNavigationDirective } from '../../directives/sidenav-layout-navigation.directive'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { BehaviorSubject, Observable, Subject } from 'rxjs'; +import { Direction } from '@angular/cdk/bidi'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'adf-sidenav-layout', @@ -45,7 +48,7 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy static STEP_OVER = 600; /** The direction of the layout. 'ltr' or 'rtl' */ - @Input() direction = 'ltr'; + dir = 'ltr'; /** The side that the drawer is attached to. Possible values are 'start' and 'end'. */ @Input() position = 'start'; @@ -86,7 +89,9 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy isMenuMinimized: () => this.isMenuMinimized }; - constructor(private mediaMatcher: MediaMatcher) { + private onDestroy$ = new Subject(); + + constructor(private mediaMatcher: MediaMatcher, private userPreferencesService: UserPreferencesService ) { this.onMediaQueryChange = this.onMediaQueryChange.bind(this); } @@ -101,6 +106,13 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy this.mediaQueryList = this.mediaMatcher.matchMedia(`(max-width: ${stepOver}px)`); this.mediaQueryList.addListener(this.onMediaQueryChange); + + this.userPreferencesService + .select('textOrientation') + .pipe(takeUntil(this.onDestroy$)) + .subscribe((direction: Direction) => { + this.dir = direction; + }); } ngAfterViewInit() { @@ -109,6 +121,8 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy ngOnDestroy(): void { this.mediaQueryList.removeListener(this.onMediaQueryChange); + this.onDestroy$.next(true); + this.onDestroy$.complete(); } toggleMenu() { diff --git a/lib/core/layout/helpers/animations.ts b/lib/core/layout/helpers/animations.ts index 3316c0a277..aaf91c9628 100644 --- a/lib/core/layout/helpers/animations.ts +++ b/lib/core/layout/helpers/animations.ts @@ -23,14 +23,14 @@ export const sidenavAnimation: AnimationTriggerMetadata = trigger('sidenavAnimat transition('compact <=> expanded', animate('0.4s cubic-bezier(0.25, 0.8, 0.25, 1)')) ]); -export const contentAnimationLeft: AnimationTriggerMetadata = trigger('contentAnimationLeft', [ - state('expanded', style({ 'margin-left': '{{ margin }}px' }), { params : { margin: 0 } }), - state('compact', style({'margin-left': '{{ margin }}px' }), { params : { margin: 0 } }), - transition('expanded <=> compact', animate('400ms cubic-bezier(0.25, 0.8, 0.25, 1)')) -]); - -export const contentAnimationRight: AnimationTriggerMetadata = trigger('contentAnimationRight', [ - state('expanded', style({ 'margin-right': '{{ margin }}px' }), { params : { margin: 0 } }), - state('compact', style({'margin-right': '{{ margin }}px' }), { params : { margin: 0 } }), +export const contentAnimation: AnimationTriggerMetadata = trigger('contentAnimationLeft', [ + state('expanded', style({ + 'margin-left': '{{ margin-left }}px', + 'margin-right': '{{ margin-right }}px' + }), { params: { 'margin-left': 0, 'margin-right': 0 } }), + state('compact', style({ + 'margin-left': '{{ margin-left }}px', + 'margin-right': '{{ margin-right }}px' + }), { params: { 'margin-left': 0, 'margin-right': 0 } }), transition('expanded <=> compact', animate('400ms cubic-bezier(0.25, 0.8, 0.25, 1)')) ]); diff --git a/lib/core/material.module.ts b/lib/core/material.module.ts index 59c69749d7..f414d7b467 100644 --- a/lib/core/material.module.ts +++ b/lib/core/material.module.ts @@ -24,7 +24,7 @@ import { MatInputModule, MatListModule, MatNativeDateModule, MatOptionModule, MatProgressSpinnerModule, MatRadioModule, MatRippleModule, MatSelectModule, MatSlideToggleModule, MatTableModule, MatTabsModule, MatMenuModule, MatProgressBarModule, MatSidenavModule, MatSnackBarModule, MatToolbarModule, - MatTooltipModule, MatExpansionModule, MAT_DIALOG_DEFAULT_OPTIONS, MatDialogConfig + MatTooltipModule, MatExpansionModule } from '@angular/material'; export function modules() { @@ -40,13 +40,6 @@ export function modules() { @NgModule({ imports: modules(), - providers: [ - MatDialogConfig, - { - provide: MAT_DIALOG_DEFAULT_OPTIONS, - useValue: MAT_DIALOG_DEFAULT_OPTIONS - } - ], exports: modules() }) export class MaterialModule {} diff --git a/lib/core/services/authentication.service.spec.ts b/lib/core/services/authentication.service.spec.ts index 863c5adb80..19b57e111f 100644 --- a/lib/core/services/authentication.service.spec.ts +++ b/lib/core/services/authentication.service.spec.ts @@ -47,6 +47,9 @@ describe('AuthenticationService', () => { jasmine.Ajax.install(); appConfigService = TestBed.get(AppConfigService); + appConfigService.config.pagination = { + supportedPageSizes: [] + }; }); afterEach(() => { diff --git a/lib/core/services/dialog-config.service.ts b/lib/core/services/dialog-config.service.ts deleted file mode 100644 index 5529daa78a..0000000000 --- a/lib/core/services/dialog-config.service.ts +++ /dev/null @@ -1,52 +0,0 @@ -/*! - * @license - * Copyright 2019 Alfresco Software, Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* spellchecker: disable */ -import { Injectable, Inject } from '@angular/core'; -import { MAT_DIALOG_DEFAULT_OPTIONS, MatDialogConfig } from '@angular/material/dialog'; -import { Direction } from '@angular/cdk/bidi'; -import { UserPreferencesService } from '../services/user-preferences.service'; - -@Injectable({ - providedIn: 'root' -}) -export class DialogConfigService { - constructor( - @Inject(MAT_DIALOG_DEFAULT_OPTIONS) private defaultOptions: MatDialogConfig, - private matDialogConfig: MatDialogConfig, - private userPreferencesService: UserPreferencesService - ) { - this.userPreferencesService - .select('textOrientation') - .subscribe((direction: Direction) => { - this.changeDirection(direction); - }); - } - - loadDefaults() { - Object.assign(this.defaultOptions, this.matDialogConfig, { - autoFocus: true, - closeOnNavigation: true - }); - } - - private changeDirection(direction: Direction) { - Object.assign(this.defaultOptions, this.matDialogConfig, { - direction - }); - } -} diff --git a/lib/core/services/dialog-config-factory.ts b/lib/core/services/directionality-config-factory.ts similarity index 70% rename from lib/core/services/dialog-config-factory.ts rename to lib/core/services/directionality-config-factory.ts index 1318f31acc..d3e2ec58a0 100644 --- a/lib/core/services/dialog-config-factory.ts +++ b/lib/core/services/directionality-config-factory.ts @@ -14,10 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { DialogConfigService } from '../services/dialog-config.service'; -export function dialogConfigFactory( - dialogConfigService: DialogConfigService -): Function { - return () => dialogConfigService.loadDefaults(); -} +import { DirectionalityConfigService } from '../services/directionality-config.service'; + +export function directionalityConfigFactory( + directionalityConfigService: DirectionalityConfigService +): Function { return () => directionalityConfigService; } diff --git a/lib/core/services/dialog-config.service.spec.ts b/lib/core/services/directionality-config.service.spec.ts similarity index 51% rename from lib/core/services/dialog-config.service.spec.ts rename to lib/core/services/directionality-config.service.spec.ts index 354df78b66..6605836292 100644 --- a/lib/core/services/dialog-config.service.spec.ts +++ b/lib/core/services/directionality-config.service.spec.ts @@ -16,54 +16,33 @@ */ import { TestBed } from '@angular/core/testing'; -import { DialogConfigService } from './dialog-config.service'; +import { DirectionalityConfigService } from './directionality-config.service'; import { CoreTestingModule } from '../testing/core.testing.module'; import { setupTestBed } from '../testing/setupTestBed'; -import { MAT_DIALOG_DEFAULT_OPTIONS, MatDialogConfig } from '@angular/material/dialog'; import { UserPreferencesService } from './user-preferences.service'; -import { Direction } from '@angular/cdk/bidi'; -describe('DialogConfigService', () => { - let matDialogConfig: MatDialogConfig; +describe('DirectionalityConfigService', () => { let userPreferencesService: UserPreferencesService; - let matDialogGlobalOptions: MatDialogConfig; setupTestBed({ imports: [CoreTestingModule], providers: [ - DialogConfigService, - UserPreferencesService, - { - provide: MAT_DIALOG_DEFAULT_OPTIONS, - useValue: MAT_DIALOG_DEFAULT_OPTIONS - } + DirectionalityConfigService, + UserPreferencesService ] }); beforeEach(() => { userPreferencesService = TestBed.get(UserPreferencesService); - matDialogConfig = TestBed.get(MatDialogConfig); - matDialogGlobalOptions = TestBed.get(MAT_DIALOG_DEFAULT_OPTIONS); }); - it('should load custom defaults', () => { - expect(matDialogConfig).toEqual(jasmine.objectContaining({ - autoFocus: true, - closeOnNavigation: true - })); - }); - - it('should set dialog direction option on textOrientation event', () => { + it('should set document direction on textOrientation event to `rtl`', () => { userPreferencesService.set('textOrientation', 'rtl'); + expect(document.body.getAttribute('dir')).toBe('rtl'); + }); - expect(matDialogGlobalOptions).toEqual(jasmine.objectContaining({ - direction: 'rtl' - })); - + it('should set document direction on textOrientation event to `ltr`', () => { userPreferencesService.set('textOrientation', 'ltr'); - - expect(matDialogGlobalOptions).toEqual(jasmine.objectContaining({ - direction: 'ltr' - })); + expect(document.body.getAttribute('dir')).toBe('ltr'); }); }); diff --git a/lib/core/services/directionality-config.service.ts b/lib/core/services/directionality-config.service.ts new file mode 100644 index 0000000000..4b4289faf1 --- /dev/null +++ b/lib/core/services/directionality-config.service.ts @@ -0,0 +1,42 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable, Inject, Renderer2, RendererFactory2 } from '@angular/core'; +import { Directionality, Direction } from '@angular/cdk/bidi'; +import { UserPreferencesService } from '../services/user-preferences.service'; +import { DOCUMENT } from '@angular/platform-browser'; + +@Injectable({ + providedIn: 'root' +}) +export class DirectionalityConfigService { + constructor( + @Inject(DOCUMENT) private document: any, + private rendererFactory: RendererFactory2, + private userPreferencesService: UserPreferencesService, + private directionality: Directionality + ) { + const renderer: Renderer2 = this.rendererFactory.createRenderer(null, null); + + this.userPreferencesService + .select('textOrientation') + .subscribe((direction: Direction) => { + renderer.setAttribute(this.document.body, 'dir', direction); + ( this.directionality).value = direction; + }); + } +} diff --git a/lib/core/services/storage.service.spec.ts b/lib/core/services/storage.service.spec.ts index f9c2b8c377..99bf60d2b3 100644 --- a/lib/core/services/storage.service.spec.ts +++ b/lib/core/services/storage.service.spec.ts @@ -66,9 +66,8 @@ describe('StorageService', () => { appConfig.load().then(() => { storage.setItem(key, value); - const storageKey = localStorage.key(0); - expect(storageKey).toBe('ADF_APP_' + key); - expect(localStorage.getItem(storageKey)).toBe(value); + expect(localStorage.hasOwnProperty('ADF_APP_' + key)).not.toBe(null); + expect(localStorage.getItem('ADF_APP_' + key)).toBe(value); done(); }); }); @@ -79,10 +78,8 @@ describe('StorageService', () => { appConfig.load().then(() => { storage.setItem(key, value); - - const storageKey = localStorage.key(0); - expect(storageKey).toBe(key); - expect(localStorage.getItem(storageKey)).toBe(value); + expect(localStorage.hasOwnProperty(key)).not.toBe(null); + expect(localStorage.getItem(key)).toBe(value); done(); }); }); diff --git a/lib/core/services/user-preferences.service.spec.ts b/lib/core/services/user-preferences.service.spec.ts index 9461354816..e675192f44 100644 --- a/lib/core/services/user-preferences.service.spec.ts +++ b/lib/core/services/user-preferences.service.spec.ts @@ -53,6 +53,10 @@ describe('UserPreferencesService', () => { translate = TestBed.get(TranslateService); }); + beforeEach(() => { + storage.clear(); + }); + afterEach(() => { if (changeDisposable) { changeDisposable.unsubscribe(); diff --git a/lib/core/testing/core.testing.module.ts b/lib/core/testing/core.testing.module.ts index bbe171b658..d24e9439b3 100644 --- a/lib/core/testing/core.testing.module.ts +++ b/lib/core/testing/core.testing.module.ts @@ -28,7 +28,6 @@ import { TranslationMock } from '../mock/translation.service.mock'; import { DatePipe } from '@angular/common'; import { CookieService } from '../services/cookie.service'; import { CookieServiceMock } from '../mock/cookie.service.mock'; -import { DialogConfigService } from 'core/services/dialog-config.service'; @NgModule({ imports: [NoopAnimationsModule, RouterTestingModule, CoreModule.forRoot()], @@ -37,8 +36,7 @@ import { DialogConfigService } from 'core/services/dialog-config.service'; { provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }, { provide: AppConfigService, useClass: AppConfigServiceMock }, { provide: TranslationService, useClass: TranslationMock }, - { provide: CookieService, useClass: CookieServiceMock }, - { provide: DialogConfigService, useValue: { loadDefaults: () => {} } } + { provide: CookieService, useClass: CookieServiceMock } ], exports: [