diff --git a/frontend/src/metabase/containers/DataPicker/DataPickerContainer.tsx b/frontend/src/metabase/containers/DataPicker/DataPickerContainer.tsx index bcc293d78c99fa95c49b179b679f2463df117e44..a21f7d08d8d8719607e538dc050b4cbe38a89125 100644 --- a/frontend/src/metabase/containers/DataPicker/DataPickerContainer.tsx +++ b/frontend/src/metabase/containers/DataPicker/DataPickerContainer.tsx @@ -18,13 +18,13 @@ import { SAVED_QUESTIONS_VIRTUAL_DB_ID, } from "metabase-lib/metadata/utils/saved-questions"; +import { DEFAULT_DATA_PICKER_FILTERS } from "./constants"; +import { DataPickerContextProvider, useDataPicker } from "./DataPickerContext"; import type { DataPickerProps as DataPickerOwnProps, DataPickerDataType, } from "./types"; - -import { DataPickerContextProvider, useDataPicker } from "./DataPickerContext"; -import { getDataTypes, DEFAULT_DATA_PICKER_FILTERS } from "./utils"; +import { getDataTypes } from "./utils"; import DataPickerView from "./DataPickerView"; diff --git a/frontend/src/metabase/containers/DataPicker/DataPickerView.tsx b/frontend/src/metabase/containers/DataPicker/DataPickerView.tsx index f90af0b15c54845a5e9bd24150a68b820e8eebac..9e091c2c6dfda125191bfce19422af3e014e5aaf 100644 --- a/frontend/src/metabase/containers/DataPicker/DataPickerView.tsx +++ b/frontend/src/metabase/containers/DataPicker/DataPickerView.tsx @@ -3,8 +3,11 @@ import { t } from "ttag"; import { MIN_SEARCH_LENGTH } from "./constants"; -import type { DataPickerProps, DataPickerDataType } from "./types"; -import type { DataTypeInfoItem } from "./utils"; +import type { + DataPickerProps, + DataPickerDataType, + DataTypeInfoItem, +} from "./types"; import CardPicker from "./CardPicker"; import DataTypePicker from "./DataTypePicker"; diff --git a/frontend/src/metabase/containers/DataPicker/DataTypePicker/DataTypePicker.tsx b/frontend/src/metabase/containers/DataPicker/DataTypePicker/DataTypePicker.tsx index b68f35ea1751e2e18f11655ce8f5fbd071f618dd..d9f47c03c3c27092b5a1a8bca1e8d06895b4a3f2 100644 --- a/frontend/src/metabase/containers/DataPicker/DataTypePicker/DataTypePicker.tsx +++ b/frontend/src/metabase/containers/DataPicker/DataTypePicker/DataTypePicker.tsx @@ -1,5 +1,4 @@ -import type { DataTypeInfoItem } from "../utils"; -import type { DataPickerDataType } from "../types"; +import type { DataPickerDataType, DataTypeInfoItem } from "../types"; import { List, diff --git a/frontend/src/metabase/containers/DataPicker/constants.ts b/frontend/src/metabase/containers/DataPicker/constants.ts deleted file mode 100644 index adb548e3d6dcc9b0ecb34283ecd38662fd020d8f..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/containers/DataPicker/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const MIN_SEARCH_LENGTH = 2; diff --git a/frontend/src/metabase/containers/DataPicker/constants.tsx b/frontend/src/metabase/containers/DataPicker/constants.tsx new file mode 100644 index 0000000000000000000000000000000000000000..52f06f6d56907a3ef6f71caffd0426da70b57d87 --- /dev/null +++ b/frontend/src/metabase/containers/DataPicker/constants.tsx @@ -0,0 +1,43 @@ +import { t } from "ttag"; + +import { + DataPickerDataType, + DataPickerFilters, + DataTypeInfoItem, +} from "./types"; + +export const MIN_SEARCH_LENGTH = 2; + +export const DATA_BUCKET: Record<string, DataPickerDataType> = { + DATASETS: "models", + RAW_DATA: "raw-data", + SAVED_QUESTIONS: "questions", +} as const; + +export const DEFAULT_DATA_PICKER_FILTERS: DataPickerFilters = { + types: () => true, + databases: () => true, + schemas: () => true, + tables: () => true, +}; + +export const DATASETS_INFO_ITEM: DataTypeInfoItem = { + id: DATA_BUCKET.DATASETS, + icon: "model", + name: t`Models`, + description: t`The best starting place for new questions.`, +}; + +export const RAW_DATA_INFO_ITEM: DataTypeInfoItem = { + id: DATA_BUCKET.RAW_DATA, + icon: "database", + name: t`Raw Data`, + description: t`Unaltered tables in connected databases.`, +}; + +export const SAVED_QUESTIONS_INFO_ITEM: DataTypeInfoItem = { + id: DATA_BUCKET.SAVED_QUESTIONS, + name: t`Saved Questions`, + icon: "folder", + description: t`Use any question’s results to start a new question.`, +}; diff --git a/frontend/src/metabase/containers/DataPicker/index.ts b/frontend/src/metabase/containers/DataPicker/index.ts index 589aee81b824e59350399778047c3a5ff95ea944..2d2de21507878ed4792277fd09e981b5b674f4f5 100644 --- a/frontend/src/metabase/containers/DataPicker/index.ts +++ b/frontend/src/metabase/containers/DataPicker/index.ts @@ -1,12 +1,13 @@ // eslint-disable-next-line import/no-default-export -- deprecated usage export { default } from "./DataPickerContainer"; export { useDataPicker } from "./DataPickerContext"; - -export { default as useDataPickerValue } from "./useDataPickerValue"; - +export { DATA_BUCKET } from "./constants"; export type { DataPickerDataType, - DataPickerValue, - DataPickerProps, DataPickerFiltersProp, + DataPickerProps, + DataPickerValue, + DataTypeInfoItem, } from "./types"; +export { useDataPickerValue } from "./useDataPickerValue"; +export { getDataTypes } from "./utils"; diff --git a/frontend/src/metabase/containers/DataPicker/tests/common.tsx b/frontend/src/metabase/containers/DataPicker/tests/common.tsx index 88d4c124686f60da8fdbb2bcc0534708bd64c4eb..9e0b77a7bbc22264bbe05fca7dee2438e1479fc9 100644 --- a/frontend/src/metabase/containers/DataPicker/tests/common.tsx +++ b/frontend/src/metabase/containers/DataPicker/tests/common.tsx @@ -1,15 +1,15 @@ /* istanbul ignore file */ import { - renderWithProviders, - screen, - waitForElementToBeRemoved, -} from "__support__/ui"; -import { - setupCollectionsEndpoints, setupCollectionVirtualSchemaEndpoints, + setupCollectionsEndpoints, setupDatabasesEndpoints, setupSearchEndpoints, } from "__support__/server-mocks"; +import { + renderWithProviders, + screen, + waitForElementToBeRemoved, +} from "__support__/ui"; import Input from "metabase/core/components/Input"; import { ROOT_COLLECTION } from "metabase/entities/collections"; @@ -23,10 +23,11 @@ import { } from "metabase-types/api/mocks"; 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"; +import DataPicker, { + useDataPicker, + useDataPickerValue, +} from "../../DataPicker"; +import type { DataPickerFiltersProp, DataPickerValue } from "../types"; export const SAMPLE_TABLE = createMockTable({ id: 1, diff --git a/frontend/src/metabase/containers/DataPicker/types.ts b/frontend/src/metabase/containers/DataPicker/types.ts index f352de3d7f09f15495c1649dbd9284b8ca016b53..8bfa1277ec7bba85a94cc989df24dc440a32a6b1 100644 --- a/frontend/src/metabase/containers/DataPicker/types.ts +++ b/frontend/src/metabase/containers/DataPicker/types.ts @@ -5,9 +5,10 @@ import type { TableId, } from "metabase-types/api"; +import type { IconName } from "metabase/core/components/Icon"; import type Database from "metabase-lib/metadata/Database"; -import type Table from "metabase-lib/metadata/Table"; import type Schema from "metabase-lib/metadata/Schema"; +import type Table from "metabase-lib/metadata/Table"; export type DataPickerDataType = "models" | "raw-data" | "questions"; @@ -39,3 +40,10 @@ export type DataPickerSelectedItem = { type: "database" | "schema" | "collection" | "table"; id: string | number; }; + +export type DataTypeInfoItem = { + id: DataPickerDataType; + icon: IconName; + name: string; + description: string; +}; diff --git a/frontend/src/metabase/containers/DataPicker/useDataPickerValue.ts b/frontend/src/metabase/containers/DataPicker/useDataPickerValue.ts index b186ac51be9432e2577b227a6716c0c535915f36..e448d8654f668d9b76ade43142387a1351b13b68 100644 --- a/frontend/src/metabase/containers/DataPicker/useDataPickerValue.ts +++ b/frontend/src/metabase/containers/DataPicker/useDataPickerValue.ts @@ -57,7 +57,7 @@ function cleanValue(value: Partial<DataPickerValue>): DataPickerValue { type HookResult = [DataPickerValue, (value: DataPickerValue) => void]; -function useDataPickerValue( +export function useDataPickerValue( initialValue: Partial<DataPickerValue> = {}, ): HookResult { const [value, _setValue] = useState<DataPickerValue>( @@ -70,6 +70,3 @@ function useDataPickerValue( return [value, setValue]; } - -// eslint-disable-next-line import/no-default-export -- deprecated usage -export default useDataPickerValue; diff --git a/frontend/src/metabase/containers/DataPicker/utils.ts b/frontend/src/metabase/containers/DataPicker/utils.ts index ee7e0a816a576fe3c64f43f17bde0df6281c5888..ec5505251410315d5fae5d438c9371987e438181 100644 --- a/frontend/src/metabase/containers/DataPicker/utils.ts +++ b/frontend/src/metabase/containers/DataPicker/utils.ts @@ -1,59 +1,30 @@ -import { t } from "ttag"; - -import { IconName } from "metabase/core/components/Icon"; -import type { DataPickerDataType, DataPickerFilters } from "./types"; - -export type DataTypeInfoItem = { - id: DataPickerDataType; - icon: IconName; - name: string; - description: string; -}; +import { + DATASETS_INFO_ITEM, + RAW_DATA_INFO_ITEM, + SAVED_QUESTIONS_INFO_ITEM, +} from "./constants"; +import { DataTypeInfoItem } from "./types"; export function getDataTypes({ + hasModels, hasNestedQueriesEnabled, hasSavedQuestions, - hasModels, }: { + hasModels: boolean; hasNestedQueriesEnabled: boolean; hasSavedQuestions: boolean; - hasModels: boolean; }): DataTypeInfoItem[] { - const dataTypes: DataTypeInfoItem[] = [ - { - id: "raw-data", - icon: "database", - name: t`Raw Data`, - description: t`Unaltered tables in connected databases.`, - }, - ]; + const dataTypes: DataTypeInfoItem[] = []; + + if (hasNestedQueriesEnabled && hasModels) { + dataTypes.push(DATASETS_INFO_ITEM); + } - if (hasNestedQueriesEnabled) { - if (hasModels) { - dataTypes.unshift({ - id: "models", - icon: "model", - name: t`Models`, - description: t`The best starting place for new questions.`, - }); - } + dataTypes.push(RAW_DATA_INFO_ITEM); - if (hasSavedQuestions) { - dataTypes.push({ - id: "questions", - name: t`Saved Questions`, - icon: "folder", - description: t`Use any question’s results to start a new question.`, - }); - } + if (hasNestedQueriesEnabled && hasSavedQuestions) { + dataTypes.push(SAVED_QUESTIONS_INFO_ITEM); } return dataTypes; } - -export const DEFAULT_DATA_PICKER_FILTERS: DataPickerFilters = { - types: () => true, - databases: () => true, - schemas: () => true, - tables: () => true, -}; diff --git a/frontend/src/metabase/containers/DataPicker/utils.unit.spec.ts b/frontend/src/metabase/containers/DataPicker/utils.unit.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..12375efd30ea5ed71075459b408506d37b127861 --- /dev/null +++ b/frontend/src/metabase/containers/DataPicker/utils.unit.spec.ts @@ -0,0 +1,54 @@ +import { getDataTypes } from "metabase/containers/DataPicker"; + +describe("DataPicker > utils", () => { + describe("when nested queries are enabled", () => { + it("should return all non empty buckets - models & saved questions", () => { + const result = getDataTypes({ + hasModels: true, + hasSavedQuestions: true, + hasNestedQueriesEnabled: true, + }); + + expect(result).toMatchObject([ + { name: "Models" }, + { name: "Raw Data" }, + { name: "Saved Questions" }, + ]); + }); + + it("should return all non empty buckets - saved questions", () => { + const result = getDataTypes({ + hasModels: false, + hasSavedQuestions: true, + hasNestedQueriesEnabled: true, + }); + + expect(result).toMatchObject([ + { name: "Raw Data" }, + { name: "Saved Questions" }, + ]); + }); + + it("should return all non empty buckets - models", () => { + const result = getDataTypes({ + hasModels: true, + hasSavedQuestions: false, + hasNestedQueriesEnabled: true, + }); + + expect(result).toMatchObject([{ name: "Models" }, { name: "Raw Data" }]); + }); + }); + + describe("when nested queries are disabled", () => { + it("should not return models nor saved questions", () => { + const result = getDataTypes({ + hasModels: true, + hasSavedQuestions: true, + hasNestedQueriesEnabled: false, + }); + + expect(result).toMatchObject([{ name: "Raw Data" }]); + }); + }); +}); diff --git a/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.jsx b/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.jsx index c95992f7a6974a379a9bb694626d98553e764a3a..a949c1d0f88c648abc3d3ff565fcd1ce83e4c51c 100644 --- a/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.jsx +++ b/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.jsx @@ -11,8 +11,7 @@ import ListSearchField from "metabase/components/ListSearchField"; import { Icon } from "metabase/core/components/Icon"; import PopoverWithTrigger from "metabase/components/PopoverWithTrigger"; import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper"; - -import MetabaseSettings from "metabase/lib/settings"; +import { DATA_BUCKET, getDataTypes } from "metabase/containers/DataPicker"; import Databases from "metabase/entities/databases"; import Schemas from "metabase/entities/schemas"; @@ -21,6 +20,7 @@ import Search from "metabase/entities/search"; import { getMetadata } from "metabase/selectors/metadata"; import { getHasDataAccess } from "metabase/selectors/data"; +import { getSetting } from "metabase/selectors/settings"; import { getSchemaName } from "metabase-lib/metadata/utils/schema"; import { isVirtualCardId, @@ -42,8 +42,6 @@ import { TableSearchContainer, } from "./DataSelector.styled"; -import { DATA_BUCKET } from "./constants"; - import "./DataSelector.css"; const MIN_SEARCH_LENGTH = 2; @@ -182,6 +180,7 @@ const DataSelector = _.compose( entityQuery: { include: "tables" }, }), hasDataAccess: getHasDataAccess(ownProps.allDatabases ?? []), + hasNestedQueriesEnabled: getSetting(state, "enable-nested-queries"), }), { fetchDatabases: databaseQuery => @@ -493,7 +492,11 @@ export class UnconnectedDataSelector extends Component { hasUsableDatasets = () => { // As datasets are actually saved questions, nested queries must be enabled - return this.hasDatasets() && MetabaseSettings.get("enable-nested-queries"); + return this.hasDatasets() && this.props.hasNestedQueriesEnabled; + }; + + hasSavedQuestions = () => { + return this.state.databases.some(database => database.is_saved_questions); }; getDatabases = () => { @@ -503,7 +506,7 @@ export class UnconnectedDataSelector extends Component { // "Saved Questions" are presented in a different picker step // So it should be excluded from a regular databases list const shouldRemoveSavedQuestionDatabaseFromList = - !MetabaseSettings.get("enable-nested-queries") || this.hasDatasets(); + !this.props.hasNestedQueriesEnabled || this.hasDatasets(); return shouldRemoveSavedQuestionDatabaseFromList ? databases.filter(db => !db.is_saved_questions) @@ -662,6 +665,12 @@ export class UnconnectedDataSelector extends Component { async loadStepData(stepName) { const loadersForSteps = { // NOTE: make sure to return the action's resulting promise + [DATA_BUCKET_STEP]: () => { + return Promise.all([ + this.props.fetchDatabases(this.props.databaseQuery), + this.props.fetchDatabases({ saved: true }), + ]); + }, [DATABASE_STEP]: () => { return Promise.all([ this.props.fetchDatabases(this.props.databaseQuery), @@ -856,7 +865,7 @@ export class UnconnectedDataSelector extends Component { }; renderActiveStep() { - const { combineDatabaseSchemaSteps } = this.props; + const { combineDatabaseSchemaSteps, hasNestedQueriesEnabled } = this.props; const props = { ...this.state, @@ -878,7 +887,16 @@ export class UnconnectedDataSelector extends Component { switch (this.state.activeStep) { case DATA_BUCKET_STEP: - return <DataBucketPicker {...props} />; + return ( + <DataBucketPicker + dataTypes={getDataTypes({ + hasModels: this.hasDatasets(), + hasNestedQueriesEnabled, + hasSavedQuestions: this.hasSavedQuestions(), + })} + {...props} + /> + ); case DATABASE_STEP: return combineDatabaseSchemaSteps ? ( <DatabaseSchemaPicker @@ -969,7 +987,7 @@ export class UnconnectedDataSelector extends Component { getSearchModels = () => { const { selectedDataBucketId, isSavedQuestionPickerShown } = this.state; - if (!MetabaseSettings.get("enable-nested-queries")) { + if (!this.props.hasNestedQueriesEnabled) { return ["table"]; } if (!this.hasUsableDatasets()) { diff --git a/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.unit.spec.js b/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.unit.spec.js index 9db8ed5cb4e95b387f1ab8b28cb4d535167eacbe..5bd65db5897c657fd9e258b361b3b2bb6b945e14 100644 --- a/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.unit.spec.js +++ b/frontend/src/metabase/query_builder/components/DataSelector/DataSelector.unit.spec.js @@ -1,17 +1,31 @@ import fetchMock from "fetch-mock"; import { createMockMetadata } from "__support__/metadata"; -import { render, fireEvent, screen, getIcon } from "__support__/ui"; +import { + fireEvent, + getIcon, + render, + renderWithProviders, + screen, +} from "__support__/ui"; import { delay } from "metabase/lib/promise"; import { UnconnectedDataSelector as DataSelector } from "metabase/query_builder/components/DataSelector"; -import { createMockDatabase, createMockTable } from "metabase-types/api/mocks"; import { - createSampleDatabase, + createMockDatabase, + createMockSavedQuestionsDatabase, + createMockTable, +} from "metabase-types/api/mocks"; +import { SAMPLE_DB_ID, + createSampleDatabase, } from "metabase-types/api/mocks/presets"; +import { + createMockSettingsState, + createMockState, +} from "metabase-types/store/mocks"; const MULTI_SCHEMA_DB_ID = 2; const MULTI_SCHEMA_TABLE1_ID = 100; @@ -85,6 +99,11 @@ describe("DataSelector", () => { const metadata = createMockMetadata({ databases }); const emptyMetadata = createMockMetadata({}); + const storeInitialState = createMockState({ + settings: createMockSettingsState({ + "enable-nested-queries": true, + }), + }); const SAMPLE_DATABASE = metadata.database(SAMPLE_DB_ID); const ANOTHER_DATABASE = metadata.database(EMPTY_DB_ID); @@ -92,6 +111,7 @@ describe("DataSelector", () => { const OTHER_MULTI_SCHEMA_DATABASE = metadata.database( OTHER_MULTI_SCHEMA_DB_ID, ); + const SAVED_QUESTIONS_DATABASE = createMockSavedQuestionsDatabase(); it("should allow selecting db, schema, and table", () => { const setTable = jest.fn(); @@ -463,4 +483,42 @@ describe("DataSelector", () => { screen.getByText("To pick some data, you'll need to add some first"), ).toBeInTheDocument(); }); + + it("should show 'Saved Questions' option when there are saved questions", async () => { + renderWithProviders( + <DataSelector + steps={["BUCKET", "DATABASE", "SCHEMA", "TABLE"]} + combineDatabaseSchemaSteps + databases={[SAMPLE_DATABASE, SAVED_QUESTIONS_DATABASE]} + hasNestedQueriesEnabled + hasTableSearch + loaded + search={[{}]} + triggerElement={<div />} + isOpen + />, + { storeInitialState }, + ); + + expect(screen.getByText("Saved Questions")).toBeInTheDocument(); + }); + + it("should not show 'Saved Questions' option when there are no saved questions (metabase#29760)", async () => { + renderWithProviders( + <DataSelector + steps={["BUCKET", "DATABASE", "SCHEMA", "TABLE"]} + combineDatabaseSchemaSteps + databases={[SAMPLE_DATABASE]} + hasNestedQueriesEnabled + hasTableSearch + loaded + search={[{}]} + triggerElement={<div />} + isOpen + />, + { storeInitialState }, + ); + + expect(screen.queryByText("Saved Questions")).not.toBeInTheDocument(); + }); }); diff --git a/frontend/src/metabase/query_builder/components/DataSelector/DataSelectorDataBucketPicker/DataSelectorDataBucketPicker.tsx b/frontend/src/metabase/query_builder/components/DataSelector/DataSelectorDataBucketPicker/DataSelectorDataBucketPicker.tsx index abd8a607cb4b1ce7abdead6dca86ba067fba1430..e3b0feff45275011aabfeb8194a6deb808c88010 100644 --- a/frontend/src/metabase/query_builder/components/DataSelector/DataSelectorDataBucketPicker/DataSelectorDataBucketPicker.tsx +++ b/frontend/src/metabase/query_builder/components/DataSelector/DataSelectorDataBucketPicker/DataSelectorDataBucketPicker.tsx @@ -1,80 +1,64 @@ -import { t } from "ttag"; - -import { IconName } from "metabase/core/components/Icon"; -import { DATA_BUCKET } from "../constants"; +import { DataTypeInfoItem } from "metabase/containers/DataPicker"; import { - DataBucketList as List, DataBucketListItemContainer as ItemContainer, - DataBucketTitleContainer as TitleContainer, + DataBucketListItemDescription as ItemDescription, + DataBucketListItemDescriptionContainer as ItemDescriptionContainer, DataBucketListItemIcon as ItemIcon, DataBucketListItemTitle as ItemTitle, - DataBucketListItemDescriptionContainer as ItemDescriptionContainer, - DataBucketListItemDescription as ItemDescription, + DataBucketList as List, + DataBucketTitleContainer as TitleContainer, } from "./DataSelectorDataBucketPicker.styled"; type DataSelectorDataBucketPickerProps = { - onChangeDataBucket: () => void; + dataTypes: DataTypeInfoItem[]; + onChangeDataBucket: (id: DataTypeInfoItem["id"]) => void; }; -type Bucket = { - id: string; - icon: IconName; - name: string; - description: string; - onSelect: () => void; -}; - -const BUCKETS = [ - { - id: DATA_BUCKET.DATASETS, - icon: "model" as const, - name: t`Models`, - description: t`The best starting place for new questions.`, - }, - { - id: DATA_BUCKET.RAW_DATA, - icon: "database" as const, - name: t`Raw Data`, - description: t`Unaltered tables in connected databases.`, - }, - { - id: DATA_BUCKET.SAVED_QUESTIONS, - name: t`Saved Questions`, - icon: "folder" as const, - description: t`Use any question’s results to start a new question.`, - }, -]; - const DataSelectorDataBucketPicker = ({ + dataTypes, onChangeDataBucket, }: DataSelectorDataBucketPickerProps) => ( <List> - {BUCKETS.map(bucket => ( + {dataTypes.map(({ id, icon, name, description }) => ( <DataBucketListItem - {...bucket} - key={bucket.id} - onSelect={onChangeDataBucket} + description={description} + id={id} + icon={icon} + key={id} + name={name} + onSelect={() => onChangeDataBucket(id)} /> ))} </List> ); -const DataBucketListItem = (props: Bucket) => { - const { name, icon, description } = props; - - return ( - <ItemContainer {...props}> - <TitleContainer> - <ItemIcon name={icon} size={18} /> - <ItemTitle>{name}</ItemTitle> - </TitleContainer> - <ItemDescriptionContainer> - <ItemDescription>{description}</ItemDescription> - </ItemDescriptionContainer> - </ItemContainer> - ); +type DataBucketListItemProps = DataTypeInfoItem & { + onSelect: () => void; }; +const DataBucketListItem = ({ + description, + icon, + id, + name, + onSelect, +}: DataBucketListItemProps) => ( + <ItemContainer + data-testid="data-bucket-list-item" + id={id} + name={name} + onSelect={onSelect} + > + <TitleContainer> + <ItemIcon name={icon} size={18} /> + <ItemTitle>{name}</ItemTitle> + </TitleContainer> + <ItemDescriptionContainer> + <ItemDescription>{description}</ItemDescription> + </ItemDescriptionContainer> + </ItemContainer> +); + // eslint-disable-next-line import/no-default-export -- deprecated usage export default DataSelectorDataBucketPicker; diff --git a/frontend/src/metabase/query_builder/components/DataSelector/DataSelectorDataBucketPicker/DataSelectorDataBucketPicker.unit.spec.tsx b/frontend/src/metabase/query_builder/components/DataSelector/DataSelectorDataBucketPicker/DataSelectorDataBucketPicker.unit.spec.tsx index 63ac1d31276416f1d94f348799f7eb7fc6da1085..27bcb0b2cefec9942bcf4f8f8deefebec59ac22d 100644 --- a/frontend/src/metabase/query_builder/components/DataSelector/DataSelectorDataBucketPicker/DataSelectorDataBucketPicker.unit.spec.tsx +++ b/frontend/src/metabase/query_builder/components/DataSelector/DataSelectorDataBucketPicker/DataSelectorDataBucketPicker.unit.spec.tsx @@ -1,13 +1,44 @@ import { render, screen } from "@testing-library/react"; +import { DataTypeInfoItem, getDataTypes } from "metabase/containers/DataPicker"; + import DataSelectorDataBucketPicker from "./DataSelectorDataBucketPicker"; +const setup = (dataTypes: DataTypeInfoItem[]) => { + return render( + <DataSelectorDataBucketPicker + dataTypes={dataTypes} + onChangeDataBucket={jest.fn()} + />, + ); +}; + describe("DataSelectorDataBucketPicker", () => { - it("displays bucket names", () => { - render(<DataSelectorDataBucketPicker onChangeDataBucket={jest.fn()} />); + it("should display all buckets", () => { + const dataTypes = getDataTypes({ + hasModels: true, + hasSavedQuestions: true, + hasNestedQueriesEnabled: true, + }); + setup(dataTypes); expect(screen.getByText("Models")).toBeInTheDocument(); expect(screen.getByText("Raw Data")).toBeInTheDocument(); expect(screen.getByText("Saved Questions")).toBeInTheDocument(); + expect(screen.queryAllByTestId("data-bucket-list-item").length).toBe( + dataTypes.length, + ); + }); + + it("should display no buckets", () => { + const dataTypes: DataTypeInfoItem[] = []; + setup(dataTypes); + + expect(screen.queryByText("Models")).not.toBeInTheDocument(); + expect(screen.queryByText("Raw Data")).not.toBeInTheDocument(); + expect(screen.queryByText("Saved Questions")).not.toBeInTheDocument(); + expect(screen.queryAllByTestId("data-bucket-list-item").length).toBe( + dataTypes.length, + ); }); }); diff --git a/frontend/src/metabase/query_builder/components/DataSelector/constants.ts b/frontend/src/metabase/query_builder/components/DataSelector/constants.ts deleted file mode 100644 index ff05a0faa97d383399449defd9009811953eac3f..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/query_builder/components/DataSelector/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const DATA_BUCKET = { - DATASETS: "datasets", - RAW_DATA: "raw-data", - SAVED_QUESTIONS: "saved-questions", -}; diff --git a/frontend/src/metabase/query_builder/components/notebook/steps/JoinStep/JoinStep.jsx b/frontend/src/metabase/query_builder/components/notebook/steps/JoinStep/JoinStep.jsx index 546c1e1a65455c34efcd629e74edb8c2e5a5336e..85a511e38bf607be20e0d6d7ef19369e4a3c2667 100644 --- a/frontend/src/metabase/query_builder/components/notebook/steps/JoinStep/JoinStep.jsx +++ b/frontend/src/metabase/query_builder/components/notebook/steps/JoinStep/JoinStep.jsx @@ -6,7 +6,7 @@ import { t } from "ttag"; import PopoverWithTrigger from "metabase/components/PopoverWithTrigger"; import { DataSourceSelector } from "metabase/query_builder/components/DataSelector"; -import { DATA_BUCKET } from "metabase/query_builder/components/DataSelector/constants"; +import { DATA_BUCKET } from "metabase/containers/DataPicker"; import FieldList from "metabase/query_builder/components/FieldList"; import Select from "metabase/core/components/Select"; import { isDateTimeField } from "metabase-lib/queries/utils/field-ref";