Skip to content
Snippets Groups Projects
Commit 08c53cf6 authored by Sameer Al-Sakran's avatar Sameer Al-Sakran
Browse files

Merge branch 'master' into add-minmax-to-datetime-fingerprints

parents a449cb44 d97214bc
No related branches found
No related tags found
No related merge requests found
Showing
with 128 additions and 59 deletions
......@@ -4,6 +4,10 @@
.*/node_modules/react-resizable/.*
.*/node_modules/documentation/.*
.*/node_modules/.*/\(lib\|test\).*\.json$
.*/node_modules/react-element-to-jsx-string/.*
.*/node_modules/react-element-to-jsx-string/.*
.*/node_modules/resize-observer-polyfill/.*
.*/node_modules/react-virtualized/.*
[include]
.*/frontend/.*
......
......@@ -37,9 +37,22 @@ const getErrorComponent = ({ status, data, context }) => {
@connect(mapStateToProps)
export default class App extends Component {
state = {
hasError: false,
};
componentDidCatch(error, info) {
console.error("Error caught in <App>", error, info);
this.setState({ hasError: true });
}
render() {
const { children, location, errorPage } = this.props;
if (this.state.hasError) {
return <div>😢</div>;
}
return (
<div className="spread flex flex-column">
<Navbar location={location} className="flex-no-shrink" />
......
import React, { Component, PropTypes } from "react";
import React, { Component } from "react";
import PropTypes from "prop-types";
import _ from "underscore";
import cx from "classnames";
......
......@@ -31,15 +31,25 @@ export default class Calendar extends Component {
componentWillReceiveProps(nextProps) {
if (
!moment(nextProps.selected).isSame(this.props.selected, "day") ||
!moment(nextProps.selectedEnd).isSame(this.props.selectedEnd, "day")
// `selected` became null or not null
(nextProps.selected == null) !== (this.props.selected == null) ||
// `selectedEnd` became null or not null
(nextProps.selectedEnd == null) !== (this.props.selectedEnd == null) ||
// `selected` is not null and doesn't match previous `selected`
(nextProps.selected != null &&
!moment(nextProps.selected).isSame(this.props.selected, "day")) ||
// `selectedEnd` is not null and doesn't match previous `selectedEnd`
(nextProps.selectedEnd != null &&
!moment(nextProps.selectedEnd).isSame(this.props.selectedEnd, "day"))
) {
let resetCurrent = false;
if (nextProps.selected && nextProps.selectedEnd) {
if (nextProps.selected != null && nextProps.selectedEnd != null) {
// reset if `current` isn't between `selected` and `selectedEnd` month
resetCurrent =
nextProps.selected.isAfter(this.state.current, "month") &&
nextProps.selectedEnd.isBefore(this.state.current, "month");
} else if (nextProps.selected) {
} else if (nextProps.selected != null) {
// reset if `current` isn't in `selected` month
resetCurrent =
nextProps.selected.isAfter(this.state.current, "month") ||
nextProps.selected.isBefore(this.state.current, "month");
......
......@@ -24,19 +24,22 @@ export default ComposedComponent =>
this._mql.addListener(this._updateSize);
}
// resize observer, ensure re-layout when container element changes size
this._ro = new ResizeObserver((entries, observer) => {
const element = ReactDOM.findDOMNode(this);
for (const entry of entries) {
if (entry.target === element) {
this._updateSize();
break;
const element = ReactDOM.findDOMNode(this);
if (element) {
// resize observer, ensure re-layout when container element changes size
this._ro = new ResizeObserver((entries, observer) => {
const element = ReactDOM.findDOMNode(this);
for (const entry of entries) {
if (entry.target === element) {
this._updateSize();
break;
}
}
}
});
this._ro.observe(ReactDOM.findDOMNode(this));
});
this._ro.observe(element);
this._updateSize();
this._updateSize();
}
}
componentDidUpdate() {
......@@ -53,11 +56,12 @@ export default ComposedComponent =>
}
_updateSize = () => {
const { width, height } = ReactDOM.findDOMNode(
this,
).getBoundingClientRect();
if (this.state.width !== width || this.state.height !== height) {
this.setState({ width, height });
const element = ReactDOM.findDOMNode(this);
if (element) {
const { width, height } = element.getBoundingClientRect();
if (this.state.width !== width || this.state.height !== height) {
this.setState({ width, height });
}
}
};
......
......@@ -5,7 +5,7 @@ import cx from "classnames";
import { getScrollX, getScrollY } from "metabase/lib/dom";
import ReactCSSTransitionGroup from "react-addons-css-transition-group";
import { CSSTransitionGroup } from "react-transition-group";
import { Motion, spring } from "react-motion";
import OnClickOutsideWrapper from "./OnClickOutsideWrapper.jsx";
......@@ -88,7 +88,7 @@ export class WindowModal extends Component {
"flex justify-center align-center fixed top left bottom right";
ReactDOM.unstable_renderSubtreeIntoContainer(
this,
<ReactCSSTransitionGroup
<CSSTransitionGroup
transitionName="Modal"
transitionAppear={true}
transitionAppearTimeout={250}
......@@ -104,7 +104,7 @@ export class WindowModal extends Component {
{this._modalComponent()}
</div>
)}
</ReactCSSTransitionGroup>,
</CSSTransitionGroup>,
this._modalElement,
);
}
......
import React, { Component } from "react";
import PropTypes from "prop-types";
import ReactDOM from "react-dom";
import ReactCSSTransitionGroup from "react-addons-css-transition-group";
import { CSSTransitionGroup } from "react-transition-group";
import OnClickOutsideWrapper from "./OnClickOutsideWrapper";
import Tether from "tether";
......@@ -259,7 +259,7 @@ export default class Popover extends Component {
const popoverElement = this._getPopoverElement();
ReactDOM.unstable_renderSubtreeIntoContainer(
this,
<ReactCSSTransitionGroup
<CSSTransitionGroup
transitionName="Popover"
transitionAppear
transitionEnter
......@@ -269,7 +269,7 @@ export default class Popover extends Component {
transitionLeaveTimeout={POPOVER_TRANSITION_LEAVE}
>
{isOpen ? this._popoverComponent() : null}
</ReactCSSTransitionGroup>,
</CSSTransitionGroup>,
popoverElement,
);
......
......@@ -50,7 +50,7 @@ export default class UserAvatar extends Component {
return (
<div
className={cx(classes)}
style={Object.assign(this.styles, this.props.style)}
style={{ ...this.styles, ...this.props.style }}
>
{this.userInitials()}
</div>
......
import React, { Component } from "react";
import PropTypes from "prop-types";
import ReactCSSTransitionGroup from "react-addons-css-transition-group";
import { CSSTransitionGroup } from "react-transition-group";
import FormField from "metabase/components/FormField.jsx";
import ModalContent from "metabase/components/ModalContent.jsx";
......@@ -201,7 +201,7 @@ export default class SaveQuestionModal extends Component {
>
<form className="Form-inputs" onSubmit={this.formSubmitted}>
{saveOrUpdate}
<ReactCSSTransitionGroup
<CSSTransitionGroup
transitionName="saveQuestionModalFields"
transitionEnterTimeout={500}
transitionLeaveTimeout={500}
......@@ -275,7 +275,7 @@ export default class SaveQuestionModal extends Component {
</CollectionList>
</div>
)}
</ReactCSSTransitionGroup>
</CSSTransitionGroup>
</form>
</ModalContent>
);
......
......@@ -11,7 +11,7 @@ import { t } from "c-3po";
import Icon from "metabase/components/Icon";
import BodyComponent from "metabase/components/BodyComponent";
import ReactCSSTransitionGroup from "react-addons-css-transition-group";
import { CSSTransitionGroup } from "react-transition-group";
const mapStateToProps = (state, props) => {
return {
......@@ -37,7 +37,7 @@ export default class UndoListing extends Component {
const { undos, performUndo, dismissUndo } = this.props;
return (
<ul className={S.listing}>
<ReactCSSTransitionGroup
<CSSTransitionGroup
transitionName="UndoListing"
transitionEnterTimeout={300}
transitionLeaveTimeout={300}
......@@ -65,7 +65,7 @@ export default class UndoListing extends Component {
)}
</li>
))}
</ReactCSSTransitionGroup>
</CSSTransitionGroup>
</ul>
);
}
......
......@@ -4,8 +4,12 @@ import React, { Component } from "react";
import { Link, Route } from "react-router";
import { slugify } from "metabase/lib/formatting";
// $FlowFixMe: react-virtualized ignored
import reactElementToJSXString from "react-element-to-jsx-string";
import COMPONENTS from "../lib/components-webpack";
import AceEditor from "metabase/components/TextEditor";
import CopyButton from "metabase/components/CopyButton";
......
......@@ -203,6 +203,9 @@ export function addCSSRule(selector, rules, index = 0) {
}
export function constrainToScreen(element, direction, padding) {
if (!element) {
return false;
}
if (direction === "bottom") {
let screenBottom = window.innerHeight + getScrollY();
let overflowY = element.getBoundingClientRect().bottom - screenBottom;
......
import React, { Component } from "react";
import PropTypes from "prop-types";
import Calendar from "metabase/components/Calendar.jsx";
import moment from "moment";
const SEPARATOR = "~"; // URL-safe
function parseDateRangeValue(value) {
const [start, end] = (value || "").split(SEPARATOR);
return { start, end };
}
function serializeDateRangeValue({ start, end }) {
return [start, end].join(SEPARATOR);
}
export default class DateRangeWidget extends Component {
constructor(props, context) {
super(props, context);
this.state = {
start: null,
end: null,
};
this.state = parseDateRangeValue(props.value);
}
static propTypes = {};
static propTypes = {
value: PropTypes.string,
setValue: PropTypes.func.isRequired,
};
static defaultProps = {};
static format = value => {
const [start, end] = (value || "").split(SEPARATOR);
const { start, end } = parseDateRangeValue(value);
return start && end
? moment(start).format("MMMM D, YYYY") +
" - " +
......@@ -26,13 +35,10 @@ export default class DateRangeWidget extends Component {
: "";
};
componentWillMount() {
this.componentWillReceiveProps(this.props);
}
componentWillReceiveProps(nextProps) {
const [start, end] = (nextProps.value || "").split(SEPARATOR);
this.setState({ start, end });
if (nextProps.value !== this.props.value) {
this.setState(parseDateRangeValue(nextProps.value));
}
}
render() {
......@@ -47,7 +53,7 @@ export default class DateRangeWidget extends Component {
if (end == null) {
this.setState({ start, end });
} else {
this.props.setValue([start, end].join(SEPARATOR));
this.props.setValue(serializeDateRangeValue({ start, end }));
}
}}
/>
......
......@@ -54,7 +54,7 @@ export default class AggregationPopover extends Component {
customFields: PropTypes.object,
availableAggregations: PropTypes.array,
// Restricts the shown options to contents of `availableActions` only
showOnlyProvidedAggregations: PropTypes.boolean,
showOnlyProvidedAggregations: PropTypes.bool,
};
componentDidUpdate() {
......
/* eslint "react/prop-types": "warn" */
import React from "react";
import React, { Component } from "react";
import { t, jt } from "c-3po";
import VisualizationErrorMessage from "./VisualizationErrorMessage";
......@@ -8,7 +8,6 @@ import Visualization from "metabase/visualizations/components/Visualization.jsx"
import { datasetContainsNoResults } from "metabase/lib/dataset";
import { DatasetQuery } from "metabase/meta/types/Card";
import { CreateAlertModalContent } from "metabase/query_builder/components/AlertModals";
import { Component } from "react/lib/ReactBaseClasses";
import Modal from "metabase/components/Modal";
import { ALERT_TYPE_ROWS } from "metabase-lib/lib/Alert";
......
......@@ -7,6 +7,7 @@ import Item from "./Item";
import S from "./List.css";
// $FlowFixMe: react-virtualized ignored
import { List as VirtalizedList, WindowScroller } from "react-virtualized";
import "react-virtualized/styles.css";
......
......@@ -2,7 +2,7 @@ import React, { Component } from "react";
import cx from "classnames";
import { connect } from "react-redux";
import { t } from "c-3po";
import ReactCSSTransitionGroup from "react-addons-css-transition-group";
import { CSSTransitionGroup } from "react-transition-group";
import S from "./Formula.css";
......@@ -52,7 +52,7 @@ export default class Formula extends Component {
<Icon name="beaker" size={24} />
<span className={S.formulaTitle}>{t`View the ${type} formula`}</span>
</div>
<ReactCSSTransitionGroup
<CSSTransitionGroup
transitionName="formulaDefinition"
transitionEnterTimeout={300}
transitionLeaveTimeout={300}
......@@ -66,7 +66,7 @@ export default class Formula extends Component {
/>
</div>
)}
</ReactCSSTransitionGroup>
</CSSTransitionGroup>
</div>
);
}
......
import * as React from "react";
import React from "react";
import { t } from "c-3po";
import AdminAwareEmptyState from "metabase/components/AdminAwareEmptyState";
......
......@@ -32,6 +32,7 @@
}
.PageFlag:after {
top: 0;
left: -12px;
border-top: 12px solid transparent;
border-bottom: 12px solid transparent;
......
......@@ -2,16 +2,39 @@ import React, { Component } from "react";
import "./PageFlag.css";
import ReactCSSTransitionGroup from "react-addons-css-transition-group";
import { CSSTransitionGroup } from "react-transition-group";
import BodyComponent from "metabase/components/BodyComponent.jsx";
import BodyComponent from "metabase/components/BodyComponent";
import cx from "classnames";
@BodyComponent
export default class PageFlag extends Component {
componentWillMount() {
// sometimes the position of target changes, track it here
this._timer = setInterval(() => {
if (this.props.target) {
const p1 = this._position;
const p2 = this.props.target.getBoundingClientRect();
if (
!p1 ||
p1.left !== p2.left ||
p1.top !== p2.top ||
p1.width !== p2.width ||
p1.height !== p2.height
) {
this.forceUpdate();
}
}
}, 100);
}
componentWillUnmount() {
clearInterval(this._timer);
}
renderPageFlag() {
let position = this.props.target.getBoundingClientRect();
let position = (this._position = this.props.target.getBoundingClientRect());
let isLarge = !!this.props.text;
let style = {
position: "absolute",
......@@ -34,13 +57,13 @@ export default class PageFlag extends Component {
render() {
return (
<ReactCSSTransitionGroup
<CSSTransitionGroup
transitionName="PageFlag"
transitionEnterTimeout={250}
transitionLeaveTimeout={250}
>
{this.props.target ? [this.renderPageFlag()] : []}
</ReactCSSTransitionGroup>
</CSSTransitionGroup>
);
}
}
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