This is an automated email from the ASF dual-hosted git repository.
alexandrusoare pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 6a801ff94f6 fix(explore): sync FilterInput value with parent filter
state (#42342)
6a801ff94f6 is described below
commit 6a801ff94f6216c8e6f1de0df2b8422c437cec95
Author: Alexandru Soare <[email protected]>
AuthorDate: Wed Jul 29 13:26:49 2026 +0300
fix(explore): sync FilterInput value with parent filter state (#42342)
---
.../DataTableControl/FilterInput.test.tsx | 30 +++++++++++++++++---
.../explore/components/DataTableControl/index.tsx | 33 +++++++++++++++++++---
.../components/DataTableControls.tsx | 7 ++++-
.../DataTablesPane/components/SamplesPane.tsx | 2 ++
.../components/SingleQueryResultPane.tsx | 1 +
.../src/explore/components/DataTablesPane/types.ts | 1 +
6 files changed, 65 insertions(+), 9 deletions(-)
diff --git
a/superset-frontend/src/explore/components/DataTableControl/FilterInput.test.tsx
b/superset-frontend/src/explore/components/DataTableControl/FilterInput.test.tsx
index 8d4d6188dd8..b65c39e6349 100755
---
a/superset-frontend/src/explore/components/DataTableControl/FilterInput.test.tsx
+++
b/superset-frontend/src/explore/components/DataTableControl/FilterInput.test.tsx
@@ -19,10 +19,19 @@
import { render, screen, userEvent } from 'spec/helpers/testing-library';
import { FilterInput } from '.';
-jest.mock('lodash', () => ({
- ...jest.requireActual('lodash'),
- debounce: (fuc: Function) => fuc,
-}));
+jest.mock('lodash', () => {
+ const debounce = <T extends (...args: never[]) => unknown>(func: T) => {
+ const debounced = (...args: Parameters<T>) => func(...args);
+ debounced.flush = jest.fn();
+ return debounced;
+ };
+
+ return {
+ __esModule: true,
+ ...jest.requireActual('lodash'),
+ debounce,
+ };
+});
test('Render a FilterInput', async () => {
const onChangeHandler = jest.fn();
@@ -53,6 +62,19 @@ test('FilterInput auto-focuses when a non-editable element
(e.g. a tab) has focu
}
});
+test('FilterInput preserves value after unmount and remount', () => {
+ const onChangeHandler = jest.fn();
+ const { unmount } = render(
+ <FilterInput onChangeHandler={onChangeHandler} value="abc" />,
+ );
+ expect((screen.getByRole('textbox') as HTMLInputElement).value).toBe('abc');
+
+ unmount();
+
+ render(<FilterInput onChangeHandler={onChangeHandler} value="abc" />);
+ expect((screen.getByRole('textbox') as HTMLInputElement).value).toBe('abc');
+});
+
test('FilterInput does not steal focus when another input already has focus',
() => {
const onChangeHandler = jest.fn();
const otherInput = document.createElement('input');
diff --git
a/superset-frontend/src/explore/components/DataTableControl/index.tsx
b/superset-frontend/src/explore/components/DataTableControl/index.tsx
index c4bfda8c852..041104bc93b 100755
--- a/superset-frontend/src/explore/components/DataTableControl/index.tsx
+++ b/superset-frontend/src/explore/components/DataTableControl/index.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { useMemo, useEffect, useRef, RefObject } from 'react';
+import { useMemo, useState, useEffect, useRef, RefObject } from 'react';
import { t } from '@apache-superset/core/translation';
import { css, styled, useTheme } from '@apache-superset/core/theme';
@@ -100,11 +100,24 @@ export const CopyToClipboardButton = ({
export const FilterInput = ({
onChangeHandler,
shouldFocus = false,
+ value: externalValue = '',
}: {
onChangeHandler(filterText: string): void;
shouldFocus?: boolean;
+ value?: string;
}) => {
const inputRef: RefObject<any> = useRef(null);
+ const [internalValue, setInternalValue] = useState(externalValue);
+ const lastEmittedValue = useRef(externalValue);
+ const onChangeRef = useRef(onChangeHandler);
+ onChangeRef.current = onChangeHandler;
+
+ useEffect(() => {
+ if (externalValue !== lastEmittedValue.current) {
+ setInternalValue(externalValue);
+ }
+ lastEmittedValue.current = externalValue;
+ }, [externalValue]);
useEffect(() => {
if (inputRef.current && shouldFocus) {
@@ -125,16 +138,28 @@ export const FilterInput = ({
}, []);
const theme = useTheme();
- const debouncedChangeHandler = debounce(
- onChangeHandler,
- Constants.SLOW_DEBOUNCE,
+ const debouncedChangeHandler = useMemo(
+ () =>
+ debounce((value: string) => {
+ lastEmittedValue.current = value;
+ onChangeRef.current(value);
+ }, Constants.SLOW_DEBOUNCE),
+ [],
);
+
+ useEffect(
+ () => () => debouncedChangeHandler.flush(),
+ [debouncedChangeHandler],
+ );
+
return (
<Input
prefix={<Icons.SearchOutlined iconSize="l" />}
placeholder={t('Search')}
+ value={internalValue}
onChange={(event: any) => {
const filterText = event.target.value;
+ setInternalValue(filterText);
debouncedChangeHandler(filterText);
}}
css={css`
diff --git
a/superset-frontend/src/explore/components/DataTablesPane/components/DataTableControls.tsx
b/superset-frontend/src/explore/components/DataTablesPane/components/DataTableControls.tsx
index 3ab00c9dcb1..092a9cf134f 100644
---
a/superset-frontend/src/explore/components/DataTablesPane/components/DataTableControls.tsx
+++
b/superset-frontend/src/explore/components/DataTablesPane/components/DataTableControls.tsx
@@ -60,6 +60,7 @@ export const TableControls = ({
data,
datasourceId,
onInputChange,
+ filterText,
columnNames,
columnTypes,
rowcount,
@@ -93,7 +94,11 @@ export const TableControls = ({
const { canCopyClipboard: copyEnabled } = usePermissions();
return (
<TableControlsWrapper>
- <FilterInput onChangeHandler={onInputChange} shouldFocus />
+ <FilterInput
+ onChangeHandler={onInputChange}
+ shouldFocus
+ value={filterText}
+ />
<div
css={css`
display: flex;
diff --git
a/superset-frontend/src/explore/components/DataTablesPane/components/SamplesPane.tsx
b/superset-frontend/src/explore/components/DataTablesPane/components/SamplesPane.tsx
index e743ad6844a..9e2fa986164 100644
---
a/superset-frontend/src/explore/components/DataTablesPane/components/SamplesPane.tsx
+++
b/superset-frontend/src/explore/components/DataTablesPane/components/SamplesPane.tsx
@@ -148,6 +148,7 @@ export const SamplesPane = ({
rowcount={rowcount}
datasourceId={datasourceId}
onInputChange={handleInputChange}
+ filterText={filterText}
isLoading={isLoading}
canDownload={canDownload}
rowLimit={rowLimit}
@@ -176,6 +177,7 @@ export const SamplesPane = ({
rowcount={rowcount}
datasourceId={datasourceId}
onInputChange={handleInputChange}
+ filterText={filterText}
isLoading={isLoading}
canDownload={canDownload}
rowLimit={rowLimit}
diff --git
a/superset-frontend/src/explore/components/DataTablesPane/components/SingleQueryResultPane.tsx
b/superset-frontend/src/explore/components/DataTablesPane/components/SingleQueryResultPane.tsx
index 8cd279f4164..0c2cb0a4e5d 100644
---
a/superset-frontend/src/explore/components/DataTablesPane/components/SingleQueryResultPane.tsx
+++
b/superset-frontend/src/explore/components/DataTablesPane/components/SingleQueryResultPane.tsx
@@ -81,6 +81,7 @@ export const SingleQueryResultPane = ({
rowcount={rowcount}
datasourceId={datasourceId}
onInputChange={handleInputChange}
+ filterText={filterText}
isLoading={false}
canDownload={canDownload}
rowLimit={rowLimit}
diff --git a/superset-frontend/src/explore/components/DataTablesPane/types.ts
b/superset-frontend/src/explore/components/DataTablesPane/types.ts
index 411e0ef3bff..e7db8500638 100644
--- a/superset-frontend/src/explore/components/DataTablesPane/types.ts
+++ b/superset-frontend/src/explore/components/DataTablesPane/types.ts
@@ -76,6 +76,7 @@ export interface TableControlsProps extends
DrillControlsProps {
// {datasource.id}__{datasource.type}, eg: 1__table
datasourceId?: string;
onInputChange: (input: string) => void;
+ filterText?: string;
columnNames: string[];
columnTypes: GenericDataType[];
isLoading: boolean;