diff --git a/packages/core/src/api/app/AppBuilder.tsx b/packages/core/src/api/app/AppBuilder.tsx
index bd40565260..6964c45a26 100644
--- a/packages/core/src/api/app/AppBuilder.tsx
+++ b/packages/core/src/api/app/AppBuilder.tsx
@@ -19,7 +19,7 @@ import { Route, Switch, Redirect } from 'react-router-dom';
import { AppContextProvider } from './AppContext';
import { App } from './types';
import BackstagePlugin from '../plugin/Plugin';
-import { FeatureFlagsEntry, FeatureFlagsContextProvider } from './FeatureFlags';
+import { FeatureFlags, FeatureFlagsEntry } from './FeatureFlags';
import {
IconComponent,
SystemIcons,
@@ -102,6 +102,8 @@ export default class AppBuilder {
}
}
+ FeatureFlags.registeredFeatureFlags = registeredFeatureFlags;
+
routes.push(
,
);
@@ -117,13 +119,6 @@ export default class AppBuilder {
rendered = ;
}
- rendered = (
-
- );
-
return () => ;
}
}
diff --git a/packages/core/src/api/app/FeatureFlags.test.tsx b/packages/core/src/api/app/FeatureFlags.test.tsx
index b803b27b3a..cb5be32394 100644
--- a/packages/core/src/api/app/FeatureFlags.test.tsx
+++ b/packages/core/src/api/app/FeatureFlags.test.tsx
@@ -16,12 +16,7 @@
import React, { ReactNode, useContext, useEffect, useRef } from 'react';
import { render } from '@testing-library/react';
-import {
- FeatureFlags,
- FeatureFlagsEntry,
- FeatureFlagsContext,
- FeatureFlagsContextProvider,
-} from './FeatureFlags';
+import { FeatureFlags } from './FeatureFlags';
import { FeatureFlagState } from '../apis/definitions/featureFlags';
function useRenderCount() {
@@ -37,13 +32,7 @@ function withFeatureFlags(
{ name: 'feature-flag-two', pluginId: 'plugin-two' },
{ name: 'feature-flag-three', pluginId: 'plugin-two' },
]),
-) {
- return (
-
- {children}
-
- );
-}
+) {}
describe('FeatureFlags', () => {
beforeEach(() => {
@@ -255,121 +244,3 @@ describe('FeatureFlags', () => {
});
});
});
-
-describe('FeatureFlagsContext', () => {
- beforeEach(() => {
- window.localStorage.clear();
- });
-
- it('returns an empty set without the context', () => {
- const Component = () => {
- const { featureFlags } = useContext(FeatureFlagsContext);
- expect(featureFlags).toEqual(new Set());
- return null;
- };
-
- render();
- });
-
- it('returns a set of registered feature flags', () => {
- expect.assertions(2);
-
- const mockFeatureFlags = new Set([
- { name: 'feature-flag-one', pluginId: 'plugin-one' },
- { name: 'feature-flag-two', pluginId: 'plugin-two' },
- ]);
-
- const Component = () => {
- const { featureFlags } = useContext(FeatureFlagsContext);
- expect(featureFlags).toEqual(mockFeatureFlags);
- return null;
- };
-
- render(withFeatureFlags(, mockFeatureFlags));
- });
-
- it('returns a set of user enabled feature flags', () => {
- expect.assertions(1);
-
- window.localStorage.setItem(
- 'featureFlags',
- JSON.stringify({
- 'feature-flag-one': true,
- 'feature-flag-three': true,
- }),
- );
-
- const Component = () => {
- const { enabledFeatureFlags } = useContext(FeatureFlagsContext);
-
- if (enabledFeatureFlags.size > 0) {
- expect(enabledFeatureFlags).toEqual(
- new Set(['feature-flag-one', 'feature-flag-three']),
- );
- }
-
- return null;
- };
-
- render(withFeatureFlags());
- });
-
- it('correctly re-renders when calling refreshEnabledFeatureFlags', () => {
- // First is the initial context
- // Second is the context with the state from FeatureFlagsContextProvider
- // Third is from calling refreshEnabledFeatureFlags
- expect.assertions(3);
-
- const FirstRender = ({ context: { enabledFeatureFlags } }) => {
- useEffect(() => {
- expect(enabledFeatureFlags).toEqual(new Set());
- }, []);
- return null;
- };
-
- const SecondRender = ({
- context: { enabledFeatureFlags, refreshEnabledFeatureFlags },
- }) => {
- useEffect(() => {
- expect(enabledFeatureFlags).toEqual(new Set());
-
- // Change localStorage
- window.localStorage.setItem(
- 'featureFlags',
- JSON.stringify({
- 'feature-flag-one': true,
- 'feature-flag-three': true,
- }),
- );
-
- // Refresh
- refreshEnabledFeatureFlags();
- }, []);
-
- return null;
- };
-
- const ThirdRender = ({ context: { enabledFeatureFlags } }) => {
- useEffect(() => {
- expect(enabledFeatureFlags).toEqual(
- new Set(['feature-flag-one', 'feature-flag-three']),
- );
- }, []);
-
- return null;
- };
-
- const Component = () => {
- const context = useContext(FeatureFlagsContext);
- const renderCount = useRenderCount();
-
- if (renderCount === 0) return ;
- if (renderCount === 1) return ;
- if (renderCount === 2) return ;
-
- return null;
- };
-
- render(withFeatureFlags());
- });
-});
diff --git a/packages/core/src/api/app/FeatureFlags.tsx b/packages/core/src/api/app/FeatureFlags.tsx
index 4f2b7da7d0..172daeb878 100644
--- a/packages/core/src/api/app/FeatureFlags.tsx
+++ b/packages/core/src/api/app/FeatureFlags.tsx
@@ -14,78 +14,17 @@
* limitations under the License.
*/
-import React, {
- ReactNode,
- createContext,
- useContext,
- useState,
- useEffect,
- FC,
-} from 'react';
import { FeatureFlagName } from '../plugin/types';
import {
FeatureFlagState,
FeatureFlagsApi,
} from '../apis/definitions/featureFlags';
-/**
- * Create a shared React context for Feature Flags.
- *
- * This will be used to propagate all available feature flags to
- * Backstage components. This enables viewing all of the available flags.
- */
export interface FeatureFlagsEntry {
pluginId: string;
name: FeatureFlagName;
}
-export interface IFeatureFlagsContext {
- featureFlags: Set;
- enabledFeatureFlags: Set;
- refreshEnabledFeatureFlags: () => void;
-}
-
-export const FeatureFlagsContext = createContext({
- featureFlags: new Set(),
- enabledFeatureFlags: new Set(),
- refreshEnabledFeatureFlags: () => {
- throw new Error(
- 'The refreshEnabledFeatureFlags method is not implemented as it is not called from within the FeatureFlagsContext context within React. ' +
- 'See the Backstage documentation for examples on how to use the Feature Flags API.',
- );
- },
-});
-
-export const FeatureFlagsContextProvider: FC<{
- featureFlags: Set;
- children: ReactNode;
-}> = ({ featureFlags, children }) => {
- const [enabledFeatureFlags, setEnabledFeatureFlags] = useState<
- Set
- >(new Set());
-
- const refreshEnabledFeatureFlags = () => {
- // eslint-disable-next-line no-use-before-define
- setEnabledFeatureFlags(FeatureFlags.getEnabledFeatureFlags());
- };
-
- // Initially populate our setEnabledFeatureFlags
- useEffect(() => {
- refreshEnabledFeatureFlags();
- }, []);
-
- return (
-
- );
-};
-
/**
* Create the FeatureFlags implementation based on the API.
*/
@@ -95,46 +34,9 @@ export const FeatureFlagsContextProvider: FC<{
class FeatureFlagsImpl implements FeatureFlagsApi {
private readonly localStorageKey = 'featureFlags';
- private get(
- enabledFeatureFlags: Set,
- name: FeatureFlagName,
- ): FeatureFlagState {
- if (enabledFeatureFlags.has(name)) {
- return FeatureFlagState.Enabled;
- }
+ public constructor(public registeredFeatureFlags: FeatureFlagsEntry[] = []) {}
- return FeatureFlagState.NotEnabled;
- }
-
- private set(name: FeatureFlagName, state: FeatureFlagState): void {
- const errors = this.checkFeatureFlagNameErrors(name);
- const flags = this.getEnabledFeatureFlags();
-
- if (errors.length > 0) {
- throw new Error(errors[0]);
- }
-
- if (state === FeatureFlagState.Enabled) {
- flags.add(name);
- } else if (state === FeatureFlagState.NotEnabled) {
- flags.delete(name);
- } else {
- throw new Error(
- 'The `state` argument requires a recognized value from the FeatureFlagState enum. ' +
- 'Please check the Backstage documentation to see all the available options.' +
- 'Example values: FeatureFlagState.NotEnabled, FeatureFlagState.Enabled',
- );
- }
-
- window.localStorage.setItem(
- this.localStorageKey,
- JSON.stringify(
- [...flags].reduce((list, flag) => ({ ...list, [flag]: true }), {}),
- ),
- );
- }
-
- getEnabledFeatureFlags(): Set {
+ private getUserEnabledFeatureFlags(): Set {
if (!('localStorage' in window)) {
throw new Error(
'Feature Flags are not supported on browsers without the Local Storage API',
diff --git a/packages/core/src/api/index.ts b/packages/core/src/api/index.ts
index 4c77e94984..e60a7efa18 100644
--- a/packages/core/src/api/index.ts
+++ b/packages/core/src/api/index.ts
@@ -16,9 +16,5 @@
export * from './api';
export * from './apis';
-export {
- FeatureFlags,
- FeatureFlagsContext,
- FeatureFlagsContextProvider,
-} from './app/FeatureFlags';
+export { FeatureFlags } from './app/FeatureFlags';
export { useApp } from './app/AppContext';