Skip to content

Commit

Permalink
feat(chat): ability to provide template as chat title (#2920)
Browse files Browse the repository at this point in the history
katebatura authored Nov 17, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent f94fd77 commit 9ccec64
Showing 10 changed files with 230 additions and 35 deletions.
6 changes: 6 additions & 0 deletions src/app/playground-components.ts
Original file line number Diff line number Diff line change
@@ -480,6 +480,12 @@ export const PLAYGROUND_COMPONENTS: ComponentLink[] = [
component: 'ChatCustomMessageComponent',
name: 'Chat Custom Message',
},
{
path: 'chat-template-title.component',
link: '/chat/chat-template-title.component',
component: 'ChatTemplateTitleComponent',
name: 'Chat Template Title',
},
],
},
{
96 changes: 96 additions & 0 deletions src/framework/theme/components/chat/chat-title.directive.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Component } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { By } from '@angular/platform-browser';
import { NbThemeModule, NbChatModule, NbChatComponent } from '@nebular/theme';

@Component({
template: `
<nb-chat [title]="title">
<ng-template nbChatTitle [context]="{ text: contextTemplateText }" let-data>
{{ staticTemplateText }} {{ data.text }}
</ng-template>
<nb-chat-message
*ngFor="let msg of messages"
[type]="msg.type"
[message]="msg.text"
[reply]="msg.reply"
[sender]="msg.user.name"
[date]="msg.date"
[avatar]="msg.user.avatar"
[customMessageData]="msg.optionalData"
>
<div *nbCustomMessage="'link'; let data">
<a [href]="data.href">{{ data.label }}</a>
</div>
</nb-chat-message>
<nb-chat-form [dropFiles]="false"> </nb-chat-form>
</nb-chat>
`,
})
export class NbChatTitleTemplateTestComponent {
messages = [
{
reply: false,
type: 'link',
optionalData: {
href: 'https://akveo.github.io/ngx-admin/',
label: 'Visit Akveo Nebular',
},
date: new Date(),
user: {
name: 'Frodo Baggins',
avatar: 'https://i.gifer.com/no.gif',
},
},
{
text: 'Hello, how are you?',
reply: true,
type: 'text',
date: new Date(),
user: {
name: 'Bilbo Baggins',
avatar: '',
},
},
];

title = 'chat title';
staticTemplateText = 'staticTemplateText';
contextTemplateText = 'contextTemplateText';
}

describe('NbChatTitleDirective', () => {
let fixture: ComponentFixture<NbChatTitleTemplateTestComponent>;
let testComponent: NbChatTitleTemplateTestComponent;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule, NbThemeModule.forRoot(), NbChatModule],
declarations: [NbChatTitleTemplateTestComponent],
});

fixture = TestBed.createComponent(NbChatTitleTemplateTestComponent);
testComponent = fixture.componentInstance;
fixture.detectChanges();
});

it('should render title template if provided', () => {
const chatHeaderElement: HTMLElement = fixture.debugElement
.query(By.directive(NbChatComponent))
.query(By.css('.header')).nativeElement;
const expectedText = ` ${testComponent.staticTemplateText} ${testComponent.contextTemplateText} `;

expect(chatHeaderElement.textContent).toEqual(expectedText);
});

it('should not render text title if template title is provided', () => {
const chatHeaderElement: HTMLElement = fixture.debugElement
.query(By.directive(NbChatComponent))
.query(By.css('.header')).nativeElement;

expect(chatHeaderElement.textContent).not.toContain(testComponent.title);
});
});
10 changes: 10 additions & 0 deletions src/framework/theme/components/chat/chat-title.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Directive, Input, TemplateRef } from '@angular/core';

@Directive({
selector: `[nbChatTitle]`,
})
export class NbChatTitleDirective {
@Input() context: Object = {};

constructor(public templateRef: TemplateRef<any>) {}
}
18 changes: 17 additions & 1 deletion src/framework/theme/components/chat/chat.component.ts
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ import { convertToBoolProperty, NbBooleanInput } from '../helpers';
import { NbChatFormComponent } from './chat-form.component';
import { NbChatMessageComponent } from './chat-message.component';
import { NbChatCustomMessageService } from './chat-custom-message.service';
import { NbChatTitleDirective } from './chat-title.directive';

/**
* Conversational UI collection - a set of components for chat-like UI construction.
@@ -101,6 +102,9 @@ import { NbChatCustomMessageService } from './chat-custom-message.service';
* </nb-chat-message> // chat message, available multiple types
* ```
*
* You could provide a chat title as a template via the `nbChatTitle` directive. It overrides `title` input.
* @stacked-example(Custom title, chat/chat-template-title.component)
*
* Two users conversation showcase:
* @stacked-example(Conversation, chat/chat-conversation-showcase.component)
*
@@ -237,7 +241,18 @@ import { NbChatCustomMessageService } from './chat-custom-message.service';
selector: 'nb-chat',
styleUrls: ['./chat.component.scss'],
template: `
<div class="header">{{ title }}</div>
<div class="header">
<ng-container
*ngIf="titleTemplate; else textTitleTemplate"
[ngTemplateOutlet]="titleTemplate.templateRef"
[ngTemplateOutletContext]="{ $implicit: titleTemplate.context }"
>
</ng-container>
<ng-template #textTitleTemplate>
{{ title }}
</ng-template>
</div>
<div class="scrollable" #scrollable>
<div class="messages">
<ng-content select="nb-chat-message"></ng-content>
@@ -283,6 +298,7 @@ export class NbChatComponent implements OnChanges, AfterContentInit, AfterViewIn
@ViewChild('scrollable') scrollable: ElementRef;
@ContentChildren(NbChatMessageComponent) messages: QueryList<NbChatMessageComponent>;
@ContentChild(NbChatFormComponent) chatForm: NbChatFormComponent;
@ContentChild(NbChatTitleDirective) titleTemplate: NbChatTitleDirective;

constructor(protected statusService: NbStatusService) {}

31 changes: 7 additions & 24 deletions src/framework/theme/components/chat/chat.module.ts
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ import { NbChatMessageMapComponent } from './chat-message-map.component';
import { NbChatOptions } from './chat.options';
import { NbChatAvatarComponent } from './chat-avatar.component';
import { NbChatCustomMessageDirective } from './chat-custom-message.directive';
import { NbChatTitleDirective } from './chat-title.directive';

const NB_CHAT_COMPONENTS = [
NbChatComponent,
@@ -33,43 +34,25 @@ const NB_CHAT_COMPONENTS = [
NbChatAvatarComponent,
];

const NB_CHAT_DIRECTIVES = [
NbChatCustomMessageDirective,
];
const NB_CHAT_DIRECTIVES = [NbChatCustomMessageDirective, NbChatTitleDirective];

@NgModule({
imports: [
NbSharedModule,
NbIconModule,
NbInputModule,
NbButtonModule,
],
declarations: [
...NB_CHAT_COMPONENTS,
...NB_CHAT_DIRECTIVES,
],
exports: [
...NB_CHAT_COMPONENTS,
...NB_CHAT_DIRECTIVES,
],
imports: [NbSharedModule, NbIconModule, NbInputModule, NbButtonModule],
declarations: [...NB_CHAT_COMPONENTS, ...NB_CHAT_DIRECTIVES],
exports: [...NB_CHAT_COMPONENTS, ...NB_CHAT_DIRECTIVES],
})
export class NbChatModule {

static forRoot(options?: NbChatOptions): ModuleWithProviders<NbChatModule> {
return {
ngModule: NbChatModule,
providers: [
{ provide: NbChatOptions, useValue: options || {} },
],
providers: [{ provide: NbChatOptions, useValue: options || {} }],
};
}

static forChild(options?: NbChatOptions): ModuleWithProviders<NbChatModule> {
return {
ngModule: NbChatModule,
providers: [
{ provide: NbChatOptions, useValue: options || {} },
],
providers: [{ provide: NbChatOptions, useValue: options || {} }],
};
}
}
1 change: 1 addition & 0 deletions src/framework/theme/public_api.ts
Original file line number Diff line number Diff line change
@@ -115,6 +115,7 @@ export * from './components/chat/chat.options';
export * from './components/chat/chat-avatar.component';
export * from './components/chat/chat-custom-message.directive';
export * from './components/chat/chat-custom-message.service';
export * from './components/chat/chat-title.directive';
export * from './components/spinner/spinner.component';
export * from './components/spinner/spinner.directive';
export * from './components/spinner/spinner.module';
11 changes: 8 additions & 3 deletions src/playground/with-layout/chat/chat-routing.module.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
*/

import { NgModule } from '@angular/core';
import { RouterModule, Route} from '@angular/router';
import { RouterModule, Route } from '@angular/router';
import { ChatColorsComponent } from './chat-colors.component';
import { ChatConversationShowcaseComponent } from './chat-conversation-showcase.component';
import { ChatDropComponent } from './chat-drop.component';
@@ -14,6 +14,7 @@ import { ChatShowcaseComponent } from './chat-showcase.component';
import { ChatSizesComponent } from './chat-sizes.component';
import { ChatTestComponent } from './chat-test.component';
import { ChatCustomMessageComponent } from './chat-custom-message.component';
import { ChatTemplateTitleComponent } from './chat-template-title.component';

const routes: Route[] = [
{
@@ -48,10 +49,14 @@ const routes: Route[] = [
path: 'chat-custom-message.component',
component: ChatCustomMessageComponent,
},
{
path: 'chat-template-title.component',
component: ChatTemplateTitleComponent,
},
];

@NgModule({
imports: [ RouterModule.forChild(routes) ],
exports: [ RouterModule ],
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ChatRoutingModule {}
21 changes: 21 additions & 0 deletions src/playground/with-layout/chat/chat-template-title.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<nb-chat size="large">
<ng-template nbChatTitle [context]="{ text: 'some text to pass into template' }" let-data>
<div>Chat title content from template. Here is the text provided via context: "{{ data.text }}"</div>
</ng-template>

<nb-chat-message
*ngFor="let msg of messages"
[type]="msg.type"
[message]="msg.text"
[reply]="msg.reply"
[sender]="msg.user.name"
[date]="msg.date"
[files]="msg.files"
[quote]="msg.quote"
[latitude]="msg.latitude"
[longitude]="msg.longitude"
[avatar]="msg.user.avatar"
>
</nb-chat-message>
<nb-chat-form (send)="sendMessage($event)" [dropFiles]="true"> </nb-chat-form>
</nb-chat>
61 changes: 61 additions & 0 deletions src/playground/with-layout/chat/chat-template-title.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* @license
* Copyright Akveo. All Rights Reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

import { Component } from '@angular/core';
import { ChatShowcaseService } from './chat-showcase.service';

@Component({
templateUrl: './chat-template-title.component.html',
providers: [ChatShowcaseService],
styles: [
`
::ng-deep nb-layout-column {
justify-content: center;
display: flex;
}
nb-chat {
width: 500px;
}
`,
],
})
export class ChatTemplateTitleComponent {
messages: any[];

constructor(protected chatShowcaseService: ChatShowcaseService) {
this.messages = this.chatShowcaseService.loadMessages();
}

sendMessage(event: any) {
const files = !event.files
? []
: event.files.map((file) => {
return {
url: file.src,
type: file.type,
icon: 'file-text-outline',
};
});

this.messages.push({
text: event.message,
date: new Date(),
reply: true,
type: files.length ? 'file' : 'text',
files: files,
user: {
name: 'Jonh Doe',
avatar: 'https://i.gifer.com/no.gif',
},
});
const botReply = this.chatShowcaseService.reply(event.message);
if (botReply) {
setTimeout(() => {
this.messages.push(botReply);
}, 500);
}
}
}
10 changes: 3 additions & 7 deletions src/playground/with-layout/chat/chat.module.ts
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ import { ChatSizesComponent } from './chat-sizes.component';
import { ChatTestComponent } from './chat-test.component';
import { ChatCustomMessageComponent } from './chat-custom-message.component';
import { ChatCustomMessageTableComponent } from './components/chat-custom-message-table.component';
import { ChatTemplateTitleComponent } from './chat-template-title.component';

@NgModule({
declarations: [
@@ -30,13 +31,8 @@ import { ChatCustomMessageTableComponent } from './components/chat-custom-messag
ChatTestComponent,
ChatCustomMessageComponent,
ChatCustomMessageTableComponent,
ChatTemplateTitleComponent,
],
imports: [
CommonModule,
NbChatModule.forRoot(),
NbCardModule,
NbButtonModule,
ChatRoutingModule,
],
imports: [CommonModule, NbChatModule.forRoot(), NbCardModule, NbButtonModule, ChatRoutingModule],
})
export class ChatModule {}

0 comments on commit 9ccec64

Please sign in to comment.