Skip to content
Snippets Groups Projects
Unverified Commit d438e16d authored by Alexander Polyankin's avatar Alexander Polyankin Committed by GitHub
Browse files

Fix data picker search (#29325)

parent abc81162
No related branches found
No related tags found
No related merge requests found
......@@ -84,9 +84,12 @@ const setup = (
React.ComponentProps<typeof ConnectedActionDashcardSettings>
>,
) => {
const searchItems = models.map(model =>
createMockCollectionItem({ ...model, model: "dataset" }),
);
const closeSpy = jest.fn();
setupSearchEndpoints(models.map(model => createMockCollectionItem(model)));
setupSearchEndpoints(searchItems);
setupCardsEndpoints(models);
setupActionsEndpoints([...actions1, ...actions2]);
......
......@@ -48,7 +48,6 @@ function DataPickerViewContent({
if (searchQuery.trim().length > MIN_SEARCH_LENGTH) {
return (
<DataSearch
value={value}
searchQuery={searchQuery}
availableDataTypes={availableDataTypes}
onChange={onChange}
......
......@@ -16,7 +16,6 @@ import { useDataPicker } from "../DataPickerContext";
import type { DataPickerValue, DataPickerDataType } from "../types";
interface DataSearchProps {
value: DataPickerValue;
searchQuery: string;
availableDataTypes: DataPickerDataType[];
onChange: (value: DataPickerValue) => void;
......@@ -68,7 +67,7 @@ function getValueForVirtualTable(table: TableSearchResult): DataPickerValue {
isDatasets: type === "models",
});
return {
type: "models",
type,
databaseId: SAVED_QUESTIONS_VIRTUAL_DB_ID,
schemaId,
collectionId: table.collection?.id || "root",
......@@ -85,7 +84,6 @@ function getNextValue(table: TableSearchResult): DataPickerValue {
}
function DataSearch({
value,
searchQuery,
availableDataTypes,
onChange,
......@@ -94,11 +92,8 @@ function DataSearch({
const { setQuery } = search;
const searchModels: SearchModel[] = useMemo(() => {
if (!value.type) {
return availableDataTypes.map(type => DATA_TYPE_SEARCH_MODEL_MAP[type]);
}
return [DATA_TYPE_SEARCH_MODEL_MAP[value.type]];
}, [value.type, availableDataTypes]);
return availableDataTypes.map(type => DATA_TYPE_SEARCH_MODEL_MAP[type]);
}, [availableDataTypes]);
const onSelect = useCallback(
(table: TableSearchResult) => {
......
......@@ -17,6 +17,7 @@ import {
SAMPLE_MODEL,
SAMPLE_MODEL_2,
SAMPLE_MODEL_3,
SAMPLE_QUESTION,
} from "./common";
const ROOT_COLLECTION_MODEL_VIRTUAL_SCHEMA_ID = getCollectionVirtualSchemaId(
......@@ -189,4 +190,54 @@ describe("DataPicker — picking models", () => {
screen.queryByRole("button", { name: /Back/i }),
).not.toBeInTheDocument();
});
it("should be able to search for a model", async () => {
const { onChange } = await setup({
filters: {
types: type => type === "models",
},
});
userEvent.type(screen.getByRole("textbox"), SAMPLE_MODEL.name);
expect(await screen.findByText(SAMPLE_MODEL.name)).toBeInTheDocument();
expect(screen.queryByText(SAMPLE_QUESTION.name)).not.toBeInTheDocument();
userEvent.click(screen.getByText(SAMPLE_MODEL.name));
expect(onChange).toHaveBeenLastCalledWith({
type: "models",
databaseId: SAVED_QUESTIONS_VIRTUAL_DB_ID,
schemaId: getCollectionVirtualSchemaId(SAMPLE_COLLECTION, {
isDatasets: true,
}),
collectionId: SAMPLE_MODEL.collection_id,
tableIds: [getQuestionVirtualTableId(SAMPLE_MODEL.id)],
});
});
it("should be able to search for a model when a question was selected", async () => {
const { onChange } = await setup({
initialValue: {
type: "questions",
databaseId: SAVED_QUESTIONS_VIRTUAL_DB_ID,
schemaId: getCollectionVirtualSchemaId(SAMPLE_COLLECTION),
collectionId: "root",
tableIds: [getQuestionVirtualTableId(SAMPLE_QUESTION.id)],
},
});
userEvent.type(screen.getByRole("textbox"), "Sample");
expect(await screen.findByText(SAMPLE_MODEL.name)).toBeInTheDocument();
expect(screen.getByText(SAMPLE_QUESTION.name)).toBeInTheDocument();
userEvent.click(screen.getByText(SAMPLE_MODEL.name));
expect(onChange).toHaveBeenLastCalledWith({
type: "models",
databaseId: SAVED_QUESTIONS_VIRTUAL_DB_ID,
schemaId: getCollectionVirtualSchemaId(SAMPLE_COLLECTION, {
isDatasets: true,
}),
collectionId: SAMPLE_MODEL.collection_id,
tableIds: [getQuestionVirtualTableId(SAMPLE_MODEL.id)],
});
});
});
......@@ -11,12 +11,13 @@ import {
} from "metabase-lib/metadata/utils/saved-questions";
import {
setup,
EMPTY_COLLECTION,
SAMPLE_COLLECTION,
SAMPLE_MODEL,
SAMPLE_QUESTION,
SAMPLE_QUESTION_2,
SAMPLE_QUESTION_3,
setup,
} from "./common";
const ROOT_COLLECTION_QUESTIONS_VIRTUAL_SCHEMA_ID =
......@@ -167,4 +168,52 @@ describe("DataPicker — picking questions", () => {
tableIds: [],
});
});
it("should be able to search for a question", async () => {
const { onChange } = await setup({
filters: {
types: type => type === "questions",
},
});
userEvent.type(screen.getByRole("textbox"), SAMPLE_QUESTION.name);
expect(await screen.findByText(SAMPLE_QUESTION.name)).toBeInTheDocument();
expect(screen.queryByText(SAMPLE_MODEL.name)).not.toBeInTheDocument();
userEvent.click(screen.getByText(SAMPLE_QUESTION.name));
expect(onChange).toHaveBeenLastCalledWith({
type: "questions",
databaseId: SAVED_QUESTIONS_VIRTUAL_DB_ID,
schemaId: getCollectionVirtualSchemaId(SAMPLE_COLLECTION),
collectionId: SAMPLE_QUESTION.collection_id,
tableIds: [getQuestionVirtualTableId(SAMPLE_QUESTION.id)],
});
});
it("should be able to search for a question when a model was selected", async () => {
const { onChange } = await setup({
initialValue: {
type: "questions",
databaseId: SAVED_QUESTIONS_VIRTUAL_DB_ID,
schemaId: getCollectionVirtualSchemaId(SAMPLE_COLLECTION, {
isDatasets: true,
}),
collectionId: "root",
tableIds: [getQuestionVirtualTableId(SAMPLE_MODEL.id)],
},
});
userEvent.type(screen.getByRole("textbox"), "Sample");
expect(await screen.findByText(SAMPLE_QUESTION.name)).toBeInTheDocument();
expect(screen.getByText(SAMPLE_MODEL.name)).toBeInTheDocument();
userEvent.click(screen.getByText(SAMPLE_QUESTION.name));
expect(onChange).toHaveBeenLastCalledWith({
type: "questions",
databaseId: SAVED_QUESTIONS_VIRTUAL_DB_ID,
schemaId: getCollectionVirtualSchemaId(SAMPLE_COLLECTION),
collectionId: SAMPLE_QUESTION.collection_id,
tableIds: [getQuestionVirtualTableId(SAMPLE_QUESTION.id)],
});
});
});
/* istanbul ignore file */
import React from "react";
import fetchMock from "fetch-mock";
import {
renderWithProviders,
......@@ -11,13 +10,16 @@ import {
setupCollectionsEndpoints,
setupCollectionVirtualSchemaEndpoints,
setupDatabasesEndpoints,
setupSearchEndpoints,
} from "__support__/server-mocks";
import Input from "metabase/core/components/Input";
import { ROOT_COLLECTION } from "metabase/entities/collections";
import {
createMockCard,
createMockCollection,
createMockCollectionItem,
createMockDatabase,
createMockTable,
} from "metabase-types/api/mocks";
......@@ -25,6 +27,7 @@ import { createMockSettingsState } from "metabase-types/store/mocks";
import type { DataPickerValue, DataPickerFiltersProp } from "../types";
import useDataPickerValue from "../useDataPickerValue";
import { useDataPicker } from "../../DataPicker";
import DataPicker from "../DataPickerContainer";
export const SAMPLE_TABLE = createMockTable({
......@@ -68,15 +71,18 @@ export const EMPTY_DATABASE = createMockDatabase({
tables: [],
});
export const SAMPLE_COLLECTION_ID = 1;
export const EMPTY_COLLECTION_ID = 2;
export const SAMPLE_COLLECTION = createMockCollection({
id: 1,
id: SAMPLE_COLLECTION_ID,
name: "Sample Collection",
location: "/",
here: ["card", "dataset"],
});
export const EMPTY_COLLECTION = createMockCollection({
id: 2,
id: EMPTY_COLLECTION_ID,
name: "Empty Collection",
location: "/",
here: [],
......@@ -87,33 +93,51 @@ export const SAMPLE_MODEL = createMockCard({
id: 1,
name: "Sample Model",
dataset: true,
collection_id: SAMPLE_COLLECTION_ID,
});
export const SAMPLE_MODEL_2 = createMockCard({
id: 2,
name: "Sample Model 2",
dataset: true,
collection_id: SAMPLE_COLLECTION_ID,
});
export const SAMPLE_MODEL_3 = createMockCard({
id: 3,
name: "Sample Model 3",
dataset: true,
collection_id: SAMPLE_COLLECTION_ID,
});
export const SAMPLE_QUESTION = createMockCard({
id: 4,
name: "Sample Saved Question",
collection_id: SAMPLE_COLLECTION_ID,
});
export const SAMPLE_QUESTION_2 = createMockCard({
id: 5,
name: "Sample Saved Question 2",
collection_id: SAMPLE_COLLECTION_ID,
});
export const SAMPLE_QUESTION_3 = createMockCard({
id: 6,
name: "Sample Saved Question 3",
collection_id: SAMPLE_COLLECTION_ID,
});
export const SAMPLE_QUESTION_SEARCH_ITEM = createMockCollectionItem({
...SAMPLE_QUESTION,
model: "card",
collection: SAMPLE_COLLECTION,
});
export const SAMPLE_MODEL_SEARCH_ITEM = createMockCollectionItem({
...SAMPLE_MODEL,
model: "dataset",
collection: SAMPLE_COLLECTION,
});
function DataPickerWrapper({
......@@ -129,18 +153,28 @@ function DataPickerWrapper({
}) {
const [value, setValue] = useDataPickerValue(initialValue);
return (
<DataPicker
value={value}
filters={filters}
isMultiSelect={isMultiSelect}
onChange={(value: DataPickerValue) => {
setValue(value);
onChange(value);
}}
/>
<DataPicker.Provider>
<DataPickerSearchInput />
<DataPicker
value={value}
filters={filters}
isMultiSelect={isMultiSelect}
onChange={(value: DataPickerValue) => {
setValue(value);
onChange(value);
}}
/>
</DataPicker.Provider>
);
}
function DataPickerSearchInput() {
const { search } = useDataPicker();
const { query, setQuery } = search;
return <Input value={query} onChange={e => setQuery(e.target.value)} />;
}
interface SetupOpts {
initialValue?: DataPickerValue;
filters?: DataPickerFiltersProp;
......@@ -182,16 +216,6 @@ export async function setup({
setupDatabasesEndpoints([], { hasSavedQuestions: false });
}
fetchMock.get(
{
url: "path:/api/search",
query: { models: "dataset", limit: 1 },
},
{
data: hasModels ? [SAMPLE_MODEL] : [],
},
);
setupCollectionsEndpoints([SAMPLE_COLLECTION, EMPTY_COLLECTION]);
setupCollectionVirtualSchemaEndpoints(createMockCollection(ROOT_COLLECTION), [
......@@ -210,6 +234,15 @@ export async function setup({
setupCollectionVirtualSchemaEndpoints(EMPTY_COLLECTION, []);
if (hasModels) {
setupSearchEndpoints([
SAMPLE_QUESTION_SEARCH_ITEM,
SAMPLE_MODEL_SEARCH_ITEM,
]);
} else {
setupSearchEndpoints([SAMPLE_QUESTION_SEARCH_ITEM]);
}
const settings = createMockSettingsState({
"enable-nested-queries": hasNestedQueriesEnabled,
});
......
......@@ -10,8 +10,8 @@ import Search from "metabase/entities/search";
const propTypes = {
databaseId: PropTypes.string,
searchQuery: PropTypes.string.required,
onSelect: PropTypes.func.required,
searchQuery: PropTypes.string.isRequired,
onSelect: PropTypes.func.isRequired,
searchModels: PropTypes.arrayOf(
PropTypes.oneOf(["card", "dataset", "table"]),
),
......
import fetchMock from "fetch-mock";
import type { SearchModelType, CollectionItem } from "metabase-types/api";
import type { CollectionItem } from "metabase-types/api";
export function setupSearchEndpoints(
items: CollectionItem[],
models: SearchModelType[] = [],
) {
fetchMock.get("path:/api/search", {
available_models: [
"dashboard",
"card",
"dataset",
"collection",
"table",
"database",
],
data: items,
total: items.length,
models, // this should reflect what is in the query param
limit: null,
offset: null,
table_db_id: null,
export function setupSearchEndpoints(items: CollectionItem[]) {
const availableModels = items.map(({ model }) => model);
fetchMock.get("path:/api/search", uri => {
const url = new URL(uri);
const models = url.searchParams.getAll("models");
const matchedItems = items.filter(({ model }) => models.includes(model));
return {
data: matchedItems,
total: matchedItems.length,
models, // this should reflect what is in the query param
available_models: availableModels,
limit: null,
offset: null,
table_db_id: null,
};
});
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment