Skip to content

Commit

Permalink
Misc changes to codebase back ported from internal fork (getredash#5129)
Browse files Browse the repository at this point in the history
* Set corejs version in .babelrc so Jest doesn't complain.

* Rewrite services/routes in TypeScript.

* Add TypeScript definitions for DialogComponent.

* Make image paths more portable

* Add current route context and hook.

* Make EmptyState more flexible by being able to pass in getSteps function.

* Rewrite ItemsList in TypeScript.

* Introduce the possibility to add custom sorters for a column.

* Rearrange props to be friendly to TypeScript.

* Type definitions for NotificationApi.

* Use Databricks query editor components for databricks_internal type of query runner.

* URL Escape password in Alembic configuration.

* Compare types in migrations.
  • Loading branch information
arikfr authored Aug 25, 2020
1 parent 2cc3bd3 commit 84d516b
Show file tree
Hide file tree
Showing 19 changed files with 426 additions and 142 deletions.
24 changes: 14 additions & 10 deletions client/.babelrc
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
{
"presets": [
["@babel/preset-env", {
"exclude": [
"@babel/plugin-transform-async-to-generator",
"@babel/plugin-transform-arrow-functions"
],
"useBuiltIns": "usage"
}],
[
"@babel/preset-env",
{
"exclude": ["@babel/plugin-transform-async-to-generator", "@babel/plugin-transform-arrow-functions"],
"corejs": "2",
"useBuiltIns": "usage"
}
],
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-object-assign",
["babel-plugin-transform-builtin-extend", {
"globals": ["Error"]
}]
[
"babel-plugin-transform-builtin-extend",
{
"globals": ["Error"]
}
]
]
}
18 changes: 13 additions & 5 deletions client/app/components/ApplicationArea/Router.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isFunction, startsWith, trimStart, trimEnd } from "lodash";
import React, { useState, useEffect, useRef } from "react";
import React, { useState, useEffect, useRef, useContext } from "react";
import PropTypes from "prop-types";
import UniversalRouter from "universal-router";
import ErrorBoundary from "@redash/viz/lib/components/ErrorBoundary";
Expand All @@ -14,6 +14,12 @@ function generateRouteKey() {
.substr(2);
}

export const CurrentRouteContext = React.createContext(null);

export function useCurrentRoute() {
return useContext(CurrentRouteContext);
}

export function stripBase(href) {
// Resolve provided link and '' (root) relative to document's base.
// If provided href is not related to current document (does not
Expand Down Expand Up @@ -53,7 +59,7 @@ export default function Router({ routes, onRouteChange }) {
errorHandlerRef.current.reset();
}

const pathname = stripBase(location.path);
const pathname = stripBase(location.path) || "/";

// This is a optimization for route resolver: if current route was already resolved
// from this path - do nothing. It also prevents router from using outdated route in a case
Expand Down Expand Up @@ -109,9 +115,11 @@ export default function Router({ routes, onRouteChange }) {
}

return (
<ErrorBoundary ref={errorHandlerRef} renderError={error => <ErrorMessage error={error} />}>
{currentRoute.render(currentRoute)}
</ErrorBoundary>
<CurrentRouteContext.Provider value={currentRoute}>
<ErrorBoundary ref={errorHandlerRef} renderError={error => <ErrorMessage error={error} />}>
{currentRoute.render(currentRoute)}
</ErrorBoundary>
</CurrentRouteContext.Provider>
);
}

Expand Down
30 changes: 30 additions & 0 deletions client/app/components/DialogWrapper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ModalProps } from "antd/lib/modal/Modal";

export interface DialogProps<ROk, RCancel> {
props: ModalProps;
close: (result: ROk) => void;
dismiss: (result: RCancel) => void;
}

export type DialogWrapperChildProps<ROk, RCancel> = {
dialog: DialogProps<ROk, RCancel>;
};

export type DialogComponentType<ROk = void, P = {}, RCancel = void> = React.ComponentType<
DialogWrapperChildProps<ROk, RCancel> & P
>;

export function wrap<ROk = void, P = {}, RCancel = void>(
DialogComponent: DialogComponentType<ROk, P, RCancel>
): {
Component: DialogComponentType<ROk, P, RCancel>;
showModal: (
props?: P
) => {
update: (props: P) => void;
onClose: (handler: (result: ROk) => Promise<void>) => void;
onDismiss: (handler: (result: RCancel) => Promise<void>) => void;
close: (result: ROk) => void;
dismiss: (result: RCancel) => void;
};
};
2 changes: 1 addition & 1 deletion client/app/components/PreviewCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ UserPreviewCard.defaultProps = {
// DataSourcePreviewCard

export function DataSourcePreviewCard({ dataSource, withLink, children, ...props }) {
const imageUrl = `/static/images/db-logos/${dataSource.type}.png`;
const imageUrl = `static/images/db-logos/${dataSource.type}.png`;
const title = withLink ? <a href={"data_sources/" + dataSource.id}>{dataSource.name}</a> : dataSource.name;
return (
<PreviewCard {...props} imageUrl={imageUrl} title={title}>
Expand Down
27 changes: 25 additions & 2 deletions client/app/components/empty-state/EmptyState.d.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,41 @@
import React from "react";

export interface EmptyStateProps {
type DefaultStepKey = "dataSources" | "queries" | "alerts" | "dashboards" | "users";
export type StepKey<K> = DefaultStepKey | K;

export interface StepItem<K> {
key: StepKey<K>;
node: React.ReactNode;
}

export interface EmptyStateProps<K = unknown> {
header?: string;
icon?: string;
description: string;
illustration: string;
illustrationPath?: string;
helpLink: string;

onboardingMode?: boolean;
showAlertStep?: boolean;
showDashboardStep?: boolean;
showDataSourceStep?: boolean;
showInviteStep?: boolean;

getStepsItems?: (items: Array<StepItem<K>>) => Array<StepItem<K>>;
}

declare const EmptyState: React.FunctionComponent<EmptyStateProps>;
declare class EmptyState<R> extends React.Component<EmptyStateProps<R>> {}

export default EmptyState;

export interface StepProps {
show: boolean;
completed: boolean;
url?: string;
urlText?: string;
text: string;
onClick?: () => void;
}

export declare const Step: React.FunctionComponent<StepProps>;
151 changes: 98 additions & 53 deletions client/app/components/empty-state/EmptyState.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { currentUser } from "@/services/auth";
import organizationStatus from "@/services/organizationStatus";
import "./empty-state.less";

function Step({ show, completed, text, url, urlText, onClick }) {
export function Step({ show, completed, text, url, urlText, onClick }) {
if (!show) {
return null;
}
Expand Down Expand Up @@ -46,10 +46,13 @@ function EmptyState({
onboardingMode,
showAlertStep,
showDashboardStep,
showDataSourceStep,
showInviteStep,
getStepsItems,
illustrationPath,
}) {
const isAvailable = {
dataSource: true,
dataSource: showDataSourceStep,
query: true,
alert: showAlertStep,
dashboard: showDashboardStep,
Expand All @@ -75,6 +78,92 @@ function EmptyState({
return null;
}

const renderDataSourcesStep = () => {
if (currentUser.isAdmin) {
return (
<Step
key="dataSources"
show={isAvailable.dataSource}
completed={isCompleted.dataSource}
url="data_sources/new"
urlText="Connect"
text="a Data Source"
/>
);
}

return (
<Step
key="dataSources"
show={isAvailable.dataSource}
completed={isCompleted.dataSource}
text="Ask an account admin to connect a data source"
/>
);
};

const defaultStepsItems = [
{
key: "dataSources",
node: renderDataSourcesStep(),
},
{
key: "queries",
node: (
<Step
key="queries"
show={isAvailable.query}
completed={isCompleted.query}
url="queries/new"
urlText="Create"
text="your first Query"
/>
),
},
{
key: "alerts",
node: (
<Step
key="alerts"
show={isAvailable.alert}
completed={isCompleted.alert}
url="alerts/new"
urlText="Create"
text="your first Alert"
/>
),
},
{
key: "dashboards",
node: (
<Step
key="dashboards"
show={isAvailable.dashboard}
completed={isCompleted.dashboard}
onClick={showCreateDashboardDialog}
urlText="Create"
text="your first Dashboard"
/>
),
},
{
key: "users",
node: (
<Step
key="users"
show={isAvailable.inviteUsers}
completed={isCompleted.inviteUsers}
url="users/new"
urlText="Invite"
text="your team members"
/>
),
},
];

const stepsItems = getStepsItems ? getStepsItems(defaultStepsItems) : defaultStepsItems;
const imageSource = illustrationPath ? illustrationPath : "static/images/illustrations/" + illustration + ".svg";

return (
<div className="empty-state bg-white tiled">
<div className="empty-state__summary">
Expand All @@ -83,60 +172,11 @@ function EmptyState({
<i className={icon} />
</h2>
<p>{description}</p>
<img
src={"/static/images/illustrations/" + illustration + ".svg"}
alt={illustration + " Illustration"}
width="75%"
/>
<img src={imageSource} alt={illustration + " Illustration"} width="75%" />
</div>
<div className="empty-state__steps">
<h4>Let&apos;s get started</h4>
<ol>
{currentUser.isAdmin && (
<Step
show={isAvailable.dataSource}
completed={isCompleted.dataSource}
url="data_sources/new"
urlText="Connect"
text="a Data Source"
/>
)}
{!currentUser.isAdmin && (
<Step
show={isAvailable.dataSource}
completed={isCompleted.dataSource}
text="Ask an account admin to connect a data source"
/>
)}
<Step
show={isAvailable.query}
completed={isCompleted.query}
url="queries/new"
urlText="Create"
text="your first Query"
/>
<Step
show={isAvailable.alert}
completed={isCompleted.alert}
url="alerts/new"
urlText="Create"
text="your first Alert"
/>
<Step
show={isAvailable.dashboard}
completed={isCompleted.dashboard}
onClick={showCreateDashboardDialog}
urlText="Create"
text="your first Dashboard"
/>
<Step
show={isAvailable.inviteUsers}
completed={isCompleted.inviteUsers}
url="users/new"
urlText="Invite"
text="your team members"
/>
</ol>
<ol>{stepsItems.map(item => item.node)}</ol>
<p>
Need more support?{" "}
<a href={helpLink} target="_blank" rel="noopener noreferrer">
Expand All @@ -154,12 +194,16 @@ EmptyState.propTypes = {
header: PropTypes.string,
description: PropTypes.string.isRequired,
illustration: PropTypes.string.isRequired,
illustrationPath: PropTypes.string,
helpLink: PropTypes.string.isRequired,

onboardingMode: PropTypes.bool,
showAlertStep: PropTypes.bool,
showDashboardStep: PropTypes.bool,
showDataSourceStep: PropTypes.bool,
showInviteStep: PropTypes.bool,

getStepItems: PropTypes.func,
};

EmptyState.defaultProps = {
Expand All @@ -169,6 +213,7 @@ EmptyState.defaultProps = {
onboardingMode: false,
showAlertStep: false,
showDashboardStep: false,
showDataSourceStep: true,
showInviteStep: false,
};

Expand Down
Loading

0 comments on commit 84d516b

Please sign in to comment.