From 2561465754470c8e3292f7de7d89f0d1541b7e2a Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Wed, 4 Feb 2026 15:48:09 -0800 Subject: [PATCH 1/5] removed metadata_modal feature flag and related references to it --- docs/feature_flags.md | 4 +- .../config/feature_flag_configs/autopush.json | 6 - .../config/feature_flag_configs/custom.json | 6 - server/config/feature_flag_configs/dev.json | 6 - server/config/feature_flag_configs/local.json | 6 - .../config/feature_flag_configs/staging.json | 6 - static/js/components/subject_page/block.tsx | 3 +- static/js/components/tiles/chart_footer.tsx | 4 +- .../facet_selector/facet_option_content.tsx | 2 +- .../shared/facet_selector/facet_selector.tsx | 411 +++++++++++++- .../facet_selector_grouped_content.tsx | 2 +- .../facet_selector/facet_selector_rich.tsx | 455 --------------- .../facet_selector/facet_selector_simple.tsx | 516 ------------------ static/js/shared/feature_flags/util.ts | 3 +- static/js/shared/util.ts | 2 +- .../js/tools/shared/metadata/tile_sources.tsx | 5 +- 16 files changed, 402 insertions(+), 1035 deletions(-) delete mode 100644 static/js/shared/facet_selector/facet_selector_rich.tsx delete mode 100644 static/js/shared/facet_selector/facet_selector_simple.tsx diff --git a/docs/feature_flags.md b/docs/feature_flags.md index 84a4b7614b..50c24aa34b 100644 --- a/docs/feature_flags.md +++ b/docs/feature_flags.md @@ -90,9 +90,9 @@ For example, to enable the "autocomplete" feature: * Enable: https://datacommons.org/explore?enable_feature=autocomplete -To enable both the autocomplete and metadata_modal features: +To enable both the autocomplete and page_overview_ga features: -* Enable: https://datacommons.org/explore?enable_feature=autocomplete&enable_feature=metadata_modal +* Enable: https://datacommons.org/explore?enable_feature=autocomplete&enable_feature=page_overview_ga To manually disable a feature flag, use the `disable_feature` URL parameter. diff --git a/server/config/feature_flag_configs/autopush.json b/server/config/feature_flag_configs/autopush.json index c0967102bd..f0f85db923 100644 --- a/server/config/feature_flag_configs/autopush.json +++ b/server/config/feature_flag_configs/autopush.json @@ -59,12 +59,6 @@ "owner": "javiervazquez", "description": "Enables the follow up questions generated from related topics to general audience." }, - { - "name": "metadata_modal", - "enabled": true, - "owner": "nick-next", - "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." - }, { "name": "page_overview_ga", "enabled": false, diff --git a/server/config/feature_flag_configs/custom.json b/server/config/feature_flag_configs/custom.json index 632322fc6e..a09af3795b 100644 --- a/server/config/feature_flag_configs/custom.json +++ b/server/config/feature_flag_configs/custom.json @@ -59,12 +59,6 @@ "owner": "javiervazquez", "description": "Enables the follow up questions generated from related topics to general audience." }, - { - "name": "metadata_modal", - "enabled": true, - "owner": "nick-next", - "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." - }, { "name": "page_overview_ga", "enabled": false, diff --git a/server/config/feature_flag_configs/dev.json b/server/config/feature_flag_configs/dev.json index 8d672963b2..e288a3b3a8 100644 --- a/server/config/feature_flag_configs/dev.json +++ b/server/config/feature_flag_configs/dev.json @@ -59,12 +59,6 @@ "owner": "javiervazquez", "description": "Enables the follow up questions generated from related topics to general audience." }, - { - "name": "metadata_modal", - "enabled": true, - "owner": "nick-next", - "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." - }, { "name": "page_overview_ga", "enabled": false, diff --git a/server/config/feature_flag_configs/local.json b/server/config/feature_flag_configs/local.json index b888e5869e..1963983ed3 100644 --- a/server/config/feature_flag_configs/local.json +++ b/server/config/feature_flag_configs/local.json @@ -59,12 +59,6 @@ "owner": "javiervazquez", "description": "Enables the follow up questions generated from related topics to general audience." }, - { - "name": "metadata_modal", - "enabled": true, - "owner": "nick-next", - "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." - }, { "name": "page_overview_ga", "enabled": false, diff --git a/server/config/feature_flag_configs/staging.json b/server/config/feature_flag_configs/staging.json index f0c23c125b..15611e41fd 100644 --- a/server/config/feature_flag_configs/staging.json +++ b/server/config/feature_flag_configs/staging.json @@ -59,12 +59,6 @@ "owner": "javiervazquez", "description": "Enables the follow up questions generated from related topics to general audience." }, - { - "name": "metadata_modal", - "enabled": true, - "owner": "nick-next", - "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." - }, { "name": "page_overview_ga", "enabled": false, diff --git a/static/js/components/subject_page/block.tsx b/static/js/components/subject_page/block.tsx index 892e3e8d7b..452ba851de 100644 --- a/static/js/components/subject_page/block.tsx +++ b/static/js/components/subject_page/block.tsx @@ -59,7 +59,6 @@ import { } from "../../shared/facet_selector/facet_selector"; import { isFeatureEnabled, - METADATA_FEATURE_FLAG, } from "../../shared/feature_flags/util"; import { usePromiseResolver } from "../../shared/hooks/promise_resolver"; import { NamedPlace, NamedTypedPlace, StatVarSpec } from "../../shared/types"; @@ -246,7 +245,7 @@ function blockEligibleForFacetSelector( columns: ColumnConfig[], isWebComponentBlock: boolean ): boolean { - if (isWebComponentBlock || !isFeatureEnabled(METADATA_FEATURE_FLAG)) { + if (isWebComponentBlock) { return false; } diff --git a/static/js/components/tiles/chart_footer.tsx b/static/js/components/tiles/chart_footer.tsx index 1d7e345074..b9f9ae8299 100644 --- a/static/js/components/tiles/chart_footer.tsx +++ b/static/js/components/tiles/chart_footer.tsx @@ -24,7 +24,6 @@ import { intl } from "../../i18n/i18n"; import { messages } from "../../i18n/i18n_messages"; import { isFeatureEnabled, - METADATA_FEATURE_FLAG, } from "../../shared/feature_flags/util"; import { GA_EVENT_TILE_DOWNLOAD, @@ -99,8 +98,7 @@ export function ChartFooter(props: ChartFooterPropType): JSX.Element { )} - {props.getObservationSpecs && - isFeatureEnabled(METADATA_FEATURE_FLAG) && ( + {props.getObservationSpecs && (
; } -interface FacetSelectorPropType { +interface FacetSelectorProps { // the variant with small used for the old tools, inline as an inline // text button and standard elsewhere variant?: "standard" | "small" | "inline"; @@ -62,19 +93,363 @@ interface FacetSelectorPropType { svFacetId: Record, metadataMap: Record ) => void; - // If set, when a facet is selected for one stat var, the corresponding - // facet is selected for all other stat vars. This only applies if all - // stat vars have the same facet choices. + // If set, when a facet is selected for one stat var, the corresponding facet + // is selected for all other stat vars. Only facets in common with all stat + // vars will be selectable. This is currently used only for bar charts. allowSelectionGrouping?: boolean; // Facet Selector facetSelector?: FacetSelectionCriteria; + // useInjectedFacet + useInjectedFacet?: boolean; + setUseInjectedFacet?: (useInjectedFacet: boolean) => void; } -export function FacetSelector(props: FacetSelectorPropType): ReactElement { - const useRichSelector = isFeatureEnabled(METADATA_FEATURE_FLAG); +/** + * This function builds the final list of stat vars and their respective facets that + * will be used by the selector. + * + * In standard mode (not grouping selections into a single list), it returns the list unchanged. + * In grouped mode, it removes facets that are not common to all stat vars, + * unless showInconsistentFacets is true. + * + * @param originalFacetList {FacetSelectorFacetInfo[] | null} List of stat vars with facets passed into the component + * @param allowSelectionGrouping {boolean} If true, the component plans to display a single list of facet. + * @param showInconsistentFacets {boolean} If true, in grouped mode the selector will not drop inconsistent facets. + */ +const buildFinalFacetList = ( + originalFacetList: FacetSelectorFacetInfo[] | null, + allowSelectionGrouping: boolean, + showInconsistentFacets: boolean +): FacetSelectorFacetInfo[] | null => { + if (showInconsistentFacets || !allowSelectionGrouping || !originalFacetList) { + return originalFacetList; + } + + const totalStatVars = originalFacetList.length; + if (totalStatVars <= 1) { + return originalFacetList; + } + + // 1. We determine the facet ids in common with all stat vars + let commonFacetIds = new Set(Object.keys(originalFacetList[0].metadataMap)); + + for (let i = 1; i < totalStatVars; i++) { + if (commonFacetIds.size === 0) { + break; + } + const currentFacetIds = Object.keys(originalFacetList[i].metadataMap); + const intersection = new Set(); + for (const facetId of currentFacetIds) { + if (commonFacetIds.has(facetId)) { + intersection.add(facetId); + } + } + commonFacetIds = intersection; + } - if (useRichSelector) { - return ; + // 2. We rebuild the facetList, only including those consistent facets. + const filteredList: FacetSelectorFacetInfo[] = []; + for (const originalFacetInfo of originalFacetList) { + const newMetadataMap: Record = {}; + for (const facetId of commonFacetIds) { + if (originalFacetInfo.metadataMap[facetId]) { + newMetadataMap[facetId] = originalFacetInfo.metadataMap[facetId]; + } + } + filteredList.push({ + ...originalFacetInfo, + metadataMap: newMetadataMap, + }); } - return ; + + return filteredList; +}; + +export function FacetSelector(props: FacetSelectorProps): ReactElement { + const { + variant = "standard", + mode, + facetList, + loading, + error, + allowSelectionGrouping = false, + } = props; + const theme = useTheme(); + const [modalOpen, setModalOpen] = useState(false); + const [useInjectedFacet, setUseInjectedFacet] = useState(true); + + const finalFacetList = useMemo(() => { + return buildFinalFacetList( + facetList, + allowSelectionGrouping, + SHOW_INCONSISTENT_FACETS + ); + }, [facetList, allowSelectionGrouping]); + + const totalFacetOptionCount = useMemo(() => { + if (!finalFacetList) { + return 0; + } + const uniqueFacetIds = new Set(); + finalFacetList.forEach((facetInfo) => { + Object.keys(facetInfo.metadataMap).forEach((facetId) => { + if (facetId !== "") { + uniqueFacetIds.add(facetId); + } + }); + }); + return uniqueFacetIds.size; + }, [finalFacetList]); + + const hasAlternativeSources = useMemo(() => { + if (loading || !finalFacetList) { + return false; + } + return finalFacetList.some( + (facetInfo) => Object.keys(facetInfo.metadataMap).length > 1 + ); + }, [finalFacetList, loading]); + + function areFacetsConsistent( + facetList: FacetSelectorFacetInfo[] | null + ): boolean { + if (!facetList || facetList.length <= 1) { + return true; + } + const firstFacetIds = Object.keys(facetList[0].metadataMap).sort(); + for (let i = 1; i < facetList.length; i++) { + const currentFacetIds = Object.keys(facetList[i].metadataMap).sort(); + if (!isEqual(firstFacetIds, currentFacetIds)) { + return false; + } + } + return true; + } + + if (!hasAlternativeSources) { + if (mode === "download") { + return null; + } + return ; + } + + const showInconsistentFacetFlag = + SHOW_INCONSISTENT_FACETS && + allowSelectionGrouping && + !loading && + !error && + !areFacetsConsistent(facetList); + + /* + This is true if more than one choice can be made in the dialog (i.e., + we are not in grouped mode, and we have more than one stat var). + */ + const multipleChoicesAvailable = + !allowSelectionGrouping && finalFacetList && finalFacetList.length > 1; + + return ( + <> + + {showInconsistentFacetFlag && ( +
+ +
+ )} + setModalOpen(false)} + multipleChoicesAvailable={multipleChoicesAvailable} + useInjectedFacet={useInjectedFacet} + setUseInjectedFacet={setUseInjectedFacet} + /> + + ); +} + +function NoFacetChoicesMessage({ + variant, + loading, +}: Pick): ReactElement | null { + const theme = useTheme(); + return ( +

+ {intl.formatMessage( + facetSelectionComponentMessages.NoAlternativeDatasets + )} +

+ ); +} + +function FacetSelectorModal( + props: FacetSelectorProps & { + open: boolean; + onClose: () => void; + multipleChoicesAvailable: boolean; + } +): ReactElement { + const { + open, + onClose, + loading, + error, + facetList, + svFacetId, + onSvFacetIdUpdated, + allowSelectionGrouping, + mode, + useInjectedFacet, + } = props; + const [modalSelections, setModalSelections] = useState(svFacetId); + + useEffect(() => { + const injectedFacetId = useInjectedFacet + ? findMatchingFacets(facetList[0]["metadataMap"], props?.facetSelector) + : undefined; + if (!_.isEmpty(injectedFacetId)) { + setModalSelections({ [facetList[0]["dcid"]]: injectedFacetId[0] }); + } + // If modal is closed without updating facets, we want to reset the + // selections in the modal. + if (!open && !useInjectedFacet) { + setModalSelections(svFacetId); + } + }, [svFacetId, open]); + + const handleSelectionChange = ( + clickedDcid: string, + clickedFacetId: string + ): void => { + setModalSelections({ + ...modalSelections, + [clickedDcid]: clickedFacetId, + }); + }; + + const handleGroupedSelectionChange = (clickedFacetId: string): void => { + const newSelections: Record = {}; + if (facetList) { + for (const facetInfo of facetList) { + newSelections[facetInfo.dcid] = clickedFacetId; + } + } + setModalSelections(newSelections); + }; + + function onConfirm(): void { + const metadataMap = {}; + facetList.forEach((facetInfo: FacetSelectorFacetInfo) => { + const selectedFacetId = modalSelections[facetInfo.dcid]; + if (selectedFacetId && selectedFacetId in facetInfo.metadataMap) { + metadataMap[selectedFacetId] = facetInfo.metadataMap[selectedFacetId]; + } + }); + onSvFacetIdUpdated(modalSelections, metadataMap); + props.setUseInjectedFacet(false); + onClose(); + } + + const showSourceOptions = facetList && !error; + + return ( + + + {intl.formatMessage( + props.multipleChoicesAvailable + ? facetSelectionComponentMessages.SelectDatasets + : facetSelectionComponentMessages.SelectDataset + )} + + + {error && ( +
+ {intl.formatMessage(facetSelectionComponentMessages.DatasetError)} +
+ )} + {showSourceOptions && + (allowSelectionGrouping ? ( + + ) : ( + + ))} +
+ + + {!error && ( + + )} + +
+ ); } diff --git a/static/js/shared/facet_selector/facet_selector_grouped_content.tsx b/static/js/shared/facet_selector/facet_selector_grouped_content.tsx index b657981d3b..62cd571b11 100644 --- a/static/js/shared/facet_selector/facet_selector_grouped_content.tsx +++ b/static/js/shared/facet_selector/facet_selector_grouped_content.tsx @@ -39,7 +39,7 @@ import { facetSelectionComponentMessages } from "../../i18n/i18n_facet_selection import { StatMetadata } from "../stat_types"; import { FacetOptionContent } from "./facet_option_content"; import { FacetSelectorFacetInfo } from "./facet_selector"; -import { SELECTOR_PREFIX } from "./facet_selector_rich"; +import { SELECTOR_PREFIX } from "./facet_selector"; interface FacetSelectorGroupedContentProps { facetList: FacetSelectorFacetInfo[]; diff --git a/static/js/shared/facet_selector/facet_selector_rich.tsx b/static/js/shared/facet_selector/facet_selector_rich.tsx deleted file mode 100644 index d06201c3df..0000000000 --- a/static/js/shared/facet_selector/facet_selector_rich.tsx +++ /dev/null @@ -1,455 +0,0 @@ -/** - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Component to allow the facet selection for a chart. It will render - * an interface where the user can select either: - * - * 1. Standard mode: a facet for each stat var, where each stat var may - * be given its own stat var. - * 2. Grouped mode: a single facet list, where only one facet is chosen - * (and this facet is applied to all stat vars). - */ - -/** @jsxImportSource @emotion/react */ - -import { css, useTheme } from "@emotion/react"; -import _, { isEqual } from "lodash"; -import React, { ReactElement, useEffect, useMemo, useState } from "react"; - -import { Button } from "../../components/elements/button/button"; -import { DebugFlag } from "../../components/elements/debug_flag"; -import { - Dialog, - DialogActions, - DialogContent, - DialogTitle, -} from "../../components/elements/dialog/dialog"; -import { intl } from "../../i18n/i18n"; -import { facetSelectionComponentMessages } from "../../i18n/i18n_facet_selection_messages"; -import { messages } from "../../i18n/i18n_messages"; -import { FacetSelectionCriteria } from "../../types/facet_selection_criteria"; -import { findMatchingFacets } from "../../utils/data_fetch_utils"; -import { StatMetadata } from "../stat_types"; -import { FacetSelectorGroupedContent } from "./facet_selector_grouped_content"; -import { FacetSelectorStandardContent } from "./facet_selector_standard_content"; - -export const SELECTOR_PREFIX = "source-selector"; - -/** - * If true, the facet selector will show all available facet options, even if they - * are inconsistent across different statistical variables in grouped mode. It will - * also display a debug flag in debug mode to indicate the issue. - * If false, it will filter the list to only show facet options that are common to - * all statistical variables. - * - * In non-grouped mode, this flag will have no effect. - */ -const SHOW_INCONSISTENT_FACETS = false; - -// The information needed in SourceSelector component for a single stat var to -// get the list of available facets -export interface FacetSelectorFacetInfo { - // dcid of the stat var - dcid: string; - // name of the stat var - name: string; - // mapping of facet id to corresponding metadata for available facets for - // this stat var - metadataMap: Record; - // mapping of facet id to the display name to use for the corresponding facet - displayNames?: Record; -} - -interface FacetSelectorRichProps { - // the variant with small used for the old tools, inline as an inline - // text button and standard elsewhere - variant?: "standard" | "small" | "inline"; - // the mode of the facet selector determines the copy used in the instructions - mode?: "chart" | "download"; - // Map of sv to selected facet id - svFacetId: Record; - // The list of available facets for each stat var - facetList: FacetSelectorFacetInfo[] | null; - // Whether the facet information is currently being loaded - loading: boolean; - // An error message to display if the fetch fails - error: boolean; - // Callback function that is run when new facets are selected - onSvFacetIdUpdated: ( - svFacetId: Record, - metadataMap: Record - ) => void; - // If set, when a facet is selected for one stat var, the corresponding facet - // is selected for all other stat vars. Only facets in common with all stat - // vars will be selectable. This is currently used only for bar charts. - allowSelectionGrouping?: boolean; - // Facet Selector - facetSelector?: FacetSelectionCriteria; - // useInjectedFacet - useInjectedFacet?: boolean; - setUseInjectedFacet?: (useInjectedFacet: boolean) => void; -} - -/** - * This function builds the final list of stat vars and their respective facets that - * will be used by the selector. - * - * In standard mode (not grouping selections into a single list), it returns the list unchanged. - * In grouped mode, it removes facets that are not common to all stat vars, - * unless showInconsistentFacets is true. - * - * @param originalFacetList {FacetSelectorFacetInfo[] | null} List of stat vars with facets passed into the component - * @param allowSelectionGrouping {boolean} If true, the component plans to display a single list of facet. - * @param showInconsistentFacets {boolean} If true, in grouped mode the selector will not drop inconsistent facets. - */ -const buildFinalFacetList = ( - originalFacetList: FacetSelectorFacetInfo[] | null, - allowSelectionGrouping: boolean, - showInconsistentFacets: boolean -): FacetSelectorFacetInfo[] | null => { - if (showInconsistentFacets || !allowSelectionGrouping || !originalFacetList) { - return originalFacetList; - } - - const totalStatVars = originalFacetList.length; - if (totalStatVars <= 1) { - return originalFacetList; - } - - // 1. We determine the facet ids in common with all stat vars - let commonFacetIds = new Set(Object.keys(originalFacetList[0].metadataMap)); - - for (let i = 1; i < totalStatVars; i++) { - if (commonFacetIds.size === 0) { - break; - } - const currentFacetIds = Object.keys(originalFacetList[i].metadataMap); - const intersection = new Set(); - for (const facetId of currentFacetIds) { - if (commonFacetIds.has(facetId)) { - intersection.add(facetId); - } - } - commonFacetIds = intersection; - } - - // 2. We rebuild the facetList, only including those consistent facets. - const filteredList: FacetSelectorFacetInfo[] = []; - for (const originalFacetInfo of originalFacetList) { - const newMetadataMap: Record = {}; - for (const facetId of commonFacetIds) { - if (originalFacetInfo.metadataMap[facetId]) { - newMetadataMap[facetId] = originalFacetInfo.metadataMap[facetId]; - } - } - filteredList.push({ - ...originalFacetInfo, - metadataMap: newMetadataMap, - }); - } - - return filteredList; -}; - -export function FacetSelectorRich(props: FacetSelectorRichProps): ReactElement { - const { - variant = "standard", - mode, - facetList, - loading, - error, - allowSelectionGrouping = false, - } = props; - const theme = useTheme(); - const [modalOpen, setModalOpen] = useState(false); - const [useInjectedFacet, setUseInjectedFacet] = useState(true); - - const finalFacetList = useMemo(() => { - return buildFinalFacetList( - facetList, - allowSelectionGrouping, - SHOW_INCONSISTENT_FACETS - ); - }, [facetList, allowSelectionGrouping]); - - const totalFacetOptionCount = useMemo(() => { - if (!finalFacetList) { - return 0; - } - const uniqueFacetIds = new Set(); - finalFacetList.forEach((facetInfo) => { - Object.keys(facetInfo.metadataMap).forEach((facetId) => { - if (facetId !== "") { - uniqueFacetIds.add(facetId); - } - }); - }); - return uniqueFacetIds.size; - }, [finalFacetList]); - - const hasAlternativeSources = useMemo(() => { - if (loading || !finalFacetList) { - return false; - } - return finalFacetList.some( - (facetInfo) => Object.keys(facetInfo.metadataMap).length > 1 - ); - }, [finalFacetList, loading]); - - function areFacetsConsistent( - facetList: FacetSelectorFacetInfo[] | null - ): boolean { - if (!facetList || facetList.length <= 1) { - return true; - } - const firstFacetIds = Object.keys(facetList[0].metadataMap).sort(); - for (let i = 1; i < facetList.length; i++) { - const currentFacetIds = Object.keys(facetList[i].metadataMap).sort(); - if (!isEqual(firstFacetIds, currentFacetIds)) { - return false; - } - } - return true; - } - - if (!hasAlternativeSources) { - if (mode === "download") { - return null; - } - return ; - } - - const showInconsistentFacetFlag = - SHOW_INCONSISTENT_FACETS && - allowSelectionGrouping && - !loading && - !error && - !areFacetsConsistent(facetList); - - /* - This is true if more than one choice can be made in the dialog (i.e., - we are not in grouped mode, and we have more than one stat var). - */ - const multipleChoicesAvailable = - !allowSelectionGrouping && finalFacetList && finalFacetList.length > 1; - - return ( - <> - - {showInconsistentFacetFlag && ( -
- -
- )} - setModalOpen(false)} - multipleChoicesAvailable={multipleChoicesAvailable} - useInjectedFacet={useInjectedFacet} - setUseInjectedFacet={setUseInjectedFacet} - /> - - ); -} - -function NoFacetChoicesMessage({ - variant, - loading, -}: Pick): ReactElement | null { - const theme = useTheme(); - return ( -

- {intl.formatMessage( - facetSelectionComponentMessages.NoAlternativeDatasets - )} -

- ); -} - -function FacetSelectorModal( - props: FacetSelectorRichProps & { - open: boolean; - onClose: () => void; - multipleChoicesAvailable: boolean; - } -): ReactElement { - const { - open, - onClose, - loading, - error, - facetList, - svFacetId, - onSvFacetIdUpdated, - allowSelectionGrouping, - mode, - useInjectedFacet, - } = props; - const [modalSelections, setModalSelections] = useState(svFacetId); - - useEffect(() => { - const injectedFacetId = useInjectedFacet - ? findMatchingFacets(facetList[0]["metadataMap"], props?.facetSelector) - : undefined; - if (!_.isEmpty(injectedFacetId)) { - setModalSelections({ [facetList[0]["dcid"]]: injectedFacetId[0] }); - } - // If modal is closed without updating facets, we want to reset the - // selections in the modal. - if (!open && !useInjectedFacet) { - setModalSelections(svFacetId); - } - }, [svFacetId, open]); - - const handleSelectionChange = ( - clickedDcid: string, - clickedFacetId: string - ): void => { - setModalSelections({ - ...modalSelections, - [clickedDcid]: clickedFacetId, - }); - }; - - const handleGroupedSelectionChange = (clickedFacetId: string): void => { - const newSelections: Record = {}; - if (facetList) { - for (const facetInfo of facetList) { - newSelections[facetInfo.dcid] = clickedFacetId; - } - } - setModalSelections(newSelections); - }; - - function onConfirm(): void { - const metadataMap = {}; - facetList.forEach((facetInfo: FacetSelectorFacetInfo) => { - const selectedFacetId = modalSelections[facetInfo.dcid]; - if (selectedFacetId && selectedFacetId in facetInfo.metadataMap) { - metadataMap[selectedFacetId] = facetInfo.metadataMap[selectedFacetId]; - } - }); - onSvFacetIdUpdated(modalSelections, metadataMap); - props.setUseInjectedFacet(false); - onClose(); - } - - const showSourceOptions = facetList && !error; - - return ( - - - {intl.formatMessage( - props.multipleChoicesAvailable - ? facetSelectionComponentMessages.SelectDatasets - : facetSelectionComponentMessages.SelectDataset - )} - - - {error && ( -
- {intl.formatMessage(facetSelectionComponentMessages.DatasetError)} -
- )} - {showSourceOptions && - (allowSelectionGrouping ? ( - - ) : ( - - ))} -
- - - {!error && ( - - )} - -
- ); -} diff --git a/static/js/shared/facet_selector/facet_selector_simple.tsx b/static/js/shared/facet_selector/facet_selector_simple.tsx deleted file mode 100644 index b16bc76019..0000000000 --- a/static/js/shared/facet_selector/facet_selector_simple.tsx +++ /dev/null @@ -1,516 +0,0 @@ -/** - * Copyright 2025 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Component to edit the facet for a list of stat vars. This is a simplified - * version that will be replaced by the rich version when the feature - * flag is removed. - */ - -/** @jsxImportSource @emotion/react */ - -import { css, useTheme } from "@emotion/react"; -import _ from "lodash"; -import React, { ReactElement, useEffect, useMemo, useState } from "react"; -import { FormGroup, Input, Label } from "reactstrap"; - -import theme from "../../../../static/js/theme/theme"; -import { Button } from "../../components/elements/button/button"; -import { - Dialog, - DialogActions, - DialogContent, - DialogTitle, -} from "../../components/elements/dialog/dialog"; -import { intl } from "../../i18n/i18n"; -import { facetSelectionComponentMessages } from "../../i18n/i18n_facet_selection_messages"; -import { messages } from "../../i18n/i18n_messages"; -import { metadataComponentMessages } from "../../i18n/i18n_metadata_messages"; -import { FacetSelectionCriteria } from "../../types/facet_selection_criteria"; -import { humanizeIsoDuration } from "../periodicity"; -import { StatMetadata } from "../stat_types"; - -const SELECTOR_PREFIX = "source-selector"; - -// The information needed in SourceSelector component for a single stat var to -// get the list of available facets -export interface FacetSelectorFacetInfo { - // dcid of the stat var - dcid: string; - // name of the stat var - name: string; - // mapping of facet id to corresponding metadata for available facets for - // this stat var - metadataMap: Record; - // mapping of facet id to the display name to use for the corresponding facet - displayNames?: Record; -} - -interface FacetSelectorSimpleProps { - // the variant with small used for the old tools, inline as an inline - // text button and standard elsewhere - variant?: "standard" | "small" | "inline"; - // the mode of the facet selector determines the copy used in the instructions - mode?: "chart" | "download"; - // Map of sv to selected facet id - svFacetId: Record; - // The list of available facets for each stat var - facetList: FacetSelectorFacetInfo[] | null; - // Whether the facet information is currently being loaded - loading: boolean; - // An error message to display if the fetch fails - error: boolean; - // Facet Selector - facetSelector?: FacetSelectionCriteria; - // Callback function that is run when new facets are selected - onSvFacetIdUpdated: ( - svFacetId: Record, - metadataMap: Record - ) => void; -} - -export function FacetSelectorSimple({ - variant = "standard", - mode, - svFacetId, - facetList, - loading, - error, - onSvFacetIdUpdated, -}: FacetSelectorSimpleProps): ReactElement { - const theme = useTheme(); - const [modalOpen, setModalOpen] = useState(false); - const [modalSelections, setModalSelections] = useState(svFacetId); - const totalFacetOptionCount = useMemo(() => { - if (!facetList) return 0; - return facetList.reduce((sum: number, facetInfo) => { - const count = Object.keys(facetInfo.metadataMap).filter( - (facetId) => facetId !== "" - ).length; - return sum + count; - }, 0); - }, [facetList]); - - const hasAlternativeSources = useMemo(() => { - if (loading || !facetList) { - return false; - } - return facetList.some( - (facetInfo) => Object.keys(facetInfo.metadataMap).length > 1 - ); - }, [facetList, loading]); - - useEffect(() => { - // If modal is closed without updating facets, we want to reset the - // selections in the modal. - if (!modalOpen) { - setModalSelections(svFacetId); - } - }, [svFacetId, modalOpen]); - - if (!hasAlternativeSources) { - if (mode === "download") { - return null; - } - return ( -

- {intl.formatMessage( - facetSelectionComponentMessages.NoAlternativeDatasets - )} -

- ); - } - - const showSourceOptions = facetList && !error; - - return ( - <> - - setModalOpen(false)} - loading={loading} - maxWidth="sm" - fullWidth - > - - {intl.formatMessage( - facetList?.length > 1 - ? facetSelectionComponentMessages.SelectDatasets - : facetSelectionComponentMessages.SelectDataset - )} - - - {error &&
{facetSelectionComponentMessages.DatasetError}
} - {facetList?.length > 1 && ( -

- {intl.formatMessage( - mode === "download" - ? facetSelectionComponentMessages.SelectDatasetsForDownloadPromptMessage - : facetSelectionComponentMessages.ExploreOtherDatasetsMultipleStatVarsPromptMessage - )} - : -

- )} - {showSourceOptions && - facetList.map((facetInfo) => { - return ( -
- {facetList.length == 1 && ( -

- {intl.formatMessage( - mode === "download" - ? facetSelectionComponentMessages.SelectDatasetForDownloadPromptMessage - : facetSelectionComponentMessages.ExploreOtherDatasetsSingleStatVarPromptMessage - )}{" "} - - “ - {facetInfo.name} - ” - -

- )} - {facetList.length > 1 && ( -

- {facetInfo.name} -

- )} -
- {getFacetOptionJsx( - facetInfo, - "", - modalSelections, - setModalSelections, - mode - )} - {getFacetOptionSectionJsx( - facetInfo, - modalSelections, - setModalSelections, - mode - )} -
-
- ); - })} -
- - - - -
- - ); - - function onConfirm(): void { - const metadataMap = {}; - facetList.forEach((facetInfo: FacetSelectorFacetInfo) => { - const selectedFacetId = modalSelections[facetInfo.dcid]; - if (selectedFacetId) { - metadataMap[selectedFacetId] = facetInfo.metadataMap[selectedFacetId]; - } - }); - onSvFacetIdUpdated(modalSelections, metadataMap); - setModalOpen(false); - } -} - -/** - * Given the metadata for a facet, gets a title for the facet - */ -function getFacetTitle( - metadata: StatMetadata, - mode?: "chart" | "download" -): string { - if (_.isEmpty(metadata)) { - return intl.formatMessage( - mode === "download" - ? facetSelectionComponentMessages.CombinedDatasetForDownloadOption - : facetSelectionComponentMessages.CombinedDatasetForChartsOption - ); - } - return metadata.importName; -} - -/** - * Gets the element for a single facet options - */ -function getFacetOptionJsx( - facetInfo: FacetSelectorFacetInfo, - facetId: string, - modalSelections: Record, - setModalSelections: (selections: Record) => void, - mode?: "chart" | "download" -): ReactElement { - const metadata = facetInfo.metadataMap[facetId] || {}; - let facetTitle = getFacetTitle(metadata, mode); - if (facetInfo.displayNames && facetId in facetInfo.displayNames) { - facetTitle = facetInfo.displayNames[facetId]; - } - const selectedFacetId = modalSelections[facetInfo.dcid] || ""; - return ( - - - - ); -} - -/** - * Gets the element for facet options section for a single stat var - */ -function getFacetOptionSectionJsx( - facetInfo: FacetSelectorFacetInfo, - modalSelections: Record, - setModalSelections: (selections: Record) => void, - mode?: "chart" | "download" -): ReactElement { - const importNameToFacetOptions: Record = {}; - const facetOptionsNoImportName: string[] = []; - let shouldShowSections = false; - Object.keys(facetInfo.metadataMap).forEach((facetId) => { - const importName = facetInfo.metadataMap[facetId].importName; - if (!importName) { - facetOptionsNoImportName.push(facetId); - return; - } - if (!(importName in importNameToFacetOptions)) { - importNameToFacetOptions[importName] = []; - } - importNameToFacetOptions[importName].push(facetId); - shouldShowSections = true; - }); - if (shouldShowSections) { - const sortedImportNames = Object.keys(importNameToFacetOptions).sort(); - return ( - <> - {sortedImportNames.map((importName) => ( -
- {importNameToFacetOptions[importName].map((facetId) => - getFacetOptionJsx( - facetInfo, - facetId, - modalSelections, - setModalSelections, - mode - ) - )} -
- ))} - {facetOptionsNoImportName.map( - (facetId) => - facetId in facetInfo.metadataMap && - getFacetOptionJsx( - facetInfo, - facetId, - modalSelections, - setModalSelections, - mode - ) - )} - - ); - } else { - return ( - <> - {Object.keys(facetInfo.metadataMap).map((facetId) => - getFacetOptionJsx( - facetInfo, - facetId, - modalSelections, - setModalSelections, - mode - ) - )} - - ); - } -} diff --git a/static/js/shared/feature_flags/util.ts b/static/js/shared/feature_flags/util.ts index 633e89b532..bae136eea0 100644 --- a/static/js/shared/feature_flags/util.ts +++ b/static/js/shared/feature_flags/util.ts @@ -16,7 +16,6 @@ // Feature flag names export const AUTOCOMPLETE_FEATURE_FLAG = "autocomplete"; -export const METADATA_FEATURE_FLAG = "metadata_modal"; export const FOLLOW_UP_QUESTIONS_GA = "follow_up_questions_ga"; export const PAGE_OVERVIEW_GA = "page_overview_ga"; export const EXPLORE_RESULT_HEADER = "explore_result_header"; @@ -96,7 +95,7 @@ export function getFeatureFlags(): Record< * * Example: * https://datacommons.org/explore?enable_feature=autocomplete will enable the autocomplete feature. - * https://datacommons.org/explore?enable_feature=autocomplete&enable_feature=metadata_modal will enable both the autocomplete and metadata_modal features. + * https://datacommons.org/explore?enable_feature=autocomplete&enable_feature=page_overview_ga will enable both the autocomplete and page_overview_ga features. * https://datacommons.org/explore?disable_feature=autocomplete will disable the autocomplete feature. * * @param featureName name of feature for which we want status. diff --git a/static/js/shared/util.ts b/static/js/shared/util.ts index e009293d89..1e359cac66 100644 --- a/static/js/shared/util.ts +++ b/static/js/shared/util.ts @@ -21,7 +21,7 @@ import { AutoCompleteResult } from "../components/nl_search_bar/auto_complete_in import { Theme } from "../theme/types"; import { stringifyFn } from "../utils/axios"; import { MAX_DATE, MAX_YEAR, SOURCE_DISPLAY_NAME } from "./constants"; -import { FacetSelectorFacetInfo } from "./facet_selector/facet_selector_rich"; +import { FacetSelectorFacetInfo } from "./facet_selector/facet_selector"; import { StatMetadata } from "./stat_types"; import { StatVarFacetMap, StatVarSpec } from "./types"; diff --git a/static/js/tools/shared/metadata/tile_sources.tsx b/static/js/tools/shared/metadata/tile_sources.tsx index 3fd0767b08..65cf495e89 100644 --- a/static/js/tools/shared/metadata/tile_sources.tsx +++ b/static/js/tools/shared/metadata/tile_sources.tsx @@ -30,7 +30,6 @@ import { intl } from "../../../i18n/i18n"; import { messages } from "../../../i18n/i18n_messages"; import { isFeatureEnabled, - METADATA_FEATURE_FLAG, } from "../../../shared/feature_flags/util"; import { GA_EVENT_TILE_EXPLORE_MORE, @@ -82,8 +81,6 @@ export function TileSources(props: { return null; } - const allowNewMetadataModal = isFeatureEnabled(METADATA_FEATURE_FLAG); - const sourceList: string[] = facets ? Array.from( new Set(Object.values(facets).map((facet) => facet.provenanceUrl)) @@ -132,7 +129,7 @@ export function TileSources(props: { <> - {allowNewMetadataModal && facets && statVarToFacets ? ( + {facets && statVarToFacets ? ( Date: Wed, 4 Feb 2026 16:49:59 -0800 Subject: [PATCH 2/5] added feature flags back in --- .../config/feature_flag_configs/autopush.json | 6 ++++++ .../config/feature_flag_configs/custom.json | 6 ++++++ server/config/feature_flag_configs/dev.json | 6 ++++++ server/config/feature_flag_configs/local.json | 12 +++++++++++ static/js/components/subject_page/block.tsx | 3 --- static/js/components/tiles/chart_footer.tsx | 21 ++++++++----------- .../shared/facet_selector/facet_selector.tsx | 4 ++-- .../js/tools/shared/metadata/tile_sources.tsx | 3 --- 8 files changed, 41 insertions(+), 20 deletions(-) diff --git a/server/config/feature_flag_configs/autopush.json b/server/config/feature_flag_configs/autopush.json index f0f85db923..c0967102bd 100644 --- a/server/config/feature_flag_configs/autopush.json +++ b/server/config/feature_flag_configs/autopush.json @@ -59,6 +59,12 @@ "owner": "javiervazquez", "description": "Enables the follow up questions generated from related topics to general audience." }, + { + "name": "metadata_modal", + "enabled": true, + "owner": "nick-next", + "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." + }, { "name": "page_overview_ga", "enabled": false, diff --git a/server/config/feature_flag_configs/custom.json b/server/config/feature_flag_configs/custom.json index a09af3795b..632322fc6e 100644 --- a/server/config/feature_flag_configs/custom.json +++ b/server/config/feature_flag_configs/custom.json @@ -59,6 +59,12 @@ "owner": "javiervazquez", "description": "Enables the follow up questions generated from related topics to general audience." }, + { + "name": "metadata_modal", + "enabled": true, + "owner": "nick-next", + "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." + }, { "name": "page_overview_ga", "enabled": false, diff --git a/server/config/feature_flag_configs/dev.json b/server/config/feature_flag_configs/dev.json index e288a3b3a8..8d672963b2 100644 --- a/server/config/feature_flag_configs/dev.json +++ b/server/config/feature_flag_configs/dev.json @@ -59,6 +59,12 @@ "owner": "javiervazquez", "description": "Enables the follow up questions generated from related topics to general audience." }, + { + "name": "metadata_modal", + "enabled": true, + "owner": "nick-next", + "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." + }, { "name": "page_overview_ga", "enabled": false, diff --git a/server/config/feature_flag_configs/local.json b/server/config/feature_flag_configs/local.json index 1963983ed3..e53b05a984 100644 --- a/server/config/feature_flag_configs/local.json +++ b/server/config/feature_flag_configs/local.json @@ -59,6 +59,18 @@ "owner": "javiervazquez", "description": "Enables the follow up questions generated from related topics to general audience." }, + { + "name": "metadata_modal", + "enabled": true, + "owner": "nick-next", + "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." + }, + { + "name": "metadata_modal", + "enabled": true, + "owner": "nick-next", + "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." + }, { "name": "page_overview_ga", "enabled": false, diff --git a/static/js/components/subject_page/block.tsx b/static/js/components/subject_page/block.tsx index 452ba851de..f1d4cad014 100644 --- a/static/js/components/subject_page/block.tsx +++ b/static/js/components/subject_page/block.tsx @@ -57,9 +57,6 @@ import { FacetSelector, FacetSelectorFacetInfo, } from "../../shared/facet_selector/facet_selector"; -import { - isFeatureEnabled, -} from "../../shared/feature_flags/util"; import { usePromiseResolver } from "../../shared/hooks/promise_resolver"; import { NamedPlace, NamedTypedPlace, StatVarSpec } from "../../shared/types"; import { diff --git a/static/js/components/tiles/chart_footer.tsx b/static/js/components/tiles/chart_footer.tsx index b9f9ae8299..d2436bcc94 100644 --- a/static/js/components/tiles/chart_footer.tsx +++ b/static/js/components/tiles/chart_footer.tsx @@ -22,9 +22,6 @@ import React, { RefObject, useState } from "react"; import { intl } from "../../i18n/i18n"; import { messages } from "../../i18n/i18n_messages"; -import { - isFeatureEnabled, -} from "../../shared/feature_flags/util"; import { GA_EVENT_TILE_DOWNLOAD, GA_EVENT_TILE_EXPLORE_MORE, @@ -99,15 +96,15 @@ export function ChartFooter(props: ChartFooterPropType): JSX.Element { )} {props.getObservationSpecs && ( -
- -
- )} +
+ +
+ )} {props.exploreLink && (
diff --git a/static/js/shared/facet_selector/facet_selector.tsx b/static/js/shared/facet_selector/facet_selector.tsx index 78c5dd9f01..b42de7c8e3 100644 --- a/static/js/shared/facet_selector/facet_selector.tsx +++ b/static/js/shared/facet_selector/facet_selector.tsx @@ -262,8 +262,8 @@ export function FacetSelector(props: FacetSelectorProps): ReactElement { ${variant === "inline" ? "padding: 0;" : ""} &:hover:not(:disabled):not([aria-disabled]) { ${variant === "inline" - ? "text-decoration: underline; border: 1px solid transparent;" - : ""} + ? "text-decoration: underline; border: 1px solid transparent;" + : ""} } `} > diff --git a/static/js/tools/shared/metadata/tile_sources.tsx b/static/js/tools/shared/metadata/tile_sources.tsx index 65cf495e89..bf463e7356 100644 --- a/static/js/tools/shared/metadata/tile_sources.tsx +++ b/static/js/tools/shared/metadata/tile_sources.tsx @@ -28,9 +28,6 @@ import { ApiButton } from "../../../components/tiles/components/api_button"; import { NL_SOURCE_REPLACEMENTS } from "../../../constants/app/explore_constants"; import { intl } from "../../../i18n/i18n"; import { messages } from "../../../i18n/i18n_messages"; -import { - isFeatureEnabled, -} from "../../../shared/feature_flags/util"; import { GA_EVENT_TILE_EXPLORE_MORE, GA_PARAM_URL, From c5b64118145eec9681cd122656e2e9885fc2c86f Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Thu, 5 Feb 2026 11:20:22 -0800 Subject: [PATCH 3/5] removed duplicate feature flag --- server/config/feature_flag_configs/local.json | 6 ------ server/config/feature_flag_configs/staging.json | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/server/config/feature_flag_configs/local.json b/server/config/feature_flag_configs/local.json index e53b05a984..b888e5869e 100644 --- a/server/config/feature_flag_configs/local.json +++ b/server/config/feature_flag_configs/local.json @@ -65,12 +65,6 @@ "owner": "nick-next", "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." }, - { - "name": "metadata_modal", - "enabled": true, - "owner": "nick-next", - "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." - }, { "name": "page_overview_ga", "enabled": false, diff --git a/server/config/feature_flag_configs/staging.json b/server/config/feature_flag_configs/staging.json index 424ab73c72..f12a189ce8 100644 --- a/server/config/feature_flag_configs/staging.json +++ b/server/config/feature_flag_configs/staging.json @@ -59,6 +59,12 @@ "owner": "javiervazquez", "description": "Enables the follow up questions generated from related topics to general audience." }, + { + "name": "metadata_modal", + "enabled": true, + "owner": "nick-next", + "description": "Feature flag to show the complete metadata modal. If not set, the older modal will be used as a fallback." + }, { "name": "page_overview_ga", "enabled": false, From fb47d029204e61f6aab343bf5ae62511b309ec02 Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Thu, 5 Feb 2026 13:59:36 -0800 Subject: [PATCH 4/5] updated snapshots --- .../timeline/__snapshots__/page.test.tsx.snap | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/static/js/tools/timeline/__snapshots__/page.test.tsx.snap b/static/js/tools/timeline/__snapshots__/page.test.tsx.snap index 9fdcf0a0b4..f0b9545d7b 100644 --- a/static/js/tools/timeline/__snapshots__/page.test.tsx.snap +++ b/static/js/tools/timeline/__snapshots__/page.test.tsx.snap @@ -3,7 +3,7 @@ exports[`Single place and single stat var 1`] = ` "
-

One facet available for this chart

+

One facet available for this chart

@@ -27,7 +27,7 @@ exports[`Single place and single stat var 1`] = `
-
+
" `; @@ -35,7 +35,7 @@ exports[`Single place and single stat var 1`] = ` exports[`Single place and single stat var 2`] = ` "
-

One facet available for this chart

+

One facet available for this chart

@@ -59,12 +59,12 @@ exports[`Single place and single stat var 2`] = `
-
+
-

One facet available for this chart

+

One facet available for this chart

@@ -88,7 +88,7 @@ exports[`Single place and single stat var 2`] = `
-
+
" `; @@ -96,7 +96,7 @@ exports[`Single place and single stat var 2`] = ` exports[`Single place and single stat var 3`] = ` "
-

One facet available for this chart

+

One facet available for this chart

@@ -120,7 +120,7 @@ exports[`Single place and single stat var 3`] = `
-
+
" `; @@ -166,7 +166,7 @@ exports[`Single place and single stat var 4`] = ` exports[`chart options 1`] = ` "
-

One facet available for this chart

+

One facet available for this chart

@@ -190,7 +190,7 @@ exports[`chart options 1`] = `
-
+
" `; @@ -198,7 +198,7 @@ exports[`chart options 1`] = ` exports[`statVar not in PV-tree 1`] = ` "
-

One facet available for this chart

+

One facet available for this chart

@@ -222,7 +222,7 @@ exports[`statVar not in PV-tree 1`] = `
-
+
" `; From c4c1c3d41802924be758571faa2da822fe81ca00 Mon Sep 17 00:00:00 2001 From: Dan Noble Date: Thu, 5 Feb 2026 15:22:26 -0800 Subject: [PATCH 5/5] test fix --- .../shared/facet_selector/facet_selector_standard_content.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/js/shared/facet_selector/facet_selector_standard_content.tsx b/static/js/shared/facet_selector/facet_selector_standard_content.tsx index d3be531597..1924143e4c 100644 --- a/static/js/shared/facet_selector/facet_selector_standard_content.tsx +++ b/static/js/shared/facet_selector/facet_selector_standard_content.tsx @@ -80,7 +80,7 @@ export function FacetSelectorStandardContent({ if (firstSelectedId) { const targetElement = itemRefs.current.get(firstSelectedId); if (targetElement) { - targetElement.scrollIntoView({ + targetElement.scrollIntoView?.({ block: "center", }); }