Skip to content

Commit

Permalink
Misc frontend changes from internal fork (getredash#5143)
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrieldutra authored and andrewdever committed Oct 5, 2020
1 parent b18e9c3 commit 9e8dbc1
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 22 deletions.
2 changes: 2 additions & 0 deletions client/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ module.exports = {
// Do not complain about useless contructors in declaration files
"no-useless-constructor": "off",
"@typescript-eslint/no-useless-constructor": "error",
// Many API fields and generated types use camelcase
"@typescript-eslint/camelcase": "off",
},
},
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
// @ts-expect-error (Must be removed after adding @redash/viz typing)
import ErrorBoundary, { ErrorBoundaryContext } from "@redash/viz/lib/components/ErrorBoundary";
import { Auth } from "@/services/auth";
import { policy } from "@/services/policy";
import { CurrentRoute } from "@/services/routes";
import organizationStatus from "@/services/organizationStatus";
import DynamicComponent from "@/components/DynamicComponent";
import ApplicationLayout from "./ApplicationLayout";
import ErrorMessage from "./ErrorMessage";

export type UserSessionWrapperRenderChildrenProps<P> = {
pageTitle?: string;
onError: (error: Error) => void;
} & P;

export interface UserSessionWrapperProps<P> {
render: (props: UserSessionWrapperRenderChildrenProps<P>) => React.ReactNode;
currentRoute: CurrentRoute<P>;
bodyClass?: string;
}

// This wrapper modifies `route.render` function and instead of passing `currentRoute` passes an object
// that contains:
// - `currentRoute.routeParams`
// - `pageTitle` field which is equal to `currentRoute.title`
// - `onError` field which is a `handleError` method of nearest error boundary

function UserSessionWrapper({ bodyClass, currentRoute, renderChildren }) {
export function UserSessionWrapper<P>({ bodyClass, currentRoute, render }: UserSessionWrapperProps<P>) {
const [isAuthenticated, setIsAuthenticated] = useState(!!Auth.isAuthenticated());

useEffect(() => {
let isCancelled = false;
Promise.all([Auth.requireSession(), organizationStatus.refresh(), policy.refresh()])
Expand Down Expand Up @@ -50,10 +62,10 @@ function UserSessionWrapper({ bodyClass, currentRoute, renderChildren }) {
return (
<ApplicationLayout>
<React.Fragment key={currentRoute.key}>
<ErrorBoundary renderError={error => <ErrorMessage error={error} />}>
<ErrorBoundary renderError={(error: Error) => <ErrorMessage error={error} />}>
<ErrorBoundaryContext.Consumer>
{({ handleError }) =>
renderChildren({ ...currentRoute.routeParams, pageTitle: currentRoute.title, onError: handleError })
{({ handleError }: { handleError: UserSessionWrapperRenderChildrenProps<P>["onError"] }) =>
render({ ...currentRoute.routeParams, pageTitle: currentRoute.title, onError: handleError })
}
</ErrorBoundaryContext.Consumer>
</ErrorBoundary>
Expand All @@ -62,21 +74,35 @@ function UserSessionWrapper({ bodyClass, currentRoute, renderChildren }) {
);
}

UserSessionWrapper.propTypes = {
bodyClass: PropTypes.string,
renderChildren: PropTypes.func,
export type RouteWithUserSessionOptions<P> = {
render: (props: UserSessionWrapperRenderChildrenProps<P>) => React.ReactNode;
bodyClass?: string;
title: string;
path: string;
};

UserSessionWrapper.defaultProps = {
bodyClass: null,
renderChildren: () => null,
};
export const UserSessionWrapperDynamicComponentName = "UserSessionWrapper";

export default function routeWithUserSession({ render, bodyClass, ...rest }) {
export default function routeWithUserSession<P extends {} = {}>({
render: originalRender,
bodyClass,
...rest
}: RouteWithUserSessionOptions<P>) {
return {
...rest,
render: currentRoute => (
<UserSessionWrapper bodyClass={bodyClass} currentRoute={currentRoute} renderChildren={render} />
),
render: (currentRoute: CurrentRoute<P>) => {
const props = {
render: originalRender,
bodyClass,
currentRoute,
};
return (
<DynamicComponent
{...props}
name={UserSessionWrapperDynamicComponentName}
fallback={<UserSessionWrapper {...props} />}
/>
);
},
};
}
8 changes: 5 additions & 3 deletions client/app/components/DynamicComponent.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isFunction, isString } from "lodash";
import { isFunction, isString, isUndefined } from "lodash";
import React from "react";
import PropTypes from "prop-types";

Expand All @@ -24,6 +24,7 @@ export function unregisterComponent(name) {
export default class DynamicComponent extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
fallback: PropTypes.node,
children: PropTypes.node,
};

Expand All @@ -40,10 +41,11 @@ export default class DynamicComponent extends React.Component {
}

render() {
const { name, children, ...props } = this.props;
const { name, children, fallback, ...props } = this.props;
const RealComponent = componentsRegistry.get(name);
if (!RealComponent) {
return children;
// return fallback if any, otherwise return children
return isUndefined(fallback) ? children : fallback;
}
return <RealComponent {...props}>{children}</RealComponent>;
}
Expand Down
10 changes: 9 additions & 1 deletion client/app/components/TimeAgo.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function toMoment(value) {
return value && value.isValid() ? value : null;
}

export default function TimeAgo({ date, placeholder, autoUpdate }) {
export default function TimeAgo({ date, placeholder, autoUpdate, variation }) {
const startDate = toMoment(date);
const [value, setValue] = useState(null);
const title = useMemo(() => (startDate ? startDate.format(clientConfig.dateTimeFormat) : null), [startDate]);
Expand All @@ -28,6 +28,13 @@ export default function TimeAgo({ date, placeholder, autoUpdate }) {
}
}, [autoUpdate, startDate, placeholder]);

if (variation === "timeAgoInTooltip") {
return (
<Tooltip title={value}>
<span data-test="TimeAgo">{title}</span>
</Tooltip>
);
}
return (
<Tooltip title={title}>
<span data-test="TimeAgo">{value}</span>
Expand All @@ -39,6 +46,7 @@ TimeAgo.propTypes = {
date: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.instanceOf(Date), Moment]),
placeholder: PropTypes.string,
autoUpdate: PropTypes.bool,
variation: PropTypes.oneOf(["timeAgoInTooltip"]),
};

TimeAgo.defaultProps = {
Expand Down
9 changes: 9 additions & 0 deletions client/app/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ export const IntervalEnum = {
MILLISECONDS: "millisecond",
};

export const AbbreviatedTimeUnits = {
SECONDS: "s",
MINUTES: "m",
HOURS: "h",
DAYS: "d",
WEEKS: "w",
MILLISECONDS: "ms",
};

export function formatDateTime(value) {
if (!value) {
return "";
Expand Down
1 change: 0 additions & 1 deletion client/app/pages/queries/QuerySource.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import useUnsavedChangesAlert from "./hooks/useUnsavedChangesAlert";
import "./QuerySource.less";

function chooseDataSourceId(dataSourceIds, availableDataSources) {
dataSourceIds = map(dataSourceIds, v => parseInt(v, 10));
availableDataSources = map(availableDataSources, ds => ds.id);
return find(dataSourceIds, id => includes(availableDataSources, id)) || null;
}
Expand Down
2 changes: 2 additions & 0 deletions client/app/services/sanitize.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ DOMPurify.addHook("afterSanitizeAttributes", function(node) {
}
});

export { DOMPurify };

export default DOMPurify.sanitize;
2 changes: 2 additions & 0 deletions viz-lib/src/services/sanitize.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ DOMPurify.addHook("afterSanitizeAttributes", function(node) {
}
});

export { DOMPurify };

export default DOMPurify.sanitize;

0 comments on commit 9e8dbc1

Please sign in to comment.