@launchdarkly/toolbar 2.5.3-beta.1 → 2.6.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/cdn/toolbar.min.js +65 -65
  2. package/dist/core/tests/mocks/providers.d.ts +132 -0
  3. package/dist/core/ui/Toolbar/components/new/AnalyticsConsentToast/AnalyticsConsentToast.d.ts +5 -0
  4. package/dist/core/ui/Toolbar/components/new/AnalyticsConsentToast/AnalyticsConsentToast.module.css.d.ts +13 -0
  5. package/dist/core/ui/Toolbar/components/new/AnalyticsConsentToast/index.d.ts +1 -0
  6. package/dist/core/ui/Toolbar/components/new/Contexts/ContextItem.d.ts +2 -2
  7. package/dist/core/ui/Toolbar/components/new/Settings/Privacy/PrivacySettings.d.ts +1 -0
  8. package/dist/core/ui/Toolbar/components/new/Settings/index.d.ts +1 -1
  9. package/dist/core/ui/Toolbar/components/new/types.d.ts +1 -1
  10. package/dist/core/ui/Toolbar/context/api/ContextsProvider.d.ts +7 -7
  11. package/dist/core/ui/Toolbar/context/state/ToolbarStateProvider.d.ts +6 -0
  12. package/dist/core/ui/Toolbar/context/telemetry/AnalyticsPreferencesProvider.d.ts +14 -0
  13. package/dist/core/ui/Toolbar/context/telemetry/index.d.ts +1 -0
  14. package/dist/core/ui/Toolbar/types/ldApi.d.ts +0 -7
  15. package/dist/core/ui/Toolbar/utils/context.d.ts +27 -8
  16. package/dist/core/ui/Toolbar/utils/localStorage.d.ts +17 -7
  17. package/dist/core/utils/analytics.d.ts +6 -1
  18. package/dist/index.cjs +1 -1
  19. package/dist/js/index.js +1 -1
  20. package/dist/js/plugins.js +446 -0
  21. package/dist/js/plugins.js.map +1 -0
  22. package/dist/js/react.js +86 -0
  23. package/dist/js/react.js.map +1 -0
  24. package/dist/js/types-entry.js +0 -0
  25. package/dist/plugins.cjs +483 -0
  26. package/dist/plugins.cjs.map +1 -0
  27. package/dist/plugins.d.ts +21 -0
  28. package/dist/react.cjs +123 -0
  29. package/dist/react.cjs.map +1 -0
  30. package/dist/react.d.ts +25 -0
  31. package/dist/types-entry.cjs +20 -0
  32. package/dist/types-entry.cjs.map +1 -0
  33. package/dist/types-entry.d.ts +12 -0
  34. package/package.json +47 -4
  35. package/dist/core/ui/Toolbar/components/new/Settings/SettingsContent.d.ts +0 -1
@@ -0,0 +1,132 @@
1
+ import { vi } from 'vitest';
2
+ /**
3
+ * Shared mock factory functions for provider contexts.
4
+ *
5
+ * GLOBAL MOCKS:
6
+ * AnalyticsPreferencesProvider is mocked globally in vitest.setup.ts.
7
+ * Most test files don't need to do anything - the mock is automatic.
8
+ *
9
+ * TESTING REAL IMPLEMENTATIONS:
10
+ * If your test file tests the REAL implementation of a provider, add this at
11
+ * the top of your test file:
12
+ *
13
+ * @example
14
+ * vi.unmock('../ui/Toolbar/context/telemetry/AnalyticsPreferencesProvider');
15
+ *
16
+ * OVERRIDING WITH DYNAMIC VALUES:
17
+ * If your test needs to change mock values during execution, override the
18
+ * global mock with createDynamicAnalyticsPreferencesProviderMock:
19
+ *
20
+ * @example
21
+ * const { getMockValue, setMockValue } = vi.hoisted(() => {
22
+ * let value = false;
23
+ * return { getMockValue: () => value, setMockValue: (v) => { value = v; } };
24
+ * });
25
+ *
26
+ * vi.mock('../ui/Toolbar/context/telemetry/AnalyticsPreferencesProvider', async () => {
27
+ * const { createDynamicAnalyticsPreferencesProviderMock } = await import('./mocks/providers');
28
+ * return createDynamicAnalyticsPreferencesProviderMock({ getIsOptedInToAnalytics: getMockValue });
29
+ * });
30
+ */
31
+ interface AnalyticsPreferencesMock {
32
+ useAnalyticsPreferences: () => {
33
+ isOptedInToAnalytics: boolean;
34
+ isOptedInToEnhancedAnalytics: boolean;
35
+ isOptedInToSessionReplay: boolean;
36
+ handleToggleAnalyticsOptOut: ReturnType<typeof vi.fn>;
37
+ handleToggleEnhancedAnalyticsOptOut: ReturnType<typeof vi.fn>;
38
+ handleToggleSessionReplayOptOut: ReturnType<typeof vi.fn>;
39
+ };
40
+ AnalyticsPreferencesProvider: ({ children }: {
41
+ children: React.ReactNode;
42
+ }) => React.ReactNode;
43
+ }
44
+ /**
45
+ * Creates a mock for AnalyticsPreferencesProvider.
46
+ * Used by vitest.setup.ts for global mocking.
47
+ */
48
+ export declare function createAnalyticsPreferencesProviderMock(overrides?: {
49
+ isOptedInToAnalytics?: boolean;
50
+ isOptedInToEnhancedAnalytics?: boolean;
51
+ isOptedInToSessionReplay?: boolean;
52
+ }): AnalyticsPreferencesMock;
53
+ /**
54
+ * Creates a mock for AnalyticsPreferencesProvider with dynamic values.
55
+ * Use this when you need to change mock values during test execution.
56
+ *
57
+ * @example
58
+ * // For dynamic isOptedInToAnalytics:
59
+ * let mockIsOptedIn = false;
60
+ * vi.mock('../ui/Toolbar/context/telemetry/AnalyticsPreferencesProvider', async () => {
61
+ * const { createDynamicAnalyticsPreferencesProviderMock } = await import('./mocks/providers');
62
+ * return createDynamicAnalyticsPreferencesProviderMock({ getIsOptedInToAnalytics: () => mockIsOptedIn });
63
+ * });
64
+ *
65
+ * // For dynamic isOptedInToEnhancedAnalytics:
66
+ * vi.mock('../ui/Toolbar/context/telemetry/AnalyticsPreferencesProvider', async () => {
67
+ * const { createDynamicAnalyticsPreferencesProviderMock } = await import('./mocks/providers');
68
+ * return createDynamicAnalyticsPreferencesProviderMock({ getIsOptedInToEnhancedAnalytics: () => mockValue });
69
+ * });
70
+ *
71
+ * // In tests:
72
+ * mockIsOptedIn = true; // Changes mock behavior
73
+ */
74
+ export declare function createDynamicAnalyticsPreferencesProviderMock(options: {
75
+ getIsOptedInToAnalytics?: () => boolean;
76
+ getIsOptedInToEnhancedAnalytics?: () => boolean;
77
+ getIsOptedInToSessionReplay?: () => boolean;
78
+ }): AnalyticsPreferencesMock;
79
+ /**
80
+ * Creates a mock for InternalClientProvider with a customizable mock LDClient.
81
+ *
82
+ * @example
83
+ * const mockLDClient = { track: vi.fn(), ... };
84
+ * vi.mock('../../ui/Toolbar/context/telemetry/InternalClientProvider', () =>
85
+ * createInternalClientProviderMock(mockLDClient)
86
+ * );
87
+ */
88
+ export declare function createInternalClientProviderMock(mockClient?: Record<string, unknown>): {
89
+ useInternalClient: () => {
90
+ client: Record<string, unknown> | null;
91
+ loading: boolean;
92
+ error: null;
93
+ updateContext: ReturnType<typeof vi.fn>;
94
+ };
95
+ InternalClientProvider: ({ children }: {
96
+ children: React.ReactNode;
97
+ }) => React.ReactNode;
98
+ };
99
+ /**
100
+ * Creates a mock for DevServerProvider.
101
+ *
102
+ * @example
103
+ * vi.mock('../../ui/Toolbar/context/DevServerProvider', () =>
104
+ * createDevServerProviderMock({ connectionStatus: 'connected' })
105
+ * );
106
+ */
107
+ export declare function createDevServerProviderMock(overrides?: {
108
+ sourceEnvironmentKey?: string;
109
+ connectionStatus?: string;
110
+ flags?: Record<string, unknown>;
111
+ isLoading?: boolean;
112
+ error?: Error | null;
113
+ }): {
114
+ useDevServerContext: () => {
115
+ state: {
116
+ sourceEnvironmentKey: string;
117
+ connectionStatus: string;
118
+ flags: Record<string, unknown>;
119
+ lastSyncTime: number;
120
+ isLoading: boolean;
121
+ error: Error | null;
122
+ };
123
+ setOverride: ReturnType<typeof vi.fn>;
124
+ clearOverride: ReturnType<typeof vi.fn>;
125
+ clearAllOverrides: ReturnType<typeof vi.fn>;
126
+ refresh: ReturnType<typeof vi.fn>;
127
+ };
128
+ DevServerProvider: ({ children }: {
129
+ children: React.ReactNode;
130
+ }) => React.ReactNode;
131
+ };
132
+ export {};
@@ -0,0 +1,5 @@
1
+ /**
2
+ * A toast component that appears at the bottom of the toolbar asking users
3
+ * to opt into analytics. Only shows the first time the toolbar is opened.
4
+ */
5
+ export declare function AnalyticsConsentToast(): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,13 @@
1
+ export declare const overlay: string;
2
+ export declare const container: string;
3
+ export declare const infoIcon: string;
4
+ export declare const content: string;
5
+ export declare const textContent: string;
6
+ export declare const title: string;
7
+ export declare const description: string;
8
+ export declare const privacyLink: string;
9
+ export declare const actions: string;
10
+ export declare const button: string;
11
+ export declare const primaryButton: string;
12
+ export declare const closeButton: string;
13
+ export declare const closeIcon: string;
@@ -0,0 +1 @@
1
+ export { AnalyticsConsentToast } from './AnalyticsConsentToast';
@@ -1,6 +1,6 @@
1
- import { Context } from '../../../types/ldApi';
1
+ import type { LDContext } from 'launchdarkly-js-client-sdk';
2
2
  interface ContextItemProps {
3
- context: Context;
3
+ context: LDContext;
4
4
  isActiveContext: boolean;
5
5
  handleHeightChange?: (index: number, height: number) => void;
6
6
  index?: number;
@@ -0,0 +1 @@
1
+ export declare function PrivacySettings(): import("react/jsx-runtime").JSX.Element;
@@ -1,5 +1,5 @@
1
- export * from './SettingsContent';
2
1
  export * from './GeneralSettings';
2
+ export * from './Privacy/PrivacySettings';
3
3
  export * from './SettingsSection';
4
4
  export * from './SettingsItem';
5
5
  export * from './ProjectSelector';
@@ -1,6 +1,6 @@
1
1
  export type FlagsSubtab = 'flags' | 'contexts';
2
2
  export type MonitoringSubtab = 'events';
3
- export type SettingsSubtab = 'general';
3
+ export type SettingsSubtab = 'general' | 'privacy';
4
4
  export type InteractiveSubtab = 'workflows';
5
5
  export type SubTab = FlagsSubtab | MonitoringSubtab | SettingsSubtab | InteractiveSubtab;
6
6
  export interface TabConfig {
@@ -1,13 +1,13 @@
1
- import { Context } from '../../types/ldApi';
1
+ import type { LDContext } from 'launchdarkly-js-client-sdk';
2
2
  interface ContextsContextType {
3
- contexts: Context[];
3
+ contexts: LDContext[];
4
4
  filter: string;
5
5
  setFilter: (filter: string) => void;
6
- addContext: (context: Context) => void;
7
- removeContext: (id: string) => void;
8
- updateContext: (id: string, newContext: Context) => void;
9
- setContext: (context: Context) => Promise<void>;
10
- activeContext: Context | null;
6
+ addContext: (context: LDContext) => void;
7
+ removeContext: (contextId: string) => void;
8
+ updateContext: (contextId: string, newContext: LDContext) => void;
9
+ setContext: (context: LDContext) => Promise<void>;
10
+ activeContext: LDContext | null;
11
11
  isAddFormOpen: boolean;
12
12
  setIsAddFormOpen: (isOpen: boolean) => void;
13
13
  }
@@ -10,6 +10,9 @@ export interface ToolbarStateContextValue {
10
10
  reloadOnFlagChangeIsEnabled: boolean;
11
11
  isAutoCollapseEnabled: boolean;
12
12
  mode: ToolbarMode;
13
+ isOptedInToAnalytics: boolean;
14
+ isOptedInToEnhancedAnalytics: boolean;
15
+ isOptedInToSessionReplay: boolean;
13
16
  toolbarRef: React.RefObject<HTMLDivElement | null>;
14
17
  handleTabChange: (tabId: string) => void;
15
18
  handleClose: () => void;
@@ -19,6 +22,9 @@ export interface ToolbarStateContextValue {
19
22
  handleCircleClick: () => void;
20
23
  setIsAnimating: Dispatch<SetStateAction<boolean>>;
21
24
  setSearchIsExpanded: Dispatch<SetStateAction<boolean>>;
25
+ handleToggleAnalyticsOptOut: (enabled: boolean) => void;
26
+ handleToggleEnhancedAnalyticsOptOut: (enabled: boolean) => void;
27
+ handleToggleSessionReplayOptOut: (enabled: boolean) => void;
22
28
  }
23
29
  export interface ToolbarStateProviderProps {
24
30
  children: ReactNode;
@@ -0,0 +1,14 @@
1
+ import { ReactNode } from 'react';
2
+ export interface AnalyticsPreferencesContextValue {
3
+ isOptedInToAnalytics: boolean;
4
+ isOptedInToEnhancedAnalytics: boolean;
5
+ isOptedInToSessionReplay: boolean;
6
+ handleToggleAnalyticsOptOut: (enabled: boolean) => void;
7
+ handleToggleEnhancedAnalyticsOptOut: (enabled: boolean) => void;
8
+ handleToggleSessionReplayOptOut: (enabled: boolean) => void;
9
+ }
10
+ export interface AnalyticsPreferencesProviderProps {
11
+ children: ReactNode;
12
+ }
13
+ export declare function AnalyticsPreferencesProvider({ children }: AnalyticsPreferencesProviderProps): import("react/jsx-runtime").JSX.Element;
14
+ export declare function useAnalyticsPreferences(): AnalyticsPreferencesContextValue;
@@ -1,3 +1,4 @@
1
1
  export * from './TelemetryBundleProvider';
2
2
  export * from './AnalyticsProvider';
3
3
  export * from './InternalClientProvider';
4
+ export * from './AnalyticsPreferencesProvider';
@@ -38,10 +38,3 @@ export interface FlagsPaginationParams {
38
38
  offset?: number;
39
39
  query?: string;
40
40
  }
41
- export interface Context {
42
- id: string;
43
- kind: string;
44
- key?: string;
45
- name: string;
46
- anonymous?: boolean;
47
- }
@@ -1,15 +1,34 @@
1
1
  import type { LDContext } from 'launchdarkly-js-client-sdk';
2
- import { Context } from '../types/ldApi';
3
2
  /**
4
- * Generates a unique ID for a context
3
+ * Generates a deterministic ID for a context based on its properties.
4
+ * The same context object will always produce the same ID.
5
+ * This is used to compare contexts for equality.
6
+ *
7
+ * For multi-kind contexts, the deprecated 'user' kind is excluded from
8
+ * hash generation to prevent false duplicate detection.
9
+ *
10
+ * Results are cached to minimize computation during React renders.
5
11
  */
6
- export declare function generateContextId(): string;
12
+ export declare function generateContextId(context?: LDContext | null): string;
7
13
  /**
8
- * Extracts normalized context info from an LDContext
9
- * Handles both single-kind and multi-kind contexts
14
+ * Check if two contexts are the same using their generated IDs
10
15
  */
11
- export declare function extractContextInfo(ldContext: LDContext | undefined): Context | null;
16
+ export declare function areContextsEqual(a: LDContext | null | undefined, b: LDContext | null | undefined): boolean;
12
17
  /**
13
- * Helper function to check if a given context matches the current SDK context
18
+ * Gets the kind of an LDContext (handles multi-kind and single-kind)
14
19
  */
15
- export declare function isCurrentContext(currentContext: Context | null, contextKind: string, contextKey: string): boolean;
20
+ export declare function getContextKind(context: LDContext | null | undefined): string;
21
+ /**
22
+ * Gets the key of an LDContext
23
+ * For multi-kind contexts, returns the key of the first nested context
24
+ */
25
+ export declare function getContextKey(context: LDContext | null | undefined): string;
26
+ /**
27
+ * Gets a display name for an LDContext
28
+ * Uses name if available, otherwise falls back to key
29
+ */
30
+ export declare function getContextDisplayName(context: LDContext | null | undefined): string;
31
+ /**
32
+ * Check if a context is anonymous
33
+ */
34
+ export declare function isContextAnonymous(context: LDContext | null | undefined): boolean;
@@ -1,12 +1,11 @@
1
- import { Context } from '../types/ldApi';
1
+ import type { LDContext } from 'launchdarkly-js-client-sdk';
2
2
  import { ToolbarPosition } from '../types/toolbar';
3
3
  export declare const TOOLBAR_STORAGE_KEYS: {
4
- readonly ENVIRONMENT: "ld-toolbar-environment";
5
4
  readonly SETTINGS: "ld-toolbar-settings";
6
5
  readonly DISABLED: "ld-toolbar-disabled";
7
- readonly PROJECT: "ld-toolbar-project";
8
6
  readonly STARRED_FLAGS: "ld-toolbar-starred-flags";
9
7
  readonly MCP_ALERT_DISMISSED: "ld-toolbar-mcp-alert-dismissed";
8
+ readonly ANALYTICS_CONSENT_SHOWN: "ld-toolbar-analytics-consent-shown";
10
9
  readonly CONTEXTS: "ld-toolbar-contexts";
11
10
  readonly ACTIVE_CONTEXT: "ld-toolbar-active-context";
12
11
  };
@@ -17,6 +16,9 @@ export interface ToolbarSettings {
17
16
  reloadOnFlagChange: boolean;
18
17
  autoCollapse: boolean;
19
18
  preferredIde: PreferredIde;
19
+ isOptedInToAnalytics: boolean;
20
+ isOptedInToEnhancedAnalytics: boolean;
21
+ isOptedInToSessionReplay: boolean;
20
22
  }
21
23
  export declare const DEFAULT_SETTINGS: ToolbarSettings;
22
24
  export declare function saveToolbarPosition(position: ToolbarPosition): void;
@@ -31,7 +33,15 @@ export declare function savePreferredIde(ide: PreferredIde): void;
31
33
  export declare function loadPreferredIde(): PreferredIde;
32
34
  export declare function saveMCPAlertDismissed(dismissed: boolean): void;
33
35
  export declare function loadMCPAlertDismissed(): boolean;
34
- export declare function loadContexts(): Array<Context>;
35
- export declare function saveContexts(contexts: Array<Context>): void;
36
- export declare function loadActiveContext(): Context | null;
37
- export declare function saveActiveContext(context: Context | null): void;
36
+ export declare function loadContexts(): Array<LDContext>;
37
+ export declare function saveContexts(contexts: Array<LDContext>): void;
38
+ export declare function loadActiveContext(): LDContext | null;
39
+ export declare function saveActiveContext(context: LDContext | null): void;
40
+ export declare function saveIsOptedInToAnalytics(isOptedInToAnalytics: boolean): void;
41
+ export declare function loadIsOptedInToAnalytics(): boolean;
42
+ export declare function saveIsOptedInToEnhancedAnalytics(isOptedInToEnhancedAnalytics: boolean): void;
43
+ export declare function saveIsOptedInToSessionReplay(isOptedInToSessionReplay: boolean): void;
44
+ export declare function loadIsOptedInToSessionReplay(): boolean;
45
+ export declare function loadIsOptedInToEnhancedAnalytics(): boolean;
46
+ export declare function saveAnalyticsConsentShown(shown: boolean): void;
47
+ export declare function loadAnalyticsConsentShown(): boolean;
@@ -8,8 +8,9 @@ export declare const ANALYTICS_EVENT_PREFIX = "ld.toolbar";
8
8
  export declare class ToolbarAnalytics {
9
9
  private ldClient;
10
10
  private mode;
11
+ private isOptedInToAnalytics;
11
12
  private searchDebounceTimer;
12
- constructor(ldClient?: LDClient | null, mode?: ToolbarMode);
13
+ constructor(ldClient?: LDClient | null, mode?: ToolbarMode, isOptedInToAnalytics?: boolean);
13
14
  /**
14
15
  * Internal method to send tracking events
15
16
  */
@@ -30,6 +31,10 @@ export declare class ToolbarAnalytics {
30
31
  * Track toolbar tab navigation
31
32
  */
32
33
  trackTabChange(fromTab: string | null, toTab: string): void;
34
+ /**
35
+ * Track toolbar subtab navigation
36
+ */
37
+ trackSubtabChange(fromSubtab: string | null, toSubtab: string): void;
33
38
  /**
34
39
  * Track search usage
35
40
  */
package/dist/index.cjs CHANGED
@@ -552,7 +552,7 @@ async function lazyLoad(signal, url) {
552
552
  throw new Error(`Could not load LaunchDarkly developer toolbar bundle from ${url}`);
553
553
  }
554
554
  }
555
- var package_namespaceObject = JSON.parse('{"rE":"2.5.3-beta.1"}');
555
+ var package_namespaceObject = JSON.parse('{"rE":"2.6.0-beta.1"}');
556
556
  function useLaunchDarklyToolbar(args) {
557
557
  const { toolbarBundleUrl, enabled, ...initConfig } = args;
558
558
  const configRef = (0, external_react_namespaceObject.useRef)(null);
package/dist/js/index.js CHANGED
@@ -518,7 +518,7 @@ async function lazyLoad(signal, url) {
518
518
  throw new Error(`Could not load LaunchDarkly developer toolbar bundle from ${url}`);
519
519
  }
520
520
  }
521
- var package_namespaceObject = JSON.parse('{"rE":"2.5.3-beta.1"}');
521
+ var package_namespaceObject = JSON.parse('{"rE":"2.6.0-beta.1"}');
522
522
  function useLaunchDarklyToolbar(args) {
523
523
  const { toolbarBundleUrl, enabled, ...initConfig } = args;
524
524
  const configRef = useRef(null);