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

Split View Header components into separate files (#44347)

parent 37abc084
No related branches found
No related tags found
No related merge requests found
Showing
with 481 additions and 434 deletions
import cx from "classnames";
import PropTypes from "prop-types";
import { useEffect, useCallback, useState } from "react";
import { useCallback, useEffect } from "react";
import { usePrevious } from "react-use";
import { t } from "ttag";
import Link from "metabase/core/components/Link";
import Tooltip from "metabase/core/components/Tooltip";
import CS from "metabase/css/core/index.css";
import { useToggle } from "metabase/hooks/use-toggle";
import { SERVER_ERROR_TYPES } from "metabase/lib/errors";
import { useDispatch, useSelector } from "metabase/lib/redux";
import MetabaseSettings from "metabase/lib/settings";
import * as Urls from "metabase/lib/urls";
import { navigateBackToDashboard } from "metabase/query_builder/actions";
import SavedQuestionHeaderButton from "metabase/query_builder/components/SavedQuestionHeaderButton/SavedQuestionHeaderButton";
import { MODAL_TYPES } from "metabase/query_builder/constants";
import { getDashboard } from "metabase/query_builder/selectors";
import * as Lib from "metabase-lib";
import { ViewHeaderContainer } from "./ViewHeader.styled";
import {
AdHocViewHeading,
SaveButton,
SavedQuestionHeaderButtonContainer,
ViewHeaderMainLeftContentContainer,
ViewHeaderLeftSubHeading,
ViewHeaderContainer,
StyledLastEditInfoLabel,
StyledQuestionDataSource,
SavedQuestionLeftSideRoot,
AdHocLeftSideRoot,
HeaderDivider,
ViewHeaderActionPanel,
ViewHeaderIconButtonContainer,
BackButton,
BackButtonContainer,
ViewRunButtonWithTooltip,
} from "./ViewHeader.styled";
import {
ToggleNativeQueryPreview,
HeadBreadcrumbs,
FilterHeaderButton,
FilterHeaderToggle,
AdHocQuestionLeftSide,
FilterHeader,
ExploreResultsLink,
QuestionActions,
QuestionNotebookButton,
QuestionDataSource,
QuestionDescription,
QuestionSummarizeWidget,
SavedQuestionLeftSide,
ViewTitleHeaderRightSide,
DashboardBackButton,
} from "./components";
import { canExploreResults } from "./utils";
const viewTitleHeaderPropTypes = {
question: PropTypes.object.isRequired,
......@@ -140,7 +103,7 @@ export function ViewTitleHeader(props) {
{isSaved ? (
<SavedQuestionLeftSide {...props} />
) : (
<AhHocQuestionLeftSide
<AdHocQuestionLeftSide
{...props}
isNative={isNative}
isSummarized={isSummarized}
......@@ -170,394 +133,4 @@ export function ViewTitleHeader(props) {
);
}
function DashboardBackButton() {
const dashboard = useSelector(getDashboard);
const dispatch = useDispatch();
const handleClick = () => {
dispatch(navigateBackToDashboard(dashboard.id));
};
if (!dashboard) {
return null;
}
const label = t`Back to ${dashboard.name}`;
return (
<Tooltip tooltip={label}>
<BackButtonContainer>
<BackButton
as={Link}
to={Urls.dashboard(dashboard)}
round
icon="arrow_left"
aria-label={label}
onClick={handleClick}
/>
</BackButtonContainer>
</Tooltip>
);
}
SavedQuestionLeftSide.propTypes = {
question: PropTypes.object.isRequired,
isObjectDetail: PropTypes.bool,
isAdditionalInfoVisible: PropTypes.bool,
isShowingQuestionDetailsSidebar: PropTypes.bool,
onOpenQuestionInfo: PropTypes.func.isRequired,
onSave: PropTypes.func,
};
function SavedQuestionLeftSide(props) {
const {
question,
isObjectDetail,
isAdditionalInfoVisible,
onOpenQuestionInfo,
onSave,
} = props;
const [showSubHeader, setShowSubHeader] = useState(true);
const hasLastEditInfo = question.lastEditInfo() != null;
const type = question.type();
const isModelOrMetric = type === "model" || type === "metric";
const onHeaderChange = useCallback(
name => {
if (name && name !== question.displayName()) {
onSave(question.setDisplayName(name));
}
},
[question, onSave],
);
const renderDataSource =
QuestionDataSource.shouldRender(props) && type === "question";
const renderLastEdit = hasLastEditInfo && isAdditionalInfoVisible;
useEffect(() => {
const timerId = setTimeout(() => {
if (isAdditionalInfoVisible && (renderDataSource || renderLastEdit)) {
setShowSubHeader(false);
}
}, 4000);
return () => clearTimeout(timerId);
}, [isAdditionalInfoVisible, renderDataSource, renderLastEdit]);
return (
<SavedQuestionLeftSideRoot
data-testid="qb-header-left-side"
showSubHeader={showSubHeader}
>
<ViewHeaderMainLeftContentContainer>
<SavedQuestionHeaderButtonContainer isModelOrMetric={isModelOrMetric}>
<HeadBreadcrumbs
divider={<HeaderDivider>/</HeaderDivider>}
parts={[
...(isAdditionalInfoVisible && isModelOrMetric
? [
<HeaderCollectionBadge
key="collection"
question={question}
/>,
]
: []),
<SavedQuestionHeaderButton
key={question.displayName()}
question={question}
onSave={onHeaderChange}
/>,
]}
/>
</SavedQuestionHeaderButtonContainer>
</ViewHeaderMainLeftContentContainer>
{isAdditionalInfoVisible && (
<ViewHeaderLeftSubHeading>
{QuestionDataSource.shouldRender(props) && !isModelOrMetric && (
<StyledQuestionDataSource
question={question}
isObjectDetail={isObjectDetail}
subHead
/>
)}
{hasLastEditInfo && isAdditionalInfoVisible && (
<StyledLastEditInfoLabel
item={question.card()}
onClick={onOpenQuestionInfo}
/>
)}
</ViewHeaderLeftSubHeading>
)}
</SavedQuestionLeftSideRoot>
);
}
AhHocQuestionLeftSide.propTypes = {
question: PropTypes.object.isRequired,
originalQuestion: PropTypes.object,
isNative: PropTypes.bool,
isObjectDetail: PropTypes.bool,
isSummarized: PropTypes.bool,
onOpenModal: PropTypes.func,
};
function AhHocQuestionLeftSide(props) {
const {
question,
originalQuestion,
isNative,
isObjectDetail,
isSummarized,
onOpenModal,
} = props;
const handleTitleClick = () => {
const { isEditable } = Lib.queryDisplayInfo(question.query());
if (isEditable) {
onOpenModal(MODAL_TYPES.SAVE);
}
};
return (
<AdHocLeftSideRoot>
<ViewHeaderMainLeftContentContainer>
<AdHocViewHeading color="medium">
{isNative ? (
t`New question`
) : (
<QuestionDescription
question={question}
originalQuestion={originalQuestion}
isObjectDetail={isObjectDetail}
onClick={handleTitleClick}
/>
)}
</AdHocViewHeading>
</ViewHeaderMainLeftContentContainer>
<ViewHeaderLeftSubHeading>
{isSummarized && (
<QuestionDataSource
className={CS.mb1}
question={question}
isObjectDetail={isObjectDetail}
subHead
/>
)}
</ViewHeaderLeftSubHeading>
</AdHocLeftSideRoot>
);
}
HeaderCollectionBadge.propTypes = {
question: PropTypes.object.isRequired,
};
function HeaderCollectionBadge({ question }) {
const { collection } = question.card();
const icon = question.type();
return (
<HeadBreadcrumbs.Badge to={Urls.collection(collection)} icon={icon}>
{collection?.name || t`Our analytics`}
</HeadBreadcrumbs.Badge>
);
}
ViewTitleHeaderRightSide.propTypes = {
question: PropTypes.object.isRequired,
result: PropTypes.object,
queryBuilderMode: PropTypes.oneOf(["view", "notebook"]),
isModelOrMetric: PropTypes.bool,
isSaved: PropTypes.bool,
isNative: PropTypes.bool,
isRunnable: PropTypes.bool,
isRunning: PropTypes.bool,
isNativeEditorOpen: PropTypes.bool,
isShowingSummarySidebar: PropTypes.bool,
isDirty: PropTypes.bool,
isResultDirty: PropTypes.bool,
isActionListVisible: PropTypes.bool,
runQuestionQuery: PropTypes.func,
updateQuestion: PropTypes.func.isRequired,
cancelQuery: PropTypes.func,
onOpenModal: PropTypes.func,
onEditSummary: PropTypes.func,
onCloseSummary: PropTypes.func,
setQueryBuilderMode: PropTypes.func,
turnDatasetIntoQuestion: PropTypes.func,
areFiltersExpanded: PropTypes.bool,
onExpandFilters: PropTypes.func,
onCollapseFilters: PropTypes.func,
isBookmarked: PropTypes.bool,
toggleBookmark: PropTypes.func,
onOpenQuestionInfo: PropTypes.func,
onCloseQuestionInfo: PropTypes.func,
isShowingQuestionInfoSidebar: PropTypes.bool,
onModelPersistenceChange: PropTypes.func,
onQueryChange: PropTypes.func,
};
function ViewTitleHeaderRightSide(props) {
const {
question,
result,
queryBuilderMode,
isBookmarked,
toggleBookmark,
isSaved,
isModelOrMetric,
isRunnable,
isRunning,
isNativeEditorOpen,
isShowingSummarySidebar,
isDirty,
isResultDirty,
isActionListVisible,
runQuestionQuery,
cancelQuery,
onOpenModal,
onEditSummary,
onCloseSummary,
setQueryBuilderMode,
turnDatasetIntoQuestion,
areFiltersExpanded,
onExpandFilters,
onCollapseFilters,
isShowingQuestionInfoSidebar,
onCloseQuestionInfo,
onOpenQuestionInfo,
onModelPersistenceChange,
} = props;
const isShowingNotebook = queryBuilderMode === "notebook";
const { isEditable } = Lib.queryDisplayInfo(question.query());
const hasExploreResultsLink =
canExploreResults(question) &&
MetabaseSettings.get("enable-nested-queries");
// Models and metrics can't be saved. But changing anything about the model/metric will prompt the user
// to save it as a new question (based on that model/metric). In other words, at this point
// the `type` field is set to "question".
const hasSaveButton =
!isModelOrMetric &&
!!isDirty &&
!question.isArchived() &&
isActionListVisible;
const isMissingPermissions =
result?.error_type === SERVER_ERROR_TYPES.missingPermissions;
const hasRunButton =
isRunnable && !isNativeEditorOpen && !isMissingPermissions;
const handleInfoClick = useCallback(() => {
if (isShowingQuestionInfoSidebar) {
onCloseQuestionInfo();
} else {
onOpenQuestionInfo();
}
}, [isShowingQuestionInfoSidebar, onOpenQuestionInfo, onCloseQuestionInfo]);
const getRunButtonLabel = useCallback(
() => (isRunning ? t`Cancel` : t`Refresh`),
[isRunning],
);
const canSave = Lib.canSave(question.query(), question.type());
const isSaveDisabled = !canSave;
const disabledSaveTooltip = getDisabledSaveTooltip(isEditable);
return (
<ViewHeaderActionPanel data-testid="qb-header-action-panel">
{FilterHeaderToggle.shouldRender(props) && (
<FilterHeaderToggle
className={cx(CS.ml2, CS.mr1)}
query={question.query()}
isExpanded={areFiltersExpanded}
onExpand={onExpandFilters}
onCollapse={onCollapseFilters}
/>
)}
{FilterHeaderButton.shouldRender(props) && (
<FilterHeaderButton
className={cx(CS.hide, CS.smShow)}
onOpenModal={onOpenModal}
/>
)}
{QuestionSummarizeWidget.shouldRender(props) && (
<QuestionSummarizeWidget
className={cx(CS.hide, CS.smShow)}
isShowingSummarySidebar={isShowingSummarySidebar}
onEditSummary={onEditSummary}
onCloseSummary={onCloseSummary}
/>
)}
{QuestionNotebookButton.shouldRender(props) && (
<ViewHeaderIconButtonContainer>
<QuestionNotebookButton
iconSize={16}
question={question}
isShowingNotebook={isShowingNotebook}
setQueryBuilderMode={setQueryBuilderMode}
/>
</ViewHeaderIconButtonContainer>
)}
{ToggleNativeQueryPreview.shouldRender(props) && (
<ToggleNativeQueryPreview question={question} />
)}
{hasExploreResultsLink && <ExploreResultsLink question={question} />}
{hasRunButton && !isShowingNotebook && (
<ViewHeaderIconButtonContainer>
<ViewRunButtonWithTooltip
iconSize={16}
onlyIcon
medium
compact
result={result}
isRunning={isRunning}
isDirty={isResultDirty}
onRun={() => runQuestionQuery({ ignoreCache: true })}
onCancel={cancelQuery}
getTooltip={getRunButtonLabel}
/>
</ViewHeaderIconButtonContainer>
)}
{isSaved && (
<QuestionActions
isShowingQuestionInfoSidebar={isShowingQuestionInfoSidebar}
isBookmarked={isBookmarked}
handleBookmark={toggleBookmark}
onOpenModal={onOpenModal}
question={question}
setQueryBuilderMode={setQueryBuilderMode}
turnDatasetIntoQuestion={turnDatasetIntoQuestion}
onInfoClick={handleInfoClick}
onModelPersistenceChange={onModelPersistenceChange}
/>
)}
{hasSaveButton && (
<SaveButton
role="button"
disabled={isSaveDisabled}
tooltip={{
tooltip: disabledSaveTooltip,
isEnabled: isSaveDisabled,
placement: "left",
}}
onClick={() => onOpenModal("save")}
>
{t`Save`}
</SaveButton>
)}
</ViewHeaderActionPanel>
);
}
ViewTitleHeader.propTypes = viewTitleHeaderPropTypes;
function getDisabledSaveTooltip(isEditable) {
if (!isEditable) {
return t`You don't have permission to save this question.`;
}
}
import PropTypes from "prop-types";
import { t } from "ttag";
import CS from "metabase/css/core/index.css";
import {
AdHocLeftSideRoot,
AdHocViewHeading,
ViewHeaderLeftSubHeading,
ViewHeaderMainLeftContentContainer,
} from "metabase/query_builder/components/view/ViewHeader/ViewHeader.styled";
import {
QuestionDataSource,
QuestionDescription,
} from "metabase/query_builder/components/view/ViewHeader/components";
import { MODAL_TYPES } from "metabase/query_builder/constants";
import * as Lib from "metabase-lib";
AdHocQuestionLeftSide.propTypes = {
question: PropTypes.object.isRequired,
originalQuestion: PropTypes.object,
isNative: PropTypes.bool,
isObjectDetail: PropTypes.bool,
isSummarized: PropTypes.bool,
onOpenModal: PropTypes.func,
};
export function AdHocQuestionLeftSide(props) {
const {
question,
originalQuestion,
isNative,
isObjectDetail,
isSummarized,
onOpenModal,
} = props;
const handleTitleClick = () => {
const { isEditable } = Lib.queryDisplayInfo(question.query());
if (isEditable) {
onOpenModal(MODAL_TYPES.SAVE);
}
};
return (
<AdHocLeftSideRoot>
<ViewHeaderMainLeftContentContainer>
<AdHocViewHeading color="medium">
{isNative ? (
t`New question`
) : (
<QuestionDescription
question={question}
originalQuestion={originalQuestion}
isObjectDetail={isObjectDetail}
onClick={handleTitleClick}
/>
)}
</AdHocViewHeading>
</ViewHeaderMainLeftContentContainer>
<ViewHeaderLeftSubHeading>
{isSummarized && (
<QuestionDataSource
className={CS.mb1}
question={question}
isObjectDetail={isObjectDetail}
subHead
/>
)}
</ViewHeaderLeftSubHeading>
</AdHocLeftSideRoot>
);
}
export * from "./AdHocQuestionLeftSide";
import { t } from "ttag";
import Link from "metabase/core/components/Link";
import Tooltip from "metabase/core/components/Tooltip";
import { useDispatch, useSelector } from "metabase/lib/redux";
import * as Urls from "metabase/lib/urls";
import { navigateBackToDashboard } from "metabase/query_builder/actions";
import {
BackButton,
BackButtonContainer,
} from "metabase/query_builder/components/view/ViewHeader/ViewHeader.styled";
import { getDashboard } from "metabase/query_builder/selectors";
export function DashboardBackButton() {
const dashboard = useSelector(getDashboard);
const dispatch = useDispatch();
const handleClick = () => {
dispatch(navigateBackToDashboard(dashboard.id));
};
if (!dashboard) {
return null;
}
const label = t`Back to ${dashboard.name}`;
return (
<Tooltip tooltip={label}>
<BackButtonContainer>
<BackButton
as={Link}
to={Urls.dashboard(dashboard)}
round
icon="arrow_left"
aria-label={label}
onClick={handleClick}
/>
</BackButtonContainer>
</Tooltip>
);
}
export * from "./DashboardBackButton";
import PropTypes from "prop-types";
import { t } from "ttag";
import * as Urls from "metabase/lib/urls";
import { HeadBreadcrumbs } from "metabase/query_builder/components/view/ViewHeader/components";
HeaderCollectionBadge.propTypes = {
question: PropTypes.object.isRequired,
};
export function HeaderCollectionBadge({ question }) {
const { collection } = question.card();
const icon = question.type();
return (
<HeadBreadcrumbs.Badge to={Urls.collection(collection)} icon={icon}>
{collection?.name || t`Our analytics`}
</HeadBreadcrumbs.Badge>
);
}
export * from "./HeaderCollectionBadge";
import PropTypes from "prop-types";
import { useCallback, useEffect, useState } from "react";
import SavedQuestionHeaderButton from "metabase/query_builder/components/SavedQuestionHeaderButton/SavedQuestionHeaderButton";
import {
HeaderDivider,
SavedQuestionHeaderButtonContainer,
SavedQuestionLeftSideRoot,
StyledLastEditInfoLabel,
StyledQuestionDataSource,
ViewHeaderLeftSubHeading,
ViewHeaderMainLeftContentContainer,
} from "metabase/query_builder/components/view/ViewHeader/ViewHeader.styled";
import {
HeadBreadcrumbs,
QuestionDataSource,
} from "metabase/query_builder/components/view/ViewHeader/components";
import { HeaderCollectionBadge } from "metabase/query_builder/components/view/ViewHeader/components/HeaderCollectionBadge/HeaderCollectionBadge";
SavedQuestionLeftSide.propTypes = {
question: PropTypes.object.isRequired,
isObjectDetail: PropTypes.bool,
isAdditionalInfoVisible: PropTypes.bool,
isShowingQuestionDetailsSidebar: PropTypes.bool,
onOpenQuestionInfo: PropTypes.func.isRequired,
onSave: PropTypes.func,
};
export function SavedQuestionLeftSide(props) {
const {
question,
isObjectDetail,
isAdditionalInfoVisible,
onOpenQuestionInfo,
onSave,
} = props;
const [showSubHeader, setShowSubHeader] = useState(true);
const hasLastEditInfo = question.lastEditInfo() != null;
const type = question.type();
const isModelOrMetric = type === "model" || type === "metric";
const onHeaderChange = useCallback(
name => {
if (name && name !== question.displayName()) {
onSave(question.setDisplayName(name));
}
},
[question, onSave],
);
const renderDataSource =
QuestionDataSource.shouldRender(props) && type === "question";
const renderLastEdit = hasLastEditInfo && isAdditionalInfoVisible;
useEffect(() => {
const timerId = setTimeout(() => {
if (isAdditionalInfoVisible && (renderDataSource || renderLastEdit)) {
setShowSubHeader(false);
}
}, 4000);
return () => clearTimeout(timerId);
}, [isAdditionalInfoVisible, renderDataSource, renderLastEdit]);
return (
<SavedQuestionLeftSideRoot
data-testid="qb-header-left-side"
showSubHeader={showSubHeader}
>
<ViewHeaderMainLeftContentContainer>
<SavedQuestionHeaderButtonContainer isModelOrMetric={isModelOrMetric}>
<HeadBreadcrumbs
divider={<HeaderDivider>/</HeaderDivider>}
parts={[
...(isAdditionalInfoVisible && isModelOrMetric
? [
<HeaderCollectionBadge
key="collection"
question={question}
/>,
]
: []),
<SavedQuestionHeaderButton
key={question.displayName()}
question={question}
onSave={onHeaderChange}
/>,
]}
/>
</SavedQuestionHeaderButtonContainer>
</ViewHeaderMainLeftContentContainer>
{isAdditionalInfoVisible && (
<ViewHeaderLeftSubHeading>
{QuestionDataSource.shouldRender(props) && !isModelOrMetric && (
<StyledQuestionDataSource
question={question}
isObjectDetail={isObjectDetail}
subHead
/>
)}
{hasLastEditInfo && isAdditionalInfoVisible && (
<StyledLastEditInfoLabel
item={question.card()}
onClick={onOpenQuestionInfo}
/>
)}
</ViewHeaderLeftSubHeading>
)}
</SavedQuestionLeftSideRoot>
);
}
export * from "./SavedQuestionLeftSide";
import cx from "classnames";
import PropTypes from "prop-types";
import { useCallback } from "react";
import { t } from "ttag";
import CS from "metabase/css/core/index.css";
import { SERVER_ERROR_TYPES } from "metabase/lib/errors";
import MetabaseSettings from "metabase/lib/settings";
import {
SaveButton,
ViewHeaderActionPanel,
ViewHeaderIconButtonContainer,
ViewRunButtonWithTooltip,
} from "metabase/query_builder/components/view/ViewHeader/ViewHeader.styled";
import {
ExploreResultsLink,
FilterHeaderButton,
FilterHeaderToggle,
QuestionActions,
QuestionNotebookButton,
QuestionSummarizeWidget,
ToggleNativeQueryPreview,
} from "metabase/query_builder/components/view/ViewHeader/components";
import { canExploreResults } from "metabase/query_builder/components/view/ViewHeader/utils";
import * as Lib from "metabase-lib";
ViewTitleHeaderRightSide.propTypes = {
question: PropTypes.object.isRequired,
result: PropTypes.object,
queryBuilderMode: PropTypes.oneOf(["view", "notebook"]),
isModelOrMetric: PropTypes.bool,
isSaved: PropTypes.bool,
isNative: PropTypes.bool,
isRunnable: PropTypes.bool,
isRunning: PropTypes.bool,
isNativeEditorOpen: PropTypes.bool,
isShowingSummarySidebar: PropTypes.bool,
isDirty: PropTypes.bool,
isResultDirty: PropTypes.bool,
isActionListVisible: PropTypes.bool,
runQuestionQuery: PropTypes.func,
updateQuestion: PropTypes.func.isRequired,
cancelQuery: PropTypes.func,
onOpenModal: PropTypes.func,
onEditSummary: PropTypes.func,
onCloseSummary: PropTypes.func,
setQueryBuilderMode: PropTypes.func,
turnDatasetIntoQuestion: PropTypes.func,
areFiltersExpanded: PropTypes.bool,
onExpandFilters: PropTypes.func,
onCollapseFilters: PropTypes.func,
isBookmarked: PropTypes.bool,
toggleBookmark: PropTypes.func,
onOpenQuestionInfo: PropTypes.func,
onCloseQuestionInfo: PropTypes.func,
isShowingQuestionInfoSidebar: PropTypes.bool,
onModelPersistenceChange: PropTypes.func,
onQueryChange: PropTypes.func,
};
export function ViewTitleHeaderRightSide(props) {
const {
question,
result,
queryBuilderMode,
isBookmarked,
toggleBookmark,
isSaved,
isModelOrMetric,
isRunnable,
isRunning,
isNativeEditorOpen,
isShowingSummarySidebar,
isDirty,
isResultDirty,
isActionListVisible,
runQuestionQuery,
cancelQuery,
onOpenModal,
onEditSummary,
onCloseSummary,
setQueryBuilderMode,
turnDatasetIntoQuestion,
areFiltersExpanded,
onExpandFilters,
onCollapseFilters,
isShowingQuestionInfoSidebar,
onCloseQuestionInfo,
onOpenQuestionInfo,
onModelPersistenceChange,
} = props;
const isShowingNotebook = queryBuilderMode === "notebook";
const { isEditable } = Lib.queryDisplayInfo(question.query());
const hasExploreResultsLink =
canExploreResults(question) &&
MetabaseSettings.get("enable-nested-queries");
// Models and metrics can't be saved. But changing anything about the model/metric will prompt the user
// to save it as a new question (based on that model/metric). In other words, at this point
// the `type` field is set to "question".
const hasSaveButton =
!isModelOrMetric &&
!!isDirty &&
!question.isArchived() &&
isActionListVisible;
const isMissingPermissions =
result?.error_type === SERVER_ERROR_TYPES.missingPermissions;
const hasRunButton =
isRunnable && !isNativeEditorOpen && !isMissingPermissions;
const handleInfoClick = useCallback(() => {
if (isShowingQuestionInfoSidebar) {
onCloseQuestionInfo();
} else {
onOpenQuestionInfo();
}
}, [isShowingQuestionInfoSidebar, onOpenQuestionInfo, onCloseQuestionInfo]);
const getRunButtonLabel = useCallback(
() => (isRunning ? t`Cancel` : t`Refresh`),
[isRunning],
);
const canSave = Lib.canSave(question.query(), question.type());
const isSaveDisabled = !canSave;
const disabledSaveTooltip = getDisabledSaveTooltip(isEditable);
return (
<ViewHeaderActionPanel data-testid="qb-header-action-panel">
{FilterHeaderToggle.shouldRender(props) && (
<FilterHeaderToggle
className={cx(CS.ml2, CS.mr1)}
query={question.query()}
isExpanded={areFiltersExpanded}
onExpand={onExpandFilters}
onCollapse={onCollapseFilters}
/>
)}
{FilterHeaderButton.shouldRender(props) && (
<FilterHeaderButton
className={cx(CS.hide, CS.smShow)}
onOpenModal={onOpenModal}
/>
)}
{QuestionSummarizeWidget.shouldRender(props) && (
<QuestionSummarizeWidget
className={cx(CS.hide, CS.smShow)}
isShowingSummarySidebar={isShowingSummarySidebar}
onEditSummary={onEditSummary}
onCloseSummary={onCloseSummary}
/>
)}
{QuestionNotebookButton.shouldRender(props) && (
<ViewHeaderIconButtonContainer>
<QuestionNotebookButton
iconSize={16}
question={question}
isShowingNotebook={isShowingNotebook}
setQueryBuilderMode={setQueryBuilderMode}
/>
</ViewHeaderIconButtonContainer>
)}
{ToggleNativeQueryPreview.shouldRender(props) && (
<ToggleNativeQueryPreview question={question} />
)}
{hasExploreResultsLink && <ExploreResultsLink question={question} />}
{hasRunButton && !isShowingNotebook && (
<ViewHeaderIconButtonContainer>
<ViewRunButtonWithTooltip
iconSize={16}
onlyIcon
medium
compact
result={result}
isRunning={isRunning}
isDirty={isResultDirty}
onRun={() => runQuestionQuery({ ignoreCache: true })}
onCancel={cancelQuery}
getTooltip={getRunButtonLabel}
/>
</ViewHeaderIconButtonContainer>
)}
{isSaved && (
<QuestionActions
isShowingQuestionInfoSidebar={isShowingQuestionInfoSidebar}
isBookmarked={isBookmarked}
handleBookmark={toggleBookmark}
onOpenModal={onOpenModal}
question={question}
setQueryBuilderMode={setQueryBuilderMode}
turnDatasetIntoQuestion={turnDatasetIntoQuestion}
onInfoClick={handleInfoClick}
onModelPersistenceChange={onModelPersistenceChange}
/>
)}
{hasSaveButton && (
<SaveButton
role="button"
disabled={isSaveDisabled}
tooltip={{
tooltip: disabledSaveTooltip,
isEnabled: isSaveDisabled,
placement: "left",
}}
onClick={() => onOpenModal("save")}
>
{t`Save`}
</SaveButton>
)}
</ViewHeaderActionPanel>
);
}
function getDisabledSaveTooltip(isEditable) {
if (!isEditable) {
return t`You don't have permission to save this question.`;
}
}
export * from "./ViewTitleHeaderRightSide";
......@@ -8,3 +8,8 @@ export * from "./QuestionNotebookButton";
export * from "./ExploreResultsLink";
export * from "./FilterHeaderButton";
export * from "./QuestionSummarizeWidget";
export * from "./AdHocQuestionLeftSide";
export * from "./HeaderCollectionBadge";
export * from "./SavedQuestionLeftSide";
export * from "./ViewTitleHeaderRightSide";
export * from "./DashboardBackButton";
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