feat: allow dynamically enabling and disabling extensions

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>
This commit is contained in:
aramissennyeydd
2026-03-08 18:08:09 -04:00
committed by Patrik Oldsberg
parent 00381fa7b2
commit 25b7ddd664
23 changed files with 458 additions and 21 deletions
+206 -2
View File
@@ -65,7 +65,8 @@ const IndexPage = PageBlueprint.make({
const page1Link = useRouteRef(page1RouteRef);
return (
<div>
op
<h1>Example Pages Plugin</h1>
<h2>Navigation</h2>
{page1Link && (
<div>
<Link to={page1Link()}>Page 1</Link>
@@ -83,6 +84,47 @@ const IndexPage = PageBlueprint.make({
<div>
<Link to="/settings">Settings</Link>
</div>
<h2>Permission Enablement Examples</h2>
<p>
The following pages demonstrate conditional extension enablement
via the <code>enabled</code> predicate using permissions. They
will only appear when the user has the required permissions.
</p>
<ul>
<li>
<Link to="/permission-gated-example">
Permission Gated Example
</Link>{' '}
requires <code>catalog.entity.create</code>
</li>
</ul>
<h2>Feature Flag Enablement Examples</h2>
<p>
The following pages demonstrate conditional extension enablement
via the <code>enabled</code> predicate. They will only appear in
the router tree when their conditions are satisfied. Toggle the
relevant feature flags in <Link to="/settings">Settings</Link>,
then refresh the app to see the pages appear.
</p>
<ul>
<li>
<Link to="/feature-flag-example">Feature Flag Example</Link>
requires the <code>experimental-features</code> flag
</li>
<li>
<Link to="/all-flags-example">All Flags Example</Link>
requires <em>both</em> <code>experimental-features</code> and{' '}
<code>advanced-features</code> (<code>$all</code>)
</li>
<li>
<Link to="/any-flag-example">Any Flag Example</Link> requires{' '}
<em>either</em> <code>experimental-features</code> or{' '}
<code>beta-access</code> (<code>$any</code>)
</li>
</ul>
<PluginInfo />
</div>
);
@@ -150,6 +192,155 @@ const ExternalPage = PageBlueprint.make({
},
});
// Example: Page enabled only when a single feature flag is active.
//
// The `enabled` predicate is evaluated once at app startup (before the router
// tree is built), so this page simply won't exist in the app until the flag is
// toggled and the page is refreshed.
//
// To test: enable the 'experimental-features' flag in Settings, then refresh.
const FeatureFlagPage = PageBlueprint.make({
name: 'featureFlagExample',
params: {
path: '/feature-flag-example',
loader: async () => {
const Component = () => {
const indexLink = useRouteRef(indexRouteRef);
return (
<div>
<h1>Feature Flag Enabled Page</h1>
<p>
This page is only present in the app when the{' '}
<code>experimental-features</code> feature flag is active.
</p>
<p>
It uses a simple{' '}
<code>
{'{ featureFlags: { $contains: "experimental-features" } }'}
</code>{' '}
predicate.
</p>
{indexLink && <Link to={indexLink()}>Go back</Link>}
</div>
);
};
return <Component />;
},
},
enabled: { featureFlags: { $contains: 'experimental-features' } },
});
// Example: Page enabled only when ALL of several feature flags are active.
//
// The $all operator requires every nested predicate to be satisfied. This page
// won't appear unless both 'experimental-features' and 'advanced-features' are
// enabled at the same time.
//
// To test: enable BOTH flags in Settings, then refresh.
const AllFlagsPage = PageBlueprint.make({
name: 'allFlagsExample',
params: {
path: '/all-flags-example',
loader: async () => {
const Component = () => {
const indexLink = useRouteRef(indexRouteRef);
return (
<div>
<h1>All Flags Required Page</h1>
<p>
This page requires <em>both</em>{' '}
<code>experimental-features</code> and{' '}
<code>advanced-features</code> to be active simultaneously.
</p>
<p>
It uses a <code>$all</code> predicate to AND the two conditions
together.
</p>
{indexLink && <Link to={indexLink()}>Go back</Link>}
</div>
);
};
return <Component />;
},
},
enabled: {
$all: [
{ featureFlags: { $contains: 'experimental-features' } },
{ featureFlags: { $contains: 'advanced-features' } },
],
},
});
// Example: Page enabled when ANY one of several feature flags is active.
//
// The $any operator is satisfied as soon as at least one nested predicate
// matches. Enabling either 'experimental-features' or 'beta-access' will make
// this page appear.
//
// To test: enable at least one of the two flags in Settings, then refresh.
const AnyFlagPage = PageBlueprint.make({
name: 'anyFlagExample',
params: {
path: '/any-flag-example',
loader: async () => {
const Component = () => {
const indexLink = useRouteRef(indexRouteRef);
return (
<div>
<h1>Any Flag Sufficient Page</h1>
<p>
This page appears when <em>either</em>{' '}
<code>experimental-features</code> or <code>beta-access</code> is
active.
</p>
<p>
It uses a <code>$any</code> predicate to OR the two conditions
together.
</p>
{indexLink && <Link to={indexLink()}>Go back</Link>}
</div>
);
};
return <Component />;
},
},
enabled: {
$any: [
{ featureFlags: { $contains: 'experimental-features' } },
{ featureFlags: { $contains: 'beta-access' } },
],
},
});
// Example: Page enabled only when the user is allowed to create catalog entities.
//
// The `enabled` predicate is evaluated once at app startup (after sign-in),
// so this page simply won't exist in the router tree if the user lacks the
// required permission.
const PermissionGatedPage = PageBlueprint.make({
name: 'permissionGatedExample',
params: {
path: '/permission-gated-example',
loader: async () => {
const Component = () => {
const indexLink = useRouteRef(indexRouteRef);
return (
<div>
<h1>Permission Gated Page</h1>
<p>
This page is only present when the user has the{' '}
<code>catalog.entity.create</code> permission.
</p>
{indexLink && <Link to={indexLink()}>Go back</Link>}
</div>
);
};
return <Component />;
},
},
enabled: { permissions: { $contains: 'catalog.entity.create' } },
});
export const pagesPlugin = createFrontendPlugin({
pluginId: 'pages',
// routes: {
@@ -170,5 +361,18 @@ export const pagesPlugin = createFrontendPlugin({
externalRoutes: {
pageX: externalPageXRouteRef,
},
extensions: [IndexPage, Page1, ExternalPage],
featureFlags: [
{ name: 'experimental-features' },
{ name: 'advanced-features' },
{ name: 'beta-access' },
],
extensions: [
IndexPage,
Page1,
ExternalPage,
FeatureFlagPage,
AllFlagsPage,
AnyFlagPage,
PermissionGatedPage,
],
});