feat(ui): add summary page feature flag to local storage and make it generic (#14807)

Co-authored-by: Chris Collins <chriscollins3456@gmail.com>
This commit is contained in:
purnimagarg1 2025-09-19 00:00:01 +05:30 committed by GitHub
parent 7768abc6eb
commit d268d14ee2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 156 additions and 13 deletions

View File

@ -64,11 +64,4 @@ describe('useShowAssetSummaryPage', () => {
const { result } = renderHook(() => useShowAssetSummaryPage());
expect(result.current).toBe(false);
});
it('should return false when appConfig is undefined', () => {
mockedUseAppConfig.mockReturnValue(undefined as any);
const { result } = renderHook(() => useShowAssetSummaryPage());
expect(result.current).toBe(false);
});
});

View File

@ -1,9 +1,6 @@
import { useAppConfig } from '@app/useAppConfig';
import { useFeatureFlag } from '@app/sharedV2/hooks/useFeatureFlag';
export function useShowAssetSummaryPage() {
const appConfig = useAppConfig();
if (appConfig?.loaded) {
return appConfig.config.featureFlags.assetSummaryPageV1;
}
return false;
const assetSummaryPageV1 = useFeatureFlag('assetSummaryPageV1');
return assetSummaryPageV1;
}

View File

@ -0,0 +1,122 @@
import { renderHook } from '@testing-library/react-hooks';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { loadFeatureFlagFromLocalStorage, useFeatureFlag } from '@app/sharedV2/hooks/useFeatureFlag';
import { useAppConfig } from '@app/useAppConfig';
// Mocks
const localStorageMock = (() => {
let store: Record<string, string> = {};
return {
getItem: (key: string) => store[key] ?? null,
setItem: (key: string, value: string) => {
store[key] = value;
},
clear: () => {
store = {};
},
};
})();
Object.defineProperty(window, 'localStorage', { value: localStorageMock });
vi.mock('@app/useAppConfig', () => ({
useAppConfig: vi.fn(),
}));
describe('useFeatureFlag', () => {
beforeEach(() => {
vi.clearAllMocks();
localStorage.clear();
});
it('should return flag value from appConfig and set localStorage when config loaded', () => {
(useAppConfig as any).mockReturnValue({
loaded: true,
config: { featureFlags: { myFlag: true } },
});
const { result } = renderHook(() => useFeatureFlag('myFlag'));
expect(result.current).toBe(true);
expect(localStorage.getItem('myFlag')).toBe('true');
});
it('should not set localStorage if value is unchanged', () => {
localStorage.setItem('myFlag', 'true');
(useAppConfig as any).mockReturnValue({
loaded: true,
config: { featureFlags: { myFlag: true } },
});
const setItemSpy = vi.spyOn(localStorage, 'setItem');
renderHook(() => useFeatureFlag('myFlag'));
expect(setItemSpy).not.toHaveBeenCalled();
});
it('should return value from localStorage if config not loaded', () => {
localStorage.setItem('myFlag', 'true');
(useAppConfig as any).mockReturnValue({
loaded: false,
config: { featureFlags: { myFlag: false } },
});
const { result } = renderHook(() => useFeatureFlag('myFlag'));
expect(result.current).toBe(true);
});
it('should return false if localStorage has no value and config not loaded', () => {
(useAppConfig as any).mockReturnValue({
loaded: false,
config: { featureFlags: {} },
});
const { result } = renderHook(() => useFeatureFlag('myFlag'));
expect(result.current).toBe(false);
});
it('should return false if feature flag is undefined in loaded config', () => {
(useAppConfig as any).mockReturnValue({
loaded: true,
config: { featureFlags: {} },
});
const { result } = renderHook(() => useFeatureFlag('myFlag'));
expect(result.current).toBe(undefined);
});
it('should store false value in localStorage if feature flag is false and config loaded', () => {
(useAppConfig as any).mockReturnValue({
loaded: true,
config: { featureFlags: { myFlag: false } },
});
renderHook(() => useFeatureFlag('myFlag'));
expect(localStorage.getItem('myFlag')).toBe('false');
});
});
describe('loadFeatureFlagFromLocalStorage', () => {
beforeEach(() => {
localStorage.clear();
});
it('should return true if localStorage value is true', () => {
localStorage.setItem('someFlag', 'true');
expect(loadFeatureFlagFromLocalStorage('someFlag')).toBe(true);
});
it('should return false if localStorage value is false', () => {
localStorage.setItem('someFlag', 'false');
expect(loadFeatureFlagFromLocalStorage('someFlag')).toBe(false);
});
it('should return false if localStorage has no key', () => {
expect(loadFeatureFlagFromLocalStorage('someFlag')).toBe(false);
});
});

View File

@ -0,0 +1,31 @@
import { useAppConfig } from '@app/useAppConfig';
export function useFeatureFlag(featureFlagKey: string) {
const appConfig = useAppConfig();
const featureFlagValue = appConfig?.config?.featureFlags?.[featureFlagKey];
if (appConfig.loaded) {
setFeatureFlagInLocalStorage(featureFlagKey, featureFlagValue);
return featureFlagValue;
}
return loadFeatureFlagFromLocalStorage(featureFlagKey);
}
function setFeatureFlagInLocalStorage(flagKey: string, value: boolean) {
const rawValue = localStorage.getItem(flagKey);
const storedValue = rawValue === null ? undefined : rawValue === 'true';
if (rawValue === null || storedValue !== value) {
saveFeatureFlagToLocalStorage(flagKey, value);
}
}
export function loadFeatureFlagFromLocalStorage(flagKey: string): boolean {
return localStorage.getItem(flagKey) === 'true';
}
function saveFeatureFlagToLocalStorage(flagKey: string, value: boolean) {
localStorage.setItem(flagKey, String(value));
}