ACS-7384: Break Layout dependency on Material Module (#9937)

This commit is contained in:
Denys Vuika 2024-07-23 11:08:18 -04:00 committed by GitHub
parent dd03ae6617
commit 63fa673709
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 160 additions and 238 deletions

View File

@ -17,7 +17,7 @@
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { AppConfigService, SidenavLayoutModule } from '@alfresco/adf-core';
import { AppConfigService, LAYOUT_DIRECTIVES } from '@alfresco/adf-core';
import { ShellLayoutComponent } from './shell.component';
import { Router, NavigationStart, RouterModule } from '@angular/router';
import { of, Subject } from 'rxjs';
@ -62,7 +62,7 @@ describe('AppLayoutComponent', () => {
CommonModule,
NoopAnimationsModule,
HttpClientModule,
SidenavLayoutModule,
...LAYOUT_DIRECTIVES,
ExtensionsModule,
RouterModule.forChild([]),
TranslateModule.forRoot(),

View File

@ -75,4 +75,3 @@ The following CSS classes are available for theming:
| `--adf-avatar-font-size` | `14px` | The font size of the initials. |
| `--adf-avatar-font-weight` | `500` | The font weight of the initials. |
| `--adf-avatar-cursor` | `auto` | The cursor style. |
```

View File

@ -19,7 +19,6 @@ import { CommonModule } from '@angular/common';
import { APP_INITIALIZER, NgModule, ModuleWithProviders } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TranslateModule, TranslateLoader, TranslateStore, TranslateService } from '@ngx-translate/core';
import { MaterialModule } from './material.module';
import { ABOUT_DIRECTIVES } from './about/about.module';
import { CardViewModule } from './card-view/card-view.module';
@ -32,7 +31,7 @@ import { PaginationModule } from './pagination/pagination.module';
import { ToolbarModule } from './toolbar/toolbar.module';
import { ViewerModule } from './viewer/viewer.module';
import { FormBaseModule } from './form/form-base.module';
import { SidenavLayoutModule } from './layout/layout.module';
import { LAYOUT_DIRECTIVES } from './layout/layout.module';
import { CommentsModule } from './comments/comments.module';
import { CommentListModule } from './comments/comment-list/comment-list.module';
import { TemplateModule } from './templates/template.module';
@ -45,7 +44,6 @@ import { PipeModule } from './pipes/pipe.module';
import { TranslationService } from './translation/translation.service';
import { SortingPickerModule } from './sorting-picker/sorting-picker.module';
import { IconModule } from './icon/icon.module';
import { TranslateLoaderService } from './translation/translate-loader.service';
import { ExtensionsModule } from '@alfresco/adf-extensions';
import { directionalityConfigFactory } from './common/services/directionality-config-factory';
@ -67,6 +65,7 @@ import { AdfDateTimeFnsAdapter } from './common/utils/datetime-fns-adapter';
import { AppConfigPipe, StoragePrefixFactory } from './app-config';
import { UnsavedChangesDialogModule } from './dialogs';
import { DynamicChipListModule } from './dynamic-chip-list';
import { IconComponent } from './icon';
@NgModule({
imports: [
@ -74,7 +73,7 @@ import { DynamicChipListModule } from './dynamic-chip-list';
ExtensionsModule,
...ABOUT_DIRECTIVES,
ViewerModule,
SidenavLayoutModule,
...LAYOUT_DIRECTIVES,
PipeModule,
CommonModule,
IdentityUserInfoModule,
@ -95,7 +94,7 @@ import { DynamicChipListModule } from './dynamic-chip-list';
InfoDrawerModule,
DataTableModule,
TemplateModule,
IconModule,
IconComponent,
SortingPickerModule,
NotificationHistoryModule,
SearchTextModule,
@ -111,7 +110,7 @@ import { DynamicChipListModule } from './dynamic-chip-list';
exports: [
...ABOUT_DIRECTIVES,
ViewerModule,
SidenavLayoutModule,
...LAYOUT_DIRECTIVES,
PipeModule,
CommonModule,
DirectiveModule,
@ -135,7 +134,7 @@ import { DynamicChipListModule } from './dynamic-chip-list';
TranslateModule,
TemplateModule,
SortingPickerModule,
IconModule,
IconComponent,
NotificationHistoryModule,
SearchTextModule,
BlankPageModule,

View File

@ -19,9 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HeaderLayoutComponent } from './header.component';
import { CoreTestingModule } from '../../../testing/core.testing.module';
import { By } from '@angular/platform-browser';
import { SidenavLayoutModule } from '../../layout.module';
import { Component } from '@angular/core';
import { MaterialModule } from '../../../material.module';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatToolbarHarness } from '@angular/material/toolbar/testing';
@ -34,7 +32,7 @@ describe('HeaderLayoutComponent', () => {
describe('Input parameters', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule]
imports: [CoreTestingModule, HeaderLayoutComponent]
});
fixture = TestBed.createComponent(HeaderLayoutComponent);
loader = TestbedHarnessEnvironment.loader(fixture);
@ -253,6 +251,8 @@ describe('HeaderLayoutComponent', () => {
describe('Template transclusion', () => {
@Component({
selector: 'adf-test-layout-header',
standalone: true,
imports: [HeaderLayoutComponent],
template: ` <adf-layout-header title="test" color="primary">
<p>Test text</p>
<p></p>
@ -262,8 +262,7 @@ describe('HeaderLayoutComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule, SidenavLayoutModule, MaterialModule],
declarations: [HeaderLayoutTesterComponent]
imports: [CoreTestingModule, HeaderLayoutTesterComponent]
});
});

View File

@ -17,7 +17,7 @@
import { applicationConfig, Meta, moduleMetadata, StoryFn } from '@storybook/angular';
import { CoreStoryModule } from '../../../testing/core.story.module';
import { SidenavLayoutModule } from '../../layout.module';
import { LAYOUT_DIRECTIVES } from '../../layout.module';
import { HeaderLayoutComponent } from './header.component';
import { RouterTestingModule } from '@angular/router/testing';
import { importProvidersFrom } from '@angular/core';
@ -27,7 +27,7 @@ export default {
title: 'Core/Layout/Header',
decorators: [
moduleMetadata({
imports: [CoreStoryModule, SidenavLayoutModule, RouterTestingModule]
imports: [CoreStoryModule, ...LAYOUT_DIRECTIVES, RouterTestingModule]
}),
applicationConfig({
providers: [importProvidersFrom(CoreStoryModule)]
@ -131,7 +131,7 @@ export default {
}
} as Meta<HeaderLayoutComponent>;
const template: StoryFn<SidenavLayoutModule> = (args) => ({
const template: StoryFn<HeaderLayoutComponent> = (args) => ({
props: args
});

View File

@ -18,9 +18,17 @@
import { Component, Input, Output, EventEmitter, ViewEncapsulation, OnInit } from '@angular/core';
import { ThemePalette } from '@angular/material/core';
import { AppConfigService } from '../../../app-config/app-config.service';
import { CommonModule } from '@angular/common';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
@Component({
selector: 'adf-layout-header',
standalone: true,
imports: [CommonModule, MatToolbarModule, MatButtonModule, MatIconModule, RouterModule, TranslateModule],
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
encapsulation: ViewEncapsulation.None,
@ -69,10 +77,7 @@ export class HeaderLayoutComponent implements OnInit {
/** The side of the page that the drawer is attached to (can be 'start' or 'end') */
@Input() position = 'start';
constructor(
private appConfigService: AppConfigService
) {
}
constructor(private appConfigService: AppConfigService) {}
toggleMenu() {
this.clicked.emit(true);

View File

@ -16,16 +16,44 @@
*/
import { Component, Input, ViewChild, OnInit, OnDestroy, ViewEncapsulation, OnChanges, SimpleChanges } from '@angular/core';
import { MatSidenav } from '@angular/material/sidenav';
import { sidenavAnimation, contentAnimation } from '../../helpers/animations';
import { MatSidenav, MatSidenavModule } from '@angular/material/sidenav';
import { Direction } from '@angular/cdk/bidi';
import { CommonModule } from '@angular/common';
import { animate, state, style, transition, trigger } from '@angular/animations';
@Component({
selector: 'adf-layout-container',
standalone: true,
imports: [CommonModule, MatSidenavModule],
templateUrl: './layout-container.component.html',
styleUrls: ['./layout-container.component.scss'],
encapsulation: ViewEncapsulation.None,
animations: [sidenavAnimation, contentAnimation]
animations: [
trigger('sidenavAnimation', [
state('expanded', style({ width: '{{ width }}px' }), { params: { width: 0 } }),
state('compact', style({ width: '{{ width }}px' }), { params: { width: 0 } }),
transition('compact <=> expanded', animate('0.4s cubic-bezier(0.25, 0.8, 0.25, 1)'))
]),
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)'))
])
]
})
export class LayoutContainerComponent implements OnInit, OnDestroy, OnChanges {
@Input() sidenavMin: number;
@ -62,13 +90,13 @@ export class LayoutContainerComponent implements OnInit, OnDestroy, OnChanges {
this.CONTENT_STATES.MOBILE = { value: 'expanded' };
this.mediaQueryList.addListener(this.onMediaQueryChange);
this.mediaQueryList?.addListener(this.onMediaQueryChange);
this.updateSidenavState();
}
ngOnDestroy(): void {
this.mediaQueryList.removeListener(this.onMediaQueryChange);
this.mediaQueryList?.removeListener(this.onMediaQueryChange);
}
ngOnChanges(changes: SimpleChanges) {
@ -87,7 +115,7 @@ export class LayoutContainerComponent implements OnInit, OnDestroy, OnChanges {
}
get isMobileScreenSize(): boolean {
return this.mediaQueryList.matches;
return !!this.mediaQueryList?.matches;
}
getContentAnimationState(): any {

View File

@ -17,10 +17,11 @@
import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MaterialModule } from '../../../material.module';
import { SidebarActionMenuComponent } from './sidebar-action-menu.component';
import { CoreTestingModule } from '../../../testing/core.testing.module';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { CommonModule } from '@angular/common';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
describe('SidebarActionMenuComponent', () => {
let element: HTMLElement;
@ -29,7 +30,7 @@ describe('SidebarActionMenuComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule]
imports: [CoreTestingModule, SidebarActionMenuComponent]
});
fixture = TestBed.createComponent(SidebarActionMenuComponent);
element = fixture.nativeElement;
@ -51,6 +52,8 @@ describe('SidebarActionMenuComponent', () => {
});
@Component({
standalone: true,
imports: [CommonModule, SidebarActionMenuComponent, MatIconModule, MatMenuModule],
template: `
<adf-sidebar-action-menu [expanded]="expanded" [title]="title">
<mat-icon adf-sidebar-menu-title-icon>arrow_drop_down</mat-icon>
@ -82,8 +85,7 @@ describe('Custom SidebarActionMenuComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [SidebarActionMenuComponent, CustomSidebarActionMenuComponent],
imports: [MaterialModule, NoopAnimationsModule]
imports: [CoreTestingModule, SidebarActionMenuComponent, CustomSidebarActionMenuComponent]
});
fixture = TestBed.createComponent(CustomSidebarActionMenuComponent);
fixture.detectChanges();

View File

@ -16,18 +16,21 @@
*/
import { ChangeDetectionStrategy, Component, Directive, Input, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu';
@Component({
selector: 'adf-sidebar-action-menu',
standalone: true,
imports: [CommonModule, MatButtonModule, MatMenuModule],
templateUrl: './sidebar-action-menu.component.html',
styleUrls: ['./sidebar-action-menu.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: { class: 'adf-sidebar-action-menu' }
})
export class SidebarActionMenuComponent {
/** The title of the sidebar action. */
@Input()
title: string;
@ -48,6 +51,20 @@ export class SidebarActionMenuComponent {
/**
* Directive selectors without adf- prefix will be deprecated on 3.0.0
*/
@Directive({ selector: '[adf-sidebar-menu-options], [sidebar-menu-options]' }) export class SidebarMenuDirective {}
@Directive({ selector: '[adf-sidebar-menu-title-icon], [sidebar-menu-title-icon]' }) export class SidebarMenuTitleIconDirective {}
@Directive({ selector: '[adf-sidebar-menu-expand-icon], [sidebar-menu-expand-icon]' }) export class SidebarMenuExpandIconDirective {}
@Directive({
selector: '[adf-sidebar-menu-options], [sidebar-menu-options]',
standalone: true
})
export class SidebarMenuDirective {}
@Directive({
selector: '[adf-sidebar-menu-title-icon], [sidebar-menu-title-icon]',
standalone: true
})
export class SidebarMenuTitleIconDirective {}
@Directive({
selector: '[adf-sidebar-menu-expand-icon], [sidebar-menu-expand-icon]',
standalone: true
})
export class SidebarMenuExpandIconDirective {}

View File

@ -17,7 +17,7 @@
import { applicationConfig, Meta, moduleMetadata, StoryFn } from '@storybook/angular';
import { CoreStoryModule } from '../../../testing/core.story.module';
import { SidenavLayoutModule } from '../../layout.module';
import { LAYOUT_DIRECTIVES } from '../../layout.module';
import { SidebarActionMenuComponent } from './sidebar-action-menu.component';
import { importProvidersFrom } from '@angular/core';
@ -26,7 +26,7 @@ export default {
title: 'Core/Layout/Sidebar Action Menu',
decorators: [
moduleMetadata({
imports: [CoreStoryModule, SidenavLayoutModule]
imports: [CoreStoryModule, ...LAYOUT_DIRECTIVES]
}),
applicationConfig({
providers: [importProvidersFrom(CoreStoryModule)]
@ -71,7 +71,7 @@ export default {
}
} as Meta<SidebarActionMenuComponent>;
const template: StoryFn<SidenavLayoutModule> = (args) => ({
const template: StoryFn<SidebarActionMenuComponent> = (args) => ({
props: args
});

View File

@ -18,40 +18,24 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { SidenavLayoutComponent } from './sidenav-layout.component';
import { Component, CUSTOM_ELEMENTS_SCHEMA, Input } from '@angular/core';
import { LayoutModule, MediaMatcher } from '@angular/cdk/layout';
import { PlatformModule } from '@angular/cdk/platform';
import { MaterialModule } from '../../../material.module';
import { Component } from '@angular/core';
import { MediaMatcher } from '@angular/cdk/layout';
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 '../../../common/services/user-preferences.service';
import { CommonModule } from '@angular/common';
import { Direction } from '@angular/cdk/bidi';
import { of } from 'rxjs';
@Component({
selector: 'adf-layout-container',
template: ` <ng-content select="[app-layout-navigation]"></ng-content>
<ng-content select="[app-layout-content]"></ng-content>`
})
export class DummyLayoutContainerComponent {
@Input() sidenavMin: number;
@Input() sidenavMax: number;
@Input() position: string;
@Input() direction: Direction;
@Input() mediaQueryList: MediaQueryList;
@Input() hideSidenav: boolean;
@Input() expandedSidenav: boolean;
toggleMenu() {}
}
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@Component({
selector: 'adf-test-component-for-sidenav',
standalone: true,
imports: [SidenavLayoutComponent, SidenavLayoutHeaderDirective, SidenavLayoutNavigationDirective, SidenavLayoutContentDirective],
template: ` <adf-sidenav-layout [sidenavMin]="70" [sidenavMax]="320" [stepOver]="600" [hideSidenav]="false">
<adf-sidenav-layout-header>
<ng-template let-toggleMenu="toggleMenu">
<div role="button" id="header-test" (click)="toggleMenu()" role="button" tabindex="0" (keyup.enter)="toggleMenu()"></div>
<div id="header-test" (click)="toggleMenu()" role="button" tabindex="0" (keyup.enter)="toggleMenu()"></div>
</ng-template>
</adf-sidenav-layout-header>
@ -72,22 +56,14 @@ export class SidenavLayoutTesterComponent {}
describe('SidenavLayoutComponent', () => {
let fixture: ComponentFixture<any>;
let mediaMatcher: MediaMatcher;
let mediaQueryList: any;
let component: SidenavLayoutComponent;
let mediaMatcher: MediaMatcher;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CommonModule, PlatformModule, LayoutModule, MaterialModule],
declarations: [
DummyLayoutContainerComponent,
SidenavLayoutComponent,
SidenavLayoutContentDirective,
SidenavLayoutHeaderDirective,
SidenavLayoutNavigationDirective
],
providers: [MediaMatcher, { provide: UserPreferencesService, useValue: { select: () => of() } }],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
imports: [CommonModule, NoopAnimationsModule, SidenavLayoutComponent],
providers: [MediaMatcher, { provide: UserPreferencesService, useValue: { select: () => of() } }]
});
mediaQueryList = {
mediaFn: null,
@ -99,12 +75,7 @@ describe('SidenavLayoutComponent', () => {
};
mediaMatcher = TestBed.inject(MediaMatcher);
spyOn(mediaMatcher, 'matchMedia').and.callFake((mediaQuery) => {
mediaQueryList.originalMediaQueryPassed = mediaQuery;
spyOn(mediaQueryList, 'addListener').and.callThrough();
spyOn(mediaQueryList, 'removeListener').and.stub();
return mediaQueryList;
});
spyOn(mediaMatcher, 'matchMedia').and.callFake(() => mediaQueryList);
fixture = TestBed.createComponent(SidenavLayoutComponent);
component = fixture.componentInstance;
@ -115,40 +86,10 @@ describe('SidenavLayoutComponent', () => {
TestBed.resetTestingModule();
});
describe('General behaviour', () => {
beforeEach(() => fixture.detectChanges());
it('should pass through input parameters', () => {
component.sidenavMin = 1;
component.sidenavMax = 2;
component.hideSidenav = true;
component.expandedSidenav = false;
fixture.detectChanges();
const layoutContainerComponent = fixture.debugElement.query(By.directive(DummyLayoutContainerComponent)).componentInstance;
expect(layoutContainerComponent.sidenavMin).toBe(component.sidenavMin);
expect(layoutContainerComponent.sidenavMax).toBe(component.sidenavMax);
expect(layoutContainerComponent.hideSidenav).toBe(component.hideSidenav);
expect(layoutContainerComponent.expandedSidenav).toBe(component.expandedSidenav);
expect(layoutContainerComponent.mediaQueryList.originalMediaQueryPassed).toBe(`(max-width: 600px)`);
});
it('addListener of mediaQueryList should have been called', () => {
expect(mediaQueryList.addListener).toHaveBeenCalledTimes(1);
expect(mediaQueryList.addListener).toHaveBeenCalledWith(component.onMediaQueryChange);
});
it('addListener of mediaQueryList should have been called', () => {
fixture.destroy();
expect(mediaQueryList.removeListener).toHaveBeenCalledTimes(1);
expect(mediaQueryList.removeListener).toHaveBeenCalledWith(component.onMediaQueryChange);
});
});
describe('toggleMenu', () => {
beforeEach(() => fixture.detectChanges());
beforeEach(() => {
component.ngOnInit();
});
it('should toggle the isMenuMinimized if the mediaQueryList.matches is false (we are on desktop)', () => {
mediaQueryList.matches = false;
@ -178,7 +119,7 @@ describe('SidenavLayoutComponent', () => {
describe('menuOpenState', () => {
it('should be true by default', (done) => {
fixture.detectChanges();
component.ngOnInit();
component.menuOpenState$.subscribe((value) => {
expect(value).toBe(true);
@ -188,7 +129,7 @@ describe('SidenavLayoutComponent', () => {
it('should be the same as the expanded Sidenav value by default', (done) => {
component.expandedSidenav = false;
fixture.detectChanges();
component.ngOnInit();
component.menuOpenState$.subscribe((value) => {
expect(value).toBe(false);
@ -198,8 +139,8 @@ describe('SidenavLayoutComponent', () => {
it('should emit value on toggleMenu action', (done) => {
component.expandedSidenav = false;
fixture.detectChanges();
component.ngOnInit();
component.toggleMenu();
component.menuOpenState$.subscribe((value) => {
@ -213,31 +154,22 @@ describe('SidenavLayoutComponent', () => {
describe('Template transclusion', () => {
let fixture: ComponentFixture<any>;
let mediaMatcher: MediaMatcher;
const mediaQueryList: any = {
matches: false,
addListener: () => {},
removeListener: () => {}
};
let mediaQueryList: any;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CommonModule, PlatformModule, LayoutModule, MaterialModule],
declarations: [
DummyLayoutContainerComponent,
SidenavLayoutTesterComponent,
SidenavLayoutComponent,
SidenavLayoutContentDirective,
SidenavLayoutHeaderDirective,
SidenavLayoutNavigationDirective
],
imports: [CommonModule, NoopAnimationsModule, SidenavLayoutTesterComponent],
providers: [MediaMatcher, { provide: UserPreferencesService, useValue: { select: () => of() } }]
});
mediaQueryList = {
matches: false,
addListener: () => {},
removeListener: () => {}
};
mediaMatcher = TestBed.inject(MediaMatcher);
spyOn(mediaMatcher, 'matchMedia').and.callFake(() => {
spyOn(mediaQueryList, 'addListener').and.stub();
spyOn(mediaQueryList, 'removeListener').and.stub();
return mediaQueryList;
});
spyOn(mediaMatcher, 'matchMedia').and.callFake(() => mediaQueryList);
fixture = TestBed.createComponent(SidenavLayoutTesterComponent);
fixture.detectChanges();
@ -267,32 +199,17 @@ describe('Template transclusion', () => {
mediaQueryList.matches = false;
fixture.detectChanges();
const outerHeaderElement = fixture.debugElement.query(outerHeaderSelector);
const innerHeaderElement = fixture.debugElement.query(innerHeaderSelector);
expect(outerHeaderElement === null).toBe(false, 'Outer header should be shown');
expect(innerHeaderElement === null).toBe(true, 'Inner header should not be shown');
expect(outerHeaderElement).toBeDefined();
});
it('should contain the transcluded header template inside of the layout-container', () => {
mediaQueryList.matches = true;
fixture.detectChanges();
const outerHeaderElement = fixture.debugElement.query(outerHeaderSelector);
const innerHeaderElement = fixture.debugElement.query(innerHeaderSelector);
expect(outerHeaderElement === null).toBe(true, 'Outer header should not be shown');
expect(innerHeaderElement === null).toBe(false, 'Inner header should be shown');
});
it(`should call through the layout container's toggleMenu method`, () => {
mediaQueryList.matches = false;
fixture.detectChanges();
const layoutContainerComponent = fixture.debugElement.query(By.directive(DummyLayoutContainerComponent)).componentInstance;
spyOn(layoutContainerComponent, 'toggleMenu');
const outerHeaderElement = fixture.debugElement.query(outerHeaderSelector);
outerHeaderElement.triggerEventHandler('click', {});
expect(layoutContainerComponent.toggleMenu).toHaveBeenCalled();
expect(innerHeaderElement).toBeDefined();
});
});

View File

@ -17,7 +17,7 @@
import { applicationConfig, Meta, moduleMetadata, StoryFn } from '@storybook/angular';
import { CoreStoryModule } from '../../../testing/core.story.module';
import { SidenavLayoutModule } from '../../layout.module';
import { LAYOUT_DIRECTIVES } from '../../layout.module';
import { SidenavLayoutComponent } from './sidenav-layout.component';
import { MatListModule } from '@angular/material/list';
import { MatIconModule } from '@angular/material/icon';
@ -29,7 +29,7 @@ export default {
title: 'Core/Layout/Sidenav Layout',
decorators: [
moduleMetadata({
imports: [SidenavLayoutModule, RouterTestingModule, MatIconModule, MatListModule]
imports: [CoreStoryModule, ...LAYOUT_DIRECTIVES, RouterTestingModule, MatIconModule, MatListModule]
}),
applicationConfig({
providers: [importProvidersFrom(CoreStoryModule)]
@ -136,7 +136,7 @@ export default {
}
} as Meta<SidenavLayoutComponent>;
const template: StoryFn<SidenavLayoutModule> = (args) => ({
const template: StoryFn<SidenavLayoutComponent> = (args) => ({
props: args,
template: `
<adf-sidenav-layout

View File

@ -33,12 +33,16 @@ import { UserPreferencesService } from '../../../common/services/user-preference
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, Subject } from 'rxjs';
import { BehaviorSubject, Subject } from 'rxjs';
import { Direction } from '@angular/cdk/bidi';
import { takeUntil } from 'rxjs/operators';
import { CommonModule } from '@angular/common';
import { LayoutContainerComponent } from '../layout-container/layout-container.component';
@Component({
selector: 'adf-sidenav-layout',
standalone: true,
imports: [CommonModule, LayoutContainerComponent],
templateUrl: './sidenav-layout.component.html',
styleUrls: ['./sidenav-layout.component.scss'],
encapsulation: ViewEncapsulation.None,
@ -80,8 +84,8 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy
@ContentChild(SidenavLayoutContentDirective)
contentDirective: SidenavLayoutContentDirective;
private menuOpenStateSubject: BehaviorSubject<boolean>;
public menuOpenState$: Observable<boolean>;
private menuOpenStateSubject = new BehaviorSubject<boolean>(false);
public menuOpenState$ = this.menuOpenStateSubject.asObservable();
@ViewChild('container', { static: true }) container: any;
@ViewChild('emptyTemplate', { static: true }) emptyTemplate: any;
@ -96,16 +100,13 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy
private onDestroy$ = new Subject<boolean>();
constructor(private mediaMatcher: MediaMatcher, private userPreferencesService: UserPreferencesService ) {
constructor(private mediaMatcher: MediaMatcher, private userPreferencesService: UserPreferencesService) {
this.onMediaQueryChange = this.onMediaQueryChange.bind(this);
}
ngOnInit() {
const initialMenuState = !this.expandedSidenav;
this.menuOpenStateSubject = new BehaviorSubject<boolean>(initialMenuState);
this.menuOpenState$ = this.menuOpenStateSubject.asObservable();
const stepOver = this.stepOver || SidenavLayoutComponent.STEP_OVER;
this.isMenuMinimized = initialMenuState;

View File

@ -18,7 +18,8 @@
import { ContentChild, Directive, TemplateRef } from '@angular/core';
@Directive({
selector: 'adf-sidenav-layout-content'
selector: 'adf-sidenav-layout-content',
standalone: true
})
export class SidenavLayoutContentDirective {
@ContentChild(TemplateRef)

View File

@ -18,7 +18,8 @@
import { ContentChild, Directive, TemplateRef } from '@angular/core';
@Directive({
selector: 'adf-sidenav-layout-header'
selector: 'adf-sidenav-layout-header',
standalone: true
})
export class SidenavLayoutHeaderDirective {
@ContentChild(TemplateRef)

View File

@ -18,7 +18,8 @@
import { ContentChild, Directive, TemplateRef } from '@angular/core';
@Directive({
selector: 'adf-sidenav-layout-navigation'
selector: 'adf-sidenav-layout-navigation',
standalone: true
})
export class SidenavLayoutNavigationDirective {
@ContentChild(TemplateRef)

View File

@ -1,36 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { trigger, transition, animate, style, state, AnimationTriggerMetadata } from '@angular/animations';
export const sidenavAnimation: AnimationTriggerMetadata = trigger('sidenavAnimation', [
state('expanded', style({ width: '{{ width }}px' }), { params : { width: 0 } }),
state('compact', style({ width: '{{ width }}px' }), { params : { width: 0 } }),
transition('compact <=> expanded', animate('0.4s cubic-bezier(0.25, 0.8, 0.25, 1)'))
]);
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

@ -15,49 +15,36 @@
* limitations under the License.
*/
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
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 { SidenavLayoutComponent } from './components/sidenav-layout/sidenav-layout.component';
import { LayoutContainerComponent } from './components/layout-container/layout-container.component';
import { SidebarActionMenuComponent, SidebarMenuDirective,
SidebarMenuExpandIconDirective, SidebarMenuTitleIconDirective } from './components/sidebar-action/sidebar-action-menu.component';
import {
SidebarActionMenuComponent,
SidebarMenuDirective,
SidebarMenuExpandIconDirective,
SidebarMenuTitleIconDirective
} from './components/sidebar-action/sidebar-action-menu.component';
import { HeaderLayoutComponent } from './components/header/header.component';
import { TranslateModule } from '@ngx-translate/core';
export const LAYOUT_DIRECTIVES = [
SidenavLayoutHeaderDirective,
SidenavLayoutContentDirective,
SidenavLayoutNavigationDirective,
SidebarMenuDirective,
SidebarMenuExpandIconDirective,
SidebarMenuTitleIconDirective,
HeaderLayoutComponent,
SidebarActionMenuComponent,
LayoutContainerComponent,
SidenavLayoutComponent
] as const;
/** @deprecated Use `...LAYOUT_DIRECTIVES` instead, or import standalone components directly */
@NgModule({
imports: [
CommonModule,
MaterialModule,
RouterModule,
TranslateModule
],
exports: [
SidenavLayoutHeaderDirective,
SidenavLayoutContentDirective,
SidenavLayoutNavigationDirective,
SidenavLayoutComponent,
LayoutContainerComponent,
SidebarActionMenuComponent,
SidebarMenuDirective,
SidebarMenuExpandIconDirective,
SidebarMenuTitleIconDirective,
HeaderLayoutComponent
],
declarations: [
SidenavLayoutHeaderDirective,
SidenavLayoutContentDirective,
SidenavLayoutNavigationDirective,
SidenavLayoutComponent,
LayoutContainerComponent,
SidebarActionMenuComponent,
SidebarMenuDirective,
SidebarMenuExpandIconDirective,
SidebarMenuTitleIconDirective,
HeaderLayoutComponent
]
imports: [...LAYOUT_DIRECTIVES],
exports: [...LAYOUT_DIRECTIVES]
})
export class SidenavLayoutModule {}

View File

@ -44,6 +44,7 @@ import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatBadgeModule } from '@angular/material/badge';
/** @deprecated This module is deprecated and will be removed in a future release. */
@NgModule({
imports: [
MatAutocompleteModule,