Skip to content
Snippets Groups Projects
Unverified Commit c114973e authored by github-automation-metabase's avatar github-automation-metabase Committed by GitHub
Browse files

:robot: backported "In sidesheets, show correct sources for a model" (#49290)

parent c6378ef4
No related branches found
No related tags found
No related merge requests found
......@@ -153,6 +153,9 @@ function QuestionTableBadges({ tables, subHead, hasLink, isLast }) {
}
function getTableURL(table) {
if (!table) {
return "";
}
if (isVirtualCardId(table.id)) {
const cardId = getQuestionIdFromVirtualTableId(table.id);
return Urls.question({ id: cardId, name: table.displayName() });
......
......@@ -2,6 +2,7 @@ import cx from "classnames";
import { useState } from "react";
import { c, t } from "ttag";
import ErrorBoundary from "metabase/ErrorBoundary";
import { getCollectionName } from "metabase/collections/utils";
import { SidesheetCardSection } from "metabase/common/components/Sidesheet";
import DateTime from "metabase/components/DateTime";
......@@ -71,7 +72,9 @@ export const QuestionDetails = ({ question }: { question: Question }) => {
</Flex>
</SidesheetCardSection>
<SharingDisplay question={question} />
<QuestionSources question={question} />
<ErrorBoundary>
<QuestionSources />
</ErrorBoundary>
</>
);
};
......
......@@ -3,30 +3,42 @@ import { c } from "ttag";
import { SidesheetCardSection } from "metabase/common/components/Sidesheet";
import Link from "metabase/core/components/Link";
import { useSelector } from "metabase/lib/redux";
import { getQuestionWithParameters } from "metabase/query_builder/selectors";
import { Flex, FixedSizeIcon as Icon } from "metabase/ui";
import type Question from "metabase-lib/v1/Question";
import { getDataSourceParts } from "../../../ViewHeader/components/QuestionDataSource/utils";
import type { QuestionSource } from "./types";
import { getIconPropsForSource } from "./utils";
export const QuestionSources = ({ question }: { question: Question }) => {
const sources = getDataSourceParts({
question,
subHead: false,
isObjectDetail: true,
formatTableAsComponent: false,
}) as unknown as QuestionSource[];
export const QuestionSources = () => {
const questionWithParameters = useSelector(getQuestionWithParameters);
const sourcesWithIcons: QuestionSource[] = useMemo(() => {
return sources.map(source => ({
...source,
iconProps: getIconPropsForSource(source),
}));
}, [sources]);
const sources = questionWithParameters
? (getDataSourceParts({
question: questionWithParameters,
subHead: false,
isObjectDetail: true,
formatTableAsComponent: false,
}) as QuestionSource[] | QuestionSource)
: [];
if (!sources.length) {
const sourceArray = Array.isArray(sources) ? sources : [sources];
return (
sourceArray
// we can't get a table for native questions in v51
.filter(source => "href" in source)
.map(source => ({
...source,
iconProps: getIconPropsForSource(source as QuestionSource),
}))
);
}, [questionWithParameters]);
if (!questionWithParameters || !sourcesWithIcons.length) {
return null;
}
......@@ -47,7 +59,9 @@ export const QuestionSources = ({ question }: { question: Question }) => {
{name}
</Flex>
</Link>
{index < sources.length - 1 && <Flex lh="1.25rem">{"/"}</Flex>}
{index < sourcesWithIcons.length - 1 && (
<Flex lh="1.25rem">{"/"}</Flex>
)}
</Fragment>
))}
</Flex>
......
......@@ -5,13 +5,14 @@ import { mockSettings } from "__support__/settings";
import { createMockEntitiesState } from "__support__/store";
import { renderWithProviders, screen, within } from "__support__/ui";
import { modelIconMap } from "metabase/lib/icon";
import { checkNotNull } from "metabase/lib/types";
import { getMetadata } from "metabase/selectors/metadata";
import { convertSavedQuestionToVirtualTable } from "metabase-lib/v1/metadata/utils/saved-questions";
import type { Card, NormalizedTable } from "metabase-types/api";
import { createMockCard, createMockSettings } from "metabase-types/api/mocks";
import { createSampleDatabase } from "metabase-types/api/mocks/presets";
import { createMockState } from "metabase-types/store/mocks";
import {
createMockQueryBuilderState,
createMockState,
} from "metabase-types/store/mocks";
import { QuestionSources } from "./QuestionSources";
......@@ -25,6 +26,7 @@ const setup = async ({
sourceCard,
}: SetupOpts = {}) => {
const state = createMockState({
qb: createMockQueryBuilderState({ card }),
settings: mockSettings(createMockSettings()),
entities: createMockEntitiesState({
databases: [createSampleDatabase()],
......@@ -54,16 +56,11 @@ const setup = async ({
};
}
const metadata = getMetadata(state);
const question = checkNotNull(metadata.question(card.id));
return renderWithProviders(
<Route
path="/"
component={() => <QuestionSources question={question} />}
/>,
<Route path="/" component={() => <QuestionSources />} />,
{
withRouter: true,
storeInitialState: state,
},
);
};
......@@ -142,4 +139,37 @@ describe("QuestionSources", () => {
"/question/2-my-source-question",
);
});
it("shows source information for a model (its database and table)", async () => {
const model = createMockCard({
name: "My Model",
type: "model",
});
await setup({ card: model });
const databaseLink = await screen.findByRole("link", {
name: /Sample Database/i,
});
expect(
await within(databaseLink).findByLabelText("database icon"),
).toBeInTheDocument();
expect(databaseLink).toHaveAttribute(
"href",
"/browse/databases/1-sample-database",
);
expect(screen.getByText("/")).toBeInTheDocument();
const tableLink = await screen.findByRole("link", { name: /Products/i });
expect(tableLink).toBeInTheDocument();
expect(
await within(tableLink).findByLabelText(`table icon`),
).toBeInTheDocument();
expect(tableLink).toHaveAttribute(
"href",
expect.stringMatching(/^\/question#[a-zA-Z0-9]{20}/),
);
});
});
......@@ -20,7 +20,10 @@ import {
createMockUser,
} from "metabase-types/api/mocks";
import { createSampleDatabase } from "metabase-types/api/mocks/presets";
import { createMockState } from "metabase-types/store/mocks";
import {
createMockQueryBuilderState,
createMockState,
} from "metabase-types/store/mocks";
import { QuestionInfoSidebar } from "../QuestionInfoSidebar";
......@@ -45,6 +48,7 @@ export const setup = async ({
const state = createMockState({
currentUser,
settings: mockSettings(settings),
qb: createMockQueryBuilderState({ card }),
entities: createMockEntitiesState({
databases: [createSampleDatabase()],
questions: [card],
......
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