Skip to content
Snippets Groups Projects
Unverified Commit 89705fcb authored by Oisin Coveney's avatar Oisin Coveney Committed by GitHub
Browse files

Make tooltip consistent for each icon (#31120)

parent 09123258
No related branches found
No related tags found
No related merge requests found
import { useCallback } from "react";
import { t } from "ttag";
import { connect } from "react-redux";
import * as Urls from "metabase/lib/urls";
import Button from "metabase/core/components/Button";
......@@ -14,12 +13,12 @@ import { MODAL_TYPES } from "metabase/query_builder/constants";
import { softReloadCard } from "metabase/query_builder/actions";
import { getUserIsAdmin } from "metabase/selectors/user";
import { State } from "metabase-types/store";
import { color } from "metabase/lib/colors";
import BookmarkToggle from "metabase/core/components/BookmarkToggle";
import { getSetting } from "metabase/selectors/settings";
import { canUseMetabotOnDatabase } from "metabase/metabot/utils";
import { useDispatch, useSelector } from "metabase/lib/redux";
import Question from "metabase-lib/Question";
import {
......@@ -41,19 +40,9 @@ const TOGGLE_MODEL_PERSISTENCE_TESTID = "toggle-persistence";
const CLONE_TESTID = "clone-button";
const ARCHIVE_TESTID = "archive-button";
const mapStateToProps = (state: State) => ({
isModerator: getUserIsAdmin(state),
isMetabotEnabled: getSetting(state, "is-metabot-enabled"),
});
const mapDispatchToProps = {
softReloadCard,
};
interface Props {
isBookmarked: boolean;
isShowingQuestionInfoSidebar: boolean;
isMetabotEnabled: boolean;
handleBookmark: () => void;
onOpenModal: (modalType: string) => void;
question: Question;
......@@ -64,14 +53,11 @@ interface Props {
turnDatasetIntoQuestion: () => void;
onInfoClick: () => void;
onModelPersistenceChange: () => void;
isModerator: boolean;
softReloadCard: () => void;
}
const QuestionActions = ({
isBookmarked,
isShowingQuestionInfoSidebar,
isMetabotEnabled,
handleBookmark,
onOpenModal,
question,
......@@ -79,10 +65,15 @@ const QuestionActions = ({
turnDatasetIntoQuestion,
onInfoClick,
onModelPersistenceChange,
isModerator,
softReloadCard,
}: Props) => {
const bookmarkTooltip = isBookmarked ? t`Remove from bookmarks` : t`Bookmark`;
const isMetabotEnabled = useSelector(state =>
getSetting(state, "is-metabot-enabled"),
);
const isModerator = useSelector(getUserIsAdmin);
const dispatch = useDispatch();
const dispatchSoftReloadCard = () => dispatch(softReloadCard());
const infoButtonColor = isShowingQuestionInfoSidebar
? color("brand")
......@@ -135,7 +126,11 @@ const QuestionActions = ({
}
extraButtons.push(
PLUGIN_MODERATION.getMenuItems(question, isModerator, softReloadCard),
PLUGIN_MODERATION.getMenuItems(
question,
isModerator,
dispatchSoftReloadCard,
),
);
if (isDataset) {
......@@ -221,13 +216,13 @@ const QuestionActions = ({
return (
<>
<QuestionActionsDivider />
<Tooltip tooltip={bookmarkTooltip}>
<ViewHeaderIconButtonContainer>
<BookmarkToggle
onCreateBookmark={handleBookmark}
onDeleteBookmark={handleBookmark}
isBookmarked={isBookmarked}
/>
</Tooltip>
</ViewHeaderIconButtonContainer>
<Tooltip tooltip={t`More info`}>
<ViewHeaderIconButtonContainer>
<Button
......@@ -251,4 +246,4 @@ const QuestionActions = ({
};
// eslint-disable-next-line import/no-default-export -- deprecated usage
export default connect(mapStateToProps, mapDispatchToProps)(QuestionActions);
export default QuestionActions;
import userEvent from "@testing-library/user-event";
import { renderWithProviders, screen } from "__support__/ui";
import { createMockState } from "metabase-types/store/mocks";
import { createMockEntitiesState } from "__support__/store";
import QuestionActions from "metabase/query_builder/components/QuestionActions";
import { createMockCard, createMockNativeCard } from "metabase-types/api/mocks";
import { getMetadata } from "metabase/selectors/metadata";
import { Card } from "metabase-types/api";
import Question from "metabase-lib/Question";
const TEST_STRUCTURED_CARD = createMockCard();
const TEST_NATIVE_CARD = createMockNativeCard();
const iconList = [
{ label: "bookmark icon", tooltipText: "Bookmark" },
{ label: "info icon", tooltipText: "More info" },
{
label: "Move, archive, and more...",
tooltipText: "Move, archive, and more...",
},
];
function setup({ card }: { card: Card }) {
const state = createMockState({
entities: createMockEntitiesState({
questions: [card],
}),
});
const metadata = getMetadata(state);
const question = metadata.question(card.id) as Question;
renderWithProviders(
<QuestionActions
isBookmarked={false}
isShowingQuestionInfoSidebar={false}
handleBookmark={jest.fn()}
onOpenModal={jest.fn()}
question={question}
setQueryBuilderMode={jest.fn()}
turnDatasetIntoQuestion={jest.fn()}
onInfoClick={jest.fn()}
onModelPersistenceChange={jest.fn()}
/>,
{ storeInitialState: state },
);
}
describe("Question Actions | Icons", () => {
["structured", "native"].forEach(queryType => {
iconList.forEach(({ label, tooltipText }) => {
it(`should display the "${label}" icon with the "${tooltipText}" tooltip for ${queryType} questions`, async () => {
setup({
card:
queryType === "structured"
? TEST_STRUCTURED_CARD
: TEST_NATIVE_CARD,
});
await userEvent.hover(screen.getByRole("button", { name: label }));
const tooltip = screen.getByRole("tooltip", { name: tooltipText });
expect(tooltip).toHaveAttribute("data-placement", "top");
expect(tooltip).toHaveTextContent(tooltipText);
});
});
});
});
......@@ -23,7 +23,7 @@ export default function RunButtonWithTooltip({
...props
}) {
return (
<Tooltip tooltip={getTooltip(props)}>
<Tooltip tooltip={getTooltip(props)} placement="top">
<RunButton {...props} />
</Tooltip>
);
......
......@@ -28,7 +28,7 @@ const ConvertQueryButton = ({
}, [onOpenModal]);
return (
<Tooltip tooltip={tooltip} placement="bottom">
<Tooltip tooltip={tooltip} placement="top">
<SqlButton
onClick={handleClick}
aria-label={tooltip}
......
......@@ -15,7 +15,7 @@ export default function QuestionNotebookButton({
return (
<Tooltip
tooltip={isShowingNotebook ? t`Hide editor` : t`Show editor`}
placement="bottom"
placement="top"
>
<Button
borderless={!isShowingNotebook}
......
......@@ -449,6 +449,11 @@ function ViewTitleHeaderRightSide(props) {
}
}, [isShowingQuestionInfoSidebar, onOpenQuestionInfo, onCloseQuestionInfo]);
const getRunButtonLabel = useCallback(
() => (isRunning ? t`Cancel` : t`Refresh`),
[isRunning],
);
return (
<ViewHeaderActionPanel data-testid="qb-header-action-panel">
{QuestionFilters.shouldRender(props) && (
......@@ -510,6 +515,7 @@ function ViewTitleHeaderRightSide(props) {
isDirty={isResultDirty}
onRun={() => runQuestionQuery({ ignoreCache: true })}
onCancel={cancelQuery}
getTooltip={getRunButtonLabel}
/>
</ViewHeaderIconButtonContainer>
)}
......
......@@ -260,6 +260,15 @@ describe("ViewHeader", () => {
).not.toBeInTheDocument();
expect(screen.getByLabelText("refresh icon")).toBeInTheDocument();
});
it("displays refresh button tooltip above the refresh button", async () => {
setup({ card });
const refreshButton = screen.getByLabelText("refresh icon");
await userEvent.hover(refreshButton);
const tooltip = screen.getByRole("tooltip");
expect(tooltip).toHaveAttribute("data-placement", "top");
expect(tooltip).toHaveTextContent("Refresh");
});
});
});
});
......@@ -303,6 +312,17 @@ describe("ViewHeader", () => {
expect(setQueryBuilderMode).toHaveBeenCalledWith("notebook");
});
it("displays `Show editor` tooltip above notebook icon", async () => {
setup({
card,
});
const notebookButton = screen.getByLabelText("notebook icon");
await userEvent.hover(notebookButton);
const tooltip = screen.getByRole("tooltip");
expect(tooltip).toHaveAttribute("data-placement", "top");
expect(tooltip).toHaveTextContent("Show editor");
});
it("allows to close notebook editor", () => {
const { setQueryBuilderMode } = setup({
card,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment