Skip to content

Commit 4f1ab70

Browse files
Add support for organizationId and TextWidget component (#108)
* feature: text widget * update packages readme
1 parent 64af28e commit 4f1ab70

File tree

14 files changed

+360
-173
lines changed

14 files changed

+360
-173
lines changed

packages/widget-angular/README.md

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# @livechat/widget-angular
22

3-
> This library allows to render and interact with the [LiveChat Chat Widget](https://developers.livechat.com/open-chat-widget/) inside an [Angular](http://angular.io/) application.
3+
> This library lets you easily add [chat widget functionality](https://platform.text.com/open-chat-widget) to your [Angular](http://angular.io/) application, whether you have an account on [livechat.com](https://livechat.com) or [text.com](https://text.com). This package supports both the LiveChat and Text widgets.
44
55
[![mit](https://img.shields.io/badge/license-MIT-blue.svg)](https://choosealicense.com/licenses/mit/)
66
![Github lerna version](https://img.shields.io/github/lerna-json/v/livechat/chat-widget-adapters?label=version)
@@ -23,7 +23,9 @@ yarn add @livechat/widget-angular
2323

2424
## Usage
2525

26-
### Render
26+
### 1. LiveChat Widget (for livechat.com accounts)
27+
28+
If you have an account on [livechat.com](https://livechat.com), use the `livechat-widget` component and provide your license number:
2729

2830
```ts
2931
// app.module.ts
@@ -64,22 +66,65 @@ export class AppComponent {
6466
></livechat-widget>
6567
```
6668

69+
### 2. Text Widget (for text.com accounts)
70+
71+
If you have an account on [text.com](https://text.com), use the `text-widget` component and provide your `organizationId`:
72+
73+
```ts
74+
// app.module.ts
75+
76+
import { NgModule } from '@angular/core'
77+
import { LiveChatWidgetModule } from '@livechat/widget-angular'
78+
79+
@NgModule({
80+
/* ... */
81+
imports: [LiveChatWidgetModule],
82+
})
83+
export class AppModule {}
84+
```
85+
86+
```ts
87+
// app.component.ts
88+
89+
import { Component } from '@angular/core'
90+
import { EventHandlerPayload } from '@livechat/widget-angular'
91+
92+
@Component({
93+
/* ... */
94+
templateUrl: './app.component.html',
95+
})
96+
export class AppComponent {
97+
handleNewEvent(event: EventHandlerPayload<'onNewEvent'>) {
98+
console.log('TextWidget.onNewEvent', event)
99+
}
100+
}
101+
```
102+
103+
```html
104+
<!-- app.component.html -->
105+
<text-widget
106+
organizationId="614fe72f-3319-43c6-9ae6-c410c65df230"
107+
visibility="maximized"
108+
(onNewEvent)="handleNewEvent($event)"
109+
></text-widget>
110+
```
111+
67112
### Assignable properties
68113

69114
#### Config data
70115

71116
All properties described below are used for initialization on the first render and later updates of the chat widget with new values on change.
72117

73-
| Prop | Type |
74-
| ---------------------- | -------------------------------------- |
75-
| license | string (required) |
76-
| customerName | string |
77-
| group | string |
78-
| customerEmail | string |
79-
| chatBetweenGroups | boolean |
80-
| sessionVariables | Record<string, string> |
81-
| visibility | 'maximized' \| 'minimized' \| 'hidden' |
82-
| customIdentityProvider | () => CustomerAuth |
118+
| Prop | Type |
119+
| ------------------------ | -------------------------------------- |
120+
| license / organizationId | string (required) |
121+
| customerName | string |
122+
| group | string |
123+
| customerEmail | string |
124+
| chatBetweenGroups | boolean |
125+
| sessionVariables | Record<string, string> |
126+
| visibility | 'maximized' \| 'minimized' \| 'hidden' |
127+
| customIdentityProvider | () => CustomerAuth |
83128

84129
CustomerAuth:
85130

@@ -107,7 +152,7 @@ All event handlers listed below are registered if provided for the first time. T
107152

108153
### Services
109154

110-
The `LiveChatWidgetModule`, exported from this package, registers a set of injectable services. All of them expose a subscribable [BehaviorSubject](https://rxjs.dev/api/index/class/BehaviorSubject) instance. It allows consuming reactive data from the chat widget in any place of the application, as long as the `LiveChatWidget` component is rendered in the tree.
155+
The `LiveChatWidgetModule`, exported from this package, registers a set of injectable services. All of them expose a subscribable [BehaviorSubject](https://rxjs.dev/api/index/class/BehaviorSubject) instance. It allows consuming reactive data from the chat widget in any place of the application, as long as the `LiveChatWidget` or `TextWidget` component is rendered in the tree.
111156

112157
#### WidgetStateService
113158

packages/widget-angular/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"@livechat/widget-core": "^1.3.5"
3131
},
3232
"peerDependencies": {
33-
"@angular/core": "12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20",
33+
"@angular/core": "12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21",
3434
"rxjs": "6 || 7"
3535
},
3636
"devDependencies": {

packages/widget-angular/src/LiveChatWidget.component.ts

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import { Component, Input, Output, OnInit, OnDestroy, OnChanges, EventEmitter } from '@angular/core'
1+
import { Component, Directive, Input, Output, OnInit, OnDestroy, OnChanges, EventEmitter } from '@angular/core'
22
import { createWidget } from '@livechat/widget-core'
3-
import type { ExtendedWindow, WidgetInstance, WidgetConfig, EventHandlerPayload } from '@livechat/widget-core'
3+
import type {
4+
ExtendedWindow,
5+
WidgetInstance,
6+
WidgetConfig,
7+
EventHandlerPayload,
8+
ProductName,
9+
} from '@livechat/widget-core'
410

511
declare const window: ExtendedWindow
612

@@ -13,13 +19,10 @@ type Changes = Partial<{
1319
}
1420
}>
1521

16-
@Component({
17-
selector: 'livechat-widget',
18-
template: '',
19-
styles: [],
20-
})
21-
export class LiveChatWidgetComponent implements OnInit, OnDestroy, OnChanges {
22-
@Input() license: WidgetConfig['license'] = ''
22+
@Directive()
23+
abstract class BaseWidgetComponent implements OnInit, OnDestroy, OnChanges {
24+
@Input() license: WidgetConfig['license']
25+
@Input() organizationId: WidgetConfig['organizationId']
2326
@Input() group: WidgetConfig['group']
2427
@Input() visibility: WidgetConfig['visibility']
2528
@Input() customerName: WidgetConfig['customerName']
@@ -40,13 +43,14 @@ export class LiveChatWidgetComponent implements OnInit, OnDestroy, OnChanges {
4043
@Output() onAvailabilityChanged = new EventEmitter<EventHandlerPayload<'onAvailabilityChanged'>>()
4144

4245
widget: WidgetInstance | null = null
46+
protected abstract product: ProductName
4347

4448
ngOnInit() {
4549
this.setupWidget()
4650
}
4751

4852
ngOnChanges(changes: Changes) {
49-
const fullReloadProps: Array<keyof WidgetConfig> = ['license', 'group', 'chatBetweenGroups']
53+
const fullReloadProps: Array<keyof WidgetConfig> = ['license', 'organizationId', 'group', 'chatBetweenGroups']
5054
if (fullReloadProps.some((prop) => changes[prop] !== undefined && !changes[prop]?.isFirstChange())) {
5155
this.reinitialize()
5256
return
@@ -74,6 +78,7 @@ export class LiveChatWidgetComponent implements OnInit, OnDestroy, OnChanges {
7478
this.widget = createWidget({
7579
group: this.group,
7680
license: this.license,
81+
organizationId: this.organizationId,
7782
visibility: this.visibility,
7883
customerName: this.customerName,
7984
customerEmail: this.customerEmail,
@@ -92,6 +97,9 @@ export class LiveChatWidgetComponent implements OnInit, OnDestroy, OnChanges {
9297
onAvailabilityChanged: (availability) => this.onAvailabilityChanged.emit(availability),
9398
})
9499
window.__lc.integration_name = process.env.PACKAGE_NAME
100+
if (this.product === 'textapp') {
101+
window.__lc.product_name = 'text'
102+
}
95103
this.widget.init()
96104
}
97105

@@ -100,3 +108,21 @@ export class LiveChatWidgetComponent implements OnInit, OnDestroy, OnChanges {
100108
this.setupWidget()
101109
}
102110
}
111+
112+
@Component({
113+
selector: 'livechat-widget',
114+
template: '',
115+
styles: [],
116+
})
117+
export class LiveChatWidgetComponent extends BaseWidgetComponent {
118+
protected product: ProductName = 'livechat'
119+
}
120+
121+
@Component({
122+
selector: 'text-widget',
123+
template: '',
124+
styles: [],
125+
})
126+
export class TextWidgetComponent extends BaseWidgetComponent {
127+
protected product: ProductName = 'textapp'
128+
}

packages/widget-angular/src/LiveChatWidget.module.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { NgModule } from '@angular/core'
22

3-
import { LiveChatWidgetComponent } from './LiveChatWidget.component'
3+
import { LiveChatWidgetComponent, TextWidgetComponent } from './LiveChatWidget.component'
44
import { WidgetIsReadyService } from './services/WidgetIsReady.service'
55
import { WidgetStateService } from './services/WidgetState.service'
66
import { WidgetCustomerDataService } from './services/WidgetCustomerData.service'
@@ -9,8 +9,8 @@ import { WidgetGreetingService } from './services/WidgetGreeting.service'
99

1010
@NgModule({
1111
imports: [],
12-
declarations: [LiveChatWidgetComponent],
13-
exports: [LiveChatWidgetComponent],
12+
declarations: [LiveChatWidgetComponent, TextWidgetComponent],
13+
exports: [LiveChatWidgetComponent, TextWidgetComponent],
1414
providers: [
1515
WidgetIsReadyService,
1616
WidgetStateService,

packages/widget-angular/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export * from './services'
22
export { LiveChatWidgetModule } from './LiveChatWidget.module'
3-
export { LiveChatWidgetComponent } from './LiveChatWidget.component'
3+
export { LiveChatWidgetComponent, TextWidgetComponent } from './LiveChatWidget.component'
44

55
export type {
66
ChatEvent,

packages/widget-core/src/__tests__/assign-configuration.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,22 @@ describe('assignConfiguration', () => {
6969
it('should throw an error if license is missing', () => {
7070
expect(() => assignConfiguration({} as any)).toThrow()
7171
})
72+
73+
it('should allow to pass only organizationId', () => {
74+
assignConfiguration({ organizationId: 'test-org-id' })
75+
expect(window.__lc).toMatchInlineSnapshot(`
76+
Object {
77+
"organizationId": "test-org-id",
78+
}
79+
`)
80+
})
81+
82+
it('should prefer organizationId over license', () => {
83+
assignConfiguration({ organizationId: 'test-org-id', license: '123456' })
84+
expect(window.__lc).toMatchInlineSnapshot(`
85+
Object {
86+
"organizationId": "test-org-id",
87+
}
88+
`)
89+
})
7290
})

packages/widget-core/src/assign-configuration.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,20 @@ declare const window: ExtendedWindow
44

55
export function assignConfiguration({
66
license,
7+
organizationId,
78
group,
89
chatBetweenGroups,
910
sessionVariables,
1011
customIdentityProvider,
1112
}: ConfigurationOptions): void {
1213
window.__lc = window.__lc || {}
1314

14-
if (typeof license === 'string') {
15+
if (typeof organizationId === 'string' && organizationId.trim() !== '') {
16+
window.__lc.organizationId = organizationId
17+
} else if (typeof license === 'string' && license.trim() !== '') {
1518
window.__lc.license = Number(license)
1619
} else {
17-
throw new Error('[LiveChatWidget] The license property is required for initialization')
20+
throw new Error('[LiveChatWidget] The license or organizationId property is required for initialization')
1821
}
1922
if (typeof group !== 'undefined') {
2023
window.__lc.group = Number(group)

packages/widget-core/src/types.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
export type ExtendedWindow = Window & {
22
__lc: {
33
license: number
4+
organizationId: string
45
group?: number
56
params?: Array<{ name: string; value: string }>
67
chat_between_groups?: boolean
78
integration_name?: string
9+
product_name?: string
810
custom_identity_provider?: CustomIdentityProvider
911
}
1012
LiveChatWidget: {
@@ -58,7 +60,8 @@ export type CustomerData = MutableCustomerData & {
5860
}
5961

6062
export type ConfigurationOptions = {
61-
license: string
63+
license?: string
64+
organizationId?: string
6265
group?: string
6366
chatBetweenGroups?: boolean
6467
sessionVariables?: CustomerData['sessionVariables']
@@ -131,3 +134,5 @@ export type CustomIdentityProvider = () => {
131134
hasToken: () => Promise<boolean>
132135
invalidate: () => Promise<void>
133136
}
137+
138+
export type ProductName = 'livechat' | 'textapp'

packages/widget-react/README.md

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# @livechat/widget-react
22

3-
> This library allows to render and interact with the [LiveChat Chat Widget](https://developers.livechat.com/open-chat-widget/) inside a [React](https://reactjs.org/) application.
3+
> This library lets you easily add [chat widget functionality](https://platform.text.com/open-chat-widget) to your [React](https://reactjs.org/) application, whether you have an account on [livechat.com](https://livechat.com) or [text.com](https://text.com). This package supports both the LiveChat and Text widgets.
44
55
[![mit](https://img.shields.io/badge/license-MIT-blue.svg)](https://choosealicense.com/licenses/mit/)
66
![Github lerna version](https://img.shields.io/github/lerna-json/v/livechat/chat-widget-adapters?label=version)
@@ -23,7 +23,9 @@ yarn add @livechat/widget-react
2323

2424
## Usage
2525

26-
### Render
26+
### 1. LiveChat Widget (for livechat.com accounts)
27+
28+
If you have an account on [livechat.com](https://livechat.com), use the `LiveChatWidget` component and provide your license number:
2729

2830
```ts
2931
import { LiveChatWidget, EventHandlerPayload } from '@livechat/widget-react'
@@ -43,22 +45,44 @@ function App() {
4345
}
4446
```
4547

48+
### 2. Text Widget (for text.com accounts)
49+
50+
If you have an account on [text.com](https://text.com), use the `TextWidget` component and provide your `organizationId`:
51+
52+
```ts
53+
import { TextWidget, EventHandlerPayload } from '@livechat/widget-react'
54+
55+
function App() {
56+
function handleNewEvent(event: EventHandlerPayload<'onNewEvent'>) {
57+
console.log('TextWidget.onNewEvent', event)
58+
}
59+
60+
return (
61+
<TextWidget
62+
organizationId="614fe72f-3319-43c6-9ae6-c410c65df230"
63+
visibility="maximized"
64+
onNewEvent={handleNewEvent}
65+
/>
66+
)
67+
}
68+
```
69+
4670
### Props
4771

4872
#### Config data
4973

5074
All properties described below are used for initialization on the first render and later updates of the chat widget with new values on change.
5175

52-
| Prop | Type |
53-
| ---------------------- | -------------------------------------- |
54-
| license | string (required) |
55-
| customerName | string |
56-
| group | string |
57-
| customerEmail | string |
58-
| chatBetweenGroups | boolean |
59-
| sessionVariables | Record<string, string> |
60-
| visibility | 'maximized' \| 'minimized' \| 'hidden' |
61-
| customIdentityProvider | () => CustomerAuth |
76+
| Prop | Type |
77+
| ------------------------ | -------------------------------------- |
78+
| license / organizationId | string (required) |
79+
| customerName | string |
80+
| group | string |
81+
| customerEmail | string |
82+
| chatBetweenGroups | boolean |
83+
| sessionVariables | Record<string, string> |
84+
| visibility | 'maximized' \| 'minimized' \| 'hidden' |
85+
| customIdentityProvider | () => CustomerAuth |
6286

6387
CustomerAuth:
6488

@@ -86,7 +110,7 @@ All event handlers listed below are registered if provided for the first time. T
86110

87111
### Hooks
88112

89-
This package exports a set of [React Hooks](https://reactjs.org/docs/hooks-reference.html) that allows consuming reactive data from the chat widget in any place of the application as long as the `LiveChatWidget` component is rendered in the tree.
113+
This package exports a set of [React Hooks](https://reactjs.org/docs/hooks-reference.html) that allows consuming reactive data from the chat widget in any place of the application as long as the `LiveChatWidget` or `TextWidget` component is rendered in the tree.
90114

91115
#### useWidgetState
92116

0 commit comments

Comments
 (0)