Skip to content
Snippets Groups Projects
Unverified Commit 32a87d26 authored by Nick Fitzpatrick's avatar Nick Fitzpatrick Committed by GitHub
Browse files

using CSS animations for notebook previews (#39717)

* using CSS animations for notebook previews

* useMemo

* optimizations on question creation
parent 49f33697
No related branches found
No related tags found
No related merge requests found
/* eslint-disable react/prop-types */
import cx from "classnames";
import { Component } from "react";
import { Motion, spring } from "react-motion";
import { useState, useMemo } from "react";
import { t } from "ttag";
import _ from "underscore";
import QuestionResultLoader from "metabase/containers/QuestionResultLoader";
import Button from "metabase/core/components/Button";
import { useModalOpen } from "metabase/hooks/use-modal-open";
import { isReducedMotionPreferred } from "metabase/lib/dom";
import { Icon } from "metabase/ui";
import Visualization from "metabase/visualizations/components/Visualization";
......@@ -22,100 +22,83 @@ import {
const PREVIEW_ROWS_LIMIT = 10;
class NotebookStepPreview extends Component {
constructor(props) {
super(props);
this.state = {
question: this.getPreviewQuestion(props.step),
};
}
const getPreviewQuestion = step => {
const { getPreviewQuery, stageIndex } = step;
const query = getPreviewQuery();
const limit = Lib.currentLimit(query, stageIndex);
const hasSuitableLimit = limit !== null && limit <= PREVIEW_ROWS_LIMIT;
const queryWithLimit = hasSuitableLimit
? query
: Lib.limit(query, stageIndex, PREVIEW_ROWS_LIMIT);
refresh = () => {
this.setState({
question: this.getPreviewQuestion(this.props.step),
});
};
return Question.create()
.setQuery(queryWithLimit)
.setDisplay("table")
.setSettings({ "table.pivot": false });
};
getPreviewQuestion(step) {
const { getPreviewQuery, stageIndex } = step;
const query = getPreviewQuery();
const limit = Lib.currentLimit(query, stageIndex);
const hasSuitableLimit = limit !== null && limit <= PREVIEW_ROWS_LIMIT;
const queryWithLimit = hasSuitableLimit
? query
: Lib.limit(query, stageIndex, PREVIEW_ROWS_LIMIT);
const NotebookStepPreview = ({ step, onClose }) => {
const previewQuestion = useMemo(() => getPreviewQuestion(step), [step]);
const [activeQuestion, setActiveQuestion] = useState(previewQuestion);
return Question.create()
.setQuery(queryWithLimit)
.setDisplay("table")
.setSettings({ "table.pivot": false });
}
const refresh = () => {
setActiveQuestion(previewQuestion);
};
getIsDirty() {
const newQuestion = this.getPreviewQuestion(this.props.step);
return !_.isEqual(newQuestion.card(), this.state.question.card());
}
const isDirty = useMemo(
() => activeQuestion.isDirtyComparedTo(previewQuestion),
[activeQuestion, previewQuestion],
);
render() {
const { onClose } = this.props;
const { question } = this.state;
return (
<PreviewRoot data-testid="preview-root">
<PreviewHeader>
<span className="text-bold">{t`Preview`}</span>
<PreviewIconContainer>
<Icon
name="close"
onClick={onClose}
className="text-light text-medium-hover cursor-pointer ml1"
/>
</PreviewIconContainer>
</PreviewHeader>
{isDirty ? (
<PreviewButtonContainer className="bordered shadowed rounded bg-white p4">
<Button onClick={refresh}>{t`Refresh`}</Button>
</PreviewButtonContainer>
) : (
<QuestionResultLoader question={activeQuestion}>
{({ rawSeries, result }) => (
<VisualizationPreview rawSeries={rawSeries} result={result} />
)}
</QuestionResultLoader>
)}
</PreviewRoot>
);
};
const isDirty = this.getIsDirty();
const VisualizationPreview = ({ rawSeries, result }) => {
const { open } = useModalOpen();
const preferReducedMotion = isReducedMotionPreferred();
const preferReducedMotion = isReducedMotionPreferred();
const springOpts = preferReducedMotion
? { stiffness: 500 }
: { stiffness: 170 };
const transitionDuration = preferReducedMotion ? 80 : 700;
return (
<PreviewRoot data-testid="preview-root">
<PreviewHeader>
<span className="text-bold">{t`Preview`}</span>
<PreviewIconContainer>
<Icon
name="close"
onClick={onClose}
className="text-light text-medium-hover cursor-pointer ml1"
/>
</PreviewIconContainer>
</PreviewHeader>
{isDirty ? (
<PreviewButtonContainer className="bordered shadowed rounded bg-white p4">
<Button onClick={this.refresh}>{t`Refresh`}</Button>
</PreviewButtonContainer>
) : (
<QuestionResultLoader question={question}>
{({ rawSeries, result }) => (
<Motion
defaultStyle={{ height: 36 }}
style={{
height: spring(getPreviewHeightForResult(result), springOpts),
}}
>
{({ height }) => {
const targetHeight = getPreviewHeightForResult(result);
const snapHeight =
height > targetHeight / 2 ? targetHeight : 0;
const minHeight = preferReducedMotion ? snapHeight : height;
return (
<Visualization
rawSeries={rawSeries}
error={result && result.error}
className={cx("bordered shadowed rounded bg-white", {
p2: result && result.error,
})}
style={{ minHeight }}
/>
);
}}
</Motion>
)}
</QuestionResultLoader>
)}
</PreviewRoot>
);
}
}
return (
<Visualization
rawSeries={rawSeries}
error={result && result.error}
className={cx("bordered shadowed rounded bg-white", {
p2: result && result.error,
})}
style={{
height: open
? getPreviewHeightForResult(result)
: getPreviewHeightForResult(result) / 2,
transition: `height ${transitionDuration}ms cubic-bezier(0, 0, 0.2, 1)`,
}}
/>
);
};
function getPreviewHeightForResult(result) {
const rowCount = result ? result.data.rows.length : 1;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment