[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
This commit is contained in:
Cilibiu Bogdan
2019-05-29 18:17:13 +03:00
committed by Eugenio Romano
parent f3e90298e6
commit 9cf6f5519c
25 changed files with 386 additions and 221 deletions

View File

@@ -1,4 +1,4 @@
<adf-sidenav-layout [sidenavMin]="70" [sidenavMax]="220" [stepOver]="780" [hideSidenav]="hideSidenav" [direction]="direction"
<adf-sidenav-layout [sidenavMin]="70" [sidenavMax]="220" [stepOver]="780" [hideSidenav]="hideSidenav"
[expandedSidenav]="expandedSidenav" (expanded)="setState($event)" [position]="position">
<adf-sidenav-layout-header>

View File

@@ -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;

View File

@@ -69,7 +69,6 @@ sub-components (note the use of `<ng-template>` 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'. |

View File

@@ -41,7 +41,7 @@
<adf-no-permission-template>
<ng-template>
<div class="adf-no-permission__template" *ngIf="!customNoPermissionsTemplate">
<mat-icon>ic_error</mat-icon>
<mat-icon>error</mat-icon>
<p class="adf-no-permission__template--text">{{ 'ADF-DOCUMENT-LIST.NO_PERMISSION' | translate }}</p>
</div>
<ng-content select="adf-custom-no-permission-template, no-permission-content"></ng-content>

View File

@@ -41,7 +41,6 @@
font-size: 52px;
height: 52px;
width: 52px;
direction: rtl;
}
&--text {

View File

@@ -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);
});
});
});

View File

@@ -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;

View File

@@ -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
}
]

View File

@@ -10,7 +10,7 @@
</mat-sidenav>
<div>
<div class="adf-rtl-container-alignment adf-container-full-width" [@contentAnimationLeft]="getContentAnimationStateLeft()" [@contentAnimationRight]="getContentAnimationStateRight()">
<div class="adf-container-full-width" [@contentAnimationLeft]="getContentAnimationState()">
<ng-content select="[app-layout-content]"></ng-content>
</div>
</div>

View File

@@ -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;

View File

@@ -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 }
});
});
});
});

View File

@@ -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 } };
}
}
}

View File

@@ -1,10 +1,11 @@
<div [dir]="direction" class="adf-sidenav-layout-full-space">
<div class="adf-sidenav-layout-full-space">
<ng-container *ngIf="!isHeaderInside">
<ng-container class="adf-sidenav-layout-outer-header"
*ngTemplateOutlet="headerTemplate; context:templateContext"></ng-container>
</ng-container>
<adf-layout-container #container
[direction]="dir"
[position]="position"
[sidenavMin]="sidenavMin"
[sidenavMax]="sidenavMax"

View File

@@ -27,7 +27,10 @@ import { MaterialModule } from '../../../material.module';
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 { UserPreferencesService } from '../../../services/user-preferences.service';
import { CommonModule } from '@angular/common';
import { Direction } from '@angular/cdk/bidi';
import { of } from 'rxjs';
@Component({
selector: 'adf-layout-container',
@@ -39,6 +42,7 @@ export class DummyLayoutContainerComponent {
@Input() sidenavMin: number;
@Input() sidenavMax: number;
@Input() position: string;
@Input() direction: Direction;
@Input() mediaQueryList: MediaQueryList;
@Input() hideSidenav: boolean;
@Input() expandedSidenav: boolean;
@@ -67,7 +71,8 @@ describe('SidenavLayoutComponent', () => {
SidenavLayoutNavigationDirective
],
providers: [
MediaMatcher
MediaMatcher,
{ provide: UserPreferencesService, useValue: { select: () => of()} }
]
});
}));

View File

@@ -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<boolean>();
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() {

View File

@@ -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)'))
]);

View File

@@ -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 {}

View File

@@ -47,6 +47,9 @@ describe('AuthenticationService', () => {
jasmine.Ajax.install();
appConfigService = TestBed.get(AppConfigService);
appConfigService.config.pagination = {
supportedPageSizes: []
};
});
afterEach(() => {

View File

@@ -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, <MatDialogConfig> {
autoFocus: true,
closeOnNavigation: true
});
}
private changeDirection(direction: Direction) {
Object.assign(this.defaultOptions, this.matDialogConfig, <MatDialogConfig> {
direction
});
}
}

View File

@@ -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; }

View File

@@ -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: <Direction> 'rtl'
}));
it('should set document direction on textOrientation event to `ltr`', () => {
userPreferencesService.set('textOrientation', 'ltr');
expect(matDialogGlobalOptions).toEqual(jasmine.objectContaining({
direction: <Direction> 'ltr'
}));
expect(document.body.getAttribute('dir')).toBe('ltr');
});
});

View File

@@ -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);
(<any> this.directionality).value = direction;
});
}
}

View File

@@ -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();
});
});

View File

@@ -53,6 +53,10 @@ describe('UserPreferencesService', () => {
translate = TestBed.get(TranslateService);
});
beforeEach(() => {
storage.clear();
});
afterEach(() => {
if (changeDisposable) {
changeDisposable.unsubscribe();

View File

@@ -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: [