diff --git a/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts b/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts index bb0ab5d6f45e2..26267e1ef510c 100644 --- a/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts +++ b/superset-frontend/packages/superset-ui-core/src/chart/models/ChartProps.ts @@ -21,6 +21,7 @@ * TS declarations for selectors with up to 12 arguments. */ // @ts-nocheck import { createSelector } from 'reselect'; +import { RefObject } from 'react'; import { AppSection, Behavior, @@ -128,6 +129,8 @@ export default class ChartProps { isRefreshing?: boolean; + parentRef?: RefObject; + constructor(config: ChartPropsConfig & { formData?: FormData } = {}) { const { annotationData = {}, @@ -143,6 +146,7 @@ export default class ChartProps { height = DEFAULT_HEIGHT, appSection, isRefreshing, + parentRef, } = config; this.width = width; this.height = height; @@ -159,6 +163,7 @@ export default class ChartProps { this.behaviors = behaviors; this.appSection = appSection; this.isRefreshing = isRefreshing; + this.parentRef = parentRef; } } @@ -178,6 +183,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector { input => input.behaviors, input => input.appSection, input => input.isRefreshing, + input => input.parentRef, ( annotationData, datasource, @@ -192,6 +198,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector { behaviors, appSection, isRefreshing, + parentRef, ) => new ChartProps({ annotationData, @@ -207,6 +214,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector { behaviors, appSection, isRefreshing, + parentRef, }), ); }; diff --git a/superset-frontend/src/components/Select/Select.tsx b/superset-frontend/src/components/Select/Select.tsx index a36a0d569aeb7..ca714b14b0c2b 100644 --- a/superset-frontend/src/components/Select/Select.tsx +++ b/superset-frontend/src/components/Select/Select.tsx @@ -89,6 +89,12 @@ export interface SelectProps extends PickedSelectProps { * False by default. * */ allowNewOptions?: boolean; + /** + * Refobject that references the parent container + * the select component is located to prevent + * dropdown movement onscroll. + */ + getPopupContainer?: () => RefObject; /** * It adds the aria-label tag for accessibility standards. * Must be plain English and localized. @@ -271,6 +277,7 @@ const Select = ({ ariaLabel, fetchOnlyOnSearch, filterOption = true, + getPopupContainer, header = null, invertSelection = false, labelInValue = false, @@ -701,7 +708,6 @@ const Select = ({ setIsLoading(loading); } }, [isLoading, loading]); - return ( {header} @@ -709,7 +715,9 @@ const Select = ({ aria-label={ariaLabel || name} dropdownRender={dropdownRender} filterOption={handleFilterOption} - getPopupContainer={triggerNode => triggerNode.parentNode} + getPopupContainer={ + getPopupContainer || (triggerNode => triggerNode.parentNode) + } labelInValue={isAsync || labelInValue} maxTagCount={MAX_TAG_COUNT} mode={mappedMode} diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx index b164e5a7341be..2565e8c0c982a 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx @@ -22,6 +22,7 @@ import React, { useMemo, useState, useRef, + RefObject, } from 'react'; import { styled, t, DataMask, css, SupersetTheme } from '@superset-ui/core'; import Popover from 'src/components/Popover'; @@ -41,6 +42,7 @@ interface CascadePopoverProps { inView?: boolean; onVisibleChange: (visible: boolean) => void; onFilterSelectionChange: (filter: Filter, dataMask: DataMask) => void; + parentRef?: RefObject; } const StyledTitleBox = styled.div` @@ -93,6 +95,7 @@ const CascadePopover: React.FC = ({ onFilterSelectionChange, directPathToChild, inView, + parentRef, }) => { const [currentPathToChild, setCurrentPathToChild] = useState(); const dataMask = dataMaskSelected[filter.id]; @@ -163,6 +166,7 @@ const CascadePopover: React.FC = ({ directPathToChild={directPathToChild} onFilterSelectionChange={onFilterSelectionChange} inView={inView} + parentRef={parentRef} /> ); } diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx index f0c0a564a08ee..e5948cd649fdb 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React, { FC, useCallback, useMemo, useState } from 'react'; +import React, { FC, RefObject, useCallback, useMemo, useState } from 'react'; import { css } from '@emotion/react'; import { DataMask, styled, t } from '@superset-ui/core'; import { @@ -49,12 +49,14 @@ type FilterControlsProps = { directPathToChild?: string[]; dataMaskSelected: DataMaskStateWithId; onFilterSelectionChange: (filter: Filter, dataMask: DataMask) => void; + parentRef?: RefObject; }; const FilterControls: FC = ({ directPathToChild, dataMaskSelected, onFilterSelectionChange, + parentRef, }) => { const [visiblePopoverId, setVisiblePopoverId] = useState(null); const filters = useFilters(); @@ -105,6 +107,7 @@ const FilterControls: FC = ({ onFilterSelectionChange={onFilterSelectionChange} directPathToChild={directPathToChild} inView={false} + parentRef={parentRef} /> ); }, diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx index 02156b22bc62c..66f07347a9e6a 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx @@ -19,7 +19,14 @@ /* eslint-disable no-param-reassign */ import { DataMask, HandlerFunction, styled, t } from '@superset-ui/core'; -import React, { useEffect, useState, useCallback, useMemo } from 'react'; +import React, { + useEffect, + useState, + useCallback, + useMemo, + useRef, + RefObject, +} from 'react'; import { useDispatch, useSelector } from 'react-redux'; import cx from 'classnames'; import Icons from 'src/components/Icons'; @@ -159,6 +166,7 @@ const FilterBar: React.FC = ({ const filterSetFilterValues = Object.values(filterSets); const [tab, setTab] = useState(TabIds.AllFilters); const filters = useFilters(); + const parent = useRef() as RefObject | undefined; const previousFilters = usePrevious(filters); const filterValues = Object.values(filters); const dashboardId = useSelector( @@ -305,6 +313,7 @@ const FilterBar: React.FC = ({ {...getFilterBarTestId()} className={cx({ open: filtersOpen })} width={width} + ref={parent} > = ({ dataMaskSelected={dataMaskSelected} directPathToChild={directPathToChild} onFilterSelectionChange={handleFilterSelectionChange} + parentRef={parent} /> )} diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index 15d85eea9874f..5f6e6f54d7509 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -83,9 +83,9 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { setFocusedFilter, unsetFocusedFilter, appSection, - showOverflow, parentRef, } = props; + const { enableEmptyFilter, multiSelect, @@ -291,7 +291,7 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { value={filterState.value || []} disabled={isDisabled} getPopupContainer={ - showOverflow ? () => parentRef?.current : undefined + parentRef?.current ? () => parentRef?.current : undefined } showSearch={showSearch} mode={multiSelect ? 'multiple' : 'single'} diff --git a/superset-frontend/src/filters/components/Select/transformProps.ts b/superset-frontend/src/filters/components/Select/transformProps.ts index 59e58d352ef00..5931c53ad1c11 100644 --- a/superset-frontend/src/filters/components/Select/transformProps.ts +++ b/superset-frontend/src/filters/components/Select/transformProps.ts @@ -32,6 +32,7 @@ export default function transformProps( appSection, filterState, isRefreshing, + parentRef, } = chartProps; const newFormData = { ...DEFAULT_FORM_DATA, ...formData }; const { @@ -50,6 +51,7 @@ export default function transformProps( filterState, coltypeMap, appSection, + parentRef, width, behaviors, height,