Skip to content

Commit 42aff84

Browse files
authored
feat: [REL-12314] add better out-of-the-box support for Vue and Angular (#591)
* feat: add vue composable script for easier spin up * feat: add out-of-the-box angular support * fix: update supported angular versions (for angular devs) * docs: update README * refactor: address PR feedback * refactor: lift lazyLoadToolbar outside of react folder * docs: slightly update angular docs
1 parent b9d9036 commit 42aff84

File tree

11 files changed

+966
-88
lines changed

11 files changed

+966
-88
lines changed

packages/toolbar/README.md

Lines changed: 245 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ instantiating it will keep it disabled in production environments.
3232

3333
The Developer Toolbar depends on your LaunchDarkly JS client having a reference to the same `FlagOverridePlugin` and
3434
`EventInterceptionPlugin` that you pass into the Developer Toolbar. As such, ensure that you instantiate the Developer Toolbar at the same time or immediately after the LaunchDarkly JS client is instantiated.
35-
Below are a few examples on how to instantiate the toolbar, one using the `useLaunchDarklyToolbar` react hook, and one using the CDN hosted toolbar script.
3635

37-
### React Hook (Recommended for React Applications)
36+
### React Hook
3837

3938
```tsx
4039
import { render } from 'react-dom';
4140
import { asyncWithLDProvider } from 'launchdarkly-react-client-sdk';
42-
import { useLaunchDarklyToolbar, FlagOverridePlugin, EventInterceptionPlugin } from '@launchdarkly/toolbar';
41+
import { useLaunchDarklyToolbar } from '@launchdarkly/toolbar/react';
42+
import { FlagOverridePlugin, EventInterceptionPlugin } from '@launchdarkly/toolbar/plugins';
4343

4444
const flagOverridePlugin = new FlagOverridePlugin();
4545
const eventInterceptionPlugin = new EventInterceptionPlugin();
@@ -90,9 +90,118 @@ const eventInterceptionPlugin = new EventInterceptionPlugin();
9090
})();
9191
```
9292

93-
### CDN Script Tag (Framework-Agnostic)
93+
### Vue Composable
9494

95-
Works with any JavaScript framework (Vue, Angular, Svelte, vanilla JS, etc.). Add this script to your `index.html` file.
95+
```typescript
96+
import { onMounted } from 'vue';
97+
import * as LDClient from 'launchdarkly-js-client-sdk';
98+
import { useLaunchDarklyToolbar } from '@launchdarkly/toolbar/vue';
99+
import { FlagOverridePlugin, EventInterceptionPlugin } from '@launchdarkly/toolbar/plugins';
100+
101+
const flagOverridePlugin = new FlagOverridePlugin();
102+
const eventInterceptionPlugin = new EventInterceptionPlugin();
103+
104+
// Initialize LaunchDarkly client
105+
const client = LDClient.initialize(
106+
'client-side-id-123abc',
107+
{
108+
kind: 'user',
109+
key: 'user-key-123abc',
110+
name: 'Sandy Smith',
111+
email: 'sandy@example.com',
112+
},
113+
{
114+
plugins: [flagOverridePlugin, eventInterceptionPlugin],
115+
},
116+
);
117+
118+
// In your Vue component or setup function
119+
export default {
120+
setup() {
121+
onMounted(async () => {
122+
await client.waitForInitialization();
123+
});
124+
125+
// Initialize toolbar with the same plugin instances
126+
useLaunchDarklyToolbar({
127+
flagOverridePlugin,
128+
eventInterceptionPlugin,
129+
130+
// OR Dev Server Mode
131+
devServerUrl: 'http://localhost:8080',
132+
projectKey: 'my-project',
133+
134+
position: 'bottom-right',
135+
enabled: import.meta.env.DEV, // Vite
136+
// enabled: process.env.NODE_ENV === 'development', // Webpack
137+
});
138+
139+
return {
140+
// your component data
141+
};
142+
},
143+
};
144+
```
145+
146+
### Angular Service
147+
148+
```typescript
149+
import { Component, OnInit } from '@angular/core';
150+
import * as LDClient from 'launchdarkly-js-client-sdk';
151+
import LaunchDarklyToolbarService from '@launchdarkly/toolbar/angular';
152+
import { FlagOverridePlugin, EventInterceptionPlugin } from '@launchdarkly/toolbar/plugins';
153+
import { environment } from '../environments/environment';
154+
155+
@Component({
156+
selector: 'app-root',
157+
template: `<router-outlet></router-outlet>`,
158+
providers: [LaunchDarklyToolbarService],
159+
})
160+
export class AppComponent implements OnInit {
161+
private flagOverridePlugin = new FlagOverridePlugin();
162+
private eventInterceptionPlugin = new EventInterceptionPlugin();
163+
private ldClient: LDClient.LDClient;
164+
165+
constructor(private toolbarService: LaunchDarklyToolbarService) {
166+
// Initialize LaunchDarkly client
167+
this.ldClient = LDClient.initialize(
168+
'client-side-id-123abc',
169+
{
170+
kind: 'user',
171+
key: 'user-key-123abc',
172+
name: 'Sandy Smith',
173+
email: 'sandy@example.com',
174+
},
175+
{
176+
plugins: [this.flagOverridePlugin, this.eventInterceptionPlugin],
177+
},
178+
);
179+
}
180+
181+
async ngOnInit() {
182+
await this.ldClient.waitForInitialization();
183+
184+
// Initialize toolbar with the same plugin instances
185+
if (!environment.production) {
186+
await this.toolbarService.initialize({
187+
flagOverridePlugin: this.flagOverridePlugin,
188+
eventInterceptionPlugin: this.eventInterceptionPlugin,
189+
190+
// OR Dev Server Mode
191+
devServerUrl: 'http://localhost:8080',
192+
projectKey: 'my-project',
193+
194+
position: 'bottom-right',
195+
enabled: true,
196+
});
197+
}
198+
}
199+
}
200+
```
201+
202+
### CDN Script Tag
203+
204+
Works with any JavaScript framework or vanilla JS. Add this script to your `index.html` file.
96205

97206
```html
98207
<script src="https://unpkg.com/@launchdarkly/toolbar@latest/cdn/toolbar.min.js"></script>
@@ -150,6 +259,32 @@ declare global {
150259
}
151260
```
152261

262+
## Framework Support
263+
264+
The toolbar provides first-class support for popular frameworks:
265+
266+
### Import Paths
267+
268+
```typescript
269+
// Core toolbar (for CDN or vanilla JS)
270+
import { init } from '@launchdarkly/toolbar';
271+
272+
// Plugins (framework-agnostic)
273+
import { FlagOverridePlugin, EventInterceptionPlugin } from '@launchdarkly/toolbar/plugins';
274+
275+
// React hook
276+
import { useLaunchDarklyToolbar } from '@launchdarkly/toolbar/react';
277+
278+
// Vue composable
279+
import { useLaunchDarklyToolbar } from '@launchdarkly/toolbar/vue';
280+
281+
// Angular service
282+
import LaunchDarklyToolbarService from '@launchdarkly/toolbar/angular';
283+
284+
// TypeScript types
285+
import type { InitializationConfig } from '@launchdarkly/toolbar/types';
286+
```
287+
153288
## Package Structure
154289

155290
```
@@ -161,19 +296,27 @@ declare global {
161296
│ │ ├── ui/ # UI components (Toolbar, Tabs, List, etc.)
162297
│ │ ├── tests/ # Unit tests
163298
│ │ └── index.ts # Core entry point (for CDN builds)
164-
│ ├── react/ # React-specific integrations and utilities
299+
│ ├── react/ # React hook and utilities
165300
│ │ ├── useLaunchDarklyToolbar.ts # Main React hook
166301
│ │ └── lazyLoadToolbar.ts # Dynamic CDN loading
302+
│ ├── vue/ # Vue composable
303+
│ │ └── useLaunchDarklyToolbar.ts # Main Vue composable
304+
│ ├── angular/ # Angular service
305+
│ │ └── launchdarkly-toolbar.service.ts # Injectable service
167306
│ ├── types/ # TypeScript type definitions
168307
│ │ ├── config.ts # Configuration types
169308
│ │ ├── events.ts # Event types
170309
│ │ ├── plugins.ts # Plugin interfaces
171310
│ │ └── index.ts # Type exports
172311
│ └── index.ts # Main entry point (NPM package)
173312
├── dist/ # NPM package output
174-
│ ├── index.js # ES module build
175-
│ ├── index.cjs # CommonJS build
176-
│ └── index.d.ts # TypeScript definitions
313+
│ ├── js/ # ES module builds
314+
│ │ ├── index.js
315+
│ │ ├── react.js
316+
│ │ ├── vue.js
317+
│ │ └── angular.js
318+
│ ├── *.cjs # CommonJS builds
319+
│ └── *.d.ts # TypeScript definitions
177320
├── cdn/ # CDN bundle output
178321
│ └── toolbar.min.js # IIFE bundle for script tags
179322
├── .storybook/ # Storybook configuration
@@ -203,7 +346,9 @@ interface ToolbarConfig {
203346
}
204347
```
205348

206-
### React Hook Options
349+
### Framework-Specific Options
350+
351+
All framework integrations (React, Vue, Angular) support the same configuration options:
207352

208353
```typescript
209354
interface UseLaunchDarklyToolbarConfig extends ToolbarConfig {
@@ -212,12 +357,39 @@ interface UseLaunchDarklyToolbarConfig extends ToolbarConfig {
212357
}
213358
```
214359

360+
**Example for local development:**
361+
362+
```typescript
363+
// React
364+
useLaunchDarklyToolbar({
365+
toolbarBundleUrl: 'http://localhost:5764/toolbar.min.js',
366+
enabled: process.env.NODE_ENV === 'development',
367+
// ... other options
368+
});
369+
370+
// Vue
371+
useLaunchDarklyToolbar({
372+
toolbarBundleUrl: 'http://localhost:5764/toolbar.min.js',
373+
enabled: import.meta.env.DEV,
374+
// ... other options
375+
});
376+
377+
// Angular
378+
await toolbarService.initialize({
379+
toolbarBundleUrl: 'http://localhost:5764/toolbar.min.js',
380+
enabled: !environment.production,
381+
// ... other options
382+
});
383+
```
384+
215385
## Modes
216386

217387
### Dev Server Mode
218388

219389
Connect directly to a LaunchDarkly dev server to manage server-side flags:
220390

391+
**React:**
392+
221393
```tsx
222394
useLaunchDarklyToolbar({
223395
devServerUrl: 'http://localhost:5764',
@@ -226,12 +398,32 @@ useLaunchDarklyToolbar({
226398
});
227399
```
228400

229-
or if you are using the CDN script:
401+
**Vue:**
402+
403+
```typescript
404+
useLaunchDarklyToolbar({
405+
devServerUrl: 'http://localhost:5764',
406+
projectKey: 'my-project',
407+
position: 'bottom-right',
408+
});
409+
```
410+
411+
**Angular:**
412+
413+
```typescript
414+
await toolbarService.initialize({
415+
devServerUrl: 'http://localhost:5764',
416+
projectKey: 'my-project',
417+
position: 'bottom-right',
418+
});
419+
```
420+
421+
**CDN:**
230422

231423
```typescript
232424
window.LaunchDarklyToolbar.init({
233-
devServerUrl: 'http://localhost:8080',
234-
projectKey: 'my-project', // Optional
425+
devServerUrl: 'http://localhost:5764',
426+
projectKey: 'my-project',
235427
position: 'bottom-right',
236428
});
237429
```
@@ -245,19 +437,54 @@ window.LaunchDarklyToolbar.init({
245437

246438
### SDK Mode
247439

248-
Integrate with LaunchDarkly React SDK for client-side flag management:
440+
Integrate with LaunchDarkly JS SDK for client-side flag management:
441+
442+
**React:**
249443

250444
```tsx
251-
import { useFlagOverridePlugin, useEventInterceptionPlugin } from './plugins';
445+
import { FlagOverridePlugin, EventInterceptionPlugin } from '@launchdarkly/toolbar/plugins';
446+
447+
const flagOverridePlugin = new FlagOverridePlugin();
448+
const eventInterceptionPlugin = new EventInterceptionPlugin();
449+
450+
useLaunchDarklyToolbar({
451+
flagOverridePlugin,
452+
eventInterceptionPlugin,
453+
position: 'bottom-right',
454+
});
455+
```
456+
457+
**Vue:**
458+
459+
```typescript
460+
import { FlagOverridePlugin, EventInterceptionPlugin } from '@launchdarkly/toolbar/plugins';
461+
462+
const flagOverridePlugin = new FlagOverridePlugin();
463+
const eventInterceptionPlugin = new EventInterceptionPlugin();
252464

253465
useLaunchDarklyToolbar({
254-
flagOverridePlugin: useFlagOverridePlugin(),
255-
eventInterceptionPlugin: useEventInterceptionPlugin(),
466+
flagOverridePlugin,
467+
eventInterceptionPlugin,
468+
position: 'bottom-right',
469+
});
470+
```
471+
472+
**Angular:**
473+
474+
```typescript
475+
import { FlagOverridePlugin, EventInterceptionPlugin } from '@launchdarkly/toolbar/plugins';
476+
477+
const flagOverridePlugin = new FlagOverridePlugin();
478+
const eventInterceptionPlugin = new EventInterceptionPlugin();
479+
480+
await toolbarService.initialize({
481+
flagOverridePlugin,
482+
eventInterceptionPlugin,
256483
position: 'bottom-right',
257484
});
258485
```
259486

260-
or if you are using the CDN script:
487+
**CDN:**
261488

262489
```typescript
263490
window.LaunchDarklyToolbar.init({

0 commit comments

Comments
 (0)