Skip to content

Commit

Permalink
feat(platform,core): custom title and subtitle template for dynamic p…
Browse files Browse the repository at this point in the history
…age (#10065)

* fix(platform): custom subtitle template for dynamic page

* feat(core,platform): custom dynamic page title

* fix(platform): fix build

---------

Co-authored-by: Denis Severin <[email protected]>
  • Loading branch information
fkolar and N1XUS authored Jun 30, 2023
1 parent 5670ac5 commit 6cd0daa
Show file tree
Hide file tree
Showing 19 changed files with 318 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Directive, TemplateRef, inject } from '@angular/core';
import { DynamicPageTitleDirectiveContext } from '../models/title-directive-context';

@Directive({
selector: '[fdDynamicPageHeaderSubtitle]',
standalone: true
})
export class DynamicPageHeaderSubtitleDirective {
/** Template reference. */
templateRef = inject<TemplateRef<DynamicPageTitleDirectiveContext>>(TemplateRef);

/** @hidden */
static ngTemplateContextGuard(
dir: DynamicPageHeaderSubtitleDirective,
ctx: DynamicPageTitleDirectiveContext
): ctx is DynamicPageTitleDirectiveContext {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Directive, TemplateRef, inject } from '@angular/core';
import { DynamicPageTitleDirectiveContext } from '../models/title-directive-context';

@Directive({
selector: '[fdDynamicPageHeaderTitle]',
standalone: true
})
export class DynamicPageHeaderTitleDirective {
/** Template reference. */
templateRef = inject<TemplateRef<DynamicPageTitleDirectiveContext>>(TemplateRef);

/** @hidden */
static ngTemplateContextGuard(
dir: DynamicPageHeaderTitleDirective,
ctx: DynamicPageTitleDirectiveContext
): ctx is DynamicPageTitleDirectiveContext {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
<span
class="fd-dynamic-page__title"
[class.fd-dynamic-page__title--wrap]="titleWrap"
fdIgnoreClickOnSelection
fdkIgnoreClickOnSelection
>
{{ title }}
<ng-template [ngTemplateOutlet]="_titleTemplate ? titleRef : defaultTitle"></ng-template>
</span>
<ng-content select="fd-dynamic-page-title-content"></ng-content>
<div class="fd-dynamic-page__toolbar-container" (click)="stopPropagation($event)">
Expand All @@ -31,24 +31,24 @@
</div>
</div>
<div
*ngIf="_collapsed && subtitle"
*ngIf="_collapsed && (subtitle || _subtitleTemplate)"
class="fd-dynamic-page__subtitle"
[class.fd-dynamic-page__subtitle--wrap]="subtitleWrap"
fdIgnoreClickOnSelection
fdkIgnoreClickOnSelection
>
{{ subtitle }}
<ng-container *ngTemplateOutlet="_subtitleTemplate ? subtitleRef : defaultSubtitle"></ng-container>
</div>
</div>
</div>
</div>
</div>
<div
*ngIf="!_collapsed && subtitle"
*ngIf="!_collapsed && (subtitle || _subtitleTemplate)"
class="fd-dynamic-page__subtitle"
[class.fd-dynamic-page__subtitle--wrap]="subtitleWrap"
fdIgnoreClickOnSelection
fdkIgnoreClickOnSelection
>
{{ subtitle }}
<ng-container *ngTemplateOutlet="_subtitleTemplate ? subtitleRef : defaultSubtitle"></ng-container>
</div>

<ng-template #dynamicPageGlobalActionsRef>
Expand All @@ -57,3 +57,32 @@
<ng-template #dynamicPageLayoutActionsRef>
<ng-content select="fd-dynamic-page-layout-actions"></ng-content>
</ng-template>

<ng-template #titleRef>
<ng-template
[ngTemplateOutlet]="_titleTemplate!.templateRef"
[ngTemplateOutletContext]="{ $implicit: _collapsed }"
></ng-template>
</ng-template>

<ng-template #subtitleRef>
<ng-container
[ngTemplateOutlet]="_subtitleTemplate!.templateRef"
[ngTemplateOutletContext]="{ $implicit: _collapsed }"
></ng-container>
</ng-template>

<ng-template #defaultSubtitle>
{{ subtitle }}
</ng-template>

<ng-template #titleRef>
<ng-container
[ngTemplateOutlet]="_titleTemplate!.templateRef"
[ngTemplateOutletContext]="{ $implicit: _collapsed }"
></ng-container>
</ng-template>

<ng-template #defaultTitle>
{{ title }}
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CommonModule } from '@angular/common';
import { Component, ViewChild } from '@angular/core';
import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';

import { BreadcrumbModule } from '@fundamental-ngx/core/breadcrumb';
Expand Down Expand Up @@ -35,6 +35,38 @@ class TestComponent {
constructor(public dynamicPageService: DynamicPageService) {}
}

@Component({
template: `
<fd-dynamic-page-header>
<ng-container *fdDynamicPageHeaderTitle="let collapsed">
<span class="my-custom-title">Title {{ collapsed ? 'collapsed' : 'expanded' }}</span>
</ng-container>
<ng-container *fdDynamicPageHeaderSubtitle="let collapsed">
<span class="my-custom-subtitle">Subtitle {{ collapsed ? 'collapsed' : 'expanded' }}</span>
</ng-container>
<fd-breadcrumb>
<fd-breadcrumb-item>
<a fd-link [attr.href]="'#'">Men</a>
</fd-breadcrumb-item>
<fd-breadcrumb-item>
<a fd-link [attr.href]="'#'">Shoes</a>
</fd-breadcrumb-item>
</fd-breadcrumb>
<fd-dynamic-page-global-actions></fd-dynamic-page-global-actions>
<fd-dynamic-page-title-content></fd-dynamic-page-title-content>
</fd-dynamic-page-header>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [DynamicPageService]
})
class TestWithSubtitleTemplateComponent {
@ViewChild(DynamicPageHeaderComponent)
header: DynamicPageHeaderComponent;

constructor(public dynamicPageService: DynamicPageService) {}
}

describe('DynamicPageTitleComponent', () => {
let fixture: ComponentFixture<TestComponent>;
let header: DynamicPageHeaderComponent;
Expand Down Expand Up @@ -86,3 +118,63 @@ describe('DynamicPageTitleComponent', () => {
expect((<any>header)._actionsSquashed).toBe(false);
});
});

describe('DynamicPageTitleComponent with custom subtitle', () => {
let fixture: ComponentFixture<TestWithSubtitleTemplateComponent>;
let header: DynamicPageHeaderComponent;
let component: TestWithSubtitleTemplateComponent;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [CommonModule, DynamicPageModule, BreadcrumbModule, ToolbarModule, ButtonModule],
declarations: [TestWithSubtitleTemplateComponent],
providers: [DynamicPageService]
}).compileComponents();
});

beforeEach(async () => {
fixture = TestBed.createComponent(TestWithSubtitleTemplateComponent);
component = fixture.componentInstance;
await fixture.whenRenderingDone();
fixture.detectChanges();
header = component.header;
fixture.detectChanges();
});

it('should create', () => {
expect(fixture).toBeTruthy();
});

it('should set title template correctly', fakeAsync(() => {
expect(header._titleTemplate).toBeDefined();
expect(header.title).toBeUndefined();
}));

it('should set subtitle template correctly', fakeAsync(() => {
expect(header._subtitleTemplate).toBeDefined();
expect(header.subtitle).toBeUndefined();
}));

it('should set subtitle template properties correctly', async () => {
fixture.componentInstance.dynamicPageService.collapsed.next(false);
fixture.detectChanges();
await fixture.whenStable();

let subtitle = (header as any)._elementRef.nativeElement.querySelector('.my-custom-subtitle');
let title = (header as any)._elementRef.nativeElement.querySelector('.my-custom-title');

expect(subtitle).toBeDefined();
expect(subtitle.textContent).toEqual(`Subtitle expanded`);
expect(title.textContent).toEqual('Title expanded');

fixture.componentInstance.dynamicPageService.collapsed.next(true);
fixture.detectChanges();
await fixture.whenStable();

subtitle = (header as any)._elementRef.nativeElement.querySelector('.my-custom-subtitle');
title = (header as any)._elementRef.nativeElement.querySelector('.my-custom-title');

expect(subtitle.textContent).toEqual(`Subtitle collapsed`);
expect(title.textContent).toEqual('Title collapsed');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
ContentChild,
ElementRef,
Input,
NgZone,
OnDestroy,
OnInit,
Renderer2,
Expand All @@ -25,6 +24,9 @@ import { addClassNameToElement } from '../../utils';
import { DynamicPageLayoutActionsComponent } from '../actions/dynamic-page-layout-actions.component';
import { DynamicPageGlobalActionsComponent } from '../actions/dynamic-page-global-actions.component';
import { DynamicPageTitleContentComponent } from '../actions/dynamic-page-title-content.component';
import { DynamicPageHeaderSubtitleDirective } from '../../directives/dynamic-page-header-subtitle.directive';
import { Nullable } from '@fundamental-ngx/cdk/utils';
import { DynamicPageHeaderTitleDirective } from '../../directives/dynamic-page-header-title.directive';

export const ActionSquashBreakpointPx = 1280;

Expand Down Expand Up @@ -64,6 +66,20 @@ export class DynamicPageHeaderComponent implements OnInit, AfterViewInit, OnDest
@Input()
subtitleWrap = false;

/**
* @hidden
* Template used to provide a custom content for the subtitle page header area.
*/
@ContentChild(DynamicPageHeaderSubtitleDirective)
_subtitleTemplate: Nullable<DynamicPageHeaderSubtitleDirective>;

/**
* @hidden
* Template used to provide a custom content for the title page header area.
*/
@ContentChild(DynamicPageHeaderTitleDirective)
_titleTemplate: Nullable<DynamicPageHeaderSubtitleDirective>;

/** @hidden */
@ContentChild(FD_BREADCRUMB_COMPONENT)
_breadcrumbComponent: BreadcrumbComponent;
Expand Down Expand Up @@ -94,7 +110,6 @@ export class DynamicPageHeaderComponent implements OnInit, AfterViewInit, OnDest
private _elementRef: ElementRef<HTMLElement>,
private _renderer: Renderer2,
private _dynamicPageService: DynamicPageService,
private _ngZone: NgZone,
private _changeDetRef: ChangeDetectorRef
) {}

Expand Down
10 changes: 8 additions & 2 deletions libs/core/src/lib/dynamic-page/dynamic-page.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { IgnoreClickOnSelectionModule } from '@fundamental-ngx/cdk/utils';
import { DeprecatedDynamicPageCompactDirective } from './deprecated-dynamic-page-compact.directive';
import { ContentDensityModule } from '@fundamental-ngx/core/content-density';
import { ScrollbarModule } from '@fundamental-ngx/core/scrollbar';
import { DynamicPageHeaderSubtitleDirective } from './directives/dynamic-page-header-subtitle.directive';
import { DynamicPageHeaderTitleDirective } from './directives/dynamic-page-header-title.directive';

@NgModule({
declarations: [
Expand All @@ -38,7 +40,9 @@ import { ScrollbarModule } from '@fundamental-ngx/core/scrollbar';
PopoverModule,
IgnoreClickOnSelectionModule,
ContentDensityModule,
ScrollbarModule
ScrollbarModule,
DynamicPageHeaderSubtitleDirective,
DynamicPageHeaderTitleDirective
],
exports: [
DynamicPageComponent,
Expand All @@ -50,7 +54,9 @@ import { ScrollbarModule } from '@fundamental-ngx/core/scrollbar';
DynamicPageFooterComponent,
DynamicPageTitleContentComponent,
DynamicPageWrapperDirective,
DeprecatedDynamicPageCompactDirective
DeprecatedDynamicPageCompactDirective,
DynamicPageHeaderSubtitleDirective,
DynamicPageHeaderTitleDirective
]
})
export class DynamicPageModule {}
3 changes: 3 additions & 0 deletions libs/core/src/lib/dynamic-page/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ export * from './dynamic-page.module';
export * from './dynamic-page.service';
export * from './utils';
export * from './deprecated-dynamic-page-compact.directive';
export * from './models/title-directive-context';
export * from './directives/dynamic-page-header-subtitle.directive';
export * from './directives/dynamic-page-header-title.directive';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface DynamicPageTitleDirectiveContext {
/** Whether the header is collapsed */
$implicit: boolean;
}
5 changes: 3 additions & 2 deletions libs/docs/core/dynamic-page/dynamic-page-docs.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@
<description
>The Dynamic Page header can consist of various facet types, such as Form facet, Key-value facet, Image facet, and
Rating Indicator facet. When an Image facet is used in the header content, collapsing the header will bring the
image facet next to the title. Expanding the header will place it back to its original place.</description
>
image facet next to the title. Expanding the header will place it back to its original place. This Dynamic Page
header also features custom <code>subtitleTemplate</code> for custom html content.
</description>
<component-example>
<fd-dynamic-page-facets-example></fd-dynamic-page-facets-example>
</component-example>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
<button fd-button (click)="openPage()">Click to open full screen</button>
<div class="overlay" *ngIf="visible">
<fd-dynamic-page size="large" ariaLabel="Example Dynamic Page">
<fd-dynamic-page-header
title="Lorem ipsum dolor sit amet, consectetur adipiscing elit"
subtitle="sed do eiusmod tempor incididunt ut labore et dolore magna aliqua"
>
<fd-dynamic-page-header title="Lorem ipsum dolor sit amet, consectetur adipiscing elit">
<span *fdDynamicPageHeaderTitle="let collapsed">
Lorem ipsum dolor sit amet, consectetur adipiscing elit ({{
collapsed ? 'collapsed' : 'not collapsed'
}})
</span>
<ng-container *fdDynamicPageHeaderSubtitle="let collapsed">
<span class="red-subtitle">Different parts</span> | <span class="blue-subtitle"> of the</span> |
<span class="green-subtitle">subtitle</span> | with
{{ collapsed ? 'collapsed' : 'not collapsed' }} header
</ng-container>
<fd-breadcrumb>
<fd-breadcrumb-item>
<a fd-link href="#">Men</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ import { Component } from '@angular/core';
.fd-dynamic-page-section-example {
min-height: 20vh;
}
.red-subtitle {
color: red;
}
.blue-subtitle {
color: blue;
}
.green-subtitle {
color: green;
}
`
]
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@
<div class="overlay" #overlay>
<ng-container *ngIf="fullscreen">
<fdp-dynamic-page ariaLabel="Example Dynamic Page">
<fdp-dynamic-page-title [title]="pageTitle" subtitle="Oversized multimaterial sneakers with quilted effect">
<fdp-dynamic-page-title>
<span *fdpDynamicPageHeaderTitle="let collapsed">
{{ pageTitle }} ({{ collapsed ? 'collapsed' : 'not collapsed' }})
</span>
<ng-container *fdpDynamicPageHeaderSubtitle="let collapsed">
<span class="red-subtitle">Different platform parts</span> |
<span class="blue-subtitle"> of the</span> | <span class="green-subtitle">subtitle</span> | with
{{ collapsed ? 'collapsed' : 'not collapsed' }} header
</ng-container>
<!-- breadcrumb content -->
<fd-breadcrumb>
<fd-breadcrumb-item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,13 @@
background-color: rgb(255, 255, 255);
overflow-x: hidden;
}

.red-subtitle {
color: red;
}
.blue-subtitle {
color: blue;
}
.green-subtitle {
color: green;
}
Loading

0 comments on commit 6cd0daa

Please sign in to comment.