From 38a6aefa9f96f468038e3a60658624a213bac40b Mon Sep 17 00:00:00 2001 From: narendraio Date: Fri, 19 Jun 2026 23:25:31 +0530 Subject: [PATCH] fix(features): open feature permalinks for features beyond the first page A feature permalink (`?feature=`) is opened by the matching `FeatureRow`'s own effect, but rows are only rendered for the current page of results. When the linked feature lives on a later page no row exists, so the permalink was a no-op and the user simply landed on the first page. Add a `FeaturePermalinkHandler` that, when the linked feature is not present on the current page, fetches the feature and its environment feature state and renders a single hidden `FeatureRow`, reusing the existing panel-opening logic rather than duplicating it. Closes #4239 --- .../pages/features/FeaturesPage.tsx | 31 +++++++ .../components/FeaturePermalinkHandler.tsx | 86 +++++++++++++++++++ .../pages/features/components/index.ts | 1 + 3 files changed, 118 insertions(+) create mode 100644 frontend/web/components/pages/features/components/FeaturePermalinkHandler.tsx diff --git a/frontend/web/components/pages/features/FeaturesPage.tsx b/frontend/web/components/pages/features/FeaturesPage.tsx index 6bcec90d8b30..746cc58b80d9 100644 --- a/frontend/web/components/pages/features/FeaturesPage.tsx +++ b/frontend/web/components/pages/features/FeaturesPage.tsx @@ -14,6 +14,7 @@ import FeatureRowSkeleton from 'components/feature-summary/FeatureRowSkeleton' import JSONReference from 'components/JSONReference' import Permission from 'common/providers/Permission' import { + FeaturePermalinkHandler, FeaturesEmptyState, FeatureMetricsSection, FeaturesPageHeader, @@ -174,6 +175,22 @@ const FeaturesPage: FC = ({ [data?.pagination], ) + // A permalinked feature (`?feature=`) is opened by its own FeatureRow, but + // rows only exist for the current page. When the target feature lives on a later + // page we open it via a dedicated handler instead (see #4239). + const permalinkFeatureId = useMemo(() => { + const { feature } = Utils.fromParam(history.location.search) as { + feature?: string + } + const featureId = feature ? parseInt(feature) : NaN + return Number.isNaN(featureId) ? null : featureId + }, [history.location.search]) + const isPermalinkOnCurrentPage = + permalinkFeatureId !== null && + projectFlags.some( + (projectFlag: ProjectFlag) => projectFlag.id === permalinkFeatureId, + ) + usePageTracking({ context: { environmentId, @@ -378,6 +395,20 @@ const FeaturesPage: FC = ({ {renderFeaturesList()} + {!!data && + !isPermalinkOnCurrentPage && + permalinkFeatureId !== null && + !!currentEnvironment && ( + + )} + `) that is not + * present on the current page of results. + * + * `FeatureRow` opens the panel from its own effect when the feature id in the URL + * matches its feature, but rows are only rendered for the current page. For a + * feature on a later page no row exists, so the permalink would otherwise be a + * no-op and the user would simply land on the first page (see #4239). + * + * Here we fetch the feature (and its environment feature state) directly and render + * a single hidden `FeatureRow`, so the exact same panel-opening logic runs without + * having to duplicate it. + */ +const FeaturePermalinkHandler: FC = ({ + environmentApiKey, + environmentId, + experimentMode, + featureId, + minimumChangeRequestApprovals, + projectId, +}) => { + const { data: projectFlag } = useGetProjectFlagQuery({ + id: featureId, + project: projectId, + }) + const { data: featureStates } = useGetFeatureStatesQuery({ + environment: environmentId, + feature: featureId, + }) + + const environmentFlags = useMemo(() => { + const environmentFeatureState = featureStates?.results?.find( + (featureState) => !featureState.feature_segment && !featureState.identity, + ) + return environmentFeatureState + ? { [featureId]: environmentFeatureState } + : {} + }, [featureStates, featureId]) + + if (!projectFlag) { + return null + } + + return ( +
+ + {({ permission }) => ( + + )} + +
+ ) +} + +export default FeaturePermalinkHandler diff --git a/frontend/web/components/pages/features/components/index.ts b/frontend/web/components/pages/features/components/index.ts index deceb0bbe1e3..673db728d2a8 100644 --- a/frontend/web/components/pages/features/components/index.ts +++ b/frontend/web/components/pages/features/components/index.ts @@ -1,3 +1,4 @@ +export { default as FeaturePermalinkHandler } from './FeaturePermalinkHandler' export { FeaturesEmptyState } from './FeaturesEmptyState' export { FeatureMetricsSection } from './FeatureMetricsSection' export { FeaturesPageHeader } from './FeaturesPageHeader'