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

Add docs url to the metric editor (#47702)

* Add metric sidebar

* Add tests

* Rename ResizableNotebook to DatasetNotebook

* Add tests

* Add tests

* Add tests

* Add tests
parent d284a703
No related branches found
No related tags found
No related merge requests found
Showing
with 216 additions and 10 deletions
......@@ -8,7 +8,10 @@ import { darken } from "metabase/lib/colors";
import { Notebook } from "metabase/querying/notebook/components/Notebook";
import { Box, Flex, rem } from "metabase/ui";
import { MetricSidebar } from "./MetricSidebar";
const propTypes = {
question: PropTypes.object.isRequired,
isResizing: PropTypes.bool.isRequired,
resizableBoxProps: PropTypes.object.isRequired,
onResizeStop: PropTypes.func.isRequired,
......@@ -50,7 +53,8 @@ const Handle = forwardRef(function Handle(props, ref) {
);
});
function ResizableNotebook({
export function DatasetNotebook({
question,
isResizing,
onResizeStop,
resizableBoxProps,
......@@ -67,13 +71,18 @@ function ResizableNotebook({
onResizeStop(...args);
}}
>
<Box w="100%" style={{ overflowY: getOverflow(isResizing) }}>
<Notebook {...notebookProps} hasVisualizeButton={false} />
</Box>
<Flex w="100%" style={{ overflowY: getOverflow(isResizing) }}>
<Box w="100%">
<Notebook
{...notebookProps}
question={question}
hasVisualizeButton={false}
/>
</Box>
{question.type() === "metric" && <MetricSidebar />}
</Flex>
</ResizableBox>
);
}
ResizableNotebook.propTypes = propTypes;
export default ResizableNotebook;
DatasetNotebook.propTypes = propTypes;
import type { ResizableBoxProps } from "react-resizable";
import { renderWithProviders, screen } from "__support__/ui";
import Question from "metabase-lib/v1/Question";
import { createMockCard } from "metabase-types/api/mocks";
import { DatasetNotebook } from "./DatasetNotebook";
type SetupOpts = {
question: Question;
reportTimezone?: string;
isDirty?: boolean;
isRunnable?: boolean;
isResultDirty?: boolean;
isResizing?: boolean;
resizableBoxProps?: ResizableBoxProps;
};
function setup({
question,
reportTimezone = "UTC",
isDirty = false,
isRunnable = false,
isResultDirty = false,
isResizing = false,
resizableBoxProps = {
axis: "y",
height: 100,
},
}: SetupOpts) {
const updateQuestion = jest.fn();
const setQueryBuilderMode = jest.fn();
const onResizeStop = jest.fn();
renderWithProviders(
<DatasetNotebook
question={question}
reportTimezone={reportTimezone}
isDirty={isDirty}
isRunnable={isRunnable}
isResultDirty={isResultDirty}
isResizing={isResizing}
resizableBoxProps={resizableBoxProps}
updateQuestion={updateQuestion}
setQueryBuilderMode={setQueryBuilderMode}
onResizeStop={onResizeStop}
/>,
);
}
describe("DatasetNotebook", () => {
it("should render a metric docs link for metrics", () => {
const question = new Question(createMockCard({ type: "metric" }));
setup({ question });
const link = screen.getByRole("link", { name: /Docs/ });
expect(link).toBeInTheDocument();
expect(link).toHaveAttribute("target", "_blank");
expect(link).toHaveAttribute(
"href",
expect.stringContaining("data-modeling/segments-and-metrics"),
);
});
it("should not render a metric docs link for non-metrics", () => {
const question = new Question(createMockCard({ type: "question" }));
setup({ question });
expect(
screen.queryByRole("link", { name: /Docs/ }),
).not.toBeInTheDocument();
});
});
import { t } from "ttag";
import ExternalLink from "metabase/core/components/ExternalLink";
import { useSelector } from "metabase/lib/redux";
import { getDocsUrl } from "metabase/selectors/settings";
import { getShowMetabaseLinks } from "metabase/selectors/whitelabel";
import { Box, Button, Icon } from "metabase/ui";
export function MetricSidebar() {
const showMetabaseLinks = useSelector(getShowMetabaseLinks);
const docsUrl = useSelector(state =>
getDocsUrl(state, {
page: "data-modeling/segments-and-metrics",
anchor: "creating-a-metric",
}),
);
return (
<Box pt="md" pr={{ sm: "sm", lg: "md" }}>
{showMetabaseLinks && (
<Button
component={ExternalLink}
href={docsUrl}
variant="subtle"
rightIcon={<Icon name="external" size={16} />}
>
{t`Docs`}
</Button>
)}
</Box>
);
}
export * from "./MetricSidebar";
import { screen } from "__support__/ui";
import { setup } from "./setup";
describe("MetricSidebar (OSS)", () => {
it("should render the metric docs link", () => {
setup();
const link = screen.getByRole("link", { name: /Docs/ });
expect(link).toBeInTheDocument();
expect(link).toHaveAttribute("target", "_blank");
expect(link).toHaveAttribute(
"href",
expect.stringContaining("data-modeling/segments-and-metrics"),
);
});
});
import { screen } from "__support__/ui";
import { type SetupOpts, setup as baseSetup } from "./setup";
function setup(opts: SetupOpts = {}) {
baseSetup({ hasEnterprisePlugins: true, ...opts });
}
describe("MetricSidebar (EE without a token)", () => {
it("should render the metric docs link by default", () => {
setup({ showMetabaseLinks: true });
expect(screen.getByRole("link", { name: /Docs/ })).toBeInTheDocument();
});
it("should render the metric docs link even if the setting is turned off", () => {
setup({ showMetabaseLinks: false });
expect(screen.getByRole("link", { name: /Docs/ })).toBeInTheDocument();
});
});
import { setupEnterprisePlugins } from "__support__/enterprise";
import { mockSettings } from "__support__/settings";
import { renderWithProviders } from "__support__/ui";
import type { TokenFeatures } from "metabase-types/api";
import { createMockTokenFeatures } from "metabase-types/api/mocks";
import { createMockState } from "metabase-types/store/mocks";
import { MetricSidebar } from "../MetricSidebar";
export type SetupOpts = {
tokenFeatures?: Partial<TokenFeatures>;
showMetabaseLinks?: boolean;
hasEnterprisePlugins?: boolean;
};
export function setup({
tokenFeatures,
showMetabaseLinks,
hasEnterprisePlugins,
}: SetupOpts = {}) {
const state = createMockState({
settings: mockSettings({
"show-metabase-links": showMetabaseLinks,
"token-features": createMockTokenFeatures(tokenFeatures),
}),
});
if (hasEnterprisePlugins) {
setupEnterprisePlugins();
}
renderWithProviders(<MetricSidebar />, { storeInitialState: state });
}
import { screen } from "__support__/ui";
import { type SetupOpts, setup as baseSetup } from "./setup";
function setup(opts: SetupOpts = {}) {
baseSetup({
tokenFeatures: { whitelabel: true },
hasEnterprisePlugins: true,
...opts,
});
}
describe("MetricSidebar (EE with a whitelabel token)", () => {
it("should render the metric docs link by default", () => {
setup({ showMetabaseLinks: true });
expect(screen.getByRole("link", { name: /Docs/ })).toBeInTheDocument();
});
it("should not render the metric docs link when the setting is turned off", () => {
setup({ showMetabaseLinks: false });
expect(
screen.queryByRole("link", { name: /Docs/ }),
).not.toBeInTheDocument();
});
});
export * from "./DatasetNotebook";
......@@ -6,7 +6,7 @@ import { isReducedMotionPreferred } from "metabase/lib/dom";
import NativeQueryEditor from "metabase/query_builder/components/NativeQueryEditor";
import * as Lib from "metabase-lib";
import ResizableNotebook from "./ResizableNotebook";
import { DatasetNotebook } from "./DatasetNotebook";
const QueryEditorContainer = styled.div`
visibility: ${props => (props.isActive ? "visible" : "hidden")};
......@@ -83,7 +83,7 @@ function DatasetQueryEditor({
onSetDatabaseId={onSetDatabaseId}
/>
) : (
<ResizableNotebook
<DatasetNotebook
{...props}
question={question}
isResizing={isResizing}
......
export { default } from "./ResizableNotebook";
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