Skip to content

Commit e15c9fa

Browse files
authored
chore: [REL-10965] toolbar redesign part two (#362)
* feat: add branching path based on feature flag * refactor: update toolbar state to be context instead of hook * feat: scaffold out new layout * feat: click to drag + collapse toolbar * feat: add feature flag for the interactive tab * refactor: remove unused icon * feat: add FilterTuneIcon * refactor: move things around * feat: set active tab for new UI * feat: scaffold out dynamic content renderer * feat: a whole bunch more refactoring * feat: even more refactoring * feat: clean up new Settings tab * feat: work on Flag item styling * refactor: clean up placeholders * refactor: remove placeholder Toggle component * feat: scaffold out rough new Events/Monitoring tab * feat: Add EventsContent * refactor: remove extra dummy data * refactor: clean up * fix: fix bug * fix: set default tab if missing * fix: fix failing e2e tests * test: add tests * feat: show all icons, disable based on feature flag * chore: run pnpm format * feat: add new dropdown for sub-tab selection * chore: run pnpm format * fix: remove unused import * refactor: restructure project (#361) * refactor: minor updates * feat: wire up search functionality * test: add tests for TabSearchProvider * test: fix tests * feat: filter events based on search term with new UI * feat: setting filtering + tests * feat: pull flags dynamically for new ui * feat: get closer to matching parity for feature flag list * chore: run pnpm format * feat: almost get things working * feat: get height working for JSON * feat: add override indicator next to flag name * feat: more styling updates * feat: pretty much matching parity now (sdk mode) * fix: fix dev server mode * test: add tests * refactor: clean up * refactor: clean up * refactor: consistent components * fix: fix failing tests * refactor: address some PR feedback * fix: fix issue * fix: actually fix this time * refactor: update border colors * refactor: big refactor of context definitions * refactor: final clean up * refactor: providers throw errors if used improperly
1 parent 540b8a1 commit e15c9fa

File tree

86 files changed

+3395
-524
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+3395
-524
lines changed

packages/toolbar/src/core/services/DevServerClient.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1+
import { ApiVariation } from '../ui/Toolbar/types/ldApi';
2+
13
export interface DevServerProjectResponse {
24
_lastSyncedFromSource: number;
3-
availableVariations: Record<string, Variation[]>;
5+
availableVariations: Record<string, ApiVariation[]>;
46
flagsState: Record<string, FlagState>;
57
overrides: Record<string, Override>;
68
sourceEnvironmentKey: string;
79
}
810

9-
export interface Variation {
10-
_id: string;
11-
name: string;
12-
value: any;
13-
}
14-
1511
export interface FlagState {
1612
value: any;
1713
version: number;

packages/toolbar/src/core/services/FlagStateManager.ts

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { DevServerClient, Variation } from './DevServerClient';
1+
import { DevServerClient } from './DevServerClient';
22
import { EnhancedFlag } from '../types/devServer';
3-
import { ApiFlag } from '../ui/Toolbar/types/ldApi';
3+
import { ApiFlag, ApiVariation } from '../ui/Toolbar/types/ldApi';
44

55
export class FlagStateManager {
66
private devServerClient: DevServerClient;
@@ -39,7 +39,7 @@ export class FlagStateManager {
3939
isOverridden: !!override,
4040
originalValue: flagState.value,
4141
availableVariations: variations,
42-
type: apiFlag?.kind || this.determineFlagType(variations, currentValue),
42+
type: this.determineFlagType(variations, currentValue),
4343
sourceEnvironment: devServerData.sourceEnvironmentKey,
4444
enabled: flagState.value !== null && flagState.value !== undefined,
4545
};
@@ -57,33 +57,21 @@ export class FlagStateManager {
5757
}
5858

5959
private determineFlagType(
60-
variations: Variation[],
60+
variations: ApiVariation[],
6161
currentValue: any,
6262
): 'boolean' | 'multivariate' | 'string' | 'number' | 'object' {
6363
// If we have exactly 2 variations and they're both boolean, it's a boolean flag
6464
if (variations.length === 2 && variations.every((v) => typeof v.value === 'boolean')) {
6565
return 'boolean';
6666
}
6767

68-
// If we have more than 2 variations, it's multivariate
69-
if (variations.length > 2) {
68+
if (variations.length >= 2 && !variations.some((v) => typeof v.value === 'object')) {
7069
return 'multivariate';
7170
}
7271

73-
// Determine type based on current value
74-
if (typeof currentValue === 'string') {
75-
return 'string';
76-
}
77-
78-
if (typeof currentValue === 'number') {
79-
return 'number';
80-
}
81-
82-
if (typeof currentValue === 'object') {
83-
return 'object';
84-
}
85-
86-
// Default to boolean for simple flags
72+
if (typeof currentValue === 'string') return 'string';
73+
if (typeof currentValue === 'number') return 'number';
74+
if (typeof currentValue === 'object') return 'object';
8775
return 'boolean';
8876
}
8977

packages/toolbar/src/core/tests/ApiProvider.test.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
import { render, screen, waitFor, act } from '@testing-library/react';
22
import { expect, test, describe, vi, beforeEach, afterEach } from 'vitest';
3-
import { ApiProvider, useApi } from '../ui/Toolbar/context/ApiProvider';
4-
import { getErrorTopic, getResponseTopic, IFRAME_COMMANDS, IFRAME_EVENTS } from '../ui/Toolbar/context/IFrameProvider';
3+
import { ApiProvider, useApi } from '../ui/Toolbar/context/api/ApiProvider';
4+
import {
5+
getErrorTopic,
6+
getResponseTopic,
7+
IFRAME_COMMANDS,
8+
IFRAME_EVENTS,
9+
} from '../ui/Toolbar/context/api/IFrameProvider';
510
import '@testing-library/jest-dom/vitest';
611
import React from 'react';
712

813
// Mock the AuthProvider
9-
vi.mock('../ui/Toolbar/context/AuthProvider', () => ({
14+
vi.mock('../ui/Toolbar/context/api/AuthProvider', () => ({
1015
useAuthContext: vi.fn(),
1116
}));
1217

1318
// Mock the IFrameProvider
14-
vi.mock('../ui/Toolbar/context/IFrameProvider', () => ({
19+
vi.mock('../ui/Toolbar/context/api/IFrameProvider', () => ({
1520
IFRAME_COMMANDS: {
1621
GET_PROJECTS: 'GET_PROJECTS',
1722
GET_FLAGS: 'GET_FLAGS',
@@ -29,7 +34,7 @@ vi.mock('../ui/Toolbar/context/IFrameProvider', () => ({
2934
}));
3035

3136
// Mock the AnalyticsProvider
32-
vi.mock('../ui/Toolbar/context/AnalyticsProvider', () => ({
37+
vi.mock('../ui/Toolbar/context/telemetry/AnalyticsProvider', () => ({
3338
useAnalytics: vi.fn().mockReturnValue({
3439
trackLoginSuccess: vi.fn(),
3540
trackLoginCancelled: vi.fn(),
@@ -38,8 +43,8 @@ vi.mock('../ui/Toolbar/context/AnalyticsProvider', () => ({
3843
}),
3944
}));
4045

41-
import { useAuthContext } from '../ui/Toolbar/context/AuthProvider';
42-
import { useIFrameContext } from '../ui/Toolbar/context/IFrameProvider';
46+
import { useAuthContext } from '../ui/Toolbar/context/api/AuthProvider';
47+
import { useIFrameContext } from '../ui/Toolbar/context/api/IFrameProvider';
4348

4449
// Test component that uses the API context
4550
function TestConsumer() {

packages/toolbar/src/core/tests/AuthenticationModal.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import { render, screen } from '@testing-library/react';
33
import { describe, it, expect, vi, beforeEach } from 'vitest';
44
import { AuthenticationModal } from '../ui/Toolbar/components/AuthenticationModal/AuthenticationModal';
5-
import { IFrameProvider } from '../ui/Toolbar/context/IFrameProvider';
5+
import { IFrameProvider } from '../ui/Toolbar/context/api/IFrameProvider';
66

77
// Mock the oauthPopup utility
88
vi.mock('../ui/Toolbar/utils/oauthPopup', () => ({
@@ -17,7 +17,7 @@ const mockAuthContext = {
1717
setAuthenticating: vi.fn(),
1818
};
1919

20-
vi.mock('../ui/Toolbar/context/AuthProvider', () => ({
20+
vi.mock('../ui/Toolbar/context/api/AuthProvider', () => ({
2121
useAuthContext: () => mockAuthContext,
2222
AuthProvider: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
2323
}));

packages/toolbar/src/core/tests/DevServerProvider.test.tsx

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,43 @@ vi.mock('../services/FlagStateManager', () => {
5454
// Create mock for getProjects that can be overridden in tests
5555
const mockGetProjects = vi.fn().mockResolvedValue([{ key: 'test-project', name: 'Test Project' }]);
5656
const mockProjectKey = { current: 'test-project' };
57+
const mockGetProjectFlags = vi.fn().mockResolvedValue({ items: [] });
5758

58-
// Mock the ProjectProvider
59-
vi.mock('../ui/Toolbar/context/ProjectProvider', () => ({
60-
ProjectProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
59+
// Mock the api module which exports all API-related context
60+
vi.mock('../ui/Toolbar/context/api', () => ({
6161
useProjectContext: () => ({
6262
projectKey: mockProjectKey.current,
6363
projects: [{ key: 'test-project', name: 'Test Project' }],
6464
getProjects: mockGetProjects,
6565
loading: false,
6666
error: null,
6767
}),
68+
useFlagsContext: () => ({
69+
flags: [],
70+
loading: false,
71+
getProjectFlags: mockGetProjectFlags,
72+
}),
73+
useApi: () => ({
74+
apiReady: true,
75+
getFlag: vi.fn(),
76+
getProjects: vi.fn(),
77+
getFlags: vi.fn(),
78+
}),
6879
}));
6980

70-
// Create mock for getProjectFlags that we can track
71-
const mockGetProjectFlags = vi.fn().mockResolvedValue({ items: [] });
81+
// Also mock the individual files for direct imports
82+
vi.mock('../ui/Toolbar/context/api/ProjectProvider', () => ({
83+
ProjectProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
84+
useProjectContext: () => ({
85+
projectKey: mockProjectKey.current,
86+
projects: [{ key: 'test-project', name: 'Test Project' }],
87+
getProjects: mockGetProjects,
88+
loading: false,
89+
error: null,
90+
}),
91+
}));
7292

73-
// Mock the FlagsProvider
74-
vi.mock('../ui/Toolbar/context/FlagsProvider', () => ({
93+
vi.mock('../ui/Toolbar/context/api/FlagsProvider', () => ({
7594
FlagsProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
7695
useFlagsContext: () => ({
7796
flags: [],
@@ -80,6 +99,16 @@ vi.mock('../ui/Toolbar/context/FlagsProvider', () => ({
8099
}),
81100
}));
82101

102+
vi.mock('../ui/Toolbar/context/api/ApiProvider', () => ({
103+
ApiProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
104+
useApi: () => ({
105+
apiReady: true,
106+
getFlag: vi.fn(),
107+
getProjects: vi.fn(),
108+
getFlags: vi.fn(),
109+
}),
110+
}));
111+
83112
// Test component that consumes the context
84113
function TestConsumer() {
85114
const { state, refresh } = useDevServerContext();

packages/toolbar/src/core/tests/ExpandedToolbarContentLegacy.test.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { render, screen, waitFor } from '@testing-library/react';
22
import { expect, test, describe, vi, beforeEach } from 'vitest';
33
import { ExpandedToolbarContentLegacy } from '../ui/Toolbar/components/legacy';
44
import { DevServerProvider } from '../ui/Toolbar/context/DevServerProvider';
5-
import { ToolbarUIProvider } from '../ui/Toolbar/context/ToolbarUIProvider';
6-
import { SearchProvider } from '../ui/Toolbar/context/SearchProvider';
7-
import { AnalyticsProvider } from '../ui/Toolbar/context/AnalyticsProvider';
8-
import { InternalClientProvider } from '../ui/Toolbar/context/InternalClientProvider';
9-
import { ToolbarStateProvider } from '../ui/Toolbar/context/ToolbarStateProvider';
5+
import { ToolbarUIProvider } from '../ui/Toolbar/context/state/ToolbarUIProvider';
6+
import { SearchProvider } from '../ui/Toolbar/context/state/SearchProvider';
7+
import { AnalyticsProvider } from '../ui/Toolbar/context/telemetry/AnalyticsProvider';
8+
import { InternalClientProvider } from '../ui/Toolbar/context/telemetry/InternalClientProvider';
9+
import { ToolbarStateProvider } from '../ui/Toolbar/context/state/ToolbarStateProvider';
1010
import { StarredFlagsProvider } from '../ui/Toolbar/context/StarredFlagsProvider';
11-
import { PluginsProvider } from '../ui/Toolbar/context/PluginsProvider';
11+
import { PluginsProvider } from '../ui/Toolbar/context/state/PluginsProvider';
1212
import { IEventInterceptionPlugin, IFlagOverridePlugin } from '../../types';
1313
import '@testing-library/jest-dom/vitest';
1414
import React from 'react';
@@ -60,7 +60,7 @@ vi.mock('../services/FlagStateManager', () => {
6060
});
6161

6262
// Mock the AuthProvider to return authenticated state
63-
vi.mock('../ui/Toolbar/context/AuthProvider', () => ({
63+
vi.mock('../ui/Toolbar/context/api/AuthProvider', () => ({
6464
AuthProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
6565
useAuthContext: () => ({
6666
authenticated: true,
@@ -71,7 +71,7 @@ vi.mock('../ui/Toolbar/context/AuthProvider', () => ({
7171
}));
7272

7373
// Mock the IFrameProvider
74-
vi.mock('../ui/Toolbar/context/IFrameProvider', () => ({
74+
vi.mock('../ui/Toolbar/context/api/IFrameProvider', () => ({
7575
IFrameProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
7676
useIFrameContext: () => ({
7777
ref: { current: null },
@@ -80,7 +80,7 @@ vi.mock('../ui/Toolbar/context/IFrameProvider', () => ({
8080
}));
8181

8282
// Mock the ProjectProvider
83-
vi.mock('../ui/Toolbar/context/ProjectProvider', () => ({
83+
vi.mock('../ui/Toolbar/context/api/ProjectProvider', () => ({
8484
ProjectProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
8585
useProjectContext: () => ({
8686
projectKey: 'test-project',
@@ -92,7 +92,7 @@ vi.mock('../ui/Toolbar/context/ProjectProvider', () => ({
9292
}));
9393

9494
// Mock the FlagsProvider
95-
vi.mock('../ui/Toolbar/context/FlagsProvider', () => ({
95+
vi.mock('../ui/Toolbar/context/api/FlagsProvider', () => ({
9696
FlagsProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
9797
useFlagsContext: () => ({
9898
flags: {},
@@ -103,7 +103,7 @@ vi.mock('../ui/Toolbar/context/FlagsProvider', () => ({
103103
}));
104104

105105
// Mock the ActiveTabProvider
106-
vi.mock('../ui/Toolbar/context/ActiveTabProvider', () => ({
106+
vi.mock('../ui/Toolbar/context/state/ActiveTabProvider', () => ({
107107
ActiveTabProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
108108
useActiveTabContext: () => ({
109109
activeTab: 'settings',
@@ -175,8 +175,8 @@ function TestWrapper({
175175
>
176176
<InternalClientProvider>
177177
<AnalyticsProvider>
178-
<ToolbarStateProvider domId="test-toolbar">
179-
<SearchProvider>
178+
<SearchProvider>
179+
<ToolbarStateProvider domId="test-toolbar">
180180
<StarredFlagsProvider>
181181
<PluginsProvider
182182
baseUrl={baseUrl}
@@ -186,8 +186,8 @@ function TestWrapper({
186186
{children}
187187
</PluginsProvider>
188188
</StarredFlagsProvider>
189-
</SearchProvider>
190-
</ToolbarStateProvider>
189+
</ToolbarStateProvider>
190+
</SearchProvider>
191191
</AnalyticsProvider>
192192
</InternalClientProvider>
193193
</DevServerProvider>

packages/toolbar/src/core/tests/FlagSdkOverrideProvider.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const API_FLAGS = [
4141
];
4242

4343
// Mock the FlagsProvider
44-
vi.mock('../ui/Toolbar/context/FlagsProvider', () => ({
44+
vi.mock('../ui/Toolbar/context/api/FlagsProvider', () => ({
4545
FlagsProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
4646
useFlagsContext: () => ({
4747
flags: API_FLAGS,
@@ -247,7 +247,7 @@ describe('FlagSdkOverrideProvider', () => {
247247

248248
test('displays flags from LD client even when API flags are empty', async () => {
249249
// Mock FlagsProvider to return empty flags (simulating API not loaded yet)
250-
const emptyFlagsModule = await import('../ui/Toolbar/context/FlagsProvider');
250+
const emptyFlagsModule = await import('../ui/Toolbar/context/api/FlagsProvider');
251251
vi.spyOn(emptyFlagsModule, 'useFlagsContext').mockReturnValue({
252252
flags: [], // Empty API flags
253253
loading: false,

0 commit comments

Comments
 (0)