mirror of
https://github.com/getredash/redash.git
synced 2025-12-19 17:37:19 -05:00
Compare commits
10 Commits
master
...
ts-migrate
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
521ca6afa4 | ||
|
|
1ba94d30a1 | ||
|
|
e4d2c82338 | ||
|
|
95621a93bc | ||
|
|
2f1ed63bd5 | ||
|
|
f23f1d1924 | ||
|
|
c426379bef | ||
|
|
501ca0bef8 | ||
|
|
4c385f85f1 | ||
|
|
698d87ed48 |
@@ -51,7 +51,7 @@ module.exports = {
|
|||||||
"no-useless-constructor": "off",
|
"no-useless-constructor": "off",
|
||||||
"@typescript-eslint/no-useless-constructor": "error",
|
"@typescript-eslint/no-useless-constructor": "error",
|
||||||
// Many API fields and generated types use camelcase
|
// Many API fields and generated types use camelcase
|
||||||
"@typescript-eslint/camelcase": "off",
|
"@typescript-eslint/camelcase": "off","@typescript-eslint/no-empty-function": "off",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
import { configure } from "enzyme";
|
|
||||||
import Adapter from "enzyme-adapter-react-16";
|
|
||||||
|
|
||||||
configure({ adapter: new Adapter() });
|
|
||||||
5
client/app/__tests__/enzyme_setup.ts
Normal file
5
client/app/__tests__/enzyme_setup.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { configure } from "enzyme";
|
||||||
|
// @ts-expect-error ts-migrate(7016) FIXME: Could not find a declaration file for module 'enzy... Remove this comment to see the full error message
|
||||||
|
import Adapter from "enzyme-adapter-react-16";
|
||||||
|
|
||||||
|
configure({ adapter: new Adapter() });
|
||||||
@@ -3,7 +3,7 @@ import AceEditor from "react-ace";
|
|||||||
|
|
||||||
import "./AceEditorInput.less";
|
import "./AceEditorInput.less";
|
||||||
|
|
||||||
function AceEditorInput(props, ref) {
|
function AceEditorInput(props: any, ref: any) {
|
||||||
return (
|
return (
|
||||||
<div className="ace-editor-input" data-test={props["data-test"]}>
|
<div className="ace-editor-input" data-test={props["data-test"]}>
|
||||||
<AceEditor
|
<AceEditor
|
||||||
@@ -7,6 +7,7 @@ import CreateDashboardDialog from "@/components/dashboards/CreateDashboardDialog
|
|||||||
import { useCurrentRoute } from "@/components/ApplicationArea/Router";
|
import { useCurrentRoute } from "@/components/ApplicationArea/Router";
|
||||||
import { Auth, currentUser } from "@/services/auth";
|
import { Auth, currentUser } from "@/services/auth";
|
||||||
import settingsMenu from "@/services/settingsMenu";
|
import settingsMenu from "@/services/settingsMenu";
|
||||||
|
// @ts-expect-error ts-migrate(2307) FIXME: Cannot find module '@/assets/images/redash_icon_sm... Remove this comment to see the full error message
|
||||||
import logoUrl from "@/assets/images/redash_icon_small.png";
|
import logoUrl from "@/assets/images/redash_icon_small.png";
|
||||||
|
|
||||||
import DesktopOutlinedIcon from "@ant-design/icons/DesktopOutlined";
|
import DesktopOutlinedIcon from "@ant-design/icons/DesktopOutlined";
|
||||||
@@ -19,7 +20,10 @@ import SettingOutlinedIcon from "@ant-design/icons/SettingOutlined";
|
|||||||
import VersionInfo from "./VersionInfo";
|
import VersionInfo from "./VersionInfo";
|
||||||
import "./DesktopNavbar.less";
|
import "./DesktopNavbar.less";
|
||||||
|
|
||||||
function NavbarSection({ children, ...props }) {
|
function NavbarSection({
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: any) {
|
||||||
return (
|
return (
|
||||||
<Menu selectable={false} mode="vertical" theme="dark" {...props}>
|
<Menu selectable={false} mode="vertical" theme="dark" {...props}>
|
||||||
{children}
|
{children}
|
||||||
@@ -34,6 +38,7 @@ function useNavbarActiveState() {
|
|||||||
() => ({
|
() => ({
|
||||||
dashboards: includes(
|
dashboards: includes(
|
||||||
["Dashboards.List", "Dashboards.Favorites", "Dashboards.ViewOrEdit", "Dashboards.LegacyViewOrEdit"],
|
["Dashboards.List", "Dashboards.Favorites", "Dashboards.ViewOrEdit", "Dashboards.LegacyViewOrEdit"],
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
currentRoute.id
|
currentRoute.id
|
||||||
),
|
),
|
||||||
queries: includes(
|
queries: includes(
|
||||||
@@ -46,11 +51,15 @@ function useNavbarActiveState() {
|
|||||||
"Queries.New",
|
"Queries.New",
|
||||||
"Queries.Edit",
|
"Queries.Edit",
|
||||||
],
|
],
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
currentRoute.id
|
currentRoute.id
|
||||||
),
|
),
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
dataSources: includes(["DataSources.List"], currentRoute.id),
|
dataSources: includes(["DataSources.List"], currentRoute.id),
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
alerts: includes(["Alerts.List", "Alerts.New", "Alerts.View", "Alerts.Edit"], currentRoute.id),
|
alerts: includes(["Alerts.List", "Alerts.New", "Alerts.View", "Alerts.Edit"], currentRoute.id),
|
||||||
}),
|
}),
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
[currentRoute.id]
|
[currentRoute.id]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -76,6 +85,7 @@ export default function DesktopNavbar() {
|
|||||||
|
|
||||||
<NavbarSection>
|
<NavbarSection>
|
||||||
{currentUser.hasPermission("list_dashboards") && (
|
{currentUser.hasPermission("list_dashboards") && (
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
<Menu.Item key="dashboards" className={activeState.dashboards ? "navbar-active-item" : null}>
|
<Menu.Item key="dashboards" className={activeState.dashboards ? "navbar-active-item" : null}>
|
||||||
<Link href="dashboards">
|
<Link href="dashboards">
|
||||||
<DesktopOutlinedIcon />
|
<DesktopOutlinedIcon />
|
||||||
@@ -84,6 +94,7 @@ export default function DesktopNavbar() {
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
{currentUser.hasPermission("view_query") && (
|
{currentUser.hasPermission("view_query") && (
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
<Menu.Item key="queries" className={activeState.queries ? "navbar-active-item" : null}>
|
<Menu.Item key="queries" className={activeState.queries ? "navbar-active-item" : null}>
|
||||||
<Link href="queries">
|
<Link href="queries">
|
||||||
<CodeOutlinedIcon />
|
<CodeOutlinedIcon />
|
||||||
@@ -92,6 +103,7 @@ export default function DesktopNavbar() {
|
|||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
{currentUser.hasPermission("list_alerts") && (
|
{currentUser.hasPermission("list_alerts") && (
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
<Menu.Item key="alerts" className={activeState.alerts ? "navbar-active-item" : null}>
|
<Menu.Item key="alerts" className={activeState.alerts ? "navbar-active-item" : null}>
|
||||||
<Link href="alerts">
|
<Link href="alerts">
|
||||||
<AlertOutlinedIcon />
|
<AlertOutlinedIcon />
|
||||||
@@ -122,6 +134,7 @@ export default function DesktopNavbar() {
|
|||||||
)}
|
)}
|
||||||
{canCreateDashboard && (
|
{canCreateDashboard && (
|
||||||
<Menu.Item key="new-dashboard">
|
<Menu.Item key="new-dashboard">
|
||||||
|
{/* @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 0. */}
|
||||||
<a data-test="CreateDashboardMenuItem" onMouseUp={() => CreateDashboardDialog.showModal()}>
|
<a data-test="CreateDashboardMenuItem" onMouseUp={() => CreateDashboardDialog.showModal()}>
|
||||||
New Dashboard
|
New Dashboard
|
||||||
</a>
|
</a>
|
||||||
@@ -140,13 +153,16 @@ export default function DesktopNavbar() {
|
|||||||
|
|
||||||
<NavbarSection>
|
<NavbarSection>
|
||||||
<Menu.Item key="help">
|
<Menu.Item key="help">
|
||||||
|
{/* @ts-expect-error ts-migrate(2746) FIXME: This JSX tag's 'children' prop expects a single ch... Remove this comment to see the full error message */}
|
||||||
<HelpTrigger showTooltip={false} type="HOME">
|
<HelpTrigger showTooltip={false} type="HOME">
|
||||||
<QuestionCircleOutlinedIcon />
|
<QuestionCircleOutlinedIcon />
|
||||||
<span className="desktop-navbar-label">Help</span>
|
<span className="desktop-navbar-label">Help</span>
|
||||||
</HelpTrigger>
|
</HelpTrigger>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
{firstSettingsTab && (
|
{firstSettingsTab && (
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
<Menu.Item key="settings" className={activeState.dataSources ? "navbar-active-item" : null}>
|
<Menu.Item key="settings" className={activeState.dataSources ? "navbar-active-item" : null}>
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'path' does not exist on type 'number | (... Remove this comment to see the full error message */}
|
||||||
<Link href={firstSettingsTab.path} data-test="SettingsLink">
|
<Link href={firstSettingsTab.path} data-test="SettingsLink">
|
||||||
<SettingOutlinedIcon />
|
<SettingOutlinedIcon />
|
||||||
<span className="desktop-navbar-label">Settings</span>
|
<span className="desktop-navbar-label">Settings</span>
|
||||||
@@ -161,6 +177,7 @@ export default function DesktopNavbar() {
|
|||||||
popupClassName="desktop-navbar-submenu"
|
popupClassName="desktop-navbar-submenu"
|
||||||
title={
|
title={
|
||||||
<span data-test="ProfileDropdown" className="desktop-navbar-profile-menu-title">
|
<span data-test="ProfileDropdown" className="desktop-navbar-profile-menu-title">
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'profile_image_url' does not exist on typ... Remove this comment to see the full error message */}
|
||||||
<img className="profile__image_thumb" src={currentUser.profile_image_url} alt={currentUser.name} />
|
<img className="profile__image_thumb" src={currentUser.profile_image_url} alt={currentUser.name} />
|
||||||
</span>
|
</span>
|
||||||
}>
|
}>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { first } from "lodash";
|
import { first } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Button from "antd/lib/button";
|
import Button from "antd/lib/button";
|
||||||
import MenuOutlinedIcon from "@ant-design/icons/MenuOutlined";
|
import MenuOutlinedIcon from "@ant-design/icons/MenuOutlined";
|
||||||
import Dropdown from "antd/lib/dropdown";
|
import Dropdown from "antd/lib/dropdown";
|
||||||
@@ -8,11 +7,18 @@ import Menu from "antd/lib/menu";
|
|||||||
import Link from "@/components/Link";
|
import Link from "@/components/Link";
|
||||||
import { Auth, currentUser } from "@/services/auth";
|
import { Auth, currentUser } from "@/services/auth";
|
||||||
import settingsMenu from "@/services/settingsMenu";
|
import settingsMenu from "@/services/settingsMenu";
|
||||||
|
// @ts-expect-error ts-migrate(2307) FIXME: Cannot find module '@/assets/images/redash_icon_sm... Remove this comment to see the full error message
|
||||||
import logoUrl from "@/assets/images/redash_icon_small.png";
|
import logoUrl from "@/assets/images/redash_icon_small.png";
|
||||||
|
|
||||||
import "./MobileNavbar.less";
|
import "./MobileNavbar.less";
|
||||||
|
|
||||||
export default function MobileNavbar({ getPopupContainer }) {
|
type OwnProps = {
|
||||||
|
getPopupContainer?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof MobileNavbar.defaultProps;
|
||||||
|
|
||||||
|
export default function MobileNavbar({ getPopupContainer }: Props) {
|
||||||
const firstSettingsTab = first(settingsMenu.getAvailableItems());
|
const firstSettingsTab = first(settingsMenu.getAvailableItems());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -50,6 +56,7 @@ export default function MobileNavbar({ getPopupContainer }) {
|
|||||||
<Menu.Divider />
|
<Menu.Divider />
|
||||||
{firstSettingsTab && (
|
{firstSettingsTab && (
|
||||||
<Menu.Item key="settings">
|
<Menu.Item key="settings">
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'path' does not exist on type 'number | (... Remove this comment to see the full error message */}
|
||||||
<Link href={firstSettingsTab.path}>Settings</Link>
|
<Link href={firstSettingsTab.path}>Settings</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
@@ -79,10 +86,6 @@ export default function MobileNavbar({ getPopupContainer }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
MobileNavbar.propTypes = {
|
|
||||||
getPopupContainer: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
MobileNavbar.defaultProps = {
|
MobileNavbar.defaultProps = {
|
||||||
getPopupContainer: null,
|
getPopupContainer: null,
|
||||||
};
|
};
|
||||||
@@ -1,15 +1,19 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Link from "@/components/Link";
|
import Link from "@/components/Link";
|
||||||
import { clientConfig, currentUser } from "@/services/auth";
|
import { clientConfig, currentUser } from "@/services/auth";
|
||||||
|
// @ts-expect-error ts-migrate(7042) FIXME: Module '@/version.json' was resolved to '/Users/el... Remove this comment to see the full error message
|
||||||
import frontendVersion from "@/version.json";
|
import frontendVersion from "@/version.json";
|
||||||
|
|
||||||
export default function VersionInfo() {
|
export default function VersionInfo() {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div>
|
<div>
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'version' does not exist on type '{}'. */}
|
||||||
Version: {clientConfig.version}
|
Version: {clientConfig.version}
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'version' does not exist on type '{}'. */}
|
||||||
{frontendVersion !== clientConfig.version && ` (${frontendVersion.substring(0, 8)})`}
|
{frontendVersion !== clientConfig.version && ` (${frontendVersion.substring(0, 8)})`}
|
||||||
</div>
|
</div>
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'newVersionAvailable' does not exist on t... Remove this comment to see the full error message */}
|
||||||
{clientConfig.newVersionAvailable && currentUser.hasPermission("super_admin") && (
|
{clientConfig.newVersionAvailable && currentUser.hasPermission("super_admin") && (
|
||||||
<div className="m-t-10">
|
<div className="m-t-10">
|
||||||
{/* eslint-disable react/jsx-no-target-blank */}
|
{/* eslint-disable react/jsx-no-target-blank */}
|
||||||
@@ -1,27 +1,36 @@
|
|||||||
import React, { useRef, useCallback } from "react";
|
import React, { useRef, useCallback } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import DynamicComponent from "@/components/DynamicComponent";
|
import DynamicComponent from "@/components/DynamicComponent";
|
||||||
import DesktopNavbar from "./DesktopNavbar";
|
import DesktopNavbar from "./DesktopNavbar";
|
||||||
import MobileNavbar from "./MobileNavbar";
|
import MobileNavbar from "./MobileNavbar";
|
||||||
|
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
||||||
export default function ApplicationLayout({ children }) {
|
type OwnProps = {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof ApplicationLayout.defaultProps;
|
||||||
|
|
||||||
|
export default function ApplicationLayout({ children }: Props) {
|
||||||
const mobileNavbarContainerRef = useRef();
|
const mobileNavbarContainerRef = useRef();
|
||||||
|
|
||||||
const getMobileNavbarPopupContainer = useCallback(() => mobileNavbarContainerRef.current, []);
|
const getMobileNavbarPopupContainer = useCallback(() => mobileNavbarContainerRef.current, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
{/* @ts-expect-error ts-migrate(2746) FIXME: This JSX tag's 'children' prop expects a single ch... Remove this comment to see the full error message */}
|
||||||
<DynamicComponent name="ApplicationWrapper">
|
<DynamicComponent name="ApplicationWrapper">
|
||||||
<div className="application-layout-side-menu">
|
<div className="application-layout-side-menu">
|
||||||
<DynamicComponent name="ApplicationDesktopNavbar">
|
<DynamicComponent name="ApplicationDesktopNavbar">
|
||||||
|
{/* @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call. */}
|
||||||
<DesktopNavbar />
|
<DesktopNavbar />
|
||||||
</DynamicComponent>
|
</DynamicComponent>
|
||||||
</div>
|
</div>
|
||||||
<div className="application-layout-content">
|
<div className="application-layout-content">
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'MutableRefObject<undefined>' is not assignab... Remove this comment to see the full error message */}
|
||||||
<nav className="application-layout-top-menu" ref={mobileNavbarContainerRef}>
|
<nav className="application-layout-top-menu" ref={mobileNavbarContainerRef}>
|
||||||
<DynamicComponent name="ApplicationMobileNavbar" getPopupContainer={getMobileNavbarPopupContainer}>
|
<DynamicComponent name="ApplicationMobileNavbar" getPopupContainer={getMobileNavbarPopupContainer}>
|
||||||
|
{/* @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call. */}
|
||||||
<MobileNavbar getPopupContainer={getMobileNavbarPopupContainer} />
|
<MobileNavbar getPopupContainer={getMobileNavbarPopupContainer} />
|
||||||
</DynamicComponent>
|
</DynamicComponent>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -32,10 +41,6 @@ export default function ApplicationLayout({ children }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationLayout.propTypes = {
|
|
||||||
children: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
ApplicationLayout.defaultProps = {
|
ApplicationLayout.defaultProps = {
|
||||||
children: null,
|
children: null,
|
||||||
};
|
};
|
||||||
@@ -10,7 +10,9 @@ const ErrorMessages = {
|
|||||||
|
|
||||||
function mockAxiosError(status = 500, response = {}) {
|
function mockAxiosError(status = 500, response = {}) {
|
||||||
const error = new Error(`Failed with code ${status}.`);
|
const error = new Error(`Failed with code ${status}.`);
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isAxiosError' does not exist on type 'Er... Remove this comment to see the full error message
|
||||||
error.isAxiosError = true;
|
error.isAxiosError = true;
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'response' does not exist on type 'Error'... Remove this comment to see the full error message
|
||||||
error.response = { status, ...response };
|
error.response = { status, ...response };
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@@ -22,7 +24,7 @@ describe("Error Message", () => {
|
|||||||
spyError.mockReset();
|
spyError.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
function expectErrorMessageToBe(error, errorMessage) {
|
function expectErrorMessageToBe(error: any, errorMessage: any) {
|
||||||
const component = mount(<ErrorMessage error={error} />);
|
const component = mount(<ErrorMessage error={error} />);
|
||||||
|
|
||||||
expect(component.find(".error-state__details h4").text()).toBe(errorMessage);
|
expect(component.find(".error-state__details h4").text()).toBe(errorMessage);
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import { get, isObject } from "lodash";
|
import { get, isObject } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import "./ErrorMessage.less";
|
import "./ErrorMessage.less";
|
||||||
import DynamicComponent from "@/components/DynamicComponent";
|
import DynamicComponent from "@/components/DynamicComponent";
|
||||||
import { ErrorMessageDetails } from "@/components/ApplicationArea/ErrorMessageDetails";
|
import { ErrorMessageDetails } from "@/components/ApplicationArea/ErrorMessageDetails";
|
||||||
|
|
||||||
function getErrorMessageByStatus(status, defaultMessage) {
|
function getErrorMessageByStatus(status: any, defaultMessage: any) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 404:
|
case 404:
|
||||||
return "It seems like the page you're looking for cannot be found.";
|
return "It seems like the page you're looking for cannot be found.";
|
||||||
@@ -18,22 +17,31 @@ function getErrorMessageByStatus(status, defaultMessage) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getErrorMessage(error) {
|
function getErrorMessage(error: any) {
|
||||||
const message = "It seems like we encountered an error. Try refreshing this page or contact your administrator.";
|
const message = "It seems like we encountered an error. Try refreshing this page or contact your administrator.";
|
||||||
if (isObject(error)) {
|
if (isObject(error)) {
|
||||||
// HTTP errors
|
// HTTP errors
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isAxiosError' does not exist on type 'ob... Remove this comment to see the full error message
|
||||||
if (error.isAxiosError && isObject(error.response)) {
|
if (error.isAxiosError && isObject(error.response)) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'response' does not exist on type 'object... Remove this comment to see the full error message
|
||||||
return getErrorMessageByStatus(error.response.status, get(error, "response.data.message", message));
|
return getErrorMessageByStatus(error.response.status, get(error, "response.data.message", message));
|
||||||
}
|
}
|
||||||
// Router errors
|
// Router errors
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'status' does not exist on type 'object'.
|
||||||
if (error.status) {
|
if (error.status) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'status' does not exist on type 'object'.
|
||||||
return getErrorMessageByStatus(error.status, message);
|
return getErrorMessageByStatus(error.status, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ErrorMessage({ error, message }) {
|
type Props = {
|
||||||
|
error: any;
|
||||||
|
message?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ErrorMessage({ error, message }: Props) {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -62,8 +70,3 @@ export default function ErrorMessage({ error, message }) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorMessage.propTypes = {
|
|
||||||
error: PropTypes.object.isRequired,
|
|
||||||
message: PropTypes.string,
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
export function ErrorMessageDetails(props) {
|
|
||||||
return <h4>{props.message}</h4>;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorMessageDetails.propTypes = {
|
|
||||||
error: PropTypes.instanceOf(Error).isRequired,
|
|
||||||
message: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
error: any; // TODO: PropTypes.instanceOf(Error)
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ErrorMessageDetails(props: Props) {
|
||||||
|
return <h4>{props.message}</h4>;
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { isFunction, startsWith, trimStart, trimEnd } from "lodash";
|
import { isFunction, startsWith, trimStart, trimEnd } from "lodash";
|
||||||
import React, { useState, useEffect, useRef, useContext } from "react";
|
import React, { useState, useEffect, useRef, useContext } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import UniversalRouter from "universal-router";
|
import UniversalRouter from "universal-router";
|
||||||
import ErrorBoundary from "@redash/viz/lib/components/ErrorBoundary";
|
import ErrorBoundary from "@redash/viz/lib/components/ErrorBoundary";
|
||||||
import location from "@/services/location";
|
import location from "@/services/location";
|
||||||
@@ -20,7 +19,7 @@ export function useCurrentRoute() {
|
|||||||
return useContext(CurrentRouteContext);
|
return useContext(CurrentRouteContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stripBase(href) {
|
export function stripBase(href: any) {
|
||||||
// Resolve provided link and '' (root) relative to document's base.
|
// Resolve provided link and '' (root) relative to document's base.
|
||||||
// If provided href is not related to current document (does not
|
// If provided href is not related to current document (does not
|
||||||
// start with resolved root) - return false. Otherwise
|
// start with resolved root) - return false. Otherwise
|
||||||
@@ -36,7 +35,20 @@ export function stripBase(href) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Router({ routes, onRouteChange }) {
|
type OwnProps = {
|
||||||
|
routes?: {
|
||||||
|
path: string;
|
||||||
|
render?: (...args: any[]) => any;
|
||||||
|
resolve?: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
onRouteChange?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof Router.defaultProps;
|
||||||
|
|
||||||
|
export default function Router({ routes, onRouteChange }: Props) {
|
||||||
const [currentRoute, setCurrentRoute] = useState(null);
|
const [currentRoute, setCurrentRoute] = useState(null);
|
||||||
|
|
||||||
const currentPathRef = useRef(null);
|
const currentPathRef = useRef(null);
|
||||||
@@ -47,15 +59,17 @@ export default function Router({ routes, onRouteChange }) {
|
|||||||
|
|
||||||
const router = new UniversalRouter(routes, {
|
const router = new UniversalRouter(routes, {
|
||||||
resolveRoute({ route }, routeParams) {
|
resolveRoute({ route }, routeParams) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'render' does not exist on type 'Route<Co... Remove this comment to see the full error message
|
||||||
if (isFunction(route.render)) {
|
if (isFunction(route.render)) {
|
||||||
return { ...route, routeParams };
|
return { ...route, routeParams };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function resolve(action) {
|
function resolve(action: any) {
|
||||||
if (!isAbandoned) {
|
if (!isAbandoned) {
|
||||||
if (errorHandlerRef.current) {
|
if (errorHandlerRef.current) {
|
||||||
|
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||||
errorHandlerRef.current.reset();
|
errorHandlerRef.current.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +84,7 @@ export default function Router({ routes, onRouteChange }) {
|
|||||||
if (pathname === currentPathRef.current) {
|
if (pathname === currentPathRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'null'.
|
||||||
currentPathRef.current = pathname;
|
currentPathRef.current = pathname;
|
||||||
|
|
||||||
// Don't reload controller if URL was replaced
|
// Don't reload controller if URL was replaced
|
||||||
@@ -87,7 +102,8 @@ export default function Router({ routes, onRouteChange }) {
|
|||||||
.catch(error => {
|
.catch(error => {
|
||||||
if (!isAbandoned && currentPathRef.current === pathname) {
|
if (!isAbandoned && currentPathRef.current === pathname) {
|
||||||
setCurrentRoute({
|
setCurrentRoute({
|
||||||
render: currentRoute => <ErrorMessage {...currentRoute.routeParams} />,
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ render: (currentRoute: any) =>... Remove this comment to see the full error message
|
||||||
|
render: (currentRoute: any) => <ErrorMessage {...currentRoute.routeParams} />,
|
||||||
routeParams: { error },
|
routeParams: { error },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -97,7 +113,7 @@ export default function Router({ routes, onRouteChange }) {
|
|||||||
|
|
||||||
resolve("PUSH");
|
resolve("PUSH");
|
||||||
|
|
||||||
const unlisten = location.listen((unused, action) => resolve(action));
|
const unlisten = location.listen((unused: any, action: any) => resolve(action));
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
isAbandoned = true;
|
isAbandoned = true;
|
||||||
@@ -116,29 +132,15 @@ export default function Router({ routes, onRouteChange }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<CurrentRouteContext.Provider value={currentRoute}>
|
<CurrentRouteContext.Provider value={currentRoute}>
|
||||||
<ErrorBoundary ref={errorHandlerRef} renderError={error => <ErrorMessage error={error} />}>
|
{/* @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call. */}
|
||||||
|
<ErrorBoundary ref={errorHandlerRef} renderError={(error: any) => <ErrorMessage error={error} />}>
|
||||||
|
{/* @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'. */}
|
||||||
{currentRoute.render(currentRoute)}
|
{currentRoute.render(currentRoute)}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</CurrentRouteContext.Provider>
|
</CurrentRouteContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Router.propTypes = {
|
|
||||||
routes: PropTypes.arrayOf(
|
|
||||||
PropTypes.shape({
|
|
||||||
path: PropTypes.string.isRequired,
|
|
||||||
render: PropTypes.func, // (routeParams: PropTypes.object; currentRoute; location) => PropTypes.node
|
|
||||||
// Additional props to be injected into route component.
|
|
||||||
// Object keys are props names. Object values will become prop values:
|
|
||||||
// - if value is a function - it will be called without arguments, and result will be used; otherwise value will be used;
|
|
||||||
// - after previous step, if value is a promise - router will wait for it to resolve; resolved value then will be used;
|
|
||||||
// otherwise value will be used directly.
|
|
||||||
resolve: PropTypes.objectOf(PropTypes.any),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
onRouteChange: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
Router.defaultProps = {
|
Router.defaultProps = {
|
||||||
routes: [],
|
routes: [],
|
||||||
onRouteChange: () => {},
|
onRouteChange: () => {},
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { isString } from "lodash";
|
import { isString } from "lodash";
|
||||||
import navigateTo from "./navigateTo";
|
import navigateTo from "./navigateTo";
|
||||||
|
|
||||||
export default function handleNavigationIntent(event) {
|
export default function handleNavigationIntent(event: any) {
|
||||||
let element = event.target;
|
let element = event.target;
|
||||||
while (element) {
|
while (element) {
|
||||||
if (element.tagName === "A") {
|
if (element.tagName === "A") {
|
||||||
@@ -9,13 +9,15 @@ export default function ApplicationArea() {
|
|||||||
const [unhandledError, setUnhandledError] = useState(null);
|
const [unhandledError, setUnhandledError] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
if (currentRoute && currentRoute.title) {
|
if (currentRoute && currentRoute.title) {
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
document.title = currentRoute.title;
|
document.title = currentRoute.title;
|
||||||
}
|
}
|
||||||
}, [currentRoute]);
|
}, [currentRoute]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function globalErrorHandler(event) {
|
function globalErrorHandler(event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setUnhandledError(event.error);
|
setUnhandledError(event.error);
|
||||||
}
|
}
|
||||||
@@ -33,5 +35,6 @@ export default function ApplicationArea() {
|
|||||||
return <ErrorMessage error={unhandledError} />;
|
return <ErrorMessage error={unhandledError} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'RouteItem[]' is not assignable to type '{ pa... Remove this comment to see the full error message
|
||||||
return <Router routes={routes.items} onRouteChange={setCurrentRoute} />;
|
return <Router routes={routes.items} onRouteChange={setCurrentRoute} />;
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ import { stripBase } from "./Router";
|
|||||||
|
|
||||||
// When `replace` is set to `true` - it will just replace current URL
|
// When `replace` is set to `true` - it will just replace current URL
|
||||||
// without reloading current page (router will skip this location change)
|
// without reloading current page (router will skip this location change)
|
||||||
export default function navigateTo(href, replace = false) {
|
export default function navigateTo(href: any, replace = false) {
|
||||||
// Allow calling chain to roll up, and then navigate
|
// Allow calling chain to roll up, and then navigate
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const isExternal = stripBase(href) === false;
|
const isExternal = stripBase(href) === false;
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
import React, { useEffect, useState, useContext } from "react";
|
import React, { useEffect, useState, useContext } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { ErrorBoundaryContext } from "@redash/viz/lib/components/ErrorBoundary";
|
import { ErrorBoundaryContext } from "@redash/viz/lib/components/ErrorBoundary";
|
||||||
import { Auth, clientConfig } from "@/services/auth";
|
import { Auth, clientConfig } from "@/services/auth";
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
apiKey: string;
|
||||||
|
renderChildren?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof ApiKeySessionWrapper.defaultProps;
|
||||||
|
|
||||||
// This wrapper modifies `route.render` function and instead of passing `currentRoute` passes an object
|
// This wrapper modifies `route.render` function and instead of passing `currentRoute` passes an object
|
||||||
// that contains:
|
// that contains:
|
||||||
// - `currentRoute.routeParams`
|
// - `currentRoute.routeParams`
|
||||||
@@ -10,7 +16,8 @@ import { Auth, clientConfig } from "@/services/auth";
|
|||||||
// - `onError` field which is a `handleError` method of nearest error boundary
|
// - `onError` field which is a `handleError` method of nearest error boundary
|
||||||
// - `apiKey` field
|
// - `apiKey` field
|
||||||
|
|
||||||
function ApiKeySessionWrapper({ apiKey, currentRoute, renderChildren }) {
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'currentRoute' does not exist on type 'Pr... Remove this comment to see the full error message
|
||||||
|
function ApiKeySessionWrapper({ apiKey, currentRoute, renderChildren }: Props) {
|
||||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
const { handleError } = useContext(ErrorBoundaryContext);
|
const { handleError } = useContext(ErrorBoundaryContext);
|
||||||
|
|
||||||
@@ -33,6 +40,7 @@ function ApiKeySessionWrapper({ apiKey, currentRoute, renderChildren }) {
|
|||||||
};
|
};
|
||||||
}, [apiKey]);
|
}, [apiKey]);
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'disablePublicUrls' does not exist on typ... Remove this comment to see the full error message
|
||||||
if (!isAuthenticated || clientConfig.disablePublicUrls) {
|
if (!isAuthenticated || clientConfig.disablePublicUrls) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -44,20 +52,18 @@ function ApiKeySessionWrapper({ apiKey, currentRoute, renderChildren }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ApiKeySessionWrapper.propTypes = {
|
|
||||||
apiKey: PropTypes.string.isRequired,
|
|
||||||
renderChildren: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
ApiKeySessionWrapper.defaultProps = {
|
ApiKeySessionWrapper.defaultProps = {
|
||||||
renderChildren: () => null,
|
renderChildren: () => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function routeWithApiKeySession({ render, getApiKey, ...rest }) {
|
export default function routeWithApiKeySession({
|
||||||
|
render,
|
||||||
|
getApiKey,
|
||||||
|
...rest
|
||||||
|
}: any) {
|
||||||
return {
|
return {
|
||||||
...rest,
|
...rest,
|
||||||
render: currentRoute => (
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ apiKey: any; currentRoute: any; renderChil... Remove this comment to see the full error message
|
||||||
<ApiKeySessionWrapper apiKey={getApiKey(currentRoute)} currentRoute={currentRoute} renderChildren={render} />
|
render: (currentRoute: any) => <ApiKeySessionWrapper apiKey={getApiKey(currentRoute)} currentRoute={currentRoute} renderChildren={render} />,
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -60,8 +60,9 @@ export function UserSessionWrapper<P>({ bodyClass, currentRoute, render }: UserS
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ApplicationLayout>
|
<ApplicationLayout>
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'Element' is not assignable to type 'null | u... Remove this comment to see the full error message */}
|
||||||
<React.Fragment key={currentRoute.key}>
|
<React.Fragment key={currentRoute.key}>
|
||||||
{/* @ts-expect-error FIXME */}
|
{/* @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call. */}
|
||||||
<ErrorBoundary renderError={(error: Error) => <ErrorMessage error={error} />}>
|
<ErrorBoundary renderError={(error: Error) => <ErrorMessage error={error} />}>
|
||||||
<ErrorBoundaryContext.Consumer>
|
<ErrorBoundaryContext.Consumer>
|
||||||
{({ handleError } /* : { handleError: UserSessionWrapperRenderChildrenProps<P>["onError"] } FIXME bring back type */) =>
|
{({ handleError } /* : { handleError: UserSessionWrapperRenderChildrenProps<P>["onError"] } FIXME bring back type */) =>
|
||||||
|
|||||||
@@ -13,16 +13,18 @@ const Text = Typography.Text;
|
|||||||
function BeaconConsent() {
|
function BeaconConsent() {
|
||||||
const [hide, setHide] = useState(false);
|
const [hide, setHide] = useState(false);
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'showBeaconConsentMessage' does not exist... Remove this comment to see the full error message
|
||||||
if (!clientConfig.showBeaconConsentMessage || hide) {
|
if (!clientConfig.showBeaconConsentMessage || hide) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideConsentCard = () => {
|
const hideConsentCard = () => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'showBeaconConsentMessage' does not exist... Remove this comment to see the full error message
|
||||||
clientConfig.showBeaconConsentMessage = false;
|
clientConfig.showBeaconConsentMessage = false;
|
||||||
setHide(true);
|
setHide(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmConsent = confirm => {
|
const confirmConsent = (confirm: any) => {
|
||||||
let message = "🙏 Thank you.";
|
let message = "🙏 Thank you.";
|
||||||
|
|
||||||
if (!confirm) {
|
if (!confirm) {
|
||||||
@@ -39,11 +41,13 @@ function BeaconConsent() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicComponent name="BeaconConsent">
|
<DynamicComponent name="BeaconConsent">
|
||||||
|
{/* @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call. */}
|
||||||
<div className="m-t-10 tiled">
|
<div className="m-t-10 tiled">
|
||||||
<Card
|
<Card
|
||||||
title={
|
title={
|
||||||
<>
|
<>
|
||||||
Would you be ok with sharing anonymous usage data with the Redash team?{" "}
|
Would you be ok with sharing anonymous usage data with the Redash team?{" "}
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'. */}
|
||||||
<HelpTrigger type="USAGE_DATA_SHARING" />
|
<HelpTrigger type="USAGE_DATA_SHARING" />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
function BigMessage({ message, icon, children, className }) {
|
type OwnProps = {
|
||||||
|
message?: string;
|
||||||
|
icon: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof BigMessage.defaultProps;
|
||||||
|
|
||||||
|
function BigMessage({ message, icon, children, className }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className={"p-15 text-center " + className}>
|
<div className={"p-15 text-center " + className}>
|
||||||
<h3 className="m-t-0 m-b-0">
|
<h3 className="m-t-0 m-b-0">
|
||||||
@@ -14,13 +22,6 @@ function BigMessage({ message, icon, children, className }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BigMessage.propTypes = {
|
|
||||||
message: PropTypes.string,
|
|
||||||
icon: PropTypes.string.isRequired,
|
|
||||||
children: PropTypes.node,
|
|
||||||
className: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
BigMessage.defaultProps = {
|
BigMessage.defaultProps = {
|
||||||
message: "",
|
message: "",
|
||||||
children: null,
|
children: null,
|
||||||
@@ -1,24 +1,30 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Button from "antd/lib/button";
|
import Button from "antd/lib/button";
|
||||||
import Tooltip from "antd/lib/tooltip";
|
import Tooltip from "antd/lib/tooltip";
|
||||||
import CopyOutlinedIcon from "@ant-design/icons/CopyOutlined";
|
import CopyOutlinedIcon from "@ant-design/icons/CopyOutlined";
|
||||||
import "./CodeBlock.less";
|
import "./CodeBlock.less";
|
||||||
|
|
||||||
export default class CodeBlock extends React.Component {
|
type OwnProps = {
|
||||||
static propTypes = {
|
copyable?: boolean;
|
||||||
copyable: PropTypes.bool,
|
};
|
||||||
children: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
type State = any;
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof CodeBlock.defaultProps;
|
||||||
|
|
||||||
|
export default class CodeBlock extends React.Component<Props, State> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
copyable: false,
|
copyable: false,
|
||||||
children: null,
|
children: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
copyFeatureEnabled: any;
|
||||||
|
ref: any;
|
||||||
|
resetCopyState: any;
|
||||||
|
|
||||||
state = { copied: null };
|
state = { copied: null };
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.ref = React.createRef();
|
this.ref = React.createRef();
|
||||||
this.copyFeatureEnabled = props.copyable && document.queryCommandSupported("copy");
|
this.copyFeatureEnabled = props.copyable && document.queryCommandSupported("copy");
|
||||||
@@ -33,6 +39,7 @@ export default class CodeBlock extends React.Component {
|
|||||||
|
|
||||||
copy = () => {
|
copy = () => {
|
||||||
// select text
|
// select text
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
window.getSelection().selectAllChildren(this.ref.current);
|
window.getSelection().selectAllChildren(this.ref.current);
|
||||||
|
|
||||||
// copy
|
// copy
|
||||||
@@ -49,6 +56,7 @@ export default class CodeBlock extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reset selection
|
// reset selection
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
window.getSelection().removeAllRanges();
|
window.getSelection().removeAllRanges();
|
||||||
|
|
||||||
// reset tooltip
|
// reset tooltip
|
||||||
@@ -1,12 +1,20 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import AntCollapse from "antd/lib/collapse";
|
import AntCollapse from "antd/lib/collapse";
|
||||||
|
|
||||||
export default function Collapse({ collapsed, children, className, ...props }) {
|
type OwnProps = {
|
||||||
|
collapsed?: boolean;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof Collapse.defaultProps;
|
||||||
|
|
||||||
|
export default function Collapse({ collapsed, children, className, ...props }: Props) {
|
||||||
return (
|
return (
|
||||||
<AntCollapse
|
<AntCollapse
|
||||||
{...props}
|
{...props}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string | null' is not assignable to type 'st... Remove this comment to see the full error message
|
||||||
activeKey={collapsed ? null : "content"}
|
activeKey={collapsed ? null : "content"}
|
||||||
className={cx(className, "ant-collapse-headerless")}>
|
className={cx(className, "ant-collapse-headerless")}>
|
||||||
<AntCollapse.Panel key="content" header="">
|
<AntCollapse.Panel key="content" header="">
|
||||||
@@ -16,12 +24,6 @@ export default function Collapse({ collapsed, children, className, ...props }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Collapse.propTypes = {
|
|
||||||
collapsed: PropTypes.bool,
|
|
||||||
children: PropTypes.node,
|
|
||||||
className: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
Collapse.defaultProps = {
|
Collapse.defaultProps = {
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
children: null,
|
children: null,
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { isEmpty, toUpper, includes, get } from "lodash";
|
import { isEmpty, toUpper, includes, get } from "lodash";
|
||||||
import Button from "antd/lib/button";
|
import Button from "antd/lib/button";
|
||||||
import List from "antd/lib/list";
|
import List from "antd/lib/list";
|
||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
import Input from "antd/lib/input";
|
import Input from "antd/lib/input";
|
||||||
import Steps from "antd/lib/steps";
|
import Steps from "antd/lib/steps";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DialogPropType' is declared but its value is neve... Remove this comment to see the full error message
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
import Link from "@/components/Link";
|
import Link from "@/components/Link";
|
||||||
import { PreviewCard } from "@/components/PreviewCard";
|
import { PreviewCard } from "@/components/PreviewCard";
|
||||||
@@ -23,15 +23,21 @@ const StepEnum = {
|
|||||||
DONE: 2,
|
DONE: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
class CreateSourceDialog extends React.Component {
|
type OwnProps = {
|
||||||
static propTypes = {
|
// @ts-expect-error ts-migrate(2749) FIXME: 'DialogPropType' refers to a value, but is being u... Remove this comment to see the full error message
|
||||||
dialog: DialogPropType.isRequired,
|
dialog: DialogPropType;
|
||||||
types: PropTypes.arrayOf(PropTypes.object),
|
types?: any[];
|
||||||
sourceType: PropTypes.string.isRequired,
|
sourceType: string;
|
||||||
imageFolder: PropTypes.string.isRequired,
|
imageFolder: string;
|
||||||
helpTriggerPrefix: PropTypes.string,
|
helpTriggerPrefix?: string;
|
||||||
onCreate: PropTypes.func.isRequired,
|
onCreate: (...args: any[]) => any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type State = any;
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof CreateSourceDialog.defaultProps;
|
||||||
|
|
||||||
|
class CreateSourceDialog extends React.Component<Props, State> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
types: [],
|
types: [],
|
||||||
@@ -45,7 +51,7 @@ class CreateSourceDialog extends React.Component {
|
|||||||
currentStep: StepEnum.SELECT_TYPE,
|
currentStep: StepEnum.SELECT_TYPE,
|
||||||
};
|
};
|
||||||
|
|
||||||
selectType = selectedType => {
|
selectType = (selectedType: any) => {
|
||||||
this.setState({ selectedType, currentStep: StepEnum.CONFIGURE_IT });
|
this.setState({ selectedType, currentStep: StepEnum.CONFIGURE_IT });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,17 +61,19 @@ class CreateSourceDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
createSource = (values, successCallback, errorCallback) => {
|
createSource = (values: any, successCallback: any, errorCallback: any) => {
|
||||||
const { selectedType, savingSource } = this.state;
|
const { selectedType, savingSource } = this.state;
|
||||||
if (!savingSource) {
|
if (!savingSource) {
|
||||||
this.setState({ savingSource: true, currentStep: StepEnum.DONE });
|
this.setState({ savingSource: true, currentStep: StepEnum.DONE });
|
||||||
this.props
|
this.props
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onCreate' does not exist on type 'never'... Remove this comment to see the full error message
|
||||||
.onCreate(selectedType, values)
|
.onCreate(selectedType, values)
|
||||||
.then(data => {
|
.then((data: any) => {
|
||||||
successCallback("Saved.");
|
successCallback("Saved.");
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dialog' does not exist on type 'never'.
|
||||||
this.props.dialog.close({ success: true, data });
|
this.props.dialog.close({ success: true, data });
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error: any) => {
|
||||||
this.setState({ savingSource: false, currentStep: StepEnum.CONFIGURE_IT });
|
this.setState({ savingSource: false, currentStep: StepEnum.CONFIGURE_IT });
|
||||||
errorCallback(get(error, "response.data.message", "Failed saving."));
|
errorCallback(get(error, "response.data.message", "Failed saving."));
|
||||||
});
|
});
|
||||||
@@ -75,8 +83,9 @@ class CreateSourceDialog extends React.Component {
|
|||||||
renderTypeSelector() {
|
renderTypeSelector() {
|
||||||
const { types } = this.props;
|
const { types } = this.props;
|
||||||
const { searchText } = this.state;
|
const { searchText } = this.state;
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'filter' does not exist on type 'never'.
|
||||||
const filteredTypes = types.filter(
|
const filteredTypes = types.filter(
|
||||||
type => isEmpty(searchText) || includes(type.name.toLowerCase(), searchText.toLowerCase())
|
(type: any) => isEmpty(searchText) || includes(type.name.toLowerCase(), searchText.toLowerCase())
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className="m-t-10">
|
<div className="m-t-10">
|
||||||
@@ -100,22 +109,30 @@ class CreateSourceDialog extends React.Component {
|
|||||||
renderForm() {
|
renderForm() {
|
||||||
const { imageFolder, helpTriggerPrefix } = this.props;
|
const { imageFolder, helpTriggerPrefix } = this.props;
|
||||||
const { selectedType } = this.state;
|
const { selectedType } = this.state;
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'null' is not assignable to param... Remove this comment to see the full error message
|
||||||
const fields = helper.getFields(selectedType);
|
const fields = helper.getFields(selectedType);
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
const helpTriggerType = `${helpTriggerPrefix}${toUpper(selectedType.type)}`;
|
const helpTriggerType = `${helpTriggerPrefix}${toUpper(selectedType.type)}`;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="d-flex justify-content-center align-items-center">
|
<div className="d-flex justify-content-center align-items-center">
|
||||||
|
{/* @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'. */}
|
||||||
<img className="p-5" src={`${imageFolder}/${selectedType.type}.png`} alt={selectedType.name} width="48" />
|
<img className="p-5" src={`${imageFolder}/${selectedType.type}.png`} alt={selectedType.name} width="48" />
|
||||||
|
{/* @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'. */}
|
||||||
<h4 className="m-0">{selectedType.name}</h4>
|
<h4 className="m-0">{selectedType.name}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
|
{/* @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message */}
|
||||||
{HELP_TRIGGER_TYPES[helpTriggerType] && (
|
{HELP_TRIGGER_TYPES[helpTriggerType] && (
|
||||||
|
// @ts-expect-error ts-migrate(2746) FIXME: This JSX tag's 'children' prop expects a single ch... Remove this comment to see the full error message
|
||||||
<HelpTrigger className="f-13" type={helpTriggerType}>
|
<HelpTrigger className="f-13" type={helpTriggerType}>
|
||||||
Setup Instructions <i className="fa fa-question-circle" />
|
Setup Instructions <i className="fa fa-question-circle" />
|
||||||
</HelpTrigger>
|
</HelpTrigger>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'. */}
|
||||||
<DynamicForm id="sourceForm" fields={fields} onSubmit={this.createSource} feedbackIcons hideSubmitButton />
|
<DynamicForm id="sourceForm" fields={fields} onSubmit={this.createSource} feedbackIcons hideSubmitButton />
|
||||||
|
{/* @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'. */}
|
||||||
{selectedType.type === "databricks" && (
|
{selectedType.type === "databricks" && (
|
||||||
<small>
|
<small>
|
||||||
By using the Databricks Data Source you agree to the Databricks JDBC/ODBC{" "}
|
By using the Databricks Data Source you agree to the Databricks JDBC/ODBC{" "}
|
||||||
@@ -129,7 +146,7 @@ class CreateSourceDialog extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItem(item) {
|
renderItem(item: any) {
|
||||||
const { imageFolder } = this.props;
|
const { imageFolder } = this.props;
|
||||||
return (
|
return (
|
||||||
<List.Item className="p-l-10 p-r-10 clickable" onClick={() => this.selectType(item)}>
|
<List.Item className="p-l-10 p-r-10 clickable" onClick={() => this.selectType(item)}>
|
||||||
@@ -139,6 +156,7 @@ class CreateSourceDialog extends React.Component {
|
|||||||
roundedImage={false}
|
roundedImage={false}
|
||||||
data-test="PreviewItem"
|
data-test="PreviewItem"
|
||||||
data-test-type={item.type}>
|
data-test-type={item.type}>
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'Element' is not assignable to type 'null | u... Remove this comment to see the full error message */}
|
||||||
<i className="fa fa-angle-double-right" />
|
<i className="fa fa-angle-double-right" />
|
||||||
</PreviewCard>
|
</PreviewCard>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
@@ -150,11 +168,13 @@ class CreateSourceDialog extends React.Component {
|
|||||||
const { dialog, sourceType } = this.props;
|
const { dialog, sourceType } = this.props;
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'props' does not exist on type 'never'.
|
||||||
{...dialog.props}
|
{...dialog.props}
|
||||||
title={`Create a New ${sourceType}`}
|
title={`Create a New ${sourceType}`}
|
||||||
footer={
|
footer={
|
||||||
currentStep === StepEnum.SELECT_TYPE
|
currentStep === StepEnum.SELECT_TYPE
|
||||||
? [
|
? [
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dismiss' does not exist on type 'never'.
|
||||||
<Button key="cancel" onClick={() => dialog.dismiss()} data-test="CreateSourceCancelButton">
|
<Button key="cancel" onClick={() => dialog.dismiss()} data-test="CreateSourceCancelButton">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>,
|
</Button>,
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import DatePicker from "antd/lib/date-picker";
|
|
||||||
import { clientConfig } from "@/services/auth";
|
|
||||||
import { Moment } from "@/components/proptypes";
|
|
||||||
|
|
||||||
const DateInput = React.forwardRef(({ defaultValue, value, onSelect, className, ...props }, ref) => {
|
|
||||||
const format = clientConfig.dateFormat || "YYYY-MM-DD";
|
|
||||||
const additionalAttributes = {};
|
|
||||||
if (defaultValue && defaultValue.isValid()) {
|
|
||||||
additionalAttributes.defaultValue = defaultValue;
|
|
||||||
}
|
|
||||||
if (value === null || (value && value.isValid())) {
|
|
||||||
additionalAttributes.value = value;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<DatePicker
|
|
||||||
ref={ref}
|
|
||||||
className={className}
|
|
||||||
{...additionalAttributes}
|
|
||||||
format={format}
|
|
||||||
placeholder="Select Date"
|
|
||||||
onChange={onSelect}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
DateInput.propTypes = {
|
|
||||||
defaultValue: Moment,
|
|
||||||
value: Moment,
|
|
||||||
onSelect: PropTypes.func,
|
|
||||||
className: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
DateInput.defaultProps = {
|
|
||||||
defaultValue: null,
|
|
||||||
value: undefined,
|
|
||||||
onSelect: () => {},
|
|
||||||
className: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DateInput;
|
|
||||||
48
client/app/components/DateInput.tsx
Normal file
48
client/app/components/DateInput.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import React from "react";
|
||||||
|
import DatePicker from "antd/lib/date-picker";
|
||||||
|
import { clientConfig } from "@/services/auth";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'Moment' is declared but its value is never read.
|
||||||
|
import { Moment } from "@/components/proptypes";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
defaultValue?: Moment;
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
value?: Moment;
|
||||||
|
onSelect?: (...args: any[]) => any;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DateInput = React.forwardRef<any, Props>(({ defaultValue, value, onSelect, className, ...props }, ref) => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dateFormat' does not exist on type '{}'.
|
||||||
|
const format = clientConfig.dateFormat || "YYYY-MM-DD";
|
||||||
|
const additionalAttributes = {};
|
||||||
|
if (defaultValue && defaultValue.isValid()) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'defaultValue' does not exist on type '{}... Remove this comment to see the full error message
|
||||||
|
additionalAttributes.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
if (value === null || (value && value.isValid())) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type '{}'.
|
||||||
|
additionalAttributes.value = value;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<DatePicker
|
||||||
|
ref={ref}
|
||||||
|
className={className}
|
||||||
|
{...additionalAttributes}
|
||||||
|
format={format}
|
||||||
|
placeholder="Select Date"
|
||||||
|
onChange={onSelect}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
DateInput.defaultProps = {
|
||||||
|
defaultValue: null,
|
||||||
|
value: undefined,
|
||||||
|
onSelect: () => {},
|
||||||
|
className: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DateInput;
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import { isArray } from "lodash";
|
|
||||||
import React from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import DatePicker from "antd/lib/date-picker";
|
|
||||||
import { clientConfig } from "@/services/auth";
|
|
||||||
import { Moment } from "@/components/proptypes";
|
|
||||||
|
|
||||||
const { RangePicker } = DatePicker;
|
|
||||||
|
|
||||||
const DateRangeInput = React.forwardRef(({ defaultValue, value, onSelect, className, ...props }, ref) => {
|
|
||||||
const format = clientConfig.dateFormat || "YYYY-MM-DD";
|
|
||||||
const additionalAttributes = {};
|
|
||||||
if (isArray(defaultValue) && defaultValue[0].isValid() && defaultValue[1].isValid()) {
|
|
||||||
additionalAttributes.defaultValue = defaultValue;
|
|
||||||
}
|
|
||||||
if (value === null || (isArray(value) && value[0].isValid() && value[1].isValid())) {
|
|
||||||
additionalAttributes.value = value;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<RangePicker
|
|
||||||
ref={ref}
|
|
||||||
className={className}
|
|
||||||
{...additionalAttributes}
|
|
||||||
format={format}
|
|
||||||
onChange={onSelect}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
DateRangeInput.propTypes = {
|
|
||||||
defaultValue: PropTypes.arrayOf(Moment),
|
|
||||||
value: PropTypes.arrayOf(Moment),
|
|
||||||
onSelect: PropTypes.func,
|
|
||||||
className: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
DateRangeInput.defaultProps = {
|
|
||||||
defaultValue: null,
|
|
||||||
value: undefined,
|
|
||||||
onSelect: () => {},
|
|
||||||
className: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DateRangeInput;
|
|
||||||
51
client/app/components/DateRangeInput.tsx
Normal file
51
client/app/components/DateRangeInput.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { isArray } from "lodash";
|
||||||
|
import React from "react";
|
||||||
|
import DatePicker from "antd/lib/date-picker";
|
||||||
|
import { clientConfig } from "@/services/auth";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'Moment' is declared but its value is never read.
|
||||||
|
import { Moment } from "@/components/proptypes";
|
||||||
|
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
defaultValue?: Moment[];
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
value?: Moment[];
|
||||||
|
onSelect?: (...args: any[]) => any;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DateRangeInput = React.forwardRef<any, Props>(({ defaultValue, value, onSelect, className, ...props }, ref) => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dateFormat' does not exist on type '{}'.
|
||||||
|
const format = clientConfig.dateFormat || "YYYY-MM-DD";
|
||||||
|
const additionalAttributes = {};
|
||||||
|
if (isArray(defaultValue) && defaultValue[0].isValid() && defaultValue[1].isValid()) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'defaultValue' does not exist on type '{}... Remove this comment to see the full error message
|
||||||
|
additionalAttributes.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
if (value === null || (isArray(value) && value[0].isValid() && value[1].isValid())) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type '{}'.
|
||||||
|
additionalAttributes.value = value;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<RangePicker
|
||||||
|
ref={ref}
|
||||||
|
className={className}
|
||||||
|
{...additionalAttributes}
|
||||||
|
format={format}
|
||||||
|
onChange={onSelect}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
DateRangeInput.defaultProps = {
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'null' is not assignable to type 'any[] | und... Remove this comment to see the full error message
|
||||||
|
defaultValue: null,
|
||||||
|
value: undefined,
|
||||||
|
onSelect: () => {},
|
||||||
|
className: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DateRangeInput;
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import DatePicker from "antd/lib/date-picker";
|
|
||||||
import { clientConfig } from "@/services/auth";
|
|
||||||
import { Moment } from "@/components/proptypes";
|
|
||||||
|
|
||||||
const DateTimeInput = React.forwardRef(({ defaultValue, value, withSeconds, onSelect, className, ...props }, ref) => {
|
|
||||||
const format = (clientConfig.dateFormat || "YYYY-MM-DD") + (withSeconds ? " HH:mm:ss" : " HH:mm");
|
|
||||||
const additionalAttributes = {};
|
|
||||||
if (defaultValue && defaultValue.isValid()) {
|
|
||||||
additionalAttributes.defaultValue = defaultValue;
|
|
||||||
}
|
|
||||||
if (value === null || (value && value.isValid())) {
|
|
||||||
additionalAttributes.value = value;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<DatePicker
|
|
||||||
ref={ref}
|
|
||||||
className={className}
|
|
||||||
showTime
|
|
||||||
{...additionalAttributes}
|
|
||||||
format={format}
|
|
||||||
placeholder="Select Date and Time"
|
|
||||||
onChange={onSelect}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
DateTimeInput.propTypes = {
|
|
||||||
defaultValue: Moment,
|
|
||||||
value: Moment,
|
|
||||||
withSeconds: PropTypes.bool,
|
|
||||||
onSelect: PropTypes.func,
|
|
||||||
className: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
DateTimeInput.defaultProps = {
|
|
||||||
defaultValue: null,
|
|
||||||
value: undefined,
|
|
||||||
withSeconds: false,
|
|
||||||
onSelect: () => {},
|
|
||||||
className: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DateTimeInput;
|
|
||||||
51
client/app/components/DateTimeInput.tsx
Normal file
51
client/app/components/DateTimeInput.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import React from "react";
|
||||||
|
import DatePicker from "antd/lib/date-picker";
|
||||||
|
import { clientConfig } from "@/services/auth";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'Moment' is declared but its value is never read.
|
||||||
|
import { Moment } from "@/components/proptypes";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
defaultValue?: Moment;
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
value?: Moment;
|
||||||
|
withSeconds?: boolean;
|
||||||
|
onSelect?: (...args: any[]) => any;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DateTimeInput = React.forwardRef<any, Props>(({ defaultValue, value, withSeconds, onSelect, className, ...props }, ref) => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dateFormat' does not exist on type '{}'.
|
||||||
|
const format = (clientConfig.dateFormat || "YYYY-MM-DD") + (withSeconds ? " HH:mm:ss" : " HH:mm");
|
||||||
|
const additionalAttributes = {};
|
||||||
|
if (defaultValue && defaultValue.isValid()) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'defaultValue' does not exist on type '{}... Remove this comment to see the full error message
|
||||||
|
additionalAttributes.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
if (value === null || (value && value.isValid())) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type '{}'.
|
||||||
|
additionalAttributes.value = value;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<DatePicker
|
||||||
|
ref={ref}
|
||||||
|
className={className}
|
||||||
|
showTime
|
||||||
|
{...additionalAttributes}
|
||||||
|
format={format}
|
||||||
|
placeholder="Select Date and Time"
|
||||||
|
onChange={onSelect}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
DateTimeInput.defaultProps = {
|
||||||
|
defaultValue: null,
|
||||||
|
value: undefined,
|
||||||
|
withSeconds: false,
|
||||||
|
onSelect: () => {},
|
||||||
|
className: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DateTimeInput;
|
||||||
@@ -1,20 +1,33 @@
|
|||||||
import { isArray } from "lodash";
|
import { isArray } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import DatePicker from "antd/lib/date-picker";
|
import DatePicker from "antd/lib/date-picker";
|
||||||
import { clientConfig } from "@/services/auth";
|
import { clientConfig } from "@/services/auth";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'Moment' is declared but its value is never read.
|
||||||
import { Moment } from "@/components/proptypes";
|
import { Moment } from "@/components/proptypes";
|
||||||
|
|
||||||
const { RangePicker } = DatePicker;
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
const DateTimeRangeInput = React.forwardRef(
|
type Props = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
defaultValue?: Moment[];
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
value?: Moment[];
|
||||||
|
withSeconds?: boolean;
|
||||||
|
onSelect?: (...args: any[]) => any;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DateTimeRangeInput = React.forwardRef<any, Props>(
|
||||||
({ defaultValue, value, withSeconds, onSelect, className, ...props }, ref) => {
|
({ defaultValue, value, withSeconds, onSelect, className, ...props }, ref) => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dateFormat' does not exist on type '{}'.
|
||||||
const format = (clientConfig.dateFormat || "YYYY-MM-DD") + (withSeconds ? " HH:mm:ss" : " HH:mm");
|
const format = (clientConfig.dateFormat || "YYYY-MM-DD") + (withSeconds ? " HH:mm:ss" : " HH:mm");
|
||||||
const additionalAttributes = {};
|
const additionalAttributes = {};
|
||||||
if (isArray(defaultValue) && defaultValue[0].isValid() && defaultValue[1].isValid()) {
|
if (isArray(defaultValue) && defaultValue[0].isValid() && defaultValue[1].isValid()) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'defaultValue' does not exist on type '{}... Remove this comment to see the full error message
|
||||||
additionalAttributes.defaultValue = defaultValue;
|
additionalAttributes.defaultValue = defaultValue;
|
||||||
}
|
}
|
||||||
if (value === null || (isArray(value) && value[0].isValid() && value[1].isValid())) {
|
if (value === null || (isArray(value) && value[0].isValid() && value[1].isValid())) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type '{}'.
|
||||||
additionalAttributes.value = value;
|
additionalAttributes.value = value;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -31,15 +44,8 @@ const DateTimeRangeInput = React.forwardRef(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
DateTimeRangeInput.propTypes = {
|
|
||||||
defaultValue: PropTypes.arrayOf(Moment),
|
|
||||||
value: PropTypes.arrayOf(Moment),
|
|
||||||
withSeconds: PropTypes.bool,
|
|
||||||
onSelect: PropTypes.func,
|
|
||||||
className: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
DateTimeRangeInput.defaultProps = {
|
DateTimeRangeInput.defaultProps = {
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'null' is not assignable to type 'any[] | und... Remove this comment to see the full error message
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
withSeconds: false,
|
withSeconds: false,
|
||||||
@@ -3,6 +3,17 @@ import React from "react";
|
|||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
|
|
||||||
|
type DialogPropType = {
|
||||||
|
props: {
|
||||||
|
visible?: boolean;
|
||||||
|
onOk?: (...args: any[]) => any;
|
||||||
|
onCancel?: (...args: any[]) => any;
|
||||||
|
afterClose?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
close: (...args: any[]) => any;
|
||||||
|
dismiss: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Wrapper for dialogs based on Ant's <Modal> component.
|
Wrapper for dialogs based on Ant's <Modal> component.
|
||||||
|
|
||||||
@@ -75,7 +86,7 @@ import ReactDOM from "react-dom";
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
4. wrap your component and export it:
|
4. wrap your component and it:
|
||||||
|
|
||||||
export default wrapDialog(YourComponent).
|
export default wrapDialog(YourComponent).
|
||||||
|
|
||||||
@@ -96,18 +107,20 @@ import ReactDOM from "react-dom";
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const DialogPropType = PropTypes.shape({
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'Requireable<InferProps<{ props: Validator<In... Remove this comment to see the full error message
|
||||||
props: PropTypes.shape({
|
export const DialogPropType: PropTypes.Requireable<DialogPropType> = PropTypes.shape({
|
||||||
visible: PropTypes.bool,
|
props: PropTypes.shape({
|
||||||
onOk: PropTypes.func,
|
visible: PropTypes.bool,
|
||||||
onCancel: PropTypes.func,
|
onOk: PropTypes.func,
|
||||||
afterClose: PropTypes.func,
|
onCancel: PropTypes.func,
|
||||||
}).isRequired,
|
afterClose: PropTypes.func,
|
||||||
close: PropTypes.func.isRequired,
|
}).isRequired,
|
||||||
dismiss: PropTypes.func.isRequired,
|
close: PropTypes.func.isRequired,
|
||||||
|
dismiss: PropTypes.func.isRequired,
|
||||||
});
|
});
|
||||||
|
|
||||||
function openDialog(DialogComponent, props) {
|
|
||||||
|
function openDialog(DialogComponent: any, props: any) {
|
||||||
const dialog = {
|
const dialog = {
|
||||||
props: {
|
props: {
|
||||||
visible: true,
|
visible: true,
|
||||||
@@ -121,7 +134,7 @@ function openDialog(DialogComponent, props) {
|
|||||||
dismiss: () => {},
|
dismiss: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
let pendingCloseTask = null;
|
let pendingCloseTask: any = null;
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
onClose: () => {},
|
onClose: () => {},
|
||||||
@@ -143,7 +156,7 @@ function openDialog(DialogComponent, props) {
|
|||||||
}, 10);
|
}, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
function processDialogClose(result, setAdditionalDialogProps) {
|
function processDialogClose(result: any, setAdditionalDialogProps: any) {
|
||||||
dialog.props.okButtonProps = { disabled: true };
|
dialog.props.okButtonProps = { disabled: true };
|
||||||
dialog.props.cancelButtonProps = { disabled: true };
|
dialog.props.cancelButtonProps = { disabled: true };
|
||||||
setAdditionalDialogProps();
|
setAdditionalDialogProps();
|
||||||
@@ -160,9 +173,11 @@ function openDialog(DialogComponent, props) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeDialog(result) {
|
function closeDialog(result: any) {
|
||||||
if (!pendingCloseTask) {
|
if (!pendingCloseTask) {
|
||||||
|
// @ts-expect-error ts-migrate(2554) FIXME: Expected 0 arguments, but got 1.
|
||||||
pendingCloseTask = processDialogClose(handlers.onClose(result), () => {
|
pendingCloseTask = processDialogClose(handlers.onClose(result), () => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'loading' does not exist on type '{}'.
|
||||||
dialog.props.okButtonProps.loading = true;
|
dialog.props.okButtonProps.loading = true;
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
pendingCloseTask = null;
|
pendingCloseTask = null;
|
||||||
@@ -171,9 +186,11 @@ function openDialog(DialogComponent, props) {
|
|||||||
return pendingCloseTask;
|
return pendingCloseTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
function dismissDialog(result) {
|
function dismissDialog(result: any) {
|
||||||
if (!pendingCloseTask) {
|
if (!pendingCloseTask) {
|
||||||
|
// @ts-expect-error ts-migrate(2554) FIXME: Expected 0 arguments, but got 1.
|
||||||
pendingCloseTask = processDialogClose(handlers.onDismiss(result), () => {
|
pendingCloseTask = processDialogClose(handlers.onDismiss(result), () => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'loading' does not exist on type '{}'.
|
||||||
dialog.props.cancelButtonProps.loading = true;
|
dialog.props.cancelButtonProps.loading = true;
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
pendingCloseTask = null;
|
pendingCloseTask = null;
|
||||||
@@ -182,26 +199,30 @@ function openDialog(DialogComponent, props) {
|
|||||||
return pendingCloseTask;
|
return pendingCloseTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(result: any) => any' is not assignable to t... Remove this comment to see the full error message
|
||||||
dialog.props.onOk = closeDialog;
|
dialog.props.onOk = closeDialog;
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(result: any) => any' is not assignable to t... Remove this comment to see the full error message
|
||||||
dialog.props.onCancel = dismissDialog;
|
dialog.props.onCancel = dismissDialog;
|
||||||
dialog.props.afterClose = destroyDialog;
|
dialog.props.afterClose = destroyDialog;
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(result: any) => any' is not assignable to t... Remove this comment to see the full error message
|
||||||
dialog.close = closeDialog;
|
dialog.close = closeDialog;
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(result: any) => any' is not assignable to t... Remove this comment to see the full error message
|
||||||
dialog.dismiss = dismissDialog;
|
dialog.dismiss = dismissDialog;
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
close: closeDialog,
|
close: closeDialog,
|
||||||
dismiss: dismissDialog,
|
dismiss: dismissDialog,
|
||||||
update: newProps => {
|
update: (newProps: any) => {
|
||||||
props = { ...props, ...newProps };
|
props = { ...props, ...newProps };
|
||||||
render();
|
render();
|
||||||
},
|
},
|
||||||
onClose: handler => {
|
onClose: (handler: any) => {
|
||||||
if (isFunction(handler)) {
|
if (isFunction(handler)) {
|
||||||
handlers.onClose = handler;
|
handlers.onClose = handler;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
onDismiss: handler => {
|
onDismiss: (handler: any) => {
|
||||||
if (isFunction(handler)) {
|
if (isFunction(handler)) {
|
||||||
handlers.onDismiss = handler;
|
handlers.onDismiss = handler;
|
||||||
}
|
}
|
||||||
@@ -214,14 +235,9 @@ function openDialog(DialogComponent, props) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrap(DialogComponent) {
|
export function wrap(DialogComponent: any) {
|
||||||
return {
|
return {
|
||||||
Component: DialogComponent,
|
Component: DialogComponent,
|
||||||
showModal: props => openDialog(DialogComponent, props),
|
showModal: (props: any) => openDialog(DialogComponent, props),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
|
||||||
DialogPropType,
|
|
||||||
wrap,
|
|
||||||
};
|
|
||||||
@@ -1,32 +1,35 @@
|
|||||||
import { isFunction, isString, isUndefined } from "lodash";
|
import { isFunction, isString, isUndefined } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
const componentsRegistry = new Map();
|
const componentsRegistry = new Map();
|
||||||
const activeInstances = new Set();
|
const activeInstances = new Set();
|
||||||
|
|
||||||
export function registerComponent(name, component) {
|
export function registerComponent(name: any, component: any) {
|
||||||
if (isString(name) && name !== "") {
|
if (isString(name) && name !== "") {
|
||||||
componentsRegistry.set(name, isFunction(component) ? component : null);
|
componentsRegistry.set(name, isFunction(component) ? component : null);
|
||||||
// Refresh active DynamicComponent instances which use this component
|
// Refresh active DynamicComponent instances which use this component
|
||||||
activeInstances.forEach(dynamicComponent => {
|
activeInstances.forEach(dynamicComponent => {
|
||||||
|
// @ts-expect-error ts-migrate(2571) FIXME: Object is of type 'unknown'.
|
||||||
if (dynamicComponent.props.name === name) {
|
if (dynamicComponent.props.name === name) {
|
||||||
|
// @ts-expect-error ts-migrate(2571) FIXME: Object is of type 'unknown'.
|
||||||
dynamicComponent.forceUpdate();
|
dynamicComponent.forceUpdate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unregisterComponent(name) {
|
export function unregisterComponent(name: any) {
|
||||||
registerComponent(name, null);
|
registerComponent(name, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class DynamicComponent extends React.Component {
|
type OwnProps = {
|
||||||
static propTypes = {
|
name: string;
|
||||||
name: PropTypes.string.isRequired,
|
fallback?: React.ReactNode;
|
||||||
fallback: PropTypes.node,
|
};
|
||||||
children: PropTypes.node,
|
|
||||||
};
|
type Props = OwnProps & typeof DynamicComponent.defaultProps;
|
||||||
|
|
||||||
|
export default class DynamicComponent extends React.Component<Props> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
children: null,
|
children: null,
|
||||||
@@ -1,21 +1,25 @@
|
|||||||
import { trim } from "lodash";
|
import { trim } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import Input from "antd/lib/input";
|
import Input from "antd/lib/input";
|
||||||
|
|
||||||
export default class EditInPlace extends React.Component {
|
type OwnProps = {
|
||||||
static propTypes = {
|
ignoreBlanks?: boolean;
|
||||||
ignoreBlanks: PropTypes.bool,
|
isEditable?: boolean;
|
||||||
isEditable: PropTypes.bool,
|
placeholder?: string;
|
||||||
placeholder: PropTypes.string,
|
value?: string;
|
||||||
value: PropTypes.string,
|
onDone: (...args: any[]) => any;
|
||||||
onDone: PropTypes.func.isRequired,
|
onStopEditing?: (...args: any[]) => any;
|
||||||
onStopEditing: PropTypes.func,
|
multiline?: boolean;
|
||||||
multiline: PropTypes.bool,
|
editorProps?: any;
|
||||||
editorProps: PropTypes.object,
|
defaultEditing?: boolean;
|
||||||
defaultEditing: PropTypes.bool,
|
};
|
||||||
};
|
|
||||||
|
type State = any;
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof EditInPlace.defaultProps;
|
||||||
|
|
||||||
|
export default class EditInPlace extends React.Component<Props, State> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
ignoreBlanks: false,
|
ignoreBlanks: false,
|
||||||
@@ -28,14 +32,14 @@ export default class EditInPlace extends React.Component {
|
|||||||
defaultEditing: false,
|
defaultEditing: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
editing: props.defaultEditing,
|
editing: props.defaultEditing,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(_, prevState) {
|
componentDidUpdate(_: Props, prevState: State) {
|
||||||
if (!this.state.editing && prevState.editing) {
|
if (!this.state.editing && prevState.editing) {
|
||||||
this.props.onStopEditing();
|
this.props.onStopEditing();
|
||||||
}
|
}
|
||||||
@@ -47,7 +51,7 @@ export default class EditInPlace extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
stopEditing = currentValue => {
|
stopEditing = (currentValue: any) => {
|
||||||
const newValue = trim(currentValue);
|
const newValue = trim(currentValue);
|
||||||
const ignorableBlank = this.props.ignoreBlanks && newValue === "";
|
const ignorableBlank = this.props.ignoreBlanks && newValue === "";
|
||||||
if (!ignorableBlank && newValue !== this.props.value) {
|
if (!ignorableBlank && newValue !== this.props.value) {
|
||||||
@@ -56,7 +60,7 @@ export default class EditInPlace extends React.Component {
|
|||||||
this.setState({ editing: false });
|
this.setState({ editing: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleKeyDown = event => {
|
handleKeyDown = (event: any) => {
|
||||||
if (event.keyCode === 13 && !event.shiftKey) {
|
if (event.keyCode === 13 && !event.shiftKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.stopEditing(event.target.value);
|
this.stopEditing(event.target.value);
|
||||||
@@ -86,7 +90,7 @@ export default class EditInPlace extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<InputComponent
|
<InputComponent
|
||||||
defaultValue={value}
|
defaultValue={value}
|
||||||
onBlur={e => this.stopEditing(e.target.value)}
|
onBlur={(e: any) => this.stopEditing(e.target.value)}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
autoFocus
|
autoFocus
|
||||||
{...editorProps}
|
{...editorProps}
|
||||||
@@ -96,6 +100,7 @@ export default class EditInPlace extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'className' does not exist on type 'Reado... Remove this comment to see the full error message
|
||||||
<span className={cx("edit-in-place", { active: this.state.editing }, this.props.className)}>
|
<span className={cx("edit-in-place", { active: this.state.editing }, this.props.className)}>
|
||||||
{this.state.editing ? this.renderEdit() : this.renderNormal()}
|
{this.state.editing ? this.renderEdit() : this.renderNormal()}
|
||||||
</span>
|
</span>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { includes, words, capitalize, clone, isNull } from "lodash";
|
import { includes, words, capitalize, clone, isNull } from "lodash";
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Checkbox from "antd/lib/checkbox";
|
import Checkbox from "antd/lib/checkbox";
|
||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
import Form from "antd/lib/form";
|
import Form from "antd/lib/form";
|
||||||
@@ -8,6 +7,7 @@ import Button from "antd/lib/button";
|
|||||||
import Select from "antd/lib/select";
|
import Select from "antd/lib/select";
|
||||||
import Input from "antd/lib/input";
|
import Input from "antd/lib/input";
|
||||||
import Divider from "antd/lib/divider";
|
import Divider from "antd/lib/divider";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DialogPropType' is declared but its value is neve... Remove this comment to see the full error message
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
import QuerySelector from "@/components/QuerySelector";
|
import QuerySelector from "@/components/QuerySelector";
|
||||||
import { Query } from "@/services/query";
|
import { Query } from "@/services/query";
|
||||||
@@ -15,20 +15,28 @@ import { Query } from "@/services/query";
|
|||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
const formItemProps = { labelCol: { span: 6 }, wrapperCol: { span: 16 } };
|
const formItemProps = { labelCol: { span: 6 }, wrapperCol: { span: 16 } };
|
||||||
|
|
||||||
function getDefaultTitle(text) {
|
function getDefaultTitle(text: any) {
|
||||||
return capitalize(words(text).join(" ")); // humanize
|
return capitalize(words(text).join(" ")); // humanize
|
||||||
}
|
}
|
||||||
|
|
||||||
function isTypeDateRange(type) {
|
function isTypeDateRange(type: any) {
|
||||||
return /-range/.test(type);
|
return /-range/.test(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
function joinExampleList(multiValuesOptions) {
|
function joinExampleList(multiValuesOptions: any) {
|
||||||
const { prefix, suffix } = multiValuesOptions;
|
const { prefix, suffix } = multiValuesOptions;
|
||||||
return ["value1", "value2", "value3"].map(value => `${prefix}${value}${suffix}`).join(",");
|
return ["value1", "value2", "value3"].map(value => `${prefix}${value}${suffix}`).join(",");
|
||||||
}
|
}
|
||||||
|
|
||||||
function NameInput({ name, type, onChange, existingNames, setValidation }) {
|
type NameInputProps = {
|
||||||
|
name: string;
|
||||||
|
onChange: (...args: any[]) => any;
|
||||||
|
existingNames: string[];
|
||||||
|
setValidation: (...args: any[]) => any;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function NameInput({ name, type, onChange, existingNames, setValidation }: NameInputProps) {
|
||||||
let helpText = "";
|
let helpText = "";
|
||||||
let validateStatus = "";
|
let validateStatus = "";
|
||||||
|
|
||||||
@@ -41,6 +49,7 @@ function NameInput({ name, type, onChange, existingNames, setValidation }) {
|
|||||||
validateStatus = "error";
|
validateStatus = "error";
|
||||||
} else {
|
} else {
|
||||||
if (isTypeDateRange(type)) {
|
if (isTypeDateRange(type)) {
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'Element' is not assignable to type 'string'.
|
||||||
helpText = (
|
helpText = (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
Appears in query as{" "}
|
Appears in query as{" "}
|
||||||
@@ -52,21 +61,23 @@ function NameInput({ name, type, onChange, existingNames, setValidation }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type '"" | "err... Remove this comment to see the full error message
|
||||||
<Form.Item required label="Keyword" help={helpText} validateStatus={validateStatus} {...formItemProps}>
|
<Form.Item required label="Keyword" help={helpText} validateStatus={validateStatus} {...formItemProps}>
|
||||||
<Input onChange={e => onChange(e.target.value)} autoFocus />
|
<Input onChange={e => onChange(e.target.value)} autoFocus />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
NameInput.propTypes = {
|
type OwnEditParameterSettingsDialogProps = {
|
||||||
name: PropTypes.string.isRequired,
|
parameter: any;
|
||||||
onChange: PropTypes.func.isRequired,
|
// @ts-expect-error ts-migrate(2749) FIXME: 'DialogPropType' refers to a value, but is being u... Remove this comment to see the full error message
|
||||||
existingNames: PropTypes.arrayOf(PropTypes.string).isRequired,
|
dialog: DialogPropType;
|
||||||
setValidation: PropTypes.func.isRequired,
|
existingParams?: string[];
|
||||||
type: PropTypes.string.isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function EditParameterSettingsDialog(props) {
|
type EditParameterSettingsDialogProps = OwnEditParameterSettingsDialogProps & typeof EditParameterSettingsDialog.defaultProps;
|
||||||
|
|
||||||
|
function EditParameterSettingsDialog(props: EditParameterSettingsDialogProps) {
|
||||||
const [param, setParam] = useState(clone(props.parameter));
|
const [param, setParam] = useState(clone(props.parameter));
|
||||||
const [isNameValid, setIsNameValid] = useState(true);
|
const [isNameValid, setIsNameValid] = useState(true);
|
||||||
const [initialQuery, setInitialQuery] = useState();
|
const [initialQuery, setInitialQuery] = useState();
|
||||||
@@ -77,6 +88,7 @@ function EditParameterSettingsDialog(props) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const queryId = props.parameter.queryId;
|
const queryId = props.parameter.queryId;
|
||||||
if (queryId) {
|
if (queryId) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'get' does not exist on type 'typeof Quer... Remove this comment to see the full error message
|
||||||
Query.get({ id: queryId }).then(setInitialQuery);
|
Query.get({ id: queryId }).then(setInitialQuery);
|
||||||
}
|
}
|
||||||
}, [props.parameter.queryId]);
|
}, [props.parameter.queryId]);
|
||||||
@@ -157,6 +169,7 @@ function EditParameterSettingsDialog(props) {
|
|||||||
</Option>
|
</Option>
|
||||||
<Option value="enum">Dropdown List</Option>
|
<Option value="enum">Dropdown List</Option>
|
||||||
<Option value="query">Query Based Dropdown List</Option>
|
<Option value="query">Query Based Dropdown List</Option>
|
||||||
|
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'value' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
||||||
<Option disabled key="dv1">
|
<Option disabled key="dv1">
|
||||||
<Divider className="select-option-divider" />
|
<Divider className="select-option-divider" />
|
||||||
</Option>
|
</Option>
|
||||||
@@ -167,6 +180,7 @@ function EditParameterSettingsDialog(props) {
|
|||||||
Date and Time
|
Date and Time
|
||||||
</Option>
|
</Option>
|
||||||
<Option value="datetime-with-seconds">Date and Time (with seconds)</Option>
|
<Option value="datetime-with-seconds">Date and Time (with seconds)</Option>
|
||||||
|
{/* @ts-expect-error ts-migrate(2741) FIXME: Property 'value' is missing in type '{ children: E... Remove this comment to see the full error message */}
|
||||||
<Option disabled key="dv2">
|
<Option disabled key="dv2">
|
||||||
<Divider className="select-option-divider" />
|
<Divider className="select-option-divider" />
|
||||||
</Option>
|
</Option>
|
||||||
@@ -189,8 +203,11 @@ function EditParameterSettingsDialog(props) {
|
|||||||
{param.type === "query" && (
|
{param.type === "query" && (
|
||||||
<Form.Item label="Query" help="Select query to load dropdown values from" {...formItemProps}>
|
<Form.Item label="Query" help="Select query to load dropdown values from" {...formItemProps}>
|
||||||
<QuerySelector
|
<QuerySelector
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'undefined' is not assignable to type 'never'... Remove this comment to see the full error message
|
||||||
selectedQuery={initialQuery}
|
selectedQuery={initialQuery}
|
||||||
onChange={q => setParam({ ...param, queryId: q && q.id })}
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(q: any) => void' is not assignable to type ... Remove this comment to see the full error message
|
||||||
|
onChange={(q: any) => setParam({ ...param, queryId: q && q.id })}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
type="select"
|
type="select"
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -251,12 +268,6 @@ function EditParameterSettingsDialog(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
EditParameterSettingsDialog.propTypes = {
|
|
||||||
parameter: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
|
||||||
dialog: DialogPropType.isRequired,
|
|
||||||
existingParams: PropTypes.arrayOf(PropTypes.string),
|
|
||||||
};
|
|
||||||
|
|
||||||
EditParameterSettingsDialog.defaultProps = {
|
EditParameterSettingsDialog.defaultProps = {
|
||||||
existingParams: [],
|
existingParams: [],
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Dropdown from "antd/lib/dropdown";
|
import Dropdown from "antd/lib/dropdown";
|
||||||
import Menu from "antd/lib/menu";
|
import Menu from "antd/lib/menu";
|
||||||
import Button from "antd/lib/button";
|
import Button from "antd/lib/button";
|
||||||
@@ -13,7 +12,20 @@ import EllipsisOutlinedIcon from "@ant-design/icons/EllipsisOutlined";
|
|||||||
|
|
||||||
import QueryResultsLink from "./QueryResultsLink";
|
import QueryResultsLink from "./QueryResultsLink";
|
||||||
|
|
||||||
export default function QueryControlDropdown(props) {
|
type OwnProps = {
|
||||||
|
query: any;
|
||||||
|
queryResult?: any;
|
||||||
|
queryExecuting: boolean;
|
||||||
|
showEmbedDialog: (...args: any[]) => any;
|
||||||
|
embed?: boolean;
|
||||||
|
apiKey?: string;
|
||||||
|
selectedTab?: string | number;
|
||||||
|
openAddToDashboardForm: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof QueryControlDropdown.defaultProps;
|
||||||
|
|
||||||
|
export default function QueryControlDropdown(props: Props) {
|
||||||
const menu = (
|
const menu = (
|
||||||
<Menu>
|
<Menu>
|
||||||
{!props.query.isNew() && (!props.query.is_draft || !props.query.is_archived) && (
|
{!props.query.isNew() && (!props.query.is_draft || !props.query.is_archived) && (
|
||||||
@@ -23,6 +35,7 @@ export default function QueryControlDropdown(props) {
|
|||||||
</a>
|
</a>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)}
|
)}
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'disablePublicUrls' does not exist on typ... Remove this comment to see the full error message */}
|
||||||
{!clientConfig.disablePublicUrls && !props.query.isNew() && (
|
{!clientConfig.disablePublicUrls && !props.query.isNew() && (
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<a onClick={() => props.showEmbedDialog(props.query, props.selectedTab)} data-test="ShowEmbedDialogButton">
|
<a onClick={() => props.showEmbedDialog(props.query, props.selectedTab)} data-test="ShowEmbedDialogButton">
|
||||||
@@ -75,17 +88,6 @@ export default function QueryControlDropdown(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryControlDropdown.propTypes = {
|
|
||||||
query: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
|
||||||
queryResult: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
|
||||||
queryExecuting: PropTypes.bool.isRequired,
|
|
||||||
showEmbedDialog: PropTypes.func.isRequired,
|
|
||||||
embed: PropTypes.bool,
|
|
||||||
apiKey: PropTypes.string,
|
|
||||||
selectedTab: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
||||||
openAddToDashboardForm: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
QueryControlDropdown.defaultProps = {
|
QueryControlDropdown.defaultProps = {
|
||||||
queryResult: {},
|
queryResult: {},
|
||||||
embed: false,
|
embed: false,
|
||||||
@@ -1,8 +1,19 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Link from "@/components/Link";
|
import Link from "@/components/Link";
|
||||||
|
|
||||||
export default function QueryResultsLink(props) {
|
type OwnProps = {
|
||||||
|
query: any;
|
||||||
|
queryResult?: any;
|
||||||
|
fileType?: string;
|
||||||
|
disabled: boolean;
|
||||||
|
embed?: boolean;
|
||||||
|
apiKey?: string;
|
||||||
|
children: React.ReactNode[] | React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof QueryResultsLink.defaultProps;
|
||||||
|
|
||||||
|
export default function QueryResultsLink(props: Props) {
|
||||||
let href = "";
|
let href = "";
|
||||||
|
|
||||||
const { query, queryResult, fileType } = props;
|
const { query, queryResult, fileType } = props;
|
||||||
@@ -24,16 +35,6 @@ export default function QueryResultsLink(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryResultsLink.propTypes = {
|
|
||||||
query: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
|
||||||
queryResult: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
|
||||||
fileType: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool.isRequired,
|
|
||||||
embed: PropTypes.bool,
|
|
||||||
apiKey: PropTypes.string,
|
|
||||||
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
QueryResultsLink.defaultProps = {
|
QueryResultsLink.defaultProps = {
|
||||||
queryResult: {},
|
queryResult: {},
|
||||||
fileType: "csv",
|
fileType: "csv",
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Button from "antd/lib/button";
|
import Button from "antd/lib/button";
|
||||||
import FormOutlinedIcon from "@ant-design/icons/FormOutlined";
|
import FormOutlinedIcon from "@ant-design/icons/FormOutlined";
|
||||||
|
|
||||||
export default function EditVisualizationButton(props) {
|
type OwnProps = {
|
||||||
|
openVisualizationEditor: (...args: any[]) => any;
|
||||||
|
selectedTab?: string | number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof EditVisualizationButton.defaultProps;
|
||||||
|
|
||||||
|
export default function EditVisualizationButton(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
data-test="EditVisualization"
|
data-test="EditVisualization"
|
||||||
@@ -15,11 +21,6 @@ export default function EditVisualizationButton(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
EditVisualizationButton.propTypes = {
|
|
||||||
openVisualizationEditor: PropTypes.func.isRequired,
|
|
||||||
selectedTab: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
||||||
};
|
|
||||||
|
|
||||||
EditVisualizationButton.defaultProps = {
|
EditVisualizationButton.defaultProps = {
|
||||||
selectedTab: "",
|
selectedTab: "",
|
||||||
};
|
};
|
||||||
@@ -1,12 +1,21 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import { clientConfig, currentUser } from "@/services/auth";
|
import { clientConfig, currentUser } from "@/services/auth";
|
||||||
import Tooltip from "antd/lib/tooltip";
|
import Tooltip from "antd/lib/tooltip";
|
||||||
import Alert from "antd/lib/alert";
|
import Alert from "antd/lib/alert";
|
||||||
import HelpTrigger from "@/components/HelpTrigger";
|
import HelpTrigger from "@/components/HelpTrigger";
|
||||||
|
|
||||||
export default function EmailSettingsWarning({ featureName, className, mode, adminOnly }) {
|
type OwnProps = {
|
||||||
|
featureName: string;
|
||||||
|
className?: string;
|
||||||
|
mode?: "alert" | "icon";
|
||||||
|
adminOnly?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof EmailSettingsWarning.defaultProps;
|
||||||
|
|
||||||
|
export default function EmailSettingsWarning({ featureName, className, mode, adminOnly }: Props) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'mailSettingsMissing' does not exist on t... Remove this comment to see the full error message
|
||||||
if (!clientConfig.mailSettingsMissing) {
|
if (!clientConfig.mailSettingsMissing) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -18,6 +27,7 @@ export default function EmailSettingsWarning({ featureName, className, mode, adm
|
|||||||
const message = (
|
const message = (
|
||||||
<span>
|
<span>
|
||||||
Your mail server isn't configured correctly, and is needed for {featureName} to work.{" "}
|
Your mail server isn't configured correctly, and is needed for {featureName} to work.{" "}
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'. */}
|
||||||
<HelpTrigger type="MAIL_CONFIG" className="f-inherit" />
|
<HelpTrigger type="MAIL_CONFIG" className="f-inherit" />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -33,13 +43,6 @@ export default function EmailSettingsWarning({ featureName, className, mode, adm
|
|||||||
return <Alert message={message} type="error" className={className} />;
|
return <Alert message={message} type="error" className={className} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
EmailSettingsWarning.propTypes = {
|
|
||||||
featureName: PropTypes.string.isRequired,
|
|
||||||
className: PropTypes.string,
|
|
||||||
mode: PropTypes.oneOf(["alert", "icon"]),
|
|
||||||
adminOnly: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
EmailSettingsWarning.defaultProps = {
|
EmailSettingsWarning.defaultProps = {
|
||||||
className: null,
|
className: null,
|
||||||
mode: "alert",
|
mode: "alert",
|
||||||
@@ -1,19 +1,21 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
export default class FavoritesControl extends React.Component {
|
type OwnProps = {
|
||||||
static propTypes = {
|
item: {
|
||||||
item: PropTypes.shape({
|
is_favorite: boolean;
|
||||||
is_favorite: PropTypes.bool.isRequired,
|
};
|
||||||
}).isRequired,
|
onChange?: (...args: any[]) => any;
|
||||||
onChange: PropTypes.func,
|
};
|
||||||
};
|
|
||||||
|
type Props = OwnProps & typeof FavoritesControl.defaultProps;
|
||||||
|
|
||||||
|
export default class FavoritesControl extends React.Component<Props> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
onChange: () => {},
|
onChange: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleItem(event, item, callback) {
|
toggleItem(event: any, item: any, callback: any) {
|
||||||
const action = item.is_favorite ? item.unfavorite.bind(item) : item.favorite.bind(item);
|
const action = item.is_favorite ? item.unfavorite.bind(item) : item.favorite.bind(item);
|
||||||
const savedIsFavorite = item.is_favorite;
|
const savedIsFavorite = item.is_favorite;
|
||||||
|
|
||||||
@@ -8,18 +8,28 @@ import { formatColumnValue } from "@/lib/utils";
|
|||||||
const ALL_VALUES = "###Redash::Filters::SelectAll###";
|
const ALL_VALUES = "###Redash::Filters::SelectAll###";
|
||||||
const NONE_VALUES = "###Redash::Filters::Clear###";
|
const NONE_VALUES = "###Redash::Filters::Clear###";
|
||||||
|
|
||||||
export const FilterType = PropTypes.shape({
|
type FilterType = {
|
||||||
name: PropTypes.string.isRequired,
|
name: string;
|
||||||
friendlyName: PropTypes.string.isRequired,
|
friendlyName: string;
|
||||||
multiple: PropTypes.bool,
|
multiple?: boolean;
|
||||||
current: PropTypes.oneOfType([PropTypes.any, PropTypes.arrayOf(PropTypes.any)]),
|
current?: any | any[];
|
||||||
values: PropTypes.arrayOf(PropTypes.any).isRequired,
|
values: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'Requireable<InferProps<{ name: Validator<str... Remove this comment to see the full error message
|
||||||
|
const FilterType: PropTypes.Requireable<FilterType> = PropTypes.shape({
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
friendlyName: PropTypes.string.isRequired,
|
||||||
|
multiple: PropTypes.bool,
|
||||||
|
current: PropTypes.oneOfType([PropTypes.any, PropTypes.arrayOf(PropTypes.any)]),
|
||||||
|
values: PropTypes.arrayOf(PropTypes.any).isRequired,
|
||||||
});
|
});
|
||||||
|
export { FilterType };
|
||||||
|
|
||||||
export const FiltersType = PropTypes.arrayOf(FilterType);
|
export const FiltersType = PropTypes.arrayOf(FilterType);
|
||||||
|
|
||||||
function createFilterChangeHandler(filters, onChange) {
|
function createFilterChangeHandler(filters: any, onChange: any) {
|
||||||
return (filter, values) => {
|
return (filter: any, values: any) => {
|
||||||
if (isArray(values)) {
|
if (isArray(values)) {
|
||||||
values = map(values, value => filter.values[toNumber(value.key)] || value.key);
|
values = map(values, value => filter.values[toNumber(value.key)] || value.key);
|
||||||
} else {
|
} else {
|
||||||
@@ -38,7 +48,7 @@ function createFilterChangeHandler(filters, onChange) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterData(rows, filters = []) {
|
export function filterData(rows: any, filters = []) {
|
||||||
if (!isArray(rows)) {
|
if (!isArray(rows)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -49,7 +59,9 @@ export function filterData(rows, filters = []) {
|
|||||||
// "every" field's value should match "some" of corresponding filter's values
|
// "every" field's value should match "some" of corresponding filter's values
|
||||||
result = result.filter(row =>
|
result = result.filter(row =>
|
||||||
every(filters, filter => {
|
every(filters, filter => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'name' does not exist on type 'never'.
|
||||||
const rowValue = row[filter.name];
|
const rowValue = row[filter.name];
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'current' does not exist on type 'never'.
|
||||||
const filterValues = isArray(filter.current) ? filter.current : [filter.current];
|
const filterValues = isArray(filter.current) ? filter.current : [filter.current];
|
||||||
return some(filterValues, filterValue => {
|
return some(filterValues, filterValue => {
|
||||||
if (moment.isMoment(rowValue)) {
|
if (moment.isMoment(rowValue)) {
|
||||||
@@ -66,11 +78,20 @@ export function filterData(rows, filters = []) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Filters({ filters, onChange }) {
|
type OwnProps = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'FiltersType' refers to a value, but is being used... Remove this comment to see the full error message
|
||||||
|
filters: FiltersType;
|
||||||
|
onChange?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof Filters.defaultProps;
|
||||||
|
|
||||||
|
function Filters({ filters, onChange }: Props) {
|
||||||
if (filters.length === 0) {
|
if (filters.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(filter: any, values: any) => void' is not a... Remove this comment to see the full error message
|
||||||
onChange = createFilterChangeHandler(filters, onChange);
|
onChange = createFilterChangeHandler(filters, onChange);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -79,6 +100,7 @@ function Filters({ filters, onChange }) {
|
|||||||
<div className="row">
|
<div className="row">
|
||||||
{map(filters, filter => {
|
{map(filters, filter => {
|
||||||
const options = map(filter.values, (value, index) => (
|
const options = map(filter.values, (value, index) => (
|
||||||
|
// @ts-expect-error ts-migrate(2741) FIXME: Property 'value' is missing in type '{ children: a... Remove this comment to see the full error message
|
||||||
<Select.Option key={index}>{formatColumnValue(value, get(filter, "column.type"))}</Select.Option>
|
<Select.Option key={index}>{formatColumnValue(value, get(filter, "column.type"))}</Select.Option>
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -90,6 +112,7 @@ function Filters({ filters, onChange }) {
|
|||||||
<label>{filter.friendlyName}</label>
|
<label>{filter.friendlyName}</label>
|
||||||
{options.length === 0 && <Select className="w-100" disabled value="No values" />}
|
{options.length === 0 && <Select className="w-100" disabled value="No values" />}
|
||||||
{options.length > 0 && (
|
{options.length > 0 && (
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
<Select
|
<Select
|
||||||
labelInValue
|
labelInValue
|
||||||
className="w-100"
|
className="w-100"
|
||||||
@@ -111,10 +134,12 @@ function Filters({ filters, onChange }) {
|
|||||||
onChange={values => onChange(filter, values)}>
|
onChange={values => onChange(filter, values)}>
|
||||||
{!filter.multiple && options}
|
{!filter.multiple && options}
|
||||||
{filter.multiple && [
|
{filter.multiple && [
|
||||||
|
// @ts-expect-error ts-migrate(2741) FIXME: Property 'value' is missing in type '{ children: (... Remove this comment to see the full error message
|
||||||
<Select.Option key={NONE_VALUES} data-test="ClearOption">
|
<Select.Option key={NONE_VALUES} data-test="ClearOption">
|
||||||
<i className="fa fa-square-o m-r-5" />
|
<i className="fa fa-square-o m-r-5" />
|
||||||
Clear
|
Clear
|
||||||
</Select.Option>,
|
</Select.Option>,
|
||||||
|
// @ts-expect-error ts-migrate(2741) FIXME: Property 'value' is missing in type '{ children: (... Remove this comment to see the full error message
|
||||||
<Select.Option key={ALL_VALUES} data-test="SelectAllOption">
|
<Select.Option key={ALL_VALUES} data-test="SelectAllOption">
|
||||||
<i className="fa fa-check-square-o m-r-5" />
|
<i className="fa fa-check-square-o m-r-5" />
|
||||||
Select All
|
Select All
|
||||||
@@ -134,11 +159,6 @@ function Filters({ filters, onChange }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Filters.propTypes = {
|
|
||||||
filters: FiltersType.isRequired,
|
|
||||||
onChange: PropTypes.func, // (name, value) => void
|
|
||||||
};
|
|
||||||
|
|
||||||
Filters.defaultProps = {
|
Filters.defaultProps = {
|
||||||
onChange: () => {},
|
onChange: () => {},
|
||||||
};
|
};
|
||||||
@@ -51,6 +51,16 @@ export const TYPES = mapValues(
|
|||||||
([url, title]) => [DOMAIN + HELP_PATH + url, title]
|
([url, title]) => [DOMAIN + HELP_PATH + url, title]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
type?: string;
|
||||||
|
href?: string;
|
||||||
|
title?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
showTooltip?: boolean;
|
||||||
|
renderAsLink?: boolean;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
const HelpTriggerPropTypes = {
|
const HelpTriggerPropTypes = {
|
||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
href: PropTypes.string,
|
href: PropTypes.string,
|
||||||
@@ -71,7 +81,7 @@ const HelpTriggerDefaultProps = {
|
|||||||
children: <i className="fa fa-question-circle" />,
|
children: <i className="fa fa-question-circle" />,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function helpTriggerWithTypes(types, allowedDomains = [], drawerClassName = null) {
|
export function helpTriggerWithTypes(types: any, allowedDomains = [], drawerClassName = null) {
|
||||||
return class HelpTrigger extends React.Component {
|
return class HelpTrigger extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
...HelpTriggerPropTypes,
|
...HelpTriggerPropTypes,
|
||||||
@@ -97,14 +107,18 @@ export function helpTriggerWithTypes(types, allowedDomains = [], drawerClassName
|
|||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener("message", this.onPostMessageReceived);
|
window.removeEventListener("message", this.onPostMessageReceived);
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
clearTimeout(this.iframeLoadingTimeout);
|
clearTimeout(this.iframeLoadingTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadIframe = url => {
|
loadIframe = (url: any) => {
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
clearTimeout(this.iframeLoadingTimeout);
|
clearTimeout(this.iframeLoadingTimeout);
|
||||||
this.setState({ loading: true, error: false });
|
this.setState({ loading: true, error: false });
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2571) FIXME: Object is of type 'unknown'.
|
||||||
this.iframeRef.current.src = url;
|
this.iframeRef.current.src = url;
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'number' is not assignable to type 'null'.
|
||||||
this.iframeLoadingTimeout = setTimeout(() => {
|
this.iframeLoadingTimeout = setTimeout(() => {
|
||||||
this.setState({ error: url, loading: false });
|
this.setState({ error: url, loading: false });
|
||||||
}, IFRAME_TIMEOUT); // safety
|
}, IFRAME_TIMEOUT); // safety
|
||||||
@@ -112,10 +126,11 @@ export function helpTriggerWithTypes(types, allowedDomains = [], drawerClassName
|
|||||||
|
|
||||||
onIframeLoaded = () => {
|
onIframeLoaded = () => {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
clearTimeout(this.iframeLoadingTimeout);
|
clearTimeout(this.iframeLoadingTimeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
onPostMessageReceived = event => {
|
onPostMessageReceived = (event: any) => {
|
||||||
if (!some(allowedDomains, domain => startsWith(event.origin, domain))) {
|
if (!some(allowedDomains, domain => startsWith(event.origin, domain))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -129,11 +144,13 @@ export function helpTriggerWithTypes(types, allowedDomains = [], drawerClassName
|
|||||||
};
|
};
|
||||||
|
|
||||||
getUrl = () => {
|
getUrl = () => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'type' does not exist on type 'Readonly<{... Remove this comment to see the full error message
|
||||||
const helpTriggerType = get(types, this.props.type);
|
const helpTriggerType = get(types, this.props.type);
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'href' does not exist on type 'Readonly<{... Remove this comment to see the full error message
|
||||||
return helpTriggerType ? helpTriggerType[0] : this.props.href;
|
return helpTriggerType ? helpTriggerType[0] : this.props.href;
|
||||||
};
|
};
|
||||||
|
|
||||||
openDrawer = e => {
|
openDrawer = (e: any) => {
|
||||||
// keep "open in new tab" behavior
|
// keep "open in new tab" behavior
|
||||||
if (!e.shiftKey && !e.ctrlKey && !e.metaKey) {
|
if (!e.shiftKey && !e.ctrlKey && !e.metaKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -143,7 +160,7 @@ export function helpTriggerWithTypes(types, allowedDomains = [], drawerClassName
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
closeDrawer = event => {
|
closeDrawer = (event: any) => {
|
||||||
if (event) {
|
if (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
@@ -157,16 +174,20 @@ export function helpTriggerWithTypes(types, allowedDomains = [], drawerClassName
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'type' does not exist on type 'Readonly<{... Remove this comment to see the full error message
|
||||||
const tooltip = get(types, `${this.props.type}[1]`, this.props.title);
|
const tooltip = get(types, `${this.props.type}[1]`, this.props.title);
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'className' does not exist on type 'Reado... Remove this comment to see the full error message
|
||||||
const className = cx("help-trigger", this.props.className);
|
const className = cx("help-trigger", this.props.className);
|
||||||
const url = this.state.currentUrl;
|
const url = this.state.currentUrl;
|
||||||
const isAllowedDomain = some(allowedDomains, domain => startsWith(url || targetUrl, domain));
|
const isAllowedDomain = some(allowedDomains, domain => startsWith(url || targetUrl, domain));
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'renderAsLink' does not exist on type 'Re... Remove this comment to see the full error message
|
||||||
const shouldRenderAsLink = this.props.renderAsLink || !isAllowedDomain;
|
const shouldRenderAsLink = this.props.renderAsLink || !isAllowedDomain;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'showTooltip' does not exist on type 'Rea... Remove this comment to see the full error message
|
||||||
this.props.showTooltip ? (
|
this.props.showTooltip ? (
|
||||||
<>
|
<>
|
||||||
{tooltip}
|
{tooltip}
|
||||||
@@ -211,6 +232,7 @@ export function helpTriggerWithTypes(types, allowedDomains = [], drawerClassName
|
|||||||
{/* iframe */}
|
{/* iframe */}
|
||||||
{!this.state.error && (
|
{!this.state.error && (
|
||||||
<iframe
|
<iframe
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'RefObject<unknown>' is not assignable to typ... Remove this comment to see the full error message
|
||||||
ref={this.iframeRef}
|
ref={this.iframeRef}
|
||||||
title="Usage Help"
|
title="Usage Help"
|
||||||
src="about:blank"
|
src="about:blank"
|
||||||
@@ -226,6 +248,7 @@ export function helpTriggerWithTypes(types, allowedDomains = [], drawerClassName
|
|||||||
|
|
||||||
{/* error message */}
|
{/* error message */}
|
||||||
{this.state.error && (
|
{this.state.error && (
|
||||||
|
// @ts-expect-error ts-migrate(2746) FIXME: This JSX tag's 'children' prop expects a single ch... Remove this comment to see the full error message
|
||||||
<BigMessage icon="fa-exclamation-circle" className="help-message">
|
<BigMessage icon="fa-exclamation-circle" className="help-message">
|
||||||
Something went wrong.
|
Something went wrong.
|
||||||
<br />
|
<br />
|
||||||
@@ -239,6 +262,7 @@ export function helpTriggerWithTypes(types, allowedDomains = [], drawerClassName
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* extra content */}
|
{/* extra content */}
|
||||||
|
{/* @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call. */}
|
||||||
<DynamicComponent name="HelpDrawerExtraContent" onLeave={this.closeDrawer} openPageUrl={this.loadIframe} />
|
<DynamicComponent name="HelpDrawerExtraContent" onLeave={this.closeDrawer} openPageUrl={this.loadIframe} />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
@@ -247,11 +271,12 @@ export function helpTriggerWithTypes(types, allowedDomains = [], drawerClassName
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
registerComponent("HelpTrigger", helpTriggerWithTypes(TYPES, [DOMAIN]));
|
registerComponent("HelpTrigger", helpTriggerWithTypes(TYPES, [DOMAIN]));
|
||||||
|
|
||||||
export default function HelpTrigger(props) {
|
type Props = OwnProps & typeof HelpTriggerDefaultProps;
|
||||||
|
|
||||||
|
export default function HelpTrigger(props: Props) {
|
||||||
return <DynamicComponent {...props} name="HelpTrigger" />;
|
return <DynamicComponent {...props} name="HelpTrigger" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
HelpTrigger.propTypes = HelpTriggerPropTypes;
|
|
||||||
HelpTrigger.defaultProps = HelpTriggerDefaultProps;
|
HelpTrigger.defaultProps = HelpTriggerDefaultProps;
|
||||||
@@ -3,8 +3,13 @@ import Input from "antd/lib/input";
|
|||||||
import CopyOutlinedIcon from "@ant-design/icons/CopyOutlined";
|
import CopyOutlinedIcon from "@ant-design/icons/CopyOutlined";
|
||||||
import Tooltip from "antd/lib/tooltip";
|
import Tooltip from "antd/lib/tooltip";
|
||||||
|
|
||||||
export default class InputWithCopy extends React.Component {
|
type State = any;
|
||||||
constructor(props) {
|
|
||||||
|
export default class InputWithCopy extends React.Component<{}, State> {
|
||||||
|
copyFeatureSupported: any;
|
||||||
|
ref: any;
|
||||||
|
resetCopyState: any;
|
||||||
|
constructor(props: {}) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { copied: null };
|
this.state = { copied: null };
|
||||||
this.ref = React.createRef();
|
this.ref = React.createRef();
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Button from "antd/lib/button";
|
import Button from "antd/lib/button";
|
||||||
|
|
||||||
function DefaultLinkComponent(props) {
|
function DefaultLinkComponent(props: any) {
|
||||||
return <a {...props} />; // eslint-disable-line jsx-a11y/anchor-has-content
|
return <a {...props} />; // eslint-disable-line jsx-a11y/anchor-has-content
|
||||||
}
|
}
|
||||||
|
|
||||||
function Link(props) {
|
function Link(props: any) {
|
||||||
return <Link.Component {...props} />;
|
return <Link.Component {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
Link.Component = DefaultLinkComponent;
|
Link.Component = DefaultLinkComponent;
|
||||||
|
|
||||||
function DefaultButtonLinkComponent(props) {
|
function DefaultButtonLinkComponent(props: any) {
|
||||||
return <Button role="button" {...props} />;
|
return <Button role="button" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ButtonLink(props) {
|
function ButtonLink(props: any) {
|
||||||
return <ButtonLink.Component {...props} />;
|
return <ButtonLink.Component {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import BigMessage from "@/components/BigMessage";
|
|
||||||
import { TagsControl } from "@/components/tags-control/TagsControl";
|
|
||||||
|
|
||||||
export default function NoTaggedObjectsFound({ objectType, tags }) {
|
|
||||||
return (
|
|
||||||
<BigMessage icon="fa-tags">
|
|
||||||
No {objectType} found tagged with
|
|
||||||
<TagsControl className="inline-tags-control" tags={Array.from(tags)} tagSeparator={"+"} />.
|
|
||||||
</BigMessage>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
NoTaggedObjectsFound.propTypes = {
|
|
||||||
objectType: PropTypes.string.isRequired,
|
|
||||||
tags: PropTypes.oneOfType([PropTypes.array, PropTypes.objectOf(Set)]).isRequired,
|
|
||||||
};
|
|
||||||
22
client/app/components/NoTaggedObjectsFound.tsx
Normal file
22
client/app/components/NoTaggedObjectsFound.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import React from "react";
|
||||||
|
import BigMessage from "@/components/BigMessage";
|
||||||
|
import { TagsControl } from "@/components/tags-control/TagsControl";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
objectType: string;
|
||||||
|
tags: any[] | {
|
||||||
|
// @ts-expect-error ts-migrate(2314) FIXME: Generic type 'Set<T>' requires 1 type argument(s).
|
||||||
|
[key: string]: Set;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function NoTaggedObjectsFound({ objectType, tags }: Props) {
|
||||||
|
return (
|
||||||
|
// @ts-expect-error ts-migrate(2746) FIXME: This JSX tag's 'children' prop expects a single ch... Remove this comment to see the full error message
|
||||||
|
<BigMessage icon="fa-tags">
|
||||||
|
No {objectType} found tagged with
|
||||||
|
{/* @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call. */}
|
||||||
|
<TagsControl className="inline-tags-control" tags={Array.from(tags)} tagSeparator={"+"} />.
|
||||||
|
</BigMessage>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
||||||
export default function PageHeader({ title, actions }) {
|
type OwnProps = {
|
||||||
|
title?: string;
|
||||||
|
actions?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof PageHeader.defaultProps;
|
||||||
|
|
||||||
|
export default function PageHeader({ title, actions }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="page-header-wrapper">
|
<div className="page-header-wrapper">
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
@@ -12,11 +18,6 @@ export default function PageHeader({ title, actions }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
PageHeader.propTypes = {
|
|
||||||
title: PropTypes.string,
|
|
||||||
actions: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
PageHeader.defaultProps = {
|
PageHeader.defaultProps = {
|
||||||
title: "",
|
title: "",
|
||||||
actions: null,
|
actions: null,
|
||||||
@@ -1,10 +1,20 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Pagination from "antd/lib/pagination";
|
import Pagination from "antd/lib/pagination";
|
||||||
|
|
||||||
const MIN_ITEMS_PER_PAGE = 5;
|
const MIN_ITEMS_PER_PAGE = 5;
|
||||||
|
|
||||||
export default function Paginator({ page, showPageSizeSelect, pageSize, onPageSizeChange, totalCount, onChange }) {
|
type OwnProps = {
|
||||||
|
page: number;
|
||||||
|
showPageSizeSelect?: boolean;
|
||||||
|
pageSize: number;
|
||||||
|
totalCount: number;
|
||||||
|
onPageSizeChange?: (...args: any[]) => any;
|
||||||
|
onChange?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof Paginator.defaultProps;
|
||||||
|
|
||||||
|
export default function Paginator({ page, showPageSizeSelect, pageSize, onPageSizeChange, totalCount, onChange }: Props) {
|
||||||
if (totalCount <= (showPageSizeSelect ? MIN_ITEMS_PER_PAGE : pageSize)) {
|
if (totalCount <= (showPageSizeSelect ? MIN_ITEMS_PER_PAGE : pageSize)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -23,15 +33,6 @@ export default function Paginator({ page, showPageSizeSelect, pageSize, onPageSi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Paginator.propTypes = {
|
|
||||||
page: PropTypes.number.isRequired,
|
|
||||||
showPageSizeSelect: PropTypes.bool,
|
|
||||||
pageSize: PropTypes.number.isRequired,
|
|
||||||
totalCount: PropTypes.number.isRequired,
|
|
||||||
onPageSizeChange: PropTypes.func,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
Paginator.defaultProps = {
|
Paginator.defaultProps = {
|
||||||
showPageSizeSelect: false,
|
showPageSizeSelect: false,
|
||||||
onChange: () => {},
|
onChange: () => {},
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Button from "antd/lib/button";
|
import Button from "antd/lib/button";
|
||||||
import Badge from "antd/lib/badge";
|
import Badge from "antd/lib/badge";
|
||||||
import Tooltip from "antd/lib/tooltip";
|
import Tooltip from "antd/lib/tooltip";
|
||||||
import KeyboardShortcuts from "@/services/KeyboardShortcuts";
|
import KeyboardShortcuts from "@/services/KeyboardShortcuts";
|
||||||
|
|
||||||
function ParameterApplyButton({ paramCount, onClick }) {
|
type Props = {
|
||||||
|
onClick: (...args: any[]) => any;
|
||||||
|
paramCount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
function ParameterApplyButton({ paramCount, onClick }: Props) {
|
||||||
// show spinner when count is empty so the fade out is consistent
|
// show spinner when count is empty so the fade out is consistent
|
||||||
const icon = !paramCount ? "spinner fa-pulse" : "check";
|
const icon = !paramCount ? "spinner fa-pulse" : "check";
|
||||||
|
|
||||||
@@ -24,9 +28,4 @@ function ParameterApplyButton({ paramCount, onClick }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ParameterApplyButton.propTypes = {
|
|
||||||
onClick: PropTypes.func.isRequired,
|
|
||||||
paramCount: PropTypes.number.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ParameterApplyButton;
|
export default ParameterApplyButton;
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import { isString, extend, each, has, map, includes, findIndex, find, fromPairs, clone, isEmpty } from "lodash";
|
import { isString, extend, each, has, map, includes, findIndex, find, fromPairs, clone, isEmpty } from "lodash";
|
||||||
import React, { Fragment } from "react";
|
import React, { Fragment } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import Select from "antd/lib/select";
|
import Select from "antd/lib/select";
|
||||||
import Table from "antd/lib/table";
|
import Table from "antd/lib/table";
|
||||||
@@ -32,7 +31,7 @@ export const MappingType = {
|
|||||||
StaticValue: "static-value",
|
StaticValue: "static-value",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function parameterMappingsToEditableMappings(mappings, parameters, existingParameterNames = []) {
|
export function parameterMappingsToEditableMappings(mappings: any, parameters: any, existingParameterNames = []) {
|
||||||
return map(mappings, mapping => {
|
return map(mappings, mapping => {
|
||||||
const result = extend({}, mapping);
|
const result = extend({}, mapping);
|
||||||
const alreadyExists = includes(existingParameterNames, mapping.mapTo);
|
const alreadyExists = includes(existingParameterNames, mapping.mapTo);
|
||||||
@@ -57,7 +56,7 @@ export function parameterMappingsToEditableMappings(mappings, parameters, existi
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function editableMappingsToParameterMappings(mappings) {
|
export function editableMappingsToParameterMappings(mappings: any) {
|
||||||
return fromPairs(
|
return fromPairs(
|
||||||
map(
|
map(
|
||||||
// convert to map
|
// convert to map
|
||||||
@@ -92,8 +91,8 @@ export function editableMappingsToParameterMappings(mappings) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function synchronizeWidgetTitles(sourceMappings, widgets) {
|
export function synchronizeWidgetTitles(sourceMappings: any, widgets: any) {
|
||||||
const affectedWidgets = [];
|
const affectedWidgets: any = [];
|
||||||
|
|
||||||
each(sourceMappings, sourceMapping => {
|
each(sourceMappings, sourceMapping => {
|
||||||
if (sourceMapping.type === ParameterMappingType.DashboardLevel) {
|
if (sourceMapping.type === ParameterMappingType.DashboardLevel) {
|
||||||
@@ -119,13 +118,16 @@ export function synchronizeWidgetTitles(sourceMappings, widgets) {
|
|||||||
return affectedWidgets;
|
return affectedWidgets;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ParameterMappingInput extends React.Component {
|
type OwnParameterMappingInputProps = {
|
||||||
static propTypes = {
|
mapping?: any;
|
||||||
mapping: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
existingParamNames?: string[];
|
||||||
existingParamNames: PropTypes.arrayOf(PropTypes.string),
|
onChange?: (...args: any[]) => any;
|
||||||
onChange: PropTypes.func,
|
inputError?: string;
|
||||||
inputError: PropTypes.string,
|
};
|
||||||
};
|
|
||||||
|
type ParameterMappingInputProps = OwnParameterMappingInputProps & typeof ParameterMappingInput.defaultProps;
|
||||||
|
|
||||||
|
export class ParameterMappingInput extends React.Component<ParameterMappingInputProps> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
mapping: {},
|
mapping: {},
|
||||||
@@ -140,7 +142,7 @@ export class ParameterMappingInput extends React.Component {
|
|||||||
className: "form-item",
|
className: "form-item",
|
||||||
};
|
};
|
||||||
|
|
||||||
updateSourceType = type => {
|
updateSourceType = (type: any) => {
|
||||||
let {
|
let {
|
||||||
mapping: { mapTo },
|
mapping: { mapTo },
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@@ -155,26 +157,34 @@ export class ParameterMappingInput extends React.Component {
|
|||||||
this.updateParamMapping({ type, mapTo });
|
this.updateParamMapping({ type, mapTo });
|
||||||
};
|
};
|
||||||
|
|
||||||
updateParamMapping = update => {
|
updateParamMapping = (update: any) => {
|
||||||
const { onChange, mapping } = this.props;
|
const { onChange, mapping } = this.props;
|
||||||
const newMapping = extend({}, mapping, update);
|
const newMapping = extend({}, mapping, update);
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type 'never'.
|
||||||
if (newMapping.value !== mapping.value) {
|
if (newMapping.value !== mapping.value) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'param' does not exist on type 'never'.
|
||||||
newMapping.param = cloneParameter(newMapping.param);
|
newMapping.param = cloneParameter(newMapping.param);
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'param' does not exist on type 'never'.
|
||||||
newMapping.param.setValue(newMapping.value);
|
newMapping.param.setValue(newMapping.value);
|
||||||
}
|
}
|
||||||
if (has(update, "type")) {
|
if (has(update, "type")) {
|
||||||
if (update.type === MappingType.StaticValue) {
|
if (update.type === MappingType.StaticValue) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type 'never'.
|
||||||
newMapping.value = newMapping.param.value;
|
newMapping.value = newMapping.param.value;
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type 'never'.
|
||||||
newMapping.value = null;
|
newMapping.value = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
|
||||||
onChange(newMapping);
|
onChange(newMapping);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderMappingTypeSelector() {
|
renderMappingTypeSelector() {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'existingParamNames' does not exist on ty... Remove this comment to see the full error message
|
||||||
const noExisting = isEmpty(this.props.existingParamNames);
|
const noExisting = isEmpty(this.props.existingParamNames);
|
||||||
return (
|
return (
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'mapping' does not exist on type 'never'.
|
||||||
<Radio.Group value={this.props.mapping.type} onChange={e => this.updateSourceType(e.target.value)}>
|
<Radio.Group value={this.props.mapping.type} onChange={e => this.updateSourceType(e.target.value)}>
|
||||||
<Radio className="radio" value={MappingType.DashboardAddNew} data-test="NewDashboardParameterOption">
|
<Radio className="radio" value={MappingType.DashboardAddNew} data-test="NewDashboardParameterOption">
|
||||||
New dashboard parameter
|
New dashboard parameter
|
||||||
@@ -208,6 +218,7 @@ export class ParameterMappingInput extends React.Component {
|
|||||||
const { mapping, existingParamNames } = this.props;
|
const { mapping, existingParamNames } = this.props;
|
||||||
const options = map(existingParamNames, paramName => ({ label: paramName, value: paramName }));
|
const options = map(existingParamNames, paramName => ({ label: paramName, value: paramName }));
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'mapTo' does not exist on type 'never'.
|
||||||
return <Select value={mapping.mapTo} onChange={mapTo => this.updateParamMapping({ mapTo })} options={options} />;
|
return <Select value={mapping.mapTo} onChange={mapTo => this.updateParamMapping({ mapTo })} options={options} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,18 +226,25 @@ export class ParameterMappingInput extends React.Component {
|
|||||||
const { mapping } = this.props;
|
const { mapping } = this.props;
|
||||||
return (
|
return (
|
||||||
<ParameterValueInput
|
<ParameterValueInput
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
type={mapping.param.type}
|
type={mapping.param.type}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
value={mapping.param.normalizedValue}
|
value={mapping.param.normalizedValue}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
enumOptions={mapping.param.enumOptions}
|
enumOptions={mapping.param.enumOptions}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
queryId={mapping.param.queryId}
|
queryId={mapping.param.queryId}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
parameter={mapping.param}
|
parameter={mapping.param}
|
||||||
onSelect={value => this.updateParamMapping({ value })}
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(value: any) => void' is not assignable to t... Remove this comment to see the full error message
|
||||||
|
onSelect={(value: any) => this.updateParamMapping({ value })}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInputBlock() {
|
renderInputBlock() {
|
||||||
const { mapping } = this.props;
|
const { mapping } = this.props;
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'type' does not exist on type 'never'.
|
||||||
switch (mapping.type) {
|
switch (mapping.type) {
|
||||||
case MappingType.DashboardAddNew:
|
case MappingType.DashboardAddNew:
|
||||||
return ["Key", "Enter a new parameter keyword", this.renderDashboardAddNew()];
|
return ["Key", "Enter a new parameter keyword", this.renderDashboardAddNew()];
|
||||||
@@ -262,14 +280,17 @@ export class ParameterMappingInput extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MappingEditor extends React.Component {
|
type MappingEditorProps = {
|
||||||
static propTypes = {
|
mapping: any;
|
||||||
mapping: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
existingParamNames: string[];
|
||||||
existingParamNames: PropTypes.arrayOf(PropTypes.string).isRequired,
|
onChange: (...args: any[]) => any;
|
||||||
onChange: PropTypes.func.isRequired,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
type MappingEditorState = any;
|
||||||
|
|
||||||
|
class MappingEditor extends React.Component<MappingEditorProps, MappingEditorState> {
|
||||||
|
|
||||||
|
constructor(props: MappingEditorProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -278,12 +299,12 @@ class MappingEditor extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onVisibleChange = visible => {
|
onVisibleChange = (visible: any) => {
|
||||||
if (visible) this.show();
|
if (visible) this.show();
|
||||||
else this.hide();
|
else this.hide();
|
||||||
};
|
};
|
||||||
|
|
||||||
onChange = mapping => {
|
onChange = (mapping: any) => {
|
||||||
let inputError = null;
|
let inputError = null;
|
||||||
|
|
||||||
if (mapping.type === MappingType.DashboardAddNew) {
|
if (mapping.type === MappingType.DashboardAddNew) {
|
||||||
@@ -319,8 +340,10 @@ class MappingEditor extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="parameter-mapping-editor" data-test="EditParamMappingPopover">
|
<div className="parameter-mapping-editor" data-test="EditParamMappingPopover">
|
||||||
<header>
|
<header>
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'. */}
|
||||||
Edit Source and Value <HelpTrigger type="VALUE_SOURCE_OPTIONS" />
|
Edit Source and Value <HelpTrigger type="VALUE_SOURCE_OPTIONS" />
|
||||||
</header>
|
</header>
|
||||||
|
{/* @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call. */}
|
||||||
<ParameterMappingInput
|
<ParameterMappingInput
|
||||||
mapping={mapping}
|
mapping={mapping}
|
||||||
existingParamNames={this.props.existingParamNames}
|
existingParamNames={this.props.existingParamNames}
|
||||||
@@ -354,12 +377,17 @@ class MappingEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TitleEditor extends React.Component {
|
type OwnTitleEditorProps = {
|
||||||
static propTypes = {
|
existingParams?: any[];
|
||||||
existingParams: PropTypes.arrayOf(PropTypes.object),
|
mapping: any;
|
||||||
mapping: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
onChange: (...args: any[]) => any;
|
||||||
onChange: PropTypes.func.isRequired,
|
};
|
||||||
};
|
|
||||||
|
type TitleEditorState = any;
|
||||||
|
|
||||||
|
type TitleEditorProps = OwnTitleEditorProps & typeof TitleEditor.defaultProps;
|
||||||
|
|
||||||
|
class TitleEditor extends React.Component<TitleEditorProps, TitleEditorState> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
existingParams: [],
|
existingParams: [],
|
||||||
@@ -370,14 +398,14 @@ class TitleEditor extends React.Component {
|
|||||||
title: "", // will be set on editing
|
title: "", // will be set on editing
|
||||||
};
|
};
|
||||||
|
|
||||||
onPopupVisibleChange = showPopup => {
|
onPopupVisibleChange = (showPopup: any) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showPopup,
|
showPopup,
|
||||||
title: showPopup ? this.getMappingTitle() : "",
|
title: showPopup ? this.getMappingTitle() : "",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onEditingTitleChange = event => {
|
onEditingTitleChange = (event: any) => {
|
||||||
this.setState({ title: event.target.value });
|
this.setState({ title: event.target.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -472,12 +500,15 @@ class TitleEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ParameterMappingListInput extends React.Component {
|
type OwnParameterMappingListInputProps = {
|
||||||
static propTypes = {
|
mappings?: any[];
|
||||||
mappings: PropTypes.arrayOf(PropTypes.object),
|
existingParams?: any[];
|
||||||
existingParams: PropTypes.arrayOf(PropTypes.object),
|
onChange?: (...args: any[]) => any;
|
||||||
onChange: PropTypes.func,
|
};
|
||||||
};
|
|
||||||
|
type ParameterMappingListInputProps = OwnParameterMappingListInputProps & typeof ParameterMappingListInput.defaultProps;
|
||||||
|
|
||||||
|
export class ParameterMappingListInput extends React.Component<ParameterMappingListInputProps> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
mappings: [],
|
mappings: [],
|
||||||
@@ -485,7 +516,8 @@ export class ParameterMappingListInput extends React.Component {
|
|||||||
onChange: () => {},
|
onChange: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
static getStringValue(value) {
|
// @ts-expect-error ts-migrate(7023) FIXME: 'getStringValue' implicitly has return type 'any' ... Remove this comment to see the full error message
|
||||||
|
static getStringValue(value: any) {
|
||||||
// null
|
// null
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return "";
|
return "";
|
||||||
@@ -505,7 +537,7 @@ export class ParameterMappingListInput extends React.Component {
|
|||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDefaultValue(mapping, existingParams) {
|
static getDefaultValue(mapping: any, existingParams: any) {
|
||||||
const { type, mapTo, name } = mapping;
|
const { type, mapTo, name } = mapping;
|
||||||
let { param } = mapping;
|
let { param } = mapping;
|
||||||
|
|
||||||
@@ -532,7 +564,10 @@ export class ParameterMappingListInput extends React.Component {
|
|||||||
return this.getStringValue(value);
|
return this.getStringValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSourceTypeLabel({ type, mapTo }) {
|
static getSourceTypeLabel({
|
||||||
|
type,
|
||||||
|
mapTo
|
||||||
|
}: any) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MappingType.DashboardAddNew:
|
case MappingType.DashboardAddNew:
|
||||||
case MappingType.DashboardMapToExisting:
|
case MappingType.DashboardMapToExisting:
|
||||||
@@ -550,13 +585,15 @@ export class ParameterMappingListInput extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParamMapping(oldMapping, newMapping) {
|
updateParamMapping(oldMapping: any, newMapping: any) {
|
||||||
const mappings = [...this.props.mappings];
|
const mappings = [...this.props.mappings];
|
||||||
const index = findIndex(mappings, oldMapping);
|
const index = findIndex(mappings, oldMapping);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
// This should be the only possible case, but need to handle `else` too
|
// This should be the only possible case, but need to handle `else` too
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
mappings[index] = newMapping;
|
mappings[index] = newMapping;
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'any' is not assignable to parame... Remove this comment to see the full error message
|
||||||
mappings.push(newMapping);
|
mappings.push(newMapping);
|
||||||
}
|
}
|
||||||
this.props.onChange(mappings);
|
this.props.onChange(mappings);
|
||||||
@@ -592,6 +629,7 @@ export class ParameterMappingListInput extends React.Component {
|
|||||||
title="Default Value"
|
title="Default Value"
|
||||||
dataIndex="mapping"
|
dataIndex="mapping"
|
||||||
key="value"
|
key="value"
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'getDefaultValue' does not exist on type ... Remove this comment to see the full error message
|
||||||
render={mapping => this.constructor.getDefaultValue(mapping, this.props.existingParams)}
|
render={mapping => this.constructor.getDefaultValue(mapping, this.props.existingParams)}
|
||||||
/>
|
/>
|
||||||
<Table.Column
|
<Table.Column
|
||||||
@@ -605,6 +643,7 @@ export class ParameterMappingListInput extends React.Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'getSourceTypeLabel' does not exist on ty... Remove this comment to see the full error message */}
|
||||||
{this.constructor.getSourceTypeLabel(mapping)}{" "}
|
{this.constructor.getSourceTypeLabel(mapping)}{" "}
|
||||||
<MappingEditor
|
<MappingEditor
|
||||||
mapping={mapping}
|
mapping={mapping}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { isEqual, isEmpty, map } from "lodash";
|
import { isEqual, isEmpty, map } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import SelectWithVirtualScroll from "@/components/SelectWithVirtualScroll";
|
import SelectWithVirtualScroll from "@/components/SelectWithVirtualScroll";
|
||||||
import Input from "antd/lib/input";
|
import Input from "antd/lib/input";
|
||||||
import InputNumber from "antd/lib/input-number";
|
import InputNumber from "antd/lib/input-number";
|
||||||
@@ -13,19 +12,24 @@ import "./ParameterValueInput.less";
|
|||||||
const multipleValuesProps = {
|
const multipleValuesProps = {
|
||||||
maxTagCount: 3,
|
maxTagCount: 3,
|
||||||
maxTagTextLength: 10,
|
maxTagTextLength: 10,
|
||||||
maxTagPlaceholder: num => `+${num.length} more`,
|
maxTagPlaceholder: (num: any) => `+${num.length} more`,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ParameterValueInput extends React.Component {
|
type OwnProps = {
|
||||||
static propTypes = {
|
type?: string;
|
||||||
type: PropTypes.string,
|
value?: any;
|
||||||
value: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
enumOptions?: string;
|
||||||
enumOptions: PropTypes.string,
|
queryId?: number;
|
||||||
queryId: PropTypes.number,
|
parameter?: any;
|
||||||
parameter: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
onSelect?: (...args: any[]) => any;
|
||||||
onSelect: PropTypes.func,
|
className?: string;
|
||||||
className: PropTypes.string,
|
};
|
||||||
};
|
|
||||||
|
type State = any;
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof ParameterValueInput.defaultProps;
|
||||||
|
|
||||||
|
class ParameterValueInput extends React.Component<Props, State> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -37,28 +41,34 @@ class ParameterValueInput extends React.Component {
|
|||||||
className: "",
|
className: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'parameter' does not exist on type 'never... Remove this comment to see the full error message
|
||||||
value: props.parameter.hasPendingValue ? props.parameter.pendingValue : props.value,
|
value: props.parameter.hasPendingValue ? props.parameter.pendingValue : props.value,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'parameter' does not exist on type 'never... Remove this comment to see the full error message
|
||||||
isDirty: props.parameter.hasPendingValue,
|
isDirty: props.parameter.hasPendingValue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate = prevProps => {
|
componentDidUpdate = (prevProps: any) => {
|
||||||
const { value, parameter } = this.props;
|
const { value, parameter } = this.props;
|
||||||
// if value prop updated, reset dirty state
|
// if value prop updated, reset dirty state
|
||||||
if (prevProps.value !== value || prevProps.parameter !== parameter) {
|
if (prevProps.value !== value || prevProps.parameter !== parameter) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'hasPendingValue' does not exist on type ... Remove this comment to see the full error message
|
||||||
value: parameter.hasPendingValue ? parameter.pendingValue : value,
|
value: parameter.hasPendingValue ? parameter.pendingValue : value,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'hasPendingValue' does not exist on type ... Remove this comment to see the full error message
|
||||||
isDirty: parameter.hasPendingValue,
|
isDirty: parameter.hasPendingValue,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onSelect = value => {
|
onSelect = (value: any) => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type 'never'.
|
||||||
const isDirty = !isEqual(value, this.props.value);
|
const isDirty = !isEqual(value, this.props.value);
|
||||||
this.setState({ value, isDirty });
|
this.setState({ value, isDirty });
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onSelect' does not exist on type 'never'... Remove this comment to see the full error message
|
||||||
this.props.onSelect(value, isDirty);
|
this.props.onSelect(value, isDirty);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,9 +78,11 @@ class ParameterValueInput extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<DateParameter
|
<DateParameter
|
||||||
type={type}
|
type={type}
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'className' does not exist on type 'never... Remove this comment to see the full error message
|
||||||
className={this.props.className}
|
className={this.props.className}
|
||||||
value={value}
|
value={value}
|
||||||
parameter={parameter}
|
parameter={parameter}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(value: any) => void' is not assignable to t... Remove this comment to see the full error message
|
||||||
onSelect={this.onSelect}
|
onSelect={this.onSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -82,9 +94,11 @@ class ParameterValueInput extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<DateRangeParameter
|
<DateRangeParameter
|
||||||
type={type}
|
type={type}
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'className' does not exist on type 'never... Remove this comment to see the full error message
|
||||||
className={this.props.className}
|
className={this.props.className}
|
||||||
value={value}
|
value={value}
|
||||||
parameter={parameter}
|
parameter={parameter}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(value: any) => void' is not assignable to t... Remove this comment to see the full error message
|
||||||
onSelect={this.onSelect}
|
onSelect={this.onSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -93,13 +107,17 @@ class ParameterValueInput extends React.Component {
|
|||||||
renderEnumInput() {
|
renderEnumInput() {
|
||||||
const { enumOptions, parameter } = this.props;
|
const { enumOptions, parameter } = this.props;
|
||||||
const { value } = this.state;
|
const { value } = this.state;
|
||||||
const enumOptionsArray = enumOptions.split("\n").filter(v => v !== "");
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'split' does not exist on type 'never'.
|
||||||
|
const enumOptionsArray = enumOptions.split("\n").filter((v: any) => v !== "");
|
||||||
// Antd Select doesn't handle null in multiple mode
|
// Antd Select doesn't handle null in multiple mode
|
||||||
const normalize = val => (parameter.multiValuesOptions && val === null ? [] : val);
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'multiValuesOptions' does not exist on ty... Remove this comment to see the full error message
|
||||||
|
const normalize = (val: any) => parameter.multiValuesOptions && val === null ? [] : val;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectWithVirtualScroll
|
<SelectWithVirtualScroll
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'className' does not exist on type 'never... Remove this comment to see the full error message
|
||||||
className={this.props.className}
|
className={this.props.className}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '"multiple" | "default"' is not assignable to... Remove this comment to see the full error message
|
||||||
mode={parameter.multiValuesOptions ? "multiple" : "default"}
|
mode={parameter.multiValuesOptions ? "multiple" : "default"}
|
||||||
optionFilterProp="children"
|
optionFilterProp="children"
|
||||||
value={normalize(value)}
|
value={normalize(value)}
|
||||||
@@ -118,13 +136,19 @@ class ParameterValueInput extends React.Component {
|
|||||||
const { value } = this.state;
|
const { value } = this.state;
|
||||||
return (
|
return (
|
||||||
<QueryBasedParameterInput
|
<QueryBasedParameterInput
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
className={this.props.className}
|
className={this.props.className}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
mode={parameter.multiValuesOptions ? "multiple" : "default"}
|
mode={parameter.multiValuesOptions ? "multiple" : "default"}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
optionFilterProp="children"
|
optionFilterProp="children"
|
||||||
parameter={parameter}
|
parameter={parameter}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
value={value}
|
value={value}
|
||||||
queryId={queryId}
|
queryId={queryId}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(value: any) => void' is not assignable to t... Remove this comment to see the full error message
|
||||||
onSelect={this.onSelect}
|
onSelect={this.onSelect}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'number' is not assignable to type 'never'.
|
||||||
style={{ minWidth: 60 }}
|
style={{ minWidth: 60 }}
|
||||||
{...multipleValuesProps}
|
{...multipleValuesProps}
|
||||||
/>
|
/>
|
||||||
@@ -135,7 +159,7 @@ class ParameterValueInput extends React.Component {
|
|||||||
const { className } = this.props;
|
const { className } = this.props;
|
||||||
const { value } = this.state;
|
const { value } = this.state;
|
||||||
|
|
||||||
const normalize = val => (isNaN(val) ? undefined : val);
|
const normalize = (val: any) => isNaN(val) ? undefined : val;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InputNumber className={className} value={normalize(value)} onChange={val => this.onSelect(normalize(val))} />
|
<InputNumber className={className} value={normalize(value)} onChange={val => this.onSelect(normalize(val))} />
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { size, filter, forEach, extend } from "lodash";
|
import { size, filter, forEach, extend } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { SortableContainer, SortableElement, DragHandle } from "@redash/viz/lib/components/sortable";
|
import { SortableContainer, SortableElement, DragHandle } from "@redash/viz/lib/components/sortable";
|
||||||
import location from "@/services/location";
|
import location from "@/services/location";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'Parameter' is declared but its value is never rea... Remove this comment to see the full error message
|
||||||
import { Parameter, createParameter } from "@/services/parameters";
|
import { Parameter, createParameter } from "@/services/parameters";
|
||||||
import ParameterApplyButton from "@/components/ParameterApplyButton";
|
import ParameterApplyButton from "@/components/ParameterApplyButton";
|
||||||
import ParameterValueInput from "@/components/ParameterValueInput";
|
import ParameterValueInput from "@/components/ParameterValueInput";
|
||||||
@@ -11,24 +11,28 @@ import { toHuman } from "@/lib/utils";
|
|||||||
|
|
||||||
import "./Parameters.less";
|
import "./Parameters.less";
|
||||||
|
|
||||||
function updateUrl(parameters) {
|
function updateUrl(parameters: any) {
|
||||||
const params = extend({}, location.search);
|
const params = extend({}, location.search);
|
||||||
parameters.forEach(param => {
|
parameters.forEach((param: any) => {
|
||||||
extend(params, param.toUrlParams());
|
extend(params, param.toUrlParams());
|
||||||
});
|
});
|
||||||
location.setSearch(params, true);
|
location.setSearch(params, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Parameters extends React.Component {
|
type OwnProps = {
|
||||||
static propTypes = {
|
parameters?: any[]; // TODO: PropTypes.instanceOf(Parameter)
|
||||||
parameters: PropTypes.arrayOf(PropTypes.instanceOf(Parameter)),
|
editable?: boolean;
|
||||||
editable: PropTypes.bool,
|
disableUrlUpdate?: boolean;
|
||||||
disableUrlUpdate: PropTypes.bool,
|
onValuesChange?: (...args: any[]) => any;
|
||||||
onValuesChange: PropTypes.func,
|
onPendingValuesChange?: (...args: any[]) => any;
|
||||||
onPendingValuesChange: PropTypes.func,
|
onParametersEdit?: (...args: any[]) => any;
|
||||||
onParametersEdit: PropTypes.func,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
|
type State = any;
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof Parameters.defaultProps;
|
||||||
|
|
||||||
|
export default class Parameters extends React.Component<Props, State> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
parameters: [],
|
parameters: [],
|
||||||
editable: false,
|
editable: false,
|
||||||
@@ -38,7 +42,9 @@ export default class Parameters extends React.Component {
|
|||||||
onParametersEdit: () => {},
|
onParametersEdit: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
onBeforeSortStart: any;
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
const { parameters } = props;
|
const { parameters } = props;
|
||||||
this.state = { parameters };
|
this.state = { parameters };
|
||||||
@@ -47,7 +53,7 @@ export default class Parameters extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate = prevProps => {
|
componentDidUpdate = (prevProps: any) => {
|
||||||
const { parameters, disableUrlUpdate } = this.props;
|
const { parameters, disableUrlUpdate } = this.props;
|
||||||
const parametersChanged = prevProps.parameters !== parameters;
|
const parametersChanged = prevProps.parameters !== parameters;
|
||||||
const disableUrlUpdateChanged = prevProps.disableUrlUpdate !== disableUrlUpdate;
|
const disableUrlUpdateChanged = prevProps.disableUrlUpdate !== disableUrlUpdate;
|
||||||
@@ -59,7 +65,7 @@ export default class Parameters extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleKeyDown = e => {
|
handleKeyDown = (e: any) => {
|
||||||
// Cmd/Ctrl/Alt + Enter
|
// Cmd/Ctrl/Alt + Enter
|
||||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey || e.altKey)) {
|
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey || e.altKey)) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -67,9 +73,11 @@ export default class Parameters extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
setPendingValue = (param, value, isDirty) => {
|
setPendingValue = (param: any, value: any, isDirty: any) => {
|
||||||
const { onPendingValuesChange } = this.props;
|
const { onPendingValuesChange } = this.props;
|
||||||
this.setState(({ parameters }) => {
|
this.setState(({
|
||||||
|
parameters
|
||||||
|
}: any) => {
|
||||||
if (isDirty) {
|
if (isDirty) {
|
||||||
param.setPendingValue(value);
|
param.setPendingValue(value);
|
||||||
} else {
|
} else {
|
||||||
@@ -80,10 +88,15 @@ export default class Parameters extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
moveParameter = ({ oldIndex, newIndex }) => {
|
moveParameter = ({
|
||||||
|
oldIndex,
|
||||||
|
newIndex
|
||||||
|
}: any) => {
|
||||||
const { onParametersEdit } = this.props;
|
const { onParametersEdit } = this.props;
|
||||||
if (oldIndex !== newIndex) {
|
if (oldIndex !== newIndex) {
|
||||||
this.setState(({ parameters }) => {
|
this.setState(({
|
||||||
|
parameters
|
||||||
|
}: any) => {
|
||||||
parameters.splice(newIndex, 0, parameters.splice(oldIndex, 1)[0]);
|
parameters.splice(newIndex, 0, parameters.splice(oldIndex, 1)[0]);
|
||||||
onParametersEdit();
|
onParametersEdit();
|
||||||
return { parameters };
|
return { parameters };
|
||||||
@@ -93,8 +106,10 @@ export default class Parameters extends React.Component {
|
|||||||
|
|
||||||
applyChanges = () => {
|
applyChanges = () => {
|
||||||
const { onValuesChange, disableUrlUpdate } = this.props;
|
const { onValuesChange, disableUrlUpdate } = this.props;
|
||||||
this.setState(({ parameters }) => {
|
this.setState(({
|
||||||
const parametersWithPendingValues = parameters.filter(p => p.hasPendingValue);
|
parameters
|
||||||
|
}: any) => {
|
||||||
|
const parametersWithPendingValues = parameters.filter((p: any) => p.hasPendingValue);
|
||||||
forEach(parameters, p => p.applyPendingValue());
|
forEach(parameters, p => p.applyPendingValue());
|
||||||
if (!disableUrlUpdate) {
|
if (!disableUrlUpdate) {
|
||||||
updateUrl(parameters);
|
updateUrl(parameters);
|
||||||
@@ -104,10 +119,12 @@ export default class Parameters extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
showParameterSettings = (parameter, index) => {
|
showParameterSettings = (parameter: any, index: any) => {
|
||||||
const { onParametersEdit } = this.props;
|
const { onParametersEdit } = this.props;
|
||||||
EditParameterSettingsDialog.showModal({ parameter }).onClose(updated => {
|
EditParameterSettingsDialog.showModal({ parameter }).onClose((updated: any) => {
|
||||||
this.setState(({ parameters }) => {
|
this.setState(({
|
||||||
|
parameters
|
||||||
|
}: any) => {
|
||||||
const updatedParameter = extend(parameter, updated);
|
const updatedParameter = extend(parameter, updated);
|
||||||
parameters[index] = createParameter(updatedParameter, updatedParameter.parentQueryId);
|
parameters[index] = createParameter(updatedParameter, updatedParameter.parentQueryId);
|
||||||
onParametersEdit();
|
onParametersEdit();
|
||||||
@@ -116,7 +133,7 @@ export default class Parameters extends React.Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
renderParameter(param, index) {
|
renderParameter(param: any, index: any) {
|
||||||
const { editable } = this.props;
|
const { editable } = this.props;
|
||||||
return (
|
return (
|
||||||
<div key={param.name} className="di-block" data-test={`ParameterName-${param.name}`}>
|
<div key={param.name} className="di-block" data-test={`ParameterName-${param.name}`}>
|
||||||
@@ -133,12 +150,18 @@ export default class Parameters extends React.Component {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ParameterValueInput
|
<ParameterValueInput
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
type={param.type}
|
type={param.type}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
value={param.normalizedValue}
|
value={param.normalizedValue}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
parameter={param}
|
parameter={param}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
enumOptions={param.enumOptions}
|
enumOptions={param.enumOptions}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
|
||||||
queryId={param.queryId}
|
queryId={param.queryId}
|
||||||
onSelect={(value, isDirty) => this.setPendingValue(param, value, isDirty)}
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(value: any, isDirty: any) => void' is not a... Remove this comment to see the full error message
|
||||||
|
onSelect={(value: any, isDirty: any) => this.setPendingValue(param, value, isDirty)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -149,6 +172,7 @@ export default class Parameters extends React.Component {
|
|||||||
const { editable } = this.props;
|
const { editable } = this.props;
|
||||||
const dirtyParamCount = size(filter(parameters, "hasPendingValue"));
|
const dirtyParamCount = size(filter(parameters, "hasPendingValue"));
|
||||||
return (
|
return (
|
||||||
|
// @ts-expect-error ts-migrate(2746) FIXME: This JSX tag's 'children' prop expects a single ch... Remove this comment to see the full error message
|
||||||
<SortableContainer
|
<SortableContainer
|
||||||
disabled={!editable}
|
disabled={!editable}
|
||||||
axis="xy"
|
axis="xy"
|
||||||
@@ -161,7 +185,7 @@ export default class Parameters extends React.Component {
|
|||||||
className: "parameter-container",
|
className: "parameter-container",
|
||||||
onKeyDown: dirtyParamCount ? this.handleKeyDown : null,
|
onKeyDown: dirtyParamCount ? this.handleKeyDown : null,
|
||||||
}}>
|
}}>
|
||||||
{parameters.map((param, index) => (
|
{parameters.map((param: any, index: any) => (
|
||||||
<SortableElement key={param.name} index={index}>
|
<SortableElement key={param.name} index={index}>
|
||||||
<div className="parameter-block" data-editable={editable || null}>
|
<div className="parameter-block" data-editable={editable || null}>
|
||||||
{editable && <DragHandle data-test={`DragHandle-${param.name}`} />}
|
{editable && <DragHandle data-test={`DragHandle-${param.name}`} />}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import { axios } from "@/services/axios";
|
import { axios } from "@/services/axios";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { each, debounce, get, find } from "lodash";
|
import { each, debounce, get, find } from "lodash";
|
||||||
import Button from "antd/lib/button";
|
import Button from "antd/lib/button";
|
||||||
import List from "antd/lib/list";
|
import List from "antd/lib/list";
|
||||||
@@ -8,6 +7,7 @@ import Modal from "antd/lib/modal";
|
|||||||
import Select from "antd/lib/select";
|
import Select from "antd/lib/select";
|
||||||
import Tag from "antd/lib/tag";
|
import Tag from "antd/lib/tag";
|
||||||
import Tooltip from "antd/lib/tooltip";
|
import Tooltip from "antd/lib/tooltip";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DialogPropType' is declared but its value is neve... Remove this comment to see the full error message
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
import { toHuman } from "@/lib/utils";
|
import { toHuman } from "@/lib/utils";
|
||||||
import HelpTrigger from "@/components/HelpTrigger";
|
import HelpTrigger from "@/components/HelpTrigger";
|
||||||
@@ -20,13 +20,13 @@ import "./index.less";
|
|||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
const DEBOUNCE_SEARCH_DURATION = 200;
|
const DEBOUNCE_SEARCH_DURATION = 200;
|
||||||
|
|
||||||
function useGrantees(url) {
|
function useGrantees(url: any) {
|
||||||
const loadGrantees = useCallback(
|
const loadGrantees = useCallback(
|
||||||
() =>
|
() =>
|
||||||
axios.get(url).then(data => {
|
axios.get(url).then(data => {
|
||||||
const resultGrantees = [];
|
const resultGrantees: any = [];
|
||||||
each(data, (grantees, accessType) => {
|
each(data, (grantees, accessType) => {
|
||||||
grantees.forEach(grantee => {
|
grantees.forEach((grantee: any) => {
|
||||||
grantee.accessType = toHuman(accessType);
|
grantee.accessType = toHuman(accessType);
|
||||||
resultGrantees.push(grantee);
|
resultGrantees.push(grantee);
|
||||||
});
|
});
|
||||||
@@ -40,6 +40,7 @@ function useGrantees(url) {
|
|||||||
(userId, accessType = "modify") =>
|
(userId, accessType = "modify") =>
|
||||||
axios
|
axios
|
||||||
.post(url, { access_type: accessType, user_id: userId })
|
.post(url, { access_type: accessType, user_id: userId })
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message
|
||||||
.catch(() => notification.error("Could not grant permission to the user")),
|
.catch(() => notification.error("Could not grant permission to the user")),
|
||||||
[url]
|
[url]
|
||||||
);
|
);
|
||||||
@@ -48,6 +49,7 @@ function useGrantees(url) {
|
|||||||
(userId, accessType = "modify") =>
|
(userId, accessType = "modify") =>
|
||||||
axios
|
axios
|
||||||
.delete(url, { data: { access_type: accessType, user_id: userId } })
|
.delete(url, { data: { access_type: accessType, user_id: userId } })
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message
|
||||||
.catch(() => notification.error("Could not remove permission from the user")),
|
.catch(() => notification.error("Could not remove permission from the user")),
|
||||||
[url]
|
[url]
|
||||||
);
|
);
|
||||||
@@ -55,37 +57,48 @@ function useGrantees(url) {
|
|||||||
return { loadGrantees, addPermission, removePermission };
|
return { loadGrantees, addPermission, removePermission };
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchUsers = searchTerm =>
|
const searchUsers = (searchTerm: any) => User.query({ q: searchTerm })
|
||||||
User.query({ q: searchTerm })
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'results' does not exist on type 'AxiosRe... Remove this comment to see the full error message
|
||||||
.then(({ results }) => results)
|
.then(({ results }) => results)
|
||||||
.catch(() => []);
|
.catch(() => []);
|
||||||
|
|
||||||
function PermissionsEditorDialogHeader({ context }) {
|
type OwnPermissionsEditorDialogHeaderProps = {
|
||||||
|
context?: "query" | "dashboard";
|
||||||
|
};
|
||||||
|
|
||||||
|
type PermissionsEditorDialogHeaderProps = OwnPermissionsEditorDialogHeaderProps & typeof PermissionsEditorDialogHeader.defaultProps;
|
||||||
|
|
||||||
|
function PermissionsEditorDialogHeader({ context }: PermissionsEditorDialogHeaderProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
Manage Permissions
|
Manage Permissions
|
||||||
<div className="modal-header-desc">
|
<div className="modal-header-desc">
|
||||||
{`Editing this ${context} is enabled for the users in this list and for admins. `}
|
{`Editing this ${context} is enabled for the users in this list and for admins. `}
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'. */}
|
||||||
<HelpTrigger type="MANAGE_PERMISSIONS" />
|
<HelpTrigger type="MANAGE_PERMISSIONS" />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
PermissionsEditorDialogHeader.propTypes = { context: PropTypes.oneOf(["query", "dashboard"]) };
|
|
||||||
PermissionsEditorDialogHeader.defaultProps = { context: "query" };
|
PermissionsEditorDialogHeader.defaultProps = { context: "query" };
|
||||||
|
|
||||||
function UserSelect({ onSelect, shouldShowUser }) {
|
type OwnUserSelectProps = {
|
||||||
|
onSelect?: (...args: any[]) => any;
|
||||||
|
shouldShowUser?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UserSelectProps = OwnUserSelectProps & typeof UserSelect.defaultProps;
|
||||||
|
|
||||||
|
function UserSelect({ onSelect, shouldShowUser }: UserSelectProps) {
|
||||||
const [loadingUsers, setLoadingUsers] = useState(true);
|
const [loadingUsers, setLoadingUsers] = useState(true);
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
|
||||||
const debouncedSearchUsers = useCallback(
|
const debouncedSearchUsers = useCallback(
|
||||||
debounce(
|
debounce(
|
||||||
search =>
|
(search: any) => searchUsers(search)
|
||||||
searchUsers(search)
|
.then(setUsers)
|
||||||
.then(setUsers)
|
.finally(() => setLoadingUsers(false)),
|
||||||
.finally(() => setLoadingUsers(false)),
|
|
||||||
DEBOUNCE_SEARCH_DURATION
|
DEBOUNCE_SEARCH_DURATION
|
||||||
),
|
),
|
||||||
[]
|
[]
|
||||||
@@ -109,6 +122,7 @@ function UserSelect({ onSelect, shouldShowUser }) {
|
|||||||
getPopupContainer={trigger => trigger.parentNode}
|
getPopupContainer={trigger => trigger.parentNode}
|
||||||
onSelect={onSelect}>
|
onSelect={onSelect}>
|
||||||
{users.filter(shouldShowUser).map(user => (
|
{users.filter(shouldShowUser).map(user => (
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'never'.
|
||||||
<Option key={user.id} value={user.id}>
|
<Option key={user.id} value={user.id}>
|
||||||
<UserPreviewCard user={user} />
|
<UserPreviewCard user={user} />
|
||||||
</Option>
|
</Option>
|
||||||
@@ -116,14 +130,19 @@ function UserSelect({ onSelect, shouldShowUser }) {
|
|||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserSelect.propTypes = {
|
|
||||||
onSelect: PropTypes.func,
|
|
||||||
shouldShowUser: PropTypes.func,
|
|
||||||
};
|
|
||||||
UserSelect.defaultProps = { onSelect: () => {}, shouldShowUser: () => true };
|
UserSelect.defaultProps = { onSelect: () => {}, shouldShowUser: () => true };
|
||||||
|
|
||||||
function PermissionsEditorDialog({ dialog, author, context, aclUrl }) {
|
type OwnPermissionsEditorDialogProps = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'DialogPropType' refers to a value, but is being u... Remove this comment to see the full error message
|
||||||
|
dialog: DialogPropType;
|
||||||
|
author: any;
|
||||||
|
context?: "query" | "dashboard";
|
||||||
|
aclUrl: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PermissionsEditorDialogProps = OwnPermissionsEditorDialogProps & typeof PermissionsEditorDialog.defaultProps;
|
||||||
|
|
||||||
|
function PermissionsEditorDialog({ dialog, author, context, aclUrl }: PermissionsEditorDialogProps) {
|
||||||
const [loadingGrantees, setLoadingGrantees] = useState(true);
|
const [loadingGrantees, setLoadingGrantees] = useState(true);
|
||||||
const [grantees, setGrantees] = useState([]);
|
const [grantees, setGrantees] = useState([]);
|
||||||
const { loadGrantees, addPermission, removePermission } = useGrantees(aclUrl);
|
const { loadGrantees, addPermission, removePermission } = useGrantees(aclUrl);
|
||||||
@@ -131,6 +150,7 @@ function PermissionsEditorDialog({ dialog, author, context, aclUrl }) {
|
|||||||
setLoadingGrantees(true);
|
setLoadingGrantees(true);
|
||||||
loadGrantees()
|
loadGrantees()
|
||||||
.then(setGrantees)
|
.then(setGrantees)
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message
|
||||||
.catch(() => notification.error("Failed to load grantees list"))
|
.catch(() => notification.error("Failed to load grantees list"))
|
||||||
.finally(() => setLoadingGrantees(false));
|
.finally(() => setLoadingGrantees(false));
|
||||||
}, [loadGrantees]);
|
}, [loadGrantees]);
|
||||||
@@ -151,8 +171,10 @@ function PermissionsEditorDialog({ dialog, author, context, aclUrl }) {
|
|||||||
title={<PermissionsEditorDialogHeader context={context} />}
|
title={<PermissionsEditorDialogHeader context={context} />}
|
||||||
footer={<Button onClick={dialog.dismiss}>Close</Button>}>
|
footer={<Button onClick={dialog.dismiss}>Close</Button>}>
|
||||||
<UserSelect
|
<UserSelect
|
||||||
onSelect={userId => addPermission(userId).then(loadUsersWithPermissions)}
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(userId: any) => Promise<void>' is not assig... Remove this comment to see the full error message
|
||||||
shouldShowUser={user => !userHasPermission(user)}
|
onSelect={(userId: any) => addPermission(userId).then(loadUsersWithPermissions)}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(user: any) => boolean' is not assignable to... Remove this comment to see the full error message
|
||||||
|
shouldShowUser={(user: any) => !userHasPermission(user)}
|
||||||
/>
|
/>
|
||||||
<div className="d-flex align-items-center m-t-5">
|
<div className="d-flex align-items-center m-t-5">
|
||||||
<h5 className="flex-fill">Users with permissions</h5>
|
<h5 className="flex-fill">Users with permissions</h5>
|
||||||
@@ -165,6 +187,7 @@ function PermissionsEditorDialog({ dialog, author, context, aclUrl }) {
|
|||||||
renderItem={user => (
|
renderItem={user => (
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<UserPreviewCard key={user.id} user={user}>
|
<UserPreviewCard key={user.id} user={user}>
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'Element' is not assignable to type 'null | u... Remove this comment to see the full error message */}
|
||||||
{user.id === author.id ? (
|
{user.id === author.id ? (
|
||||||
<Tag className="m-0">Author</Tag>
|
<Tag className="m-0">Author</Tag>
|
||||||
) : (
|
) : (
|
||||||
@@ -184,13 +207,6 @@ function PermissionsEditorDialog({ dialog, author, context, aclUrl }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
PermissionsEditorDialog.propTypes = {
|
|
||||||
dialog: DialogPropType.isRequired,
|
|
||||||
author: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
|
||||||
context: PropTypes.oneOf(["query", "dashboard"]),
|
|
||||||
aclUrl: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
PermissionsEditorDialog.defaultProps = { context: "query" };
|
PermissionsEditorDialog.defaultProps = { context: "query" };
|
||||||
|
|
||||||
export default wrapDialog(PermissionsEditorDialog);
|
export default wrapDialog(PermissionsEditorDialog);
|
||||||
@@ -1,11 +1,21 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import Link from "@/components/Link";
|
import Link from "@/components/Link";
|
||||||
|
|
||||||
|
type OwnPreviewCardProps = {
|
||||||
|
imageUrl: string;
|
||||||
|
title: React.ReactNode;
|
||||||
|
body?: React.ReactNode;
|
||||||
|
roundedImage?: boolean;
|
||||||
|
className?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PreviewCardProps = OwnPreviewCardProps & typeof PreviewCard.defaultProps;
|
||||||
|
|
||||||
// PreviewCard
|
// PreviewCard
|
||||||
|
|
||||||
export function PreviewCard({ imageUrl, roundedImage, title, body, children, className, ...props }) {
|
export function PreviewCard({ imageUrl, roundedImage, title, body, children, className, ...props }: PreviewCardProps) {
|
||||||
return (
|
return (
|
||||||
<div {...props} className={className + " w-100 d-flex align-items-center"}>
|
<div {...props} className={className + " w-100 d-flex align-items-center"}>
|
||||||
<img
|
<img
|
||||||
@@ -24,15 +34,6 @@ export function PreviewCard({ imageUrl, roundedImage, title, body, children, cla
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
PreviewCard.propTypes = {
|
|
||||||
imageUrl: PropTypes.string.isRequired,
|
|
||||||
title: PropTypes.node.isRequired,
|
|
||||||
body: PropTypes.node,
|
|
||||||
roundedImage: PropTypes.bool,
|
|
||||||
className: PropTypes.string,
|
|
||||||
children: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
PreviewCard.defaultProps = {
|
PreviewCard.defaultProps = {
|
||||||
body: null,
|
body: null,
|
||||||
roundedImage: true,
|
roundedImage: true,
|
||||||
@@ -40,36 +41,52 @@ PreviewCard.defaultProps = {
|
|||||||
children: null,
|
children: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type OwnUserPreviewCardProps = {
|
||||||
|
user: {
|
||||||
|
profile_image_url: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
};
|
||||||
|
withLink?: boolean;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UserPreviewCardProps = OwnUserPreviewCardProps & typeof UserPreviewCard.defaultProps;
|
||||||
|
|
||||||
// UserPreviewCard
|
// UserPreviewCard
|
||||||
|
|
||||||
export function UserPreviewCard({ user, withLink, children, ...props }) {
|
export function UserPreviewCard({ user, withLink, children, ...props }: UserPreviewCardProps) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type '{ profile_im... Remove this comment to see the full error message
|
||||||
const title = withLink ? <Link href={"users/" + user.id}>{user.name}</Link> : user.name;
|
const title = withLink ? <Link href={"users/" + user.id}>{user.name}</Link> : user.name;
|
||||||
return (
|
return (
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'null | un... Remove this comment to see the full error message
|
||||||
<PreviewCard {...props} imageUrl={user.profile_image_url} title={title} body={user.email}>
|
<PreviewCard {...props} imageUrl={user.profile_image_url} title={title} body={user.email}>
|
||||||
{children}
|
{children}
|
||||||
</PreviewCard>
|
</PreviewCard>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserPreviewCard.propTypes = {
|
|
||||||
user: PropTypes.shape({
|
|
||||||
profile_image_url: PropTypes.string.isRequired,
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
email: PropTypes.string.isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
withLink: PropTypes.bool,
|
|
||||||
children: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
UserPreviewCard.defaultProps = {
|
UserPreviewCard.defaultProps = {
|
||||||
withLink: false,
|
withLink: false,
|
||||||
children: null,
|
children: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type OwnDataSourcePreviewCardProps = {
|
||||||
|
dataSource: {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
withLink?: boolean;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
type DataSourcePreviewCardProps = OwnDataSourcePreviewCardProps & typeof DataSourcePreviewCard.defaultProps;
|
||||||
|
|
||||||
// DataSourcePreviewCard
|
// DataSourcePreviewCard
|
||||||
|
|
||||||
export function DataSourcePreviewCard({ dataSource, withLink, children, ...props }) {
|
export function DataSourcePreviewCard({ dataSource, withLink, children, ...props }: DataSourcePreviewCardProps) {
|
||||||
const imageUrl = `static/images/db-logos/${dataSource.type}.png`;
|
const imageUrl = `static/images/db-logos/${dataSource.type}.png`;
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type '{ name: stri... Remove this comment to see the full error message
|
||||||
const title = withLink ? <Link href={"data_sources/" + dataSource.id}>{dataSource.name}</Link> : dataSource.name;
|
const title = withLink ? <Link href={"data_sources/" + dataSource.id}>{dataSource.name}</Link> : dataSource.name;
|
||||||
return (
|
return (
|
||||||
<PreviewCard {...props} imageUrl={imageUrl} title={title}>
|
<PreviewCard {...props} imageUrl={imageUrl} title={title}>
|
||||||
@@ -78,15 +95,6 @@ export function DataSourcePreviewCard({ dataSource, withLink, children, ...props
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
DataSourcePreviewCard.propTypes = {
|
|
||||||
dataSource: PropTypes.shape({
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
type: PropTypes.string.isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
withLink: PropTypes.bool,
|
|
||||||
children: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
DataSourcePreviewCard.defaultProps = {
|
DataSourcePreviewCard.defaultProps = {
|
||||||
withLink: false,
|
withLink: false,
|
||||||
children: null,
|
children: null,
|
||||||
@@ -1,17 +1,21 @@
|
|||||||
import { find, isArray, get, first, map, intersection, isEqual, isEmpty } from "lodash";
|
import { find, isArray, get, first, map, intersection, isEqual, isEmpty } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import SelectWithVirtualScroll from "@/components/SelectWithVirtualScroll";
|
import SelectWithVirtualScroll from "@/components/SelectWithVirtualScroll";
|
||||||
|
|
||||||
export default class QueryBasedParameterInput extends React.Component {
|
type OwnProps = {
|
||||||
static propTypes = {
|
parameter?: any;
|
||||||
parameter: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
value?: any;
|
||||||
value: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
mode?: "default" | "multiple";
|
||||||
mode: PropTypes.oneOf(["default", "multiple"]),
|
queryId?: number;
|
||||||
queryId: PropTypes.number,
|
onSelect?: (...args: any[]) => any;
|
||||||
onSelect: PropTypes.func,
|
className?: string;
|
||||||
className: PropTypes.string,
|
};
|
||||||
};
|
|
||||||
|
type State = any;
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof QueryBasedParameterInput.defaultProps;
|
||||||
|
|
||||||
|
export default class QueryBasedParameterInput extends React.Component<Props, State> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
value: null,
|
value: null,
|
||||||
@@ -22,7 +26,7 @@ export default class QueryBasedParameterInput extends React.Component {
|
|||||||
className: "",
|
className: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
options: [],
|
options: [],
|
||||||
@@ -32,20 +36,26 @@ export default class QueryBasedParameterInput extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'queryId' does not exist on type 'never'.
|
||||||
this._loadOptions(this.props.queryId);
|
this._loadOptions(this.props.queryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps: Props) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'queryId' does not exist on type 'never'.
|
||||||
if (this.props.queryId !== prevProps.queryId) {
|
if (this.props.queryId !== prevProps.queryId) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'queryId' does not exist on type 'never'.
|
||||||
this._loadOptions(this.props.queryId);
|
this._loadOptions(this.props.queryId);
|
||||||
}
|
}
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type 'never'.
|
||||||
if (this.props.value !== prevProps.value) {
|
if (this.props.value !== prevProps.value) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type 'never'.
|
||||||
this.setValue(this.props.value);
|
this.setValue(this.props.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue(value) {
|
setValue(value: any) {
|
||||||
const { options } = this.state;
|
const { options } = this.state;
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'mode' does not exist on type 'never'.
|
||||||
if (this.props.mode === "multiple") {
|
if (this.props.mode === "multiple") {
|
||||||
value = isArray(value) ? value : [value];
|
value = isArray(value) ? value : [value];
|
||||||
const optionValues = map(options, option => option.value);
|
const optionValues = map(options, option => option.value);
|
||||||
@@ -53,22 +63,28 @@ export default class QueryBasedParameterInput extends React.Component {
|
|||||||
this.setState({ value: validValues });
|
this.setState({ value: validValues });
|
||||||
return validValues;
|
return validValues;
|
||||||
}
|
}
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type 'never'.
|
||||||
const found = find(options, option => option.value === this.props.value) !== undefined;
|
const found = find(options, option => option.value === this.props.value) !== undefined;
|
||||||
value = found ? value : get(first(options), "value");
|
value = found ? value : get(first(options), "value");
|
||||||
this.setState({ value });
|
this.setState({ value });
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _loadOptions(queryId) {
|
async _loadOptions(queryId: any) {
|
||||||
if (queryId && queryId !== this.state.queryId) {
|
if (queryId && queryId !== this.state.queryId) {
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'parameter' does not exist on type 'never... Remove this comment to see the full error message
|
||||||
const options = await this.props.parameter.loadDropdownValues();
|
const options = await this.props.parameter.loadDropdownValues();
|
||||||
|
|
||||||
// stale queryId check
|
// stale queryId check
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'queryId' does not exist on type 'never'.
|
||||||
if (this.props.queryId === queryId) {
|
if (this.props.queryId === queryId) {
|
||||||
this.setState({ options, loading: false }, () => {
|
this.setState({ options, loading: false }, () => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type 'never'.
|
||||||
const updatedValue = this.setValue(this.props.value);
|
const updatedValue = this.setValue(this.props.value);
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type 'never'.
|
||||||
if (!isEqual(updatedValue, this.props.value)) {
|
if (!isEqual(updatedValue, this.props.value)) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onSelect' does not exist on type 'never'... Remove this comment to see the full error message
|
||||||
this.props.onSelect(updatedValue);
|
this.props.onSelect(updatedValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -77,6 +93,7 @@ export default class QueryBasedParameterInput extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
// @ts-expect-error ts-migrate(2700) FIXME: Rest types may only be created from object types.
|
||||||
const { className, mode, onSelect, queryId, value, ...otherProps } = this.props;
|
const { className, mode, onSelect, queryId, value, ...otherProps } = this.props;
|
||||||
const { loading, options } = this.state;
|
const { loading, options } = this.state;
|
||||||
return (
|
return (
|
||||||
@@ -1,39 +1,44 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { VisualizationType } from "@redash/viz/lib";
|
import { VisualizationType } from "@redash/viz/lib";
|
||||||
import Link from "@/components/Link";
|
import Link from "@/components/Link";
|
||||||
import VisualizationName from "@/components/visualizations/VisualizationName";
|
import VisualizationName from "@/components/visualizations/VisualizationName";
|
||||||
|
|
||||||
import "./QueryLink.less";
|
import "./QueryLink.less";
|
||||||
|
|
||||||
function QueryLink({ query, visualization, readOnly }) {
|
type OwnProps = {
|
||||||
|
query: any;
|
||||||
|
visualization?: VisualizationType;
|
||||||
|
readOnly?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof QueryLink.defaultProps;
|
||||||
|
|
||||||
|
function QueryLink({ query, visualization, readOnly }: Props) {
|
||||||
const getUrl = () => {
|
const getUrl = () => {
|
||||||
let hash = null;
|
let hash = null;
|
||||||
if (visualization) {
|
if (visualization) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'type' does not exist on type 'never'.
|
||||||
if (visualization.type === "TABLE") {
|
if (visualization.type === "TABLE") {
|
||||||
// link to hard-coded table tab instead of the (hidden) visualization tab
|
// link to hard-coded table tab instead of the (hidden) visualization tab
|
||||||
hash = "table";
|
hash = "table";
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type 'never'.
|
||||||
hash = visualization.id;
|
hash = visualization.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'getUrl' does not exist on type 'never'.
|
||||||
return query.getUrl(false, hash);
|
return query.getUrl(false, hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={readOnly ? null : getUrl()} className="query-link">
|
<Link href={readOnly ? null : getUrl()} className="query-link">
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'name' does not exist on type 'never'. */}
|
||||||
<VisualizationName visualization={visualization} /> <span>{query.name}</span>
|
<VisualizationName visualization={visualization} /> <span>{query.name}</span>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryLink.propTypes = {
|
|
||||||
query: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
|
||||||
visualization: VisualizationType,
|
|
||||||
readOnly: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
QueryLink.defaultProps = {
|
QueryLink.defaultProps = {
|
||||||
visualization: null,
|
visualization: null,
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { find } from "lodash";
|
import { find } from "lodash";
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import Input from "antd/lib/input";
|
import Input from "antd/lib/input";
|
||||||
import Select from "antd/lib/select";
|
import Select from "antd/lib/select";
|
||||||
@@ -10,23 +9,38 @@ import { QueryTagsControl } from "@/components/tags-control/TagsControl";
|
|||||||
import useSearchResults from "@/lib/hooks/useSearchResults";
|
import useSearchResults from "@/lib/hooks/useSearchResults";
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
function search(term) {
|
function search(term: any) {
|
||||||
if (term === null) {
|
if (term === null) {
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get recent
|
// get recent
|
||||||
if (!term) {
|
if (!term) {
|
||||||
return Query.recent().then(results => results.filter(item => !item.is_draft)); // filter out draft
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'recent' does not exist on type 'typeof Q... Remove this comment to see the full error message
|
||||||
|
return Query.recent().then((results: any) => results.filter((item: any) => !item.is_draft)); // filter out draft
|
||||||
}
|
}
|
||||||
|
|
||||||
// search by query
|
// search by query
|
||||||
return Query.query({ q: term }).then(({ results }) => results);
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'query' does not exist on type 'typeof Qu... Remove this comment to see the full error message
|
||||||
|
return Query.query({ q: term }).then(({
|
||||||
|
results
|
||||||
|
}: any) => results);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function QuerySelector(props) {
|
type OwnProps = {
|
||||||
|
onChange: (...args: any[]) => any;
|
||||||
|
selectedQuery?: any;
|
||||||
|
type?: "select" | "default";
|
||||||
|
className?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof QuerySelector.defaultProps;
|
||||||
|
|
||||||
|
export default function QuerySelector(props: Props) {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [selectedQuery, setSelectedQuery] = useState();
|
const [selectedQuery, setSelectedQuery] = useState();
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'never[]' is not assignable to type 'null | u... Remove this comment to see the full error message
|
||||||
const [doSearch, searchResults, searching] = useSearchResults(search, { initialResults: [] });
|
const [doSearch, searchResults, searching] = useSearchResults(search, { initialResults: [] });
|
||||||
|
|
||||||
const placeholder = "Search a query by name";
|
const placeholder = "Search a query by name";
|
||||||
@@ -34,57 +48,71 @@ export default function QuerySelector(props) {
|
|||||||
const spinIcon = <i className={cx("fa fa-spinner fa-pulse hide-in-percy", { hidden: !searching })} />;
|
const spinIcon = <i className={cx("fa fa-spinner fa-pulse hide-in-percy", { hidden: !searching })} />;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
|
||||||
doSearch(searchTerm);
|
doSearch(searchTerm);
|
||||||
}, [doSearch, searchTerm]);
|
}, [doSearch, searchTerm]);
|
||||||
|
|
||||||
// set selected from prop
|
// set selected from prop
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'selectedQuery' does not exist on type 'n... Remove this comment to see the full error message
|
||||||
if (props.selectedQuery) {
|
if (props.selectedQuery) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'selectedQuery' does not exist on type 'n... Remove this comment to see the full error message
|
||||||
setSelectedQuery(props.selectedQuery);
|
setSelectedQuery(props.selectedQuery);
|
||||||
}
|
}
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'selectedQuery' does not exist on type 'n... Remove this comment to see the full error message
|
||||||
}, [props.selectedQuery]);
|
}, [props.selectedQuery]);
|
||||||
|
|
||||||
function selectQuery(queryId) {
|
function selectQuery(queryId: any) {
|
||||||
let query = null;
|
let query = null;
|
||||||
if (queryId) {
|
if (queryId) {
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
query = find(searchResults, { id: queryId });
|
query = find(searchResults, { id: queryId });
|
||||||
if (!query) {
|
if (!query) {
|
||||||
// shouldn't happen
|
// shouldn't happen
|
||||||
|
// @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 2.
|
||||||
notification.error("Something went wrong...", "Couldn't select query");
|
notification.error("Something went wrong...", "Couldn't select query");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string | null' is not assignable... Remove this comment to see the full error message
|
||||||
setSearchTerm(query ? null : ""); // empty string triggers recent fetch
|
setSearchTerm(query ? null : ""); // empty string triggers recent fetch
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'unknown' is not assignable to pa... Remove this comment to see the full error message
|
||||||
setSelectedQuery(query);
|
setSelectedQuery(query);
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onChange' does not exist on type 'never'... Remove this comment to see the full error message
|
||||||
props.onChange(query);
|
props.onChange(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderResults() {
|
function renderResults() {
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
if (!searchResults.length) {
|
if (!searchResults.length) {
|
||||||
return <div className="text-muted">No results matching search term.</div>;
|
return <div className="text-muted">No results matching search term.</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="list-group">
|
<div className="list-group">
|
||||||
{searchResults.map(q => (
|
{/* @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'. */}
|
||||||
<a
|
{searchResults.map((q: any) => <a
|
||||||
className={cx("query-selector-result", "list-group-item", { inactive: q.is_draft })}
|
className={cx("query-selector-result", "list-group-item", { inactive: q.is_draft })}
|
||||||
key={q.id}
|
key={q.id}
|
||||||
onClick={() => selectQuery(q.id)}
|
onClick={() => selectQuery(q.id)}
|
||||||
data-test={`QueryId${q.id}`}>
|
data-test={`QueryId${q.id}`}>
|
||||||
{q.name} <QueryTagsControl isDraft={q.is_draft} tags={q.tags} className="inline-tags-control" />
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ isDraft: any; tags: any; className: string... Remove this comment to see the full error message */}
|
||||||
</a>
|
{q.name} <QueryTagsControl isDraft={q.is_draft} tags={q.tags} className="inline-tags-control" />
|
||||||
))}
|
</a>)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'disabled' does not exist on type 'never'... Remove this comment to see the full error message
|
||||||
if (props.disabled) {
|
if (props.disabled) {
|
||||||
|
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||||
return <Input value={selectedQuery && selectedQuery.name} placeholder={placeholder} disabled />;
|
return <Input value={selectedQuery && selectedQuery.name} placeholder={placeholder} disabled />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'type' does not exist on type 'never'.
|
||||||
if (props.type === "select") {
|
if (props.type === "select") {
|
||||||
const suffixIcon = selectedQuery ? clearIcon : null;
|
const suffixIcon = selectedQuery ? clearIcon : null;
|
||||||
|
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||||
const value = selectedQuery ? selectedQuery.name : searchTerm;
|
const value = selectedQuery ? selectedQuery.name : searchTerm;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -99,10 +127,12 @@ export default function QuerySelector(props) {
|
|||||||
notFoundContent={null}
|
notFoundContent={null}
|
||||||
filterOption={false}
|
filterOption={false}
|
||||||
defaultActiveFirstOption={false}
|
defaultActiveFirstOption={false}
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'className' does not exist on type 'never... Remove this comment to see the full error message
|
||||||
className={props.className}
|
className={props.className}
|
||||||
data-test="QuerySelector">
|
data-test="QuerySelector">
|
||||||
{searchResults &&
|
{searchResults &&
|
||||||
searchResults.map(q => {
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'map' does not exist on type 'true | ((se... Remove this comment to see the full error message
|
||||||
|
searchResults.map((q: any) => {
|
||||||
const disabled = q.is_draft;
|
const disabled = q.is_draft;
|
||||||
return (
|
return (
|
||||||
<Option
|
<Option
|
||||||
@@ -114,6 +144,7 @@ export default function QuerySelector(props) {
|
|||||||
{q.name}{" "}
|
{q.name}{" "}
|
||||||
<QueryTagsControl
|
<QueryTagsControl
|
||||||
isDraft={q.is_draft}
|
isDraft={q.is_draft}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ isDraft: any; tags: any; className: string... Remove this comment to see the full error message
|
||||||
tags={q.tags}
|
tags={q.tags}
|
||||||
className={cx("inline-tags-control", { disabled })}
|
className={cx("inline-tags-control", { disabled })}
|
||||||
/>
|
/>
|
||||||
@@ -127,6 +158,7 @@ export default function QuerySelector(props) {
|
|||||||
return (
|
return (
|
||||||
<span data-test="QuerySelector">
|
<span data-test="QuerySelector">
|
||||||
{selectedQuery ? (
|
{selectedQuery ? (
|
||||||
|
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||||
<Input value={selectedQuery.name} suffix={clearIcon} readOnly />
|
<Input value={selectedQuery.name} suffix={clearIcon} readOnly />
|
||||||
) : (
|
) : (
|
||||||
<Input
|
<Input
|
||||||
@@ -143,14 +175,6 @@ export default function QuerySelector(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
QuerySelector.propTypes = {
|
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
selectedQuery: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
|
||||||
type: PropTypes.oneOf(["select", "default"]),
|
|
||||||
className: PropTypes.string,
|
|
||||||
disabled: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
QuerySelector.defaultProps = {
|
QuerySelector.defaultProps = {
|
||||||
selectedQuery: null,
|
selectedQuery: null,
|
||||||
type: "default",
|
type: "default",
|
||||||
@@ -1,12 +1,20 @@
|
|||||||
import d3 from "d3";
|
import d3 from "d3";
|
||||||
import React, { useRef, useMemo, useCallback, useState, useEffect } from "react";
|
import React, { useRef, useMemo, useCallback, useState, useEffect } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { Resizable as ReactResizable } from "react-resizable";
|
import { Resizable as ReactResizable } from "react-resizable";
|
||||||
import KeyboardShortcuts from "@/services/KeyboardShortcuts";
|
import KeyboardShortcuts from "@/services/KeyboardShortcuts";
|
||||||
|
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
||||||
export default function Resizable({ toggleShortcut, direction, sizeAttribute, children }) {
|
type OwnProps = {
|
||||||
|
direction?: "horizontal" | "vertical";
|
||||||
|
sizeAttribute?: string;
|
||||||
|
toggleShortcut?: string;
|
||||||
|
children?: React.ReactElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof Resizable.defaultProps;
|
||||||
|
|
||||||
|
export default function Resizable({ toggleShortcut, direction, sizeAttribute, children }: Props) {
|
||||||
const [size, setSize] = useState(0);
|
const [size, setSize] = useState(0);
|
||||||
const elementRef = useRef();
|
const elementRef = useRef();
|
||||||
const wasUsingTouchEventsRef = useRef(false);
|
const wasUsingTouchEventsRef = useRef(false);
|
||||||
@@ -19,6 +27,7 @@ export default function Resizable({ toggleShortcut, direction, sizeAttribute, ch
|
|||||||
if (!elementRef.current) {
|
if (!elementRef.current) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// @ts-expect-error ts-migrate(2532) FIXME: Object is possibly 'undefined'.
|
||||||
return Math.floor(elementRef.current.getBoundingClientRect()[sizeProp]);
|
return Math.floor(elementRef.current.getBoundingClientRect()[sizeProp]);
|
||||||
}, [sizeProp]);
|
}, [sizeProp]);
|
||||||
|
|
||||||
@@ -28,10 +37,12 @@ export default function Resizable({ toggleShortcut, direction, sizeAttribute, ch
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
const element = d3.select(elementRef.current);
|
const element = d3.select(elementRef.current);
|
||||||
let targetSize;
|
let targetSize;
|
||||||
if (savedSize.current === null) {
|
if (savedSize.current === null) {
|
||||||
targetSize = "0px";
|
targetSize = "0px";
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'null'.
|
||||||
savedSize.current = `${getElementSize()}px`;
|
savedSize.current = `${getElementSize()}px`;
|
||||||
} else {
|
} else {
|
||||||
targetSize = savedSize.current;
|
targetSize = savedSize.current;
|
||||||
@@ -42,10 +53,13 @@ export default function Resizable({ toggleShortcut, direction, sizeAttribute, ch
|
|||||||
.style(sizeAttribute, savedSize.current || "0px")
|
.style(sizeAttribute, savedSize.current || "0px")
|
||||||
.transition()
|
.transition()
|
||||||
.duration(200)
|
.duration(200)
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message
|
||||||
.ease("swing")
|
.ease("swing")
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
.style(sizeAttribute, targetSize);
|
.style(sizeAttribute, targetSize);
|
||||||
|
|
||||||
// update state to new element's size
|
// update state to new element's size
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string | null' is not assignable... Remove this comment to see the full error message
|
||||||
setSize(parseInt(targetSize) || 0);
|
setSize(parseInt(targetSize) || 0);
|
||||||
}, [getElementSize, sizeAttribute]);
|
}, [getElementSize, sizeAttribute]);
|
||||||
|
|
||||||
@@ -92,8 +106,9 @@ export default function Resizable({ toggleShortcut, direction, sizeAttribute, ch
|
|||||||
// updated here and in `draggableCore::onMouseDown` handler to ensure that right value will be used
|
// updated here and in `draggableCore::onMouseDown` handler to ensure that right value will be used
|
||||||
setSize(getElementSize());
|
setSize(getElementSize());
|
||||||
},
|
},
|
||||||
onResize: (unused, data) => {
|
onResize: (unused: any, data: any) => {
|
||||||
// update element directly for better UI responsiveness
|
// update element directly for better UI responsiveness
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
d3.select(elementRef.current).style(sizeAttribute, `${data.size[sizeProp]}px`);
|
d3.select(elementRef.current).style(sizeAttribute, `${data.size[sizeProp]}px`);
|
||||||
setSize(data.size[sizeProp]);
|
setSize(data.size[sizeProp]);
|
||||||
wasResizedRef.current = true;
|
wasResizedRef.current = true;
|
||||||
@@ -109,7 +124,7 @@ export default function Resizable({ toggleShortcut, direction, sizeAttribute, ch
|
|||||||
|
|
||||||
const draggableCoreOptions = useMemo(
|
const draggableCoreOptions = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
onMouseDown: e => {
|
onMouseDown: (e: any) => {
|
||||||
// In some cases this handler is executed twice during the same resize operation - first time
|
// In some cases this handler is executed twice during the same resize operation - first time
|
||||||
// with `touchstart` event and second time with `mousedown` (probably emulated by browser).
|
// with `touchstart` event and second time with `mousedown` (probably emulated by browser).
|
||||||
// Therefore we set the flag only when we receive `touchstart` because in ths case it's definitely
|
// Therefore we set the flag only when we receive `touchstart` because in ths case it's definitely
|
||||||
@@ -130,6 +145,7 @@ export default function Resizable({ toggleShortcut, direction, sizeAttribute, ch
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'CElement<any, Component<any, any, any>>' is ... Remove this comment to see the full error message
|
||||||
children = React.createElement(children.type, { ...children.props, ref: elementRef });
|
children = React.createElement(children.type, { ...children.props, ref: elementRef });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -148,13 +164,6 @@ export default function Resizable({ toggleShortcut, direction, sizeAttribute, ch
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Resizable.propTypes = {
|
|
||||||
direction: PropTypes.oneOf(["horizontal", "vertical"]),
|
|
||||||
sizeAttribute: PropTypes.string,
|
|
||||||
toggleShortcut: PropTypes.string,
|
|
||||||
children: PropTypes.element,
|
|
||||||
};
|
|
||||||
|
|
||||||
Resizable.defaultProps = {
|
Resizable.defaultProps = {
|
||||||
direction: "horizontal",
|
direction: "horizontal",
|
||||||
sizeAttribute: null, // "width"/"height" - depending on `direction`
|
sizeAttribute: null, // "width"/"height" - depending on `direction`
|
||||||
@@ -1,24 +1,33 @@
|
|||||||
import { filter, find, isEmpty, size } from "lodash";
|
import { filter, find, isEmpty, size } from "lodash";
|
||||||
import React, { useState, useCallback, useEffect } from "react";
|
import React, { useState, useCallback, useEffect } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
import Input from "antd/lib/input";
|
import Input from "antd/lib/input";
|
||||||
import List from "antd/lib/list";
|
import List from "antd/lib/list";
|
||||||
import Button from "antd/lib/button";
|
import Button from "antd/lib/button";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DialogPropType' is declared but its value is neve... Remove this comment to see the full error message
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
import BigMessage from "@/components/BigMessage";
|
import BigMessage from "@/components/BigMessage";
|
||||||
import LoadingState from "@/components/items-list/components/LoadingState";
|
import LoadingState from "@/components/items-list/components/LoadingState";
|
||||||
import notification from "@/services/notification";
|
import notification from "@/services/notification";
|
||||||
import useSearchResults from "@/lib/hooks/useSearchResults";
|
import useSearchResults from "@/lib/hooks/useSearchResults";
|
||||||
|
|
||||||
function ItemsList({ items, renderItem, onItemClick }) {
|
type OwnItemsListProps = {
|
||||||
|
items?: any[];
|
||||||
|
renderItem?: (...args: any[]) => any;
|
||||||
|
onItemClick?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ItemsListProps = OwnItemsListProps & typeof ItemsList.defaultProps;
|
||||||
|
|
||||||
|
function ItemsList({ items, renderItem, onItemClick }: ItemsListProps) {
|
||||||
const renderListItem = useCallback(
|
const renderListItem = useCallback(
|
||||||
item => {
|
item => {
|
||||||
const { content, className, isDisabled } = renderItem(item);
|
const { content, className, isDisabled } = renderItem(item);
|
||||||
return (
|
return (
|
||||||
<List.Item
|
<List.Item
|
||||||
className={classNames("p-l-10", "p-r-10", { clickable: !isDisabled, disabled: isDisabled }, className)}
|
className={classNames("p-l-10", "p-r-10", { clickable: !isDisabled, disabled: isDisabled }, className)}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(() => any) | null' is not assignable to typ... Remove this comment to see the full error message
|
||||||
onClick={isDisabled ? null : () => onItemClick(item)}>
|
onClick={isDisabled ? null : () => onItemClick(item)}>
|
||||||
{content}
|
{content}
|
||||||
</List.Item>
|
</List.Item>
|
||||||
@@ -30,42 +39,46 @@ function ItemsList({ items, renderItem, onItemClick }) {
|
|||||||
return <List size="small" dataSource={items} renderItem={renderListItem} />;
|
return <List size="small" dataSource={items} renderItem={renderListItem} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemsList.propTypes = {
|
|
||||||
items: PropTypes.array,
|
|
||||||
renderItem: PropTypes.func,
|
|
||||||
onItemClick: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
ItemsList.defaultProps = {
|
ItemsList.defaultProps = {
|
||||||
items: [],
|
items: [],
|
||||||
renderItem: () => {},
|
renderItem: () => {},
|
||||||
onItemClick: () => {},
|
onItemClick: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
function SelectItemsDialog({
|
type OwnSelectItemsDialogProps = {
|
||||||
dialog,
|
// @ts-expect-error ts-migrate(2749) FIXME: 'DialogPropType' refers to a value, but is being u... Remove this comment to see the full error message
|
||||||
dialogTitle,
|
dialog: DialogPropType;
|
||||||
inputPlaceholder,
|
dialogTitle?: string;
|
||||||
itemKey,
|
inputPlaceholder?: string;
|
||||||
renderItem,
|
selectedItemsTitle?: string;
|
||||||
renderStagedItem,
|
searchItems: (...args: any[]) => any;
|
||||||
searchItems,
|
itemKey?: (...args: any[]) => any;
|
||||||
selectedItemsTitle,
|
renderItem?: (...args: any[]) => any;
|
||||||
width,
|
renderStagedItem?: (...args: any[]) => any;
|
||||||
showCount,
|
width?: string | number;
|
||||||
extraFooterContent,
|
extraFooterContent?: React.ReactNode;
|
||||||
}) {
|
showCount?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SelectItemsDialogProps = OwnSelectItemsDialogProps & typeof SelectItemsDialog.defaultProps;
|
||||||
|
|
||||||
|
function SelectItemsDialog({ dialog, dialogTitle, inputPlaceholder, itemKey, renderItem, renderStagedItem, searchItems, selectedItemsTitle, width, showCount, extraFooterContent, }: SelectItemsDialogProps) {
|
||||||
const [selectedItems, setSelectedItems] = useState([]);
|
const [selectedItems, setSelectedItems] = useState([]);
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'never[]' is not assignable to type 'null | u... Remove this comment to see the full error message
|
||||||
const [search, items, isLoading] = useSearchResults(searchItems, { initialResults: [] });
|
const [search, items, isLoading] = useSearchResults(searchItems, { initialResults: [] });
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
const hasResults = items.length > 0;
|
const hasResults = items.length > 0;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
|
||||||
search();
|
search();
|
||||||
}, [search]);
|
}, [search]);
|
||||||
|
|
||||||
const isItemSelected = useCallback(
|
const isItemSelected = useCallback(
|
||||||
item => {
|
item => {
|
||||||
|
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
|
||||||
const key = itemKey(item);
|
const key = itemKey(item);
|
||||||
|
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
|
||||||
return !!find(selectedItems, i => itemKey(i) === key);
|
return !!find(selectedItems, i => itemKey(i) === key);
|
||||||
},
|
},
|
||||||
[selectedItems, itemKey]
|
[selectedItems, itemKey]
|
||||||
@@ -74,9 +87,12 @@ function SelectItemsDialog({
|
|||||||
const toggleItem = useCallback(
|
const toggleItem = useCallback(
|
||||||
item => {
|
item => {
|
||||||
if (isItemSelected(item)) {
|
if (isItemSelected(item)) {
|
||||||
|
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
|
||||||
const key = itemKey(item);
|
const key = itemKey(item);
|
||||||
|
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
|
||||||
setSelectedItems(filter(selectedItems, i => itemKey(i) !== key));
|
setSelectedItems(filter(selectedItems, i => itemKey(i) !== key));
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'any[]' is not assignable to para... Remove this comment to see the full error message
|
||||||
setSelectedItems([...selectedItems, item]);
|
setSelectedItems([...selectedItems, item]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -84,8 +100,10 @@ function SelectItemsDialog({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const save = useCallback(() => {
|
const save = useCallback(() => {
|
||||||
dialog.close(selectedItems).catch(error => {
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'close' does not exist on type 'never'.
|
||||||
|
dialog.close(selectedItems).catch((error: any) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message
|
||||||
notification.error("Failed to save some of selected items.");
|
notification.error("Failed to save some of selected items.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -93,6 +111,7 @@ function SelectItemsDialog({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'props' does not exist on type 'never'.
|
||||||
{...dialog.props}
|
{...dialog.props}
|
||||||
className="select-items-dialog"
|
className="select-items-dialog"
|
||||||
width={width}
|
width={width}
|
||||||
@@ -102,12 +121,15 @@ function SelectItemsDialog({
|
|||||||
<span className="flex-fill m-r-5" style={{ textAlign: "left", color: "rgba(0, 0, 0, 0.5)" }}>
|
<span className="flex-fill m-r-5" style={{ textAlign: "left", color: "rgba(0, 0, 0, 0.5)" }}>
|
||||||
{extraFooterContent}
|
{extraFooterContent}
|
||||||
</span>
|
</span>
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'props' does not exist on type 'never'. */}
|
||||||
<Button {...dialog.props.cancelButtonProps} onClick={dialog.dismiss}>
|
<Button {...dialog.props.cancelButtonProps} onClick={dialog.dismiss}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'props' does not exist on type 'never'.
|
||||||
{...dialog.props.okButtonProps}
|
{...dialog.props.okButtonProps}
|
||||||
onClick={save}
|
onClick={save}
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'props' does not exist on type 'never'.
|
||||||
disabled={selectedItems.length === 0 || dialog.props.okButtonProps.disabled}
|
disabled={selectedItems.length === 0 || dialog.props.okButtonProps.disabled}
|
||||||
type="primary">
|
type="primary">
|
||||||
Save
|
Save
|
||||||
@@ -117,6 +139,7 @@ function SelectItemsDialog({
|
|||||||
}>
|
}>
|
||||||
<div className="d-flex align-items-center m-b-10">
|
<div className="d-flex align-items-center m-b-10">
|
||||||
<div className="flex-fill">
|
<div className="flex-fill">
|
||||||
|
{/* @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable. */}
|
||||||
<Input.Search onChange={event => search(event.target.value)} placeholder={inputPlaceholder} autoFocus />
|
<Input.Search onChange={event => search(event.target.value)} placeholder={inputPlaceholder} autoFocus />
|
||||||
</div>
|
</div>
|
||||||
{renderStagedItem && (
|
{renderStagedItem && (
|
||||||
@@ -134,8 +157,11 @@ function SelectItemsDialog({
|
|||||||
)}
|
)}
|
||||||
{!isLoading && hasResults && (
|
{!isLoading && hasResults && (
|
||||||
<ItemsList
|
<ItemsList
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean | ((searchTerm: any) => void) | null... Remove this comment to see the full error message
|
||||||
items={items}
|
items={items}
|
||||||
renderItem={item => renderItem(item, { isSelected: isItemSelected(item) })}
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(item: any) => any' is not assignable to typ... Remove this comment to see the full error message
|
||||||
|
renderItem={(item: any) => renderItem(item, { isSelected: isItemSelected(item) })}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(item: any) => void' is not assignable to ty... Remove this comment to see the full error message
|
||||||
onItemClick={toggleItem}
|
onItemClick={toggleItem}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -145,7 +171,9 @@ function SelectItemsDialog({
|
|||||||
{selectedItems.length > 0 && (
|
{selectedItems.length > 0 && (
|
||||||
<ItemsList
|
<ItemsList
|
||||||
items={selectedItems}
|
items={selectedItems}
|
||||||
renderItem={item => renderStagedItem(item, { isSelected: true })}
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(item: any) => any' is not assignable to typ... Remove this comment to see the full error message
|
||||||
|
renderItem={(item: any) => renderStagedItem(item, { isSelected: true })}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(item: any) => void' is not assignable to ty... Remove this comment to see the full error message
|
||||||
onItemClick={toggleItem}
|
onItemClick={toggleItem}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -156,32 +184,11 @@ function SelectItemsDialog({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectItemsDialog.propTypes = {
|
|
||||||
dialog: DialogPropType.isRequired,
|
|
||||||
dialogTitle: PropTypes.string,
|
|
||||||
inputPlaceholder: PropTypes.string,
|
|
||||||
selectedItemsTitle: PropTypes.string,
|
|
||||||
searchItems: PropTypes.func.isRequired, // (searchTerm: string): Promise<Items[]> if `searchTerm === ''` load all
|
|
||||||
itemKey: PropTypes.func, // (item) => string|number - return key of item (by default `id`)
|
|
||||||
// left list
|
|
||||||
// (item, { isSelected }) => {
|
|
||||||
// content: node, // item contents
|
|
||||||
// className: string = '', // additional class for item wrapper
|
|
||||||
// isDisabled: bool = false, // is item clickable or disabled
|
|
||||||
// }
|
|
||||||
renderItem: PropTypes.func,
|
|
||||||
// right list; args/results save as for `renderItem`. if not specified - `renderItem` will be used
|
|
||||||
renderStagedItem: PropTypes.func,
|
|
||||||
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
||||||
extraFooterContent: PropTypes.node,
|
|
||||||
showCount: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectItemsDialog.defaultProps = {
|
SelectItemsDialog.defaultProps = {
|
||||||
dialogTitle: "Add Items",
|
dialogTitle: "Add Items",
|
||||||
inputPlaceholder: "Search...",
|
inputPlaceholder: "Search...",
|
||||||
selectedItemsTitle: "Selected items",
|
selectedItemsTitle: "Selected items",
|
||||||
itemKey: item => item.id,
|
itemKey: (item: any) => item.id,
|
||||||
renderItem: () => "",
|
renderItem: () => "",
|
||||||
renderStagedItem: null, // hidden by default
|
renderStagedItem: null, // hidden by default
|
||||||
width: "80%",
|
width: "80%",
|
||||||
@@ -5,20 +5,24 @@ import Link from "@/components/Link";
|
|||||||
import location from "@/services/location";
|
import location from "@/services/location";
|
||||||
import settingsMenu from "@/services/settingsMenu";
|
import settingsMenu from "@/services/settingsMenu";
|
||||||
|
|
||||||
function wrapSettingsTab(id, options, WrappedComponent) {
|
function wrapSettingsTab(id: any, options: any, WrappedComponent: any) {
|
||||||
settingsMenu.add(id, options);
|
settingsMenu.add(id, options);
|
||||||
|
|
||||||
return function SettingsTab(props) {
|
return function SettingsTab(props: any) {
|
||||||
const activeItem = settingsMenu.getActiveItem(location.path);
|
const activeItem = settingsMenu.getActiveItem(location.path);
|
||||||
return (
|
return (
|
||||||
<div className="settings-screen">
|
<div className="settings-screen">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<PageHeader title="Settings" />
|
<PageHeader title="Settings" />
|
||||||
<div className="bg-white tiled">
|
<div className="bg-white tiled">
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'title' does not exist on type 'number | ... Remove this comment to see the full error message */}
|
||||||
<Menu selectedKeys={[activeItem && activeItem.title]} selectable={false} mode="horizontal">
|
<Menu selectedKeys={[activeItem && activeItem.title]} selectable={false} mode="horizontal">
|
||||||
{settingsMenu.getAvailableItems().map(item => (
|
{settingsMenu.getAvailableItems().map(item => (
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'title' does not exist on type 'number | ... Remove this comment to see the full error message
|
||||||
<Menu.Item key={item.title}>
|
<Menu.Item key={item.title}>
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'path' does not exist on type 'number | (... Remove this comment to see the full error message */}
|
||||||
<Link href={item.path} data-test="SettingsScreenItem">
|
<Link href={item.path} data-test="SettingsScreenItem">
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'title' does not exist on type 'number | ... Remove this comment to see the full error message */}
|
||||||
{item.title}
|
{item.title}
|
||||||
</Link>
|
</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
@@ -1,19 +1,30 @@
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { isNil } from "lodash";
|
import { isNil } from "lodash";
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
// @ts-expect-error ts-migrate(6133) FIXME: 'Moment' is declared but its value is never read.
|
||||||
import { Moment } from "@/components/proptypes";
|
import { Moment } from "@/components/proptypes";
|
||||||
import { clientConfig } from "@/services/auth";
|
import { clientConfig } from "@/services/auth";
|
||||||
import Tooltip from "antd/lib/tooltip";
|
import Tooltip from "antd/lib/tooltip";
|
||||||
|
|
||||||
function toMoment(value) {
|
function toMoment(value: any) {
|
||||||
value = !isNil(value) ? moment(value) : null;
|
value = !isNil(value) ? moment(value) : null;
|
||||||
return value && value.isValid() ? value : null;
|
return value && value.isValid() ? value : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TimeAgo({ date, placeholder, autoUpdate, variation }) {
|
type OwnProps = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
date?: string | number | any | Moment;
|
||||||
|
placeholder?: string;
|
||||||
|
autoUpdate?: boolean;
|
||||||
|
variation?: "timeAgoInTooltip";
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof TimeAgo.defaultProps;
|
||||||
|
|
||||||
|
export default function TimeAgo({ date, placeholder, autoUpdate, variation }: Props) {
|
||||||
const startDate = toMoment(date);
|
const startDate = toMoment(date);
|
||||||
const [value, setValue] = useState(null);
|
const [value, setValue] = useState(null);
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dateTimeFormat' does not exist on type '... Remove this comment to see the full error message
|
||||||
const title = useMemo(() => (startDate ? startDate.format(clientConfig.dateTimeFormat) : null), [startDate]);
|
const title = useMemo(() => (startDate ? startDate.format(clientConfig.dateTimeFormat) : null), [startDate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -42,13 +53,6 @@ export default function TimeAgo({ date, placeholder, autoUpdate, variation }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = {
|
TimeAgo.defaultProps = {
|
||||||
date: null,
|
date: null,
|
||||||
placeholder: "",
|
placeholder: "",
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
import React, { useMemo, useState, useEffect } from "react";
|
import React, { useMemo, useState, useEffect } from "react";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import PropTypes from "prop-types";
|
// @ts-expect-error ts-migrate(6133) FIXME: 'Moment' is declared but its value is never read.
|
||||||
import { Moment } from "@/components/proptypes";
|
import { Moment } from "@/components/proptypes";
|
||||||
|
|
||||||
export default function Timer({ from }) {
|
type OwnProps = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
from?: string | number | any | Moment;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof Timer.defaultProps;
|
||||||
|
|
||||||
|
export default function Timer({ from }: Props) {
|
||||||
const startTime = useMemo(() => moment(from).valueOf(), [from]);
|
const startTime = useMemo(() => moment(from).valueOf(), [from]);
|
||||||
const [value, setValue] = useState(null);
|
const [value, setValue] = useState(null);
|
||||||
|
|
||||||
@@ -11,6 +18,7 @@ export default function Timer({ from }) {
|
|||||||
function update() {
|
function update() {
|
||||||
const diff = moment.now() - startTime;
|
const diff = moment.now() - startTime;
|
||||||
const format = diff > 1000 * 60 * 60 ? "HH:mm:ss" : "mm:ss"; // no HH under an hour
|
const format = diff > 1000 * 60 * 60 ? "HH:mm:ss" : "mm:ss"; // no HH under an hour
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message
|
||||||
setValue(moment.utc(diff).format(format));
|
setValue(moment.utc(diff).format(format));
|
||||||
}
|
}
|
||||||
update();
|
update();
|
||||||
@@ -22,10 +30,6 @@ export default function Timer({ from }) {
|
|||||||
return <span className="rd-timer">{value}</span>;
|
return <span className="rd-timer">{value}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer.propTypes = {
|
|
||||||
from: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.instanceOf(Date), Moment]),
|
|
||||||
};
|
|
||||||
|
|
||||||
Timer.defaultProps = {
|
Timer.defaultProps = {
|
||||||
from: null,
|
from: null,
|
||||||
};
|
};
|
||||||
@@ -1,12 +1,21 @@
|
|||||||
import { map } from "lodash";
|
import { map } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Tag from "antd/lib/tag";
|
import Tag from "antd/lib/tag";
|
||||||
import Link from "@/components/Link";
|
import Link from "@/components/Link";
|
||||||
|
|
||||||
import "./UserGroups.less";
|
import "./UserGroups.less";
|
||||||
|
|
||||||
export default function UserGroups({ groups, linkGroups, ...props }) {
|
type OwnProps = {
|
||||||
|
groups?: {
|
||||||
|
id: number | string;
|
||||||
|
name?: string;
|
||||||
|
}[];
|
||||||
|
linkGroups?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof UserGroups.defaultProps;
|
||||||
|
|
||||||
|
export default function UserGroups({ groups, linkGroups, ...props }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="user-groups" {...props}>
|
<div className="user-groups" {...props}>
|
||||||
{map(groups, group => (
|
{map(groups, group => (
|
||||||
@@ -16,16 +25,6 @@ export default function UserGroups({ groups, linkGroups, ...props }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserGroups.propTypes = {
|
|
||||||
groups: PropTypes.arrayOf(
|
|
||||||
PropTypes.shape({
|
|
||||||
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
|
|
||||||
name: PropTypes.string,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
linkGroups: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
UserGroups.defaultProps = {
|
UserGroups.defaultProps = {
|
||||||
groups: [],
|
groups: [],
|
||||||
linkGroups: true,
|
linkGroups: true,
|
||||||
@@ -1,12 +1,18 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Menu from "antd/lib/menu";
|
import Menu from "antd/lib/menu";
|
||||||
import PageHeader from "@/components/PageHeader";
|
import PageHeader from "@/components/PageHeader";
|
||||||
import Link from "@/components/Link";
|
import Link from "@/components/Link";
|
||||||
|
|
||||||
import "./layout.less";
|
import "./layout.less";
|
||||||
|
|
||||||
export default function Layout({ activeTab, children }) {
|
type OwnProps = {
|
||||||
|
activeTab?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof Layout.defaultProps;
|
||||||
|
|
||||||
|
export default function Layout({ activeTab, children }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="admin-page-layout">
|
<div className="admin-page-layout">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
@@ -30,11 +36,6 @@ export default function Layout({ activeTab, children }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout.propTypes = {
|
|
||||||
activeTab: PropTypes.string,
|
|
||||||
children: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
Layout.defaultProps = {
|
Layout.defaultProps = {
|
||||||
activeTab: "system_status",
|
activeTab: "system_status",
|
||||||
children: null,
|
children: null,
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { map } from "lodash";
|
import { map } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
import Badge from "antd/lib/badge";
|
import Badge from "antd/lib/badge";
|
||||||
import Card from "antd/lib/card";
|
import Card from "antd/lib/card";
|
||||||
@@ -8,9 +7,17 @@ import Spin from "antd/lib/spin";
|
|||||||
import Table from "antd/lib/table";
|
import Table from "antd/lib/table";
|
||||||
import { Columns } from "@/components/items-list/components/ItemsTable";
|
import { Columns } from "@/components/items-list/components/ItemsTable";
|
||||||
|
|
||||||
|
type OwnCounterCardProps = {
|
||||||
|
title: string;
|
||||||
|
value?: number | string;
|
||||||
|
loading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CounterCardProps = OwnCounterCardProps & typeof CounterCard.defaultProps;
|
||||||
|
|
||||||
// CounterCard
|
// CounterCard
|
||||||
|
|
||||||
export function CounterCard({ title, value, loading }) {
|
export function CounterCard({ title, value, loading }: CounterCardProps) {
|
||||||
return (
|
return (
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
<Card>
|
<Card>
|
||||||
@@ -21,12 +28,6 @@ export function CounterCard({ title, value, loading }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
CounterCard.propTypes = {
|
|
||||||
title: PropTypes.string.isRequired,
|
|
||||||
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
|
||||||
loading: PropTypes.bool.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
CounterCard.defaultProps = {
|
CounterCard.defaultProps = {
|
||||||
value: "",
|
value: "",
|
||||||
};
|
};
|
||||||
@@ -39,7 +40,7 @@ const queryJobsColumns = [
|
|||||||
{ title: "Org ID", dataIndex: "meta.org_id" },
|
{ title: "Org ID", dataIndex: "meta.org_id" },
|
||||||
{ title: "Data Source ID", dataIndex: "meta.data_source_id" },
|
{ title: "Data Source ID", dataIndex: "meta.data_source_id" },
|
||||||
{ title: "User ID", dataIndex: "meta.user_id" },
|
{ title: "User ID", dataIndex: "meta.user_id" },
|
||||||
Columns.custom(scheduled => scheduled.toString(), { title: "Scheduled", dataIndex: "meta.scheduled" }),
|
Columns.custom((scheduled: any) => scheduled.toString(), { title: "Scheduled", dataIndex: "meta.scheduled" }),
|
||||||
Columns.timeAgo({ title: "Start Time", dataIndex: "started_at" }),
|
Columns.timeAgo({ title: "Start Time", dataIndex: "started_at" }),
|
||||||
Columns.timeAgo({ title: "Enqueue Time", dataIndex: "enqueued_at" }),
|
Columns.timeAgo({ title: "Enqueue Time", dataIndex: "enqueued_at" }),
|
||||||
];
|
];
|
||||||
@@ -53,12 +54,11 @@ const otherJobsColumns = [
|
|||||||
|
|
||||||
const workersColumns = [
|
const workersColumns = [
|
||||||
Columns.custom(
|
Columns.custom(
|
||||||
value => (
|
(value: any) => <span>
|
||||||
<span>
|
{/* @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message */}
|
||||||
<Badge status={{ busy: "processing", idle: "default", started: "success", suspended: "warning" }[value]} />{" "}
|
<Badge status={{ busy: "processing", idle: "default", started: "success", suspended: "warning" }[value]} />{" "}
|
||||||
{value}
|
{value}
|
||||||
</span>
|
</span>,
|
||||||
),
|
|
||||||
{ title: "State", dataIndex: "state" }
|
{ title: "State", dataIndex: "state" }
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@@ -75,12 +75,27 @@ const workersColumns = [
|
|||||||
|
|
||||||
const queuesColumns = map(["Name", "Started", "Queued"], c => ({ title: c, dataIndex: c.toLowerCase() }));
|
const queuesColumns = map(["Name", "Started", "Queued"], c => ({ title: c, dataIndex: c.toLowerCase() }));
|
||||||
|
|
||||||
const TablePropTypes = {
|
type WorkersTableProps = {
|
||||||
loading: PropTypes.bool.isRequired,
|
loading: boolean;
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: any[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export function WorkersTable({ loading, items }) {
|
type QueuesTableProps = {
|
||||||
|
loading: boolean;
|
||||||
|
items: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type QueryJobsTableProps = {
|
||||||
|
loading: boolean;
|
||||||
|
items: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type OtherJobsTableProps = {
|
||||||
|
loading: boolean;
|
||||||
|
items: any[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function WorkersTable({ loading, items }: WorkersTableProps) {
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
loading={loading}
|
loading={loading}
|
||||||
@@ -96,9 +111,7 @@ export function WorkersTable({ loading, items }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkersTable.propTypes = TablePropTypes;
|
export function QueuesTable({ loading, items }: QueuesTableProps) {
|
||||||
|
|
||||||
export function QueuesTable({ loading, items }) {
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
loading={loading}
|
loading={loading}
|
||||||
@@ -114,9 +127,7 @@ export function QueuesTable({ loading, items }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueuesTable.propTypes = TablePropTypes;
|
export function QueryJobsTable({ loading, items }: QueryJobsTableProps) {
|
||||||
|
|
||||||
export function QueryJobsTable({ loading, items }) {
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
loading={loading}
|
loading={loading}
|
||||||
@@ -132,9 +143,7 @@ export function QueryJobsTable({ loading, items }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryJobsTable.propTypes = TablePropTypes;
|
export function OtherJobsTable({ loading, items }: OtherJobsTableProps) {
|
||||||
|
|
||||||
export function OtherJobsTable({ loading, items }) {
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<Table
|
||||||
loading={loading}
|
loading={loading}
|
||||||
@@ -149,5 +158,3 @@ export function OtherJobsTable({ loading, items }) {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OtherJobsTable.propTypes = TablePropTypes;
|
|
||||||
@@ -9,7 +9,9 @@ import TimeAgo from "@/components/TimeAgo";
|
|||||||
|
|
||||||
import { toHuman, prettySize } from "@/lib/utils";
|
import { toHuman, prettySize } from "@/lib/utils";
|
||||||
|
|
||||||
export function General({ info }) {
|
export function General({
|
||||||
|
info
|
||||||
|
}: any) {
|
||||||
info = toPairs(info);
|
info = toPairs(info);
|
||||||
return (
|
return (
|
||||||
<Card title="General" size="small">
|
<Card title="General" size="small">
|
||||||
@@ -19,6 +21,7 @@ export function General({ info }) {
|
|||||||
size="small"
|
size="small"
|
||||||
itemLayout="vertical"
|
itemLayout="vertical"
|
||||||
dataSource={info}
|
dataSource={info}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '([name, value]: [any, any]) => Element' is n... Remove this comment to see the full error message
|
||||||
renderItem={([name, value]) => (
|
renderItem={([name, value]) => (
|
||||||
<List.Item extra={<span className="badge">{value}</span>}>{toHuman(name)}</List.Item>
|
<List.Item extra={<span className="badge">{value}</span>}>{toHuman(name)}</List.Item>
|
||||||
)}
|
)}
|
||||||
@@ -28,7 +31,9 @@ export function General({ info }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DatabaseMetrics({ info }) {
|
export function DatabaseMetrics({
|
||||||
|
info
|
||||||
|
}: any) {
|
||||||
return (
|
return (
|
||||||
<Card title="Redash Database" size="small">
|
<Card title="Redash Database" size="small">
|
||||||
{info.length === 0 && <div className="text-muted text-center">No data</div>}
|
{info.length === 0 && <div className="text-muted text-center">No data</div>}
|
||||||
@@ -37,6 +42,7 @@ export function DatabaseMetrics({ info }) {
|
|||||||
size="small"
|
size="small"
|
||||||
itemLayout="vertical"
|
itemLayout="vertical"
|
||||||
dataSource={info}
|
dataSource={info}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '([name, size]: [any, any]) => Element' is no... Remove this comment to see the full error message
|
||||||
renderItem={([name, size]) => (
|
renderItem={([name, size]) => (
|
||||||
<List.Item extra={<span className="badge">{prettySize(size)}</span>}>{name}</List.Item>
|
<List.Item extra={<span className="badge">{prettySize(size)}</span>}>{name}</List.Item>
|
||||||
)}
|
)}
|
||||||
@@ -46,7 +52,9 @@ export function DatabaseMetrics({ info }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Queues({ info }) {
|
export function Queues({
|
||||||
|
info
|
||||||
|
}: any) {
|
||||||
info = toPairs(info);
|
info = toPairs(info);
|
||||||
return (
|
return (
|
||||||
<Card title="Queues" size="small">
|
<Card title="Queues" size="small">
|
||||||
@@ -56,6 +64,7 @@ export function Queues({ info }) {
|
|||||||
size="small"
|
size="small"
|
||||||
itemLayout="vertical"
|
itemLayout="vertical"
|
||||||
dataSource={info}
|
dataSource={info}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '([name, queue]: [any, any]) => Element' is n... Remove this comment to see the full error message
|
||||||
renderItem={([name, queue]) => (
|
renderItem={([name, queue]) => (
|
||||||
<List.Item extra={<span className="badge">{queue.size}</span>}>{name}</List.Item>
|
<List.Item extra={<span className="badge">{queue.size}</span>}>{name}</List.Item>
|
||||||
)}
|
)}
|
||||||
@@ -65,7 +74,9 @@ export function Queues({ info }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Manager({ info }) {
|
export function Manager({
|
||||||
|
info
|
||||||
|
}: any) {
|
||||||
const items = info
|
const items = info
|
||||||
? [
|
? [
|
||||||
<List.Item
|
<List.Item
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
import { map, includes, groupBy, first, find } from "lodash";
|
import { map, includes, groupBy, first, find } from "lodash";
|
||||||
import React, { useState, useMemo, useCallback } from "react";
|
import React, { useState, useMemo, useCallback } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Select from "antd/lib/select";
|
import Select from "antd/lib/select";
|
||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DialogPropType' is declared but its value is neve... Remove this comment to see the full error message
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
import { MappingType, ParameterMappingListInput } from "@/components/ParameterMappingInput";
|
import { MappingType, ParameterMappingListInput } from "@/components/ParameterMappingInput";
|
||||||
import QuerySelector from "@/components/QuerySelector";
|
import QuerySelector from "@/components/QuerySelector";
|
||||||
import notification from "@/services/notification";
|
import notification from "@/services/notification";
|
||||||
import { Query } from "@/services/query";
|
import { Query } from "@/services/query";
|
||||||
|
|
||||||
function VisualizationSelect({ query, visualization, onChange }) {
|
type OwnVisualizationSelectProps = {
|
||||||
|
query?: any;
|
||||||
|
visualization?: any;
|
||||||
|
onChange?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type VisualizationSelectProps = OwnVisualizationSelectProps & typeof VisualizationSelect.defaultProps;
|
||||||
|
|
||||||
|
function VisualizationSelect({ query, visualization, onChange }: VisualizationSelectProps) {
|
||||||
const visualizationGroups = useMemo(() => {
|
const visualizationGroups = useMemo(() => {
|
||||||
return query ? groupBy(query.visualizations, "type") : {};
|
return query ? groupBy(query.visualizations, "type") : {};
|
||||||
}, [query]);
|
}, [query]);
|
||||||
@@ -50,19 +58,19 @@ function VisualizationSelect({ query, visualization, onChange }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
VisualizationSelect.propTypes = {
|
|
||||||
query: PropTypes.object,
|
|
||||||
visualization: PropTypes.object,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
VisualizationSelect.defaultProps = {
|
VisualizationSelect.defaultProps = {
|
||||||
query: null,
|
query: null,
|
||||||
visualization: null,
|
visualization: null,
|
||||||
onChange: () => {},
|
onChange: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
function AddWidgetDialog({ dialog, dashboard }) {
|
type AddWidgetDialogProps = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'DialogPropType' refers to a value, but is being u... Remove this comment to see the full error message
|
||||||
|
dialog: DialogPropType;
|
||||||
|
dashboard: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
function AddWidgetDialog({ dialog, dashboard }: AddWidgetDialogProps) {
|
||||||
const [selectedQuery, setSelectedQuery] = useState(null);
|
const [selectedQuery, setSelectedQuery] = useState(null);
|
||||||
const [selectedVisualization, setSelectedVisualization] = useState(null);
|
const [selectedVisualization, setSelectedVisualization] = useState(null);
|
||||||
const [parameterMappings, setParameterMappings] = useState([]);
|
const [parameterMappings, setParameterMappings] = useState([]);
|
||||||
@@ -75,11 +83,13 @@ function AddWidgetDialog({ dialog, dashboard }) {
|
|||||||
setParameterMappings([]);
|
setParameterMappings([]);
|
||||||
|
|
||||||
if (queryId) {
|
if (queryId) {
|
||||||
Query.get({ id: queryId }).then(query => {
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'get' does not exist on type 'typeof Quer... Remove this comment to see the full error message
|
||||||
|
Query.get({ id: queryId }).then((query: any) => {
|
||||||
if (query) {
|
if (query) {
|
||||||
const existingParamNames = map(dashboard.getParametersDefs(), param => param.name);
|
const existingParamNames = map(dashboard.getParametersDefs(), param => param.name);
|
||||||
setSelectedQuery(query);
|
setSelectedQuery(query);
|
||||||
setParameterMappings(
|
setParameterMappings(
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ name: any; type: string; mapTo... Remove this comment to see the full error message
|
||||||
map(query.getParametersDefs(), param => ({
|
map(query.getParametersDefs(), param => ({
|
||||||
name: param.name,
|
name: param.name,
|
||||||
type: includes(existingParamNames, param.name)
|
type: includes(existingParamNames, param.name)
|
||||||
@@ -92,6 +102,7 @@ function AddWidgetDialog({ dialog, dashboard }) {
|
|||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
if (query.visualizations.length > 0) {
|
if (query.visualizations.length > 0) {
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '((prevState: null) => null) | nu... Remove this comment to see the full error message
|
||||||
setSelectedVisualization(first(query.visualizations));
|
setSelectedVisualization(first(query.visualizations));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +114,7 @@ function AddWidgetDialog({ dialog, dashboard }) {
|
|||||||
|
|
||||||
const saveWidget = useCallback(() => {
|
const saveWidget = useCallback(() => {
|
||||||
dialog.close({ visualization: selectedVisualization, parameterMappings }).catch(() => {
|
dialog.close({ visualization: selectedVisualization, parameterMappings }).catch(() => {
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message
|
||||||
notification.error("Widget could not be added");
|
notification.error("Widget could not be added");
|
||||||
});
|
});
|
||||||
}, [dialog, selectedVisualization, parameterMappings]);
|
}, [dialog, selectedVisualization, parameterMappings]);
|
||||||
@@ -121,12 +133,14 @@ function AddWidgetDialog({ dialog, dashboard }) {
|
|||||||
okText="Add to Dashboard"
|
okText="Add to Dashboard"
|
||||||
width={700}>
|
width={700}>
|
||||||
<div data-test="AddWidgetDialog">
|
<div data-test="AddWidgetDialog">
|
||||||
<QuerySelector onChange={query => selectQuery(query ? query.id : null)} />
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '(query: any) => void' is not assignable to t... Remove this comment to see the full error message */}
|
||||||
|
<QuerySelector onChange={(query: any) => selectQuery(query ? query.id : null)} />
|
||||||
|
|
||||||
{selectedQuery && (
|
{selectedQuery && (
|
||||||
<VisualizationSelect
|
<VisualizationSelect
|
||||||
query={selectedQuery}
|
query={selectedQuery}
|
||||||
visualization={selectedVisualization}
|
visualization={selectedVisualization}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'Dispatch<SetStateAction<null>>' is not assig... Remove this comment to see the full error message
|
||||||
onChange={setSelectedVisualization}
|
onChange={setSelectedVisualization}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -140,6 +154,7 @@ function AddWidgetDialog({ dialog, dashboard }) {
|
|||||||
id="parameter-mappings"
|
id="parameter-mappings"
|
||||||
mappings={parameterMappings}
|
mappings={parameterMappings}
|
||||||
existingParams={existingParams}
|
existingParams={existingParams}
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
onChange={setParameterMappings}
|
onChange={setParameterMappings}
|
||||||
/>,
|
/>,
|
||||||
]}
|
]}
|
||||||
@@ -148,9 +163,4 @@ function AddWidgetDialog({ dialog, dashboard }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
AddWidgetDialog.propTypes = {
|
|
||||||
dialog: DialogPropType.isRequired,
|
|
||||||
dashboard: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default wrapDialog(AddWidgetDialog);
|
export default wrapDialog(AddWidgetDialog);
|
||||||
@@ -19,17 +19,17 @@ export default class AutoHeightController {
|
|||||||
|
|
||||||
onHeightChange = null;
|
onHeightChange = null;
|
||||||
|
|
||||||
constructor(handler) {
|
constructor(handler: any) {
|
||||||
this.onHeightChange = handler;
|
this.onHeightChange = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(widgets) {
|
update(widgets: any) {
|
||||||
const newWidgetIds = widgets
|
const newWidgetIds = widgets
|
||||||
.filter(widget => widget.options.position.autoHeight)
|
.filter((widget: any) => widget.options.position.autoHeight)
|
||||||
.map(widget => widget.id.toString());
|
.map((widget: any) => widget.id.toString());
|
||||||
|
|
||||||
// added
|
// added
|
||||||
newWidgetIds.filter(id => !includes(Object.keys(this.widgets), id)).forEach(this.add);
|
newWidgetIds.filter((id: any) => !includes(Object.keys(this.widgets), id)).forEach(this.add);
|
||||||
|
|
||||||
// removed
|
// removed
|
||||||
Object.keys(this.widgets)
|
Object.keys(this.widgets)
|
||||||
@@ -37,12 +37,13 @@ export default class AutoHeightController {
|
|||||||
.forEach(this.remove);
|
.forEach(this.remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
add = id => {
|
add = (id: any) => {
|
||||||
if (this.isEmpty()) {
|
if (this.isEmpty()) {
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
const selector = WIDGET_SELECTOR.replace("{0}", id);
|
const selector = WIDGET_SELECTOR.replace("{0}", id);
|
||||||
|
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
|
||||||
this.widgets[id] = [
|
this.widgets[id] = [
|
||||||
function getHeight() {
|
function getHeight() {
|
||||||
const widgetEl = document.querySelector(selector);
|
const widgetEl = document.querySelector(selector);
|
||||||
@@ -66,13 +67,14 @@ export default class AutoHeightController {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
remove = id => {
|
remove = (id: any) => {
|
||||||
// ignore if not an active autoHeight widget
|
// ignore if not an active autoHeight widget
|
||||||
if (!this.exists(id)) {
|
if (!this.exists(id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not actually deleting from this.widgets to prevent case of unwanted re-adding
|
// not actually deleting from this.widgets to prevent case of unwanted re-adding
|
||||||
|
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
|
||||||
this.widgets[id.toString()] = false;
|
this.widgets[id.toString()] = false;
|
||||||
|
|
||||||
if (this.isEmpty()) {
|
if (this.isEmpty()) {
|
||||||
@@ -80,7 +82,8 @@ export default class AutoHeightController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exists = id => !!this.widgets[id.toString()];
|
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
|
||||||
|
exists = (id: any) => !!this.widgets[id.toString()];
|
||||||
|
|
||||||
isEmpty = () => !some(this.widgets);
|
isEmpty = () => !some(this.widgets);
|
||||||
|
|
||||||
@@ -88,10 +91,13 @@ export default class AutoHeightController {
|
|||||||
Object.keys(this.widgets)
|
Object.keys(this.widgets)
|
||||||
.filter(this.exists) // reject already removed items
|
.filter(this.exists) // reject already removed items
|
||||||
.forEach(id => {
|
.forEach(id => {
|
||||||
|
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
|
||||||
const [getHeight, prevHeight] = this.widgets[id];
|
const [getHeight, prevHeight] = this.widgets[id];
|
||||||
const height = getHeight();
|
const height = getHeight();
|
||||||
if (height && height !== prevHeight) {
|
if (height && height !== prevHeight) {
|
||||||
|
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
|
||||||
this.widgets[id][1] = height; // save
|
this.widgets[id][1] = height; // save
|
||||||
|
// @ts-expect-error ts-migrate(2721) FIXME: Cannot invoke an object which is possibly 'null'.
|
||||||
this.onHeightChange(id, height); // dispatch
|
this.onHeightChange(id, height); // dispatch
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -99,10 +105,12 @@ export default class AutoHeightController {
|
|||||||
|
|
||||||
start = () => {
|
start = () => {
|
||||||
this.stop();
|
this.stop();
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'number' is not assignable to type 'null'.
|
||||||
this.interval = setInterval(this.checkHeightChanges, INTERVAL);
|
this.interval = setInterval(this.checkHeightChanges, INTERVAL);
|
||||||
};
|
};
|
||||||
|
|
||||||
stop = () => {
|
stop = () => {
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
clearInterval(this.interval);
|
clearInterval(this.interval);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -114,6 +122,7 @@ export default class AutoHeightController {
|
|||||||
|
|
||||||
destroy = () => {
|
destroy = () => {
|
||||||
this.stop();
|
this.stop();
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'null' is not assignable to type '{}'.
|
||||||
this.widgets = null;
|
this.widgets = null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -3,19 +3,25 @@ import React, { useState } from "react";
|
|||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
import Input from "antd/lib/input";
|
import Input from "antd/lib/input";
|
||||||
import DynamicComponent from "@/components/DynamicComponent";
|
import DynamicComponent from "@/components/DynamicComponent";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DialogPropType' is declared but its value is neve... Remove this comment to see the full error message
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
import navigateTo from "@/components/ApplicationArea/navigateTo";
|
import navigateTo from "@/components/ApplicationArea/navigateTo";
|
||||||
import recordEvent from "@/services/recordEvent";
|
import recordEvent from "@/services/recordEvent";
|
||||||
import { policy } from "@/services/policy";
|
import { policy } from "@/services/policy";
|
||||||
import { Dashboard } from "@/services/dashboard";
|
import { Dashboard } from "@/services/dashboard";
|
||||||
|
|
||||||
function CreateDashboardDialog({ dialog }) {
|
type Props = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'DialogPropType' refers to a value, but is being u... Remove this comment to see the full error message
|
||||||
|
dialog: DialogPropType;
|
||||||
|
};
|
||||||
|
|
||||||
|
function CreateDashboardDialog({ dialog }: Props) {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [isValid, setIsValid] = useState(false);
|
const [isValid, setIsValid] = useState(false);
|
||||||
const [saveInProgress, setSaveInProgress] = useState(false);
|
const [saveInProgress, setSaveInProgress] = useState(false);
|
||||||
const isCreateDashboardEnabled = policy.isCreateDashboardEnabled();
|
const isCreateDashboardEnabled = policy.isCreateDashboardEnabled();
|
||||||
|
|
||||||
function handleNameChange(event) {
|
function handleNameChange(event: any) {
|
||||||
const value = trim(event.target.value);
|
const value = trim(event.target.value);
|
||||||
setName(value);
|
setName(value);
|
||||||
setIsValid(value !== "");
|
setIsValid(value !== "");
|
||||||
@@ -25,10 +31,12 @@ function CreateDashboardDialog({ dialog }) {
|
|||||||
if (name !== "") {
|
if (name !== "") {
|
||||||
setSaveInProgress(true);
|
setSaveInProgress(true);
|
||||||
|
|
||||||
Dashboard.save({ name }).then(data => {
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'save' does not exist on type 'typeof Das... Remove this comment to see the full error message
|
||||||
|
Dashboard.save({ name }).then((data: any) => {
|
||||||
dialog.close();
|
dialog.close();
|
||||||
navigateTo(`${data.url}?edit`);
|
navigateTo(`${data.url}?edit`);
|
||||||
});
|
});
|
||||||
|
// @ts-expect-error ts-migrate(2554) FIXME: Expected 4 arguments, but got 2.
|
||||||
recordEvent("create", "dashboard");
|
recordEvent("create", "dashboard");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,6 +63,7 @@ function CreateDashboardDialog({ dialog }) {
|
|||||||
"data-test": "CreateDashboardDialog",
|
"data-test": "CreateDashboardDialog",
|
||||||
}}>
|
}}>
|
||||||
<DynamicComponent name="CreateDashboardDialogExtra" disabled={!isCreateDashboardEnabled}>
|
<DynamicComponent name="CreateDashboardDialogExtra" disabled={!isCreateDashboardEnabled}>
|
||||||
|
{/* @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call. */}
|
||||||
<Input
|
<Input
|
||||||
defaultValue={name}
|
defaultValue={name}
|
||||||
onChange={handleNameChange}
|
onChange={handleNameChange}
|
||||||
@@ -68,8 +77,4 @@ function CreateDashboardDialog({ dialog }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateDashboardDialog.propTypes = {
|
|
||||||
dialog: DialogPropType.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default wrapDialog(CreateDashboardDialog);
|
export default wrapDialog(CreateDashboardDialog);
|
||||||
@@ -4,6 +4,7 @@ import { chain, cloneDeep, find } from "lodash";
|
|||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import { Responsive, WidthProvider } from "react-grid-layout";
|
import { Responsive, WidthProvider } from "react-grid-layout";
|
||||||
import { VisualizationWidget, TextboxWidget, RestrictedWidget } from "@/components/dashboards/dashboard-widget";
|
import { VisualizationWidget, TextboxWidget, RestrictedWidget } from "@/components/dashboards/dashboard-widget";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'FiltersType' is declared but its value is never r... Remove this comment to see the full error message
|
||||||
import { FiltersType } from "@/components/Filters";
|
import { FiltersType } from "@/components/Filters";
|
||||||
import cfg from "@/config/dashboard-grid-options";
|
import cfg from "@/config/dashboard-grid-options";
|
||||||
import AutoHeightController from "./AutoHeightController";
|
import AutoHeightController from "./AutoHeightController";
|
||||||
@@ -14,20 +15,36 @@ import "./dashboard-grid.less";
|
|||||||
|
|
||||||
const ResponsiveGridLayout = WidthProvider(Responsive);
|
const ResponsiveGridLayout = WidthProvider(Responsive);
|
||||||
|
|
||||||
const WidgetType = PropTypes.shape({
|
type WidgetType = {
|
||||||
id: PropTypes.number.isRequired,
|
id: number;
|
||||||
options: PropTypes.shape({
|
options: {
|
||||||
position: PropTypes.shape({
|
position: {
|
||||||
col: PropTypes.number.isRequired,
|
col: number;
|
||||||
row: PropTypes.number.isRequired,
|
row: number;
|
||||||
sizeY: PropTypes.number.isRequired,
|
sizeY: number;
|
||||||
minSizeY: PropTypes.number.isRequired,
|
minSizeY: number;
|
||||||
maxSizeY: PropTypes.number.isRequired,
|
maxSizeY: number;
|
||||||
sizeX: PropTypes.number.isRequired,
|
sizeX: number;
|
||||||
minSizeX: PropTypes.number.isRequired,
|
minSizeX: number;
|
||||||
maxSizeX: PropTypes.number.isRequired,
|
maxSizeX: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const WidgetType: PropTypes.Requireable<WidgetType> = PropTypes.shape({
|
||||||
|
id: PropTypes.number.isRequired,
|
||||||
|
options: PropTypes.shape({
|
||||||
|
position: PropTypes.shape({
|
||||||
|
col: PropTypes.number.isRequired,
|
||||||
|
row: PropTypes.number.isRequired,
|
||||||
|
sizeY: PropTypes.number.isRequired,
|
||||||
|
minSizeY: PropTypes.number.isRequired,
|
||||||
|
maxSizeY: PropTypes.number.isRequired,
|
||||||
|
sizeX: PropTypes.number.isRequired,
|
||||||
|
minSizeX: PropTypes.number.isRequired,
|
||||||
|
maxSizeX: PropTypes.number.isRequired,
|
||||||
|
}).isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
}).isRequired,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const SINGLE = "single-column";
|
const SINGLE = "single-column";
|
||||||
@@ -35,15 +52,25 @@ const MULTI = "multi-column";
|
|||||||
|
|
||||||
const DashboardWidget = React.memo(
|
const DashboardWidget = React.memo(
|
||||||
function DashboardWidget({
|
function DashboardWidget({
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'widget' does not exist on type '{ childr... Remove this comment to see the full error message
|
||||||
widget,
|
widget,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'dashboard' does not exist on type '{ chi... Remove this comment to see the full error message
|
||||||
dashboard,
|
dashboard,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onLoadWidget' does not exist on type '{ ... Remove this comment to see the full error message
|
||||||
onLoadWidget,
|
onLoadWidget,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onRefreshWidget' does not exist on type ... Remove this comment to see the full error message
|
||||||
onRefreshWidget,
|
onRefreshWidget,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onRemoveWidget' does not exist on type '... Remove this comment to see the full error message
|
||||||
onRemoveWidget,
|
onRemoveWidget,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'onParameterMappingsChange' does not exis... Remove this comment to see the full error message
|
||||||
onParameterMappingsChange,
|
onParameterMappingsChange,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'canEdit' does not exist on type '{ child... Remove this comment to see the full error message
|
||||||
canEdit,
|
canEdit,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isPublic' does not exist on type '{ chil... Remove this comment to see the full error message
|
||||||
isPublic,
|
isPublic,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isLoading' does not exist on type '{ chi... Remove this comment to see the full error message
|
||||||
isLoading,
|
isLoading,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'filters' does not exist on type '{ child... Remove this comment to see the full error message
|
||||||
filters,
|
filters,
|
||||||
}) {
|
}) {
|
||||||
const { type } = widget;
|
const { type } = widget;
|
||||||
@@ -68,32 +95,44 @@ const DashboardWidget = React.memo(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (type === WidgetTypeEnum.TEXTBOX) {
|
if (type === WidgetTypeEnum.TEXTBOX) {
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ widget: any; canEdit: any; isPublic: any; ... Remove this comment to see the full error message
|
||||||
return <TextboxWidget widget={widget} canEdit={canEdit} isPublic={isPublic} onDelete={onDelete} />;
|
return <TextboxWidget widget={widget} canEdit={canEdit} isPublic={isPublic} onDelete={onDelete} />;
|
||||||
}
|
}
|
||||||
return <RestrictedWidget widget={widget} />;
|
return <RestrictedWidget widget={widget} />;
|
||||||
},
|
},
|
||||||
(prevProps, nextProps) =>
|
(prevProps, nextProps) =>
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'widget' does not exist on type 'Readonly... Remove this comment to see the full error message
|
||||||
prevProps.widget === nextProps.widget &&
|
prevProps.widget === nextProps.widget &&
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'canEdit' does not exist on type 'Readonl... Remove this comment to see the full error message
|
||||||
prevProps.canEdit === nextProps.canEdit &&
|
prevProps.canEdit === nextProps.canEdit &&
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isPublic' does not exist on type 'Readon... Remove this comment to see the full error message
|
||||||
prevProps.isPublic === nextProps.isPublic &&
|
prevProps.isPublic === nextProps.isPublic &&
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'isLoading' does not exist on type 'Reado... Remove this comment to see the full error message
|
||||||
prevProps.isLoading === nextProps.isLoading &&
|
prevProps.isLoading === nextProps.isLoading &&
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'filters' does not exist on type 'Readonl... Remove this comment to see the full error message
|
||||||
prevProps.filters === nextProps.filters
|
prevProps.filters === nextProps.filters
|
||||||
);
|
);
|
||||||
|
|
||||||
class DashboardGrid extends React.Component {
|
type OwnProps = {
|
||||||
static propTypes = {
|
isEditing: boolean;
|
||||||
isEditing: PropTypes.bool.isRequired,
|
isPublic?: boolean;
|
||||||
isPublic: PropTypes.bool,
|
dashboard: any;
|
||||||
dashboard: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
widgets: WidgetType[];
|
||||||
widgets: PropTypes.arrayOf(WidgetType).isRequired,
|
// @ts-expect-error ts-migrate(2749) FIXME: 'FiltersType' refers to a value, but is being used... Remove this comment to see the full error message
|
||||||
filters: FiltersType,
|
filters?: FiltersType;
|
||||||
onBreakpointChange: PropTypes.func,
|
onBreakpointChange?: (...args: any[]) => any;
|
||||||
onLoadWidget: PropTypes.func,
|
onLoadWidget?: (...args: any[]) => any;
|
||||||
onRefreshWidget: PropTypes.func,
|
onRefreshWidget?: (...args: any[]) => any;
|
||||||
onRemoveWidget: PropTypes.func,
|
onRemoveWidget?: (...args: any[]) => any;
|
||||||
onLayoutChange: PropTypes.func,
|
onLayoutChange?: (...args: any[]) => any;
|
||||||
onParameterMappingsChange: PropTypes.func,
|
onParameterMappingsChange?: (...args: any[]) => any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type State = any;
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof DashboardGrid.defaultProps;
|
||||||
|
|
||||||
|
class DashboardGrid extends React.Component<Props, State> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
isPublic: false,
|
isPublic: false,
|
||||||
@@ -106,7 +145,7 @@ class DashboardGrid extends React.Component {
|
|||||||
onParameterMappingsChange: () => {},
|
onParameterMappingsChange: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
static normalizeFrom(widget) {
|
static normalizeFrom(widget: any) {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
options: { position: pos },
|
options: { position: pos },
|
||||||
@@ -129,7 +168,7 @@ class DashboardGrid extends React.Component {
|
|||||||
|
|
||||||
autoHeightCtrl = null;
|
autoHeightCtrl = null;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -138,7 +177,9 @@ class DashboardGrid extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// init AutoHeightController
|
// init AutoHeightController
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'AutoHeightController' is not assignable to t... Remove this comment to see the full error message
|
||||||
this.autoHeightCtrl = new AutoHeightController(this.onWidgetHeightUpdated);
|
this.autoHeightCtrl = new AutoHeightController(this.onWidgetHeightUpdated);
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
this.autoHeightCtrl.update(this.props.widgets);
|
this.autoHeightCtrl.update(this.props.widgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,14 +194,16 @@ class DashboardGrid extends React.Component {
|
|||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
// update, in case widgets added or removed
|
// update, in case widgets added or removed
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
this.autoHeightCtrl.update(this.props.widgets);
|
this.autoHeightCtrl.update(this.props.widgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
this.autoHeightCtrl.destroy();
|
this.autoHeightCtrl.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
onLayoutChange = (_, layouts) => {
|
onLayoutChange = (_: any, layouts: any) => {
|
||||||
// workaround for when dashboard starts at single mode and then multi is empty or carries single col data
|
// workaround for when dashboard starts at single mode and then multi is empty or carries single col data
|
||||||
// fixes test dashboard_spec['shows widgets with full width']
|
// fixes test dashboard_spec['shows widgets with full width']
|
||||||
// TODO: open react-grid-layout issue
|
// TODO: open react-grid-layout issue
|
||||||
@@ -170,6 +213,7 @@ class DashboardGrid extends React.Component {
|
|||||||
|
|
||||||
// workaround for https://github.com/STRML/react-grid-layout/issues/889
|
// workaround for https://github.com/STRML/react-grid-layout/issues/889
|
||||||
// remove next line when fix lands
|
// remove next line when fix lands
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '"single-column" | "multi-column"' is not ass... Remove this comment to see the full error message
|
||||||
this.mode = document.body.offsetWidth <= cfg.mobileBreakPoint ? SINGLE : MULTI;
|
this.mode = document.body.offsetWidth <= cfg.mobileBreakPoint ? SINGLE : MULTI;
|
||||||
// end workaround
|
// end workaround
|
||||||
|
|
||||||
@@ -186,14 +230,16 @@ class DashboardGrid extends React.Component {
|
|||||||
this.props.onLayoutChange(normalized);
|
this.props.onLayoutChange(normalized);
|
||||||
};
|
};
|
||||||
|
|
||||||
onBreakpointChange = mode => {
|
onBreakpointChange = (mode: any) => {
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.props.onBreakpointChange(mode === SINGLE);
|
this.props.onBreakpointChange(mode === SINGLE);
|
||||||
};
|
};
|
||||||
|
|
||||||
// height updated by auto-height
|
// height updated by auto-height
|
||||||
onWidgetHeightUpdated = (widgetId, newHeight) => {
|
onWidgetHeightUpdated = (widgetId: any, newHeight: any) => {
|
||||||
this.setState(({ layouts }) => {
|
this.setState(({
|
||||||
|
layouts
|
||||||
|
}: any) => {
|
||||||
const layout = cloneDeep(layouts[MULTI]); // must clone to allow react-grid-layout to compare prev/next state
|
const layout = cloneDeep(layouts[MULTI]); // must clone to allow react-grid-layout to compare prev/next state
|
||||||
const item = find(layout, { i: widgetId.toString() });
|
const item = find(layout, { i: widgetId.toString() });
|
||||||
if (item) {
|
if (item) {
|
||||||
@@ -206,20 +252,23 @@ class DashboardGrid extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// height updated by manual resize
|
// height updated by manual resize
|
||||||
onWidgetResize = (layout, oldItem, newItem) => {
|
onWidgetResize = (layout: any, oldItem: any, newItem: any) => {
|
||||||
if (oldItem.h !== newItem.h) {
|
if (oldItem.h !== newItem.h) {
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
this.autoHeightCtrl.remove(Number(newItem.i));
|
this.autoHeightCtrl.remove(Number(newItem.i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
this.autoHeightCtrl.resume();
|
this.autoHeightCtrl.resume();
|
||||||
};
|
};
|
||||||
|
|
||||||
normalizeTo = layout => ({
|
normalizeTo = (layout: any) => ({
|
||||||
col: layout.x,
|
col: layout.x,
|
||||||
row: layout.y,
|
row: layout.y,
|
||||||
sizeX: layout.w,
|
sizeX: layout.w,
|
||||||
sizeY: layout.h,
|
sizeY: layout.h,
|
||||||
autoHeight: this.autoHeightCtrl.exists(layout.i),
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
|
autoHeight: this.autoHeightCtrl.exists(layout.i)
|
||||||
});
|
});
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -245,6 +294,7 @@ class DashboardGrid extends React.Component {
|
|||||||
margin={[cfg.margins, cfg.margins]}
|
margin={[cfg.margins, cfg.margins]}
|
||||||
isDraggable={this.props.isEditing}
|
isDraggable={this.props.isEditing}
|
||||||
isResizable={this.props.isEditing}
|
isResizable={this.props.isEditing}
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
onResizeStart={this.autoHeightCtrl.stop}
|
onResizeStart={this.autoHeightCtrl.stop}
|
||||||
onResizeStop={this.onWidgetResize}
|
onResizeStop={this.onWidgetResize}
|
||||||
layouts={this.state.layouts}
|
layouts={this.state.layouts}
|
||||||
@@ -258,13 +308,16 @@ class DashboardGrid extends React.Component {
|
|||||||
data-widgetid={widget.id}
|
data-widgetid={widget.id}
|
||||||
data-test={`WidgetId${widget.id}`}
|
data-test={`WidgetId${widget.id}`}
|
||||||
className={cx("dashboard-widget-wrapper", {
|
className={cx("dashboard-widget-wrapper", {
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
"widget-auto-height-enabled": this.autoHeightCtrl.exists(widget.id),
|
"widget-auto-height-enabled": this.autoHeightCtrl.exists(widget.id),
|
||||||
})}>
|
})}>
|
||||||
<DashboardWidget
|
<DashboardWidget
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ dashboard: any; widget: WidgetType; filter... Remove this comment to see the full error message
|
||||||
dashboard={dashboard}
|
dashboard={dashboard}
|
||||||
widget={widget}
|
widget={widget}
|
||||||
filters={filters}
|
filters={filters}
|
||||||
isPublic={isPublic}
|
isPublic={isPublic}
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'loading' does not exist on type 'WidgetT... Remove this comment to see the full error message
|
||||||
isLoading={widget.loading}
|
isLoading={widget.loading}
|
||||||
canEdit={dashboard.canEdit()}
|
canEdit={dashboard.canEdit()}
|
||||||
onLoadWidget={onLoadWidget}
|
onLoadWidget={onLoadWidget}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { isMatch, map, find, sortBy } from "lodash";
|
import { isMatch, map, find, sortBy } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DialogPropType' is declared but its value is neve... Remove this comment to see the full error message
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
import {
|
import {
|
||||||
MappingType,
|
MappingType,
|
||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
} from "@/components/ParameterMappingInput";
|
} from "@/components/ParameterMappingInput";
|
||||||
import notification from "@/services/notification";
|
import notification from "@/services/notification";
|
||||||
|
|
||||||
export function getParamValuesSnapshot(mappings, dashboardParameters) {
|
export function getParamValuesSnapshot(mappings: any, dashboardParameters: any) {
|
||||||
return map(
|
return map(
|
||||||
sortBy(mappings, m => m.name),
|
sortBy(mappings, m => m.name),
|
||||||
m => {
|
m => {
|
||||||
@@ -32,24 +32,30 @@ export function getParamValuesSnapshot(mappings, dashboardParameters) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class EditParameterMappingsDialog extends React.Component {
|
type Props = {
|
||||||
static propTypes = {
|
dashboard: any;
|
||||||
dashboard: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
widget: any;
|
||||||
widget: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
// @ts-expect-error ts-migrate(2749) FIXME: 'DialogPropType' refers to a value, but is being u... Remove this comment to see the full error message
|
||||||
dialog: DialogPropType.isRequired,
|
dialog: DialogPropType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type State = any;
|
||||||
|
|
||||||
|
class EditParameterMappingsDialog extends React.Component<Props, State> {
|
||||||
|
|
||||||
originalParamValuesSnapshot = null;
|
originalParamValuesSnapshot = null;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
const parameterMappings = parameterMappingsToEditableMappings(
|
const parameterMappings = parameterMappingsToEditableMappings(
|
||||||
props.widget.options.parameterMappings,
|
props.widget.options.parameterMappings,
|
||||||
props.widget.query.getParametersDefs(),
|
props.widget.query.getParametersDefs(),
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'any[]' is not assignable to para... Remove this comment to see the full error message
|
||||||
map(this.props.dashboard.getParametersDefs(), p => p.name)
|
map(this.props.dashboard.getParametersDefs(), p => p.name)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(any[] | undefined)[]' is not assignable to ... Remove this comment to see the full error message
|
||||||
this.originalParamValuesSnapshot = getParamValuesSnapshot(
|
this.originalParamValuesSnapshot = getParamValuesSnapshot(
|
||||||
parameterMappings,
|
parameterMappings,
|
||||||
this.props.dashboard.getParametersDefs()
|
this.props.dashboard.getParametersDefs()
|
||||||
@@ -70,6 +76,7 @@ class EditParameterMappingsDialog extends React.Component {
|
|||||||
widget.options.parameterMappings = newMappings;
|
widget.options.parameterMappings = newMappings;
|
||||||
|
|
||||||
const valuesChanged = !isMatch(
|
const valuesChanged = !isMatch(
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'null' is not assignable to param... Remove this comment to see the full error message
|
||||||
this.originalParamValuesSnapshot,
|
this.originalParamValuesSnapshot,
|
||||||
getParamValuesSnapshot(this.state.parameterMappings, this.props.dashboard.getParametersDefs())
|
getParamValuesSnapshot(this.state.parameterMappings, this.props.dashboard.getParametersDefs())
|
||||||
);
|
);
|
||||||
@@ -84,6 +91,7 @@ class EditParameterMappingsDialog extends React.Component {
|
|||||||
this.props.dialog.close(valuesChanged);
|
this.props.dialog.close(valuesChanged);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message
|
||||||
notification.error("Widget cannot be updated");
|
notification.error("Widget cannot be updated");
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -91,7 +99,7 @@ class EditParameterMappingsDialog extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateParamMappings(parameterMappings) {
|
updateParamMappings(parameterMappings: any) {
|
||||||
this.setState({ parameterMappings });
|
this.setState({ parameterMappings });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +116,8 @@ class EditParameterMappingsDialog extends React.Component {
|
|||||||
<ParameterMappingListInput
|
<ParameterMappingListInput
|
||||||
mappings={this.state.parameterMappings}
|
mappings={this.state.parameterMappings}
|
||||||
existingParams={this.props.dashboard.getParametersDefs()}
|
existingParams={this.props.dashboard.getParametersDefs()}
|
||||||
onChange={mappings => this.updateParamMappings(mappings)}
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
|
onChange={(mappings: any) => this.updateParamMappings(mappings)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
@@ -1,13 +1,24 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import Button from "antd/lib/button";
|
import Button from "antd/lib/button";
|
||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DialogPropType' is declared but its value is neve... Remove this comment to see the full error message
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'FiltersType' is declared but its value is never r... Remove this comment to see the full error message
|
||||||
import { FiltersType } from "@/components/Filters";
|
import { FiltersType } from "@/components/Filters";
|
||||||
import VisualizationRenderer from "@/components/visualizations/VisualizationRenderer";
|
import VisualizationRenderer from "@/components/visualizations/VisualizationRenderer";
|
||||||
import VisualizationName from "@/components/visualizations/VisualizationName";
|
import VisualizationName from "@/components/visualizations/VisualizationName";
|
||||||
|
|
||||||
function ExpandedWidgetDialog({ dialog, widget, filters }) {
|
type OwnProps = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'DialogPropType' refers to a value, but is being u... Remove this comment to see the full error message
|
||||||
|
dialog: DialogPropType;
|
||||||
|
widget: any;
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'FiltersType' refers to a value, but is being used... Remove this comment to see the full error message
|
||||||
|
filters?: FiltersType;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof ExpandedWidgetDialog.defaultProps;
|
||||||
|
|
||||||
|
function ExpandedWidgetDialog({ dialog, widget, filters }: Props) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
{...dialog.props}
|
{...dialog.props}
|
||||||
@@ -28,12 +39,6 @@ function ExpandedWidgetDialog({ dialog, widget, filters }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpandedWidgetDialog.propTypes = {
|
|
||||||
dialog: DialogPropType.isRequired,
|
|
||||||
widget: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
|
||||||
filters: FiltersType,
|
|
||||||
};
|
|
||||||
|
|
||||||
ExpandedWidgetDialog.defaultProps = {
|
ExpandedWidgetDialog.defaultProps = {
|
||||||
filters: [],
|
filters: [],
|
||||||
};
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { toString } from "lodash";
|
import { toString } from "lodash";
|
||||||
|
// @ts-expect-error ts-migrate(7016) FIXME: Could not find a declaration file for module 'mark... Remove this comment to see the full error message
|
||||||
import { markdown } from "markdown";
|
import { markdown } from "markdown";
|
||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
import Input from "antd/lib/input";
|
import Input from "antd/lib/input";
|
||||||
@@ -9,12 +9,22 @@ import Tooltip from "antd/lib/tooltip";
|
|||||||
import Divider from "antd/lib/divider";
|
import Divider from "antd/lib/divider";
|
||||||
import Link from "@/components/Link";
|
import Link from "@/components/Link";
|
||||||
import HtmlContent from "@redash/viz/lib/components/HtmlContent";
|
import HtmlContent from "@redash/viz/lib/components/HtmlContent";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DialogPropType' is declared but its value is neve... Remove this comment to see the full error message
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
import notification from "@/services/notification";
|
import notification from "@/services/notification";
|
||||||
|
|
||||||
import "./TextboxDialog.less";
|
import "./TextboxDialog.less";
|
||||||
|
|
||||||
function TextboxDialog({ dialog, isNew, ...props }) {
|
type OwnProps = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'DialogPropType' refers to a value, but is being u... Remove this comment to see the full error message
|
||||||
|
dialog: DialogPropType;
|
||||||
|
isNew?: boolean;
|
||||||
|
text?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof TextboxDialog.defaultProps;
|
||||||
|
|
||||||
|
function TextboxDialog({ dialog, isNew, ...props }: Props) {
|
||||||
const [text, setText] = useState(toString(props.text));
|
const [text, setText] = useState(toString(props.text));
|
||||||
const [preview, setPreview] = useState(null);
|
const [preview, setPreview] = useState(null);
|
||||||
|
|
||||||
@@ -37,6 +47,7 @@ function TextboxDialog({ dialog, isNew, ...props }) {
|
|||||||
|
|
||||||
const saveWidget = useCallback(() => {
|
const saveWidget = useCallback(() => {
|
||||||
dialog.close(text).catch(() => {
|
dialog.close(text).catch(() => {
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string' is not assignable to par... Remove this comment to see the full error message
|
||||||
notification.error(isNew ? "Widget could not be added" : "Widget could not be saved");
|
notification.error(isNew ? "Widget could not be added" : "Widget could not be saved");
|
||||||
});
|
});
|
||||||
}, [dialog, isNew, text]);
|
}, [dialog, isNew, text]);
|
||||||
@@ -71,6 +82,7 @@ function TextboxDialog({ dialog, isNew, ...props }) {
|
|||||||
<div className="textbox-dialog">
|
<div className="textbox-dialog">
|
||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
className="resize-vertical"
|
className="resize-vertical"
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'number | ... Remove this comment to see the full error message
|
||||||
rows="5"
|
rows="5"
|
||||||
value={text}
|
value={text}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
@@ -83,6 +95,7 @@ function TextboxDialog({ dialog, isNew, ...props }) {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
href="https://www.markdownguide.org/cheat-sheet/#basic-syntax">
|
href="https://www.markdownguide.org/cheat-sheet/#basic-syntax">
|
||||||
|
{/* @ts-expect-error ts-migrate(2747) FIXME: 'Tooltip' components don't accept text as child el... Remove this comment to see the full error message */}
|
||||||
<Tooltip title="Markdown guide opens in new window">Markdown</Tooltip>
|
<Tooltip title="Markdown guide opens in new window">Markdown</Tooltip>
|
||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
@@ -91,6 +104,7 @@ function TextboxDialog({ dialog, isNew, ...props }) {
|
|||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Divider dashed />
|
<Divider dashed />
|
||||||
<strong className="preview-title">Preview:</strong>
|
<strong className="preview-title">Preview:</strong>
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ children: null; className: string; }' is n... Remove this comment to see the full error message */}
|
||||||
<HtmlContent className="preview markdown">{preview}</HtmlContent>
|
<HtmlContent className="preview markdown">{preview}</HtmlContent>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
@@ -99,12 +113,6 @@ function TextboxDialog({ dialog, isNew, ...props }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextboxDialog.propTypes = {
|
|
||||||
dialog: DialogPropType.isRequired,
|
|
||||||
isNew: PropTypes.bool,
|
|
||||||
text: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
TextboxDialog.defaultProps = {
|
TextboxDialog.defaultProps = {
|
||||||
isNew: false,
|
isNew: false,
|
||||||
text: "",
|
text: "",
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Widget from "./Widget";
|
import Widget from "./Widget";
|
||||||
|
|
||||||
function RestrictedWidget(props) {
|
function RestrictedWidget(props: any) {
|
||||||
return (
|
return (
|
||||||
<Widget {...props} className="d-flex justify-content-center align-items-center widget-restricted">
|
<Widget {...props} className="d-flex justify-content-center align-items-center widget-restricted">
|
||||||
<div className="t-body scrollbox">
|
<div className="t-body scrollbox">
|
||||||
@@ -1,19 +1,26 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
// @ts-expect-error ts-migrate(7016) FIXME: Could not find a declaration file for module 'mark... Remove this comment to see the full error message
|
||||||
import { markdown } from "markdown";
|
import { markdown } from "markdown";
|
||||||
import Menu from "antd/lib/menu";
|
import Menu from "antd/lib/menu";
|
||||||
import HtmlContent from "@redash/viz/lib/components/HtmlContent";
|
import HtmlContent from "@redash/viz/lib/components/HtmlContent";
|
||||||
import TextboxDialog from "@/components/dashboards/TextboxDialog";
|
import TextboxDialog from "@/components/dashboards/TextboxDialog";
|
||||||
import Widget from "./Widget";
|
import Widget from "./Widget";
|
||||||
|
|
||||||
function TextboxWidget(props) {
|
type OwnProps = {
|
||||||
|
widget: any;
|
||||||
|
canEdit?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof TextboxWidget.defaultProps;
|
||||||
|
|
||||||
|
function TextboxWidget(props: Props) {
|
||||||
const { widget, canEdit } = props;
|
const { widget, canEdit } = props;
|
||||||
const [text, setText] = useState(widget.text);
|
const [text, setText] = useState(widget.text);
|
||||||
|
|
||||||
const editTextBox = () => {
|
const editTextBox = () => {
|
||||||
TextboxDialog.showModal({
|
TextboxDialog.showModal({
|
||||||
text: widget.text,
|
text: widget.text,
|
||||||
}).onClose(newText => {
|
}).onClose((newText: any) => {
|
||||||
widget.text = newText;
|
widget.text = newText;
|
||||||
setText(newText);
|
setText(newText);
|
||||||
return widget.save();
|
return widget.save();
|
||||||
@@ -31,17 +38,14 @@ function TextboxWidget(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
<Widget {...props} menuOptions={canEdit ? TextboxMenuOptions : null} className="widget-text">
|
<Widget {...props} menuOptions={canEdit ? TextboxMenuOptions : null} className="widget-text">
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ children: any; className: string; }' is no... Remove this comment to see the full error message */}
|
||||||
<HtmlContent className="body-row-auto scrollbox t-body p-15 markdown">{markdown.toHTML(text || "")}</HtmlContent>
|
<HtmlContent className="body-row-auto scrollbox t-body p-15 markdown">{markdown.toHTML(text || "")}</HtmlContent>
|
||||||
</Widget>
|
</Widget>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextboxWidget.propTypes = {
|
|
||||||
widget: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
|
||||||
canEdit: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
TextboxWidget.defaultProps = {
|
TextboxWidget.defaultProps = {
|
||||||
canEdit: false,
|
canEdit: false,
|
||||||
};
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { compact, isEmpty, invoke } from "lodash";
|
import { compact, isEmpty, invoke } from "lodash";
|
||||||
|
// @ts-expect-error ts-migrate(7016) FIXME: Could not find a declaration file for module 'mark... Remove this comment to see the full error message
|
||||||
import { markdown } from "markdown";
|
import { markdown } from "markdown";
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import Menu from "antd/lib/menu";
|
import Menu from "antd/lib/menu";
|
||||||
@@ -12,22 +12,28 @@ import Link from "@/components/Link";
|
|||||||
import Parameters from "@/components/Parameters";
|
import Parameters from "@/components/Parameters";
|
||||||
import TimeAgo from "@/components/TimeAgo";
|
import TimeAgo from "@/components/TimeAgo";
|
||||||
import Timer from "@/components/Timer";
|
import Timer from "@/components/Timer";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'Moment' is declared but its value is never read.
|
||||||
import { Moment } from "@/components/proptypes";
|
import { Moment } from "@/components/proptypes";
|
||||||
import QueryLink from "@/components/QueryLink";
|
import QueryLink from "@/components/QueryLink";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'FiltersType' is declared but its value is never r... Remove this comment to see the full error message
|
||||||
import { FiltersType } from "@/components/Filters";
|
import { FiltersType } from "@/components/Filters";
|
||||||
import ExpandedWidgetDialog from "@/components/dashboards/ExpandedWidgetDialog";
|
import ExpandedWidgetDialog from "@/components/dashboards/ExpandedWidgetDialog";
|
||||||
import EditParameterMappingsDialog from "@/components/dashboards/EditParameterMappingsDialog";
|
import EditParameterMappingsDialog from "@/components/dashboards/EditParameterMappingsDialog";
|
||||||
import VisualizationRenderer from "@/components/visualizations/VisualizationRenderer";
|
import VisualizationRenderer from "@/components/visualizations/VisualizationRenderer";
|
||||||
import Widget from "./Widget";
|
import Widget from "./Widget";
|
||||||
|
|
||||||
function visualizationWidgetMenuOptions({ widget, canEditDashboard, onParametersEdit }) {
|
function visualizationWidgetMenuOptions({
|
||||||
|
widget,
|
||||||
|
canEditDashboard,
|
||||||
|
onParametersEdit
|
||||||
|
}: any) {
|
||||||
const canViewQuery = currentUser.hasPermission("view_query");
|
const canViewQuery = currentUser.hasPermission("view_query");
|
||||||
const canEditParameters = canEditDashboard && !isEmpty(invoke(widget, "query.getParametersDefs"));
|
const canEditParameters = canEditDashboard && !isEmpty(invoke(widget, "query.getParametersDefs"));
|
||||||
const widgetQueryResult = widget.getQueryResult();
|
const widgetQueryResult = widget.getQueryResult();
|
||||||
const isQueryResultEmpty = !widgetQueryResult || !widgetQueryResult.isEmpty || widgetQueryResult.isEmpty();
|
const isQueryResultEmpty = !widgetQueryResult || !widgetQueryResult.isEmpty || widgetQueryResult.isEmpty();
|
||||||
|
|
||||||
const downloadLink = fileType => widgetQueryResult.getLink(widget.getQuery().id, fileType);
|
const downloadLink = (fileType: any) => widgetQueryResult.getLink(widget.getQuery().id, fileType);
|
||||||
const downloadName = fileType => widgetQueryResult.getName(widget.getQuery().name, fileType);
|
const downloadName = (fileType: any) => widgetQueryResult.getName(widget.getQuery().name, fileType);
|
||||||
return compact([
|
return compact([
|
||||||
<Menu.Item key="download_csv" disabled={isQueryResultEmpty}>
|
<Menu.Item key="download_csv" disabled={isQueryResultEmpty}>
|
||||||
{!isQueryResultEmpty ? (
|
{!isQueryResultEmpty ? (
|
||||||
@@ -70,7 +76,14 @@ function visualizationWidgetMenuOptions({ widget, canEditDashboard, onParameters
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function RefreshIndicator({ refreshStartedAt }) {
|
type OwnRefreshIndicatorProps = {
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
refreshStartedAt?: Moment;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RefreshIndicatorProps = OwnRefreshIndicatorProps & typeof RefreshIndicator.defaultProps;
|
||||||
|
|
||||||
|
function RefreshIndicator({ refreshStartedAt }: RefreshIndicatorProps) {
|
||||||
return (
|
return (
|
||||||
<div className="refresh-indicator">
|
<div className="refresh-indicator">
|
||||||
<div className="refresh-icon">
|
<div className="refresh-icon">
|
||||||
@@ -80,11 +93,19 @@ function RefreshIndicator({ refreshStartedAt }) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshIndicator.propTypes = { refreshStartedAt: Moment };
|
|
||||||
RefreshIndicator.defaultProps = { refreshStartedAt: null };
|
RefreshIndicator.defaultProps = { refreshStartedAt: null };
|
||||||
|
|
||||||
function VisualizationWidgetHeader({ widget, refreshStartedAt, parameters, onParametersUpdate }) {
|
type OwnVisualizationWidgetHeaderProps = {
|
||||||
|
widget: any;
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
|
refreshStartedAt?: Moment;
|
||||||
|
parameters?: any[];
|
||||||
|
onParametersUpdate?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type VisualizationWidgetHeaderProps = OwnVisualizationWidgetHeaderProps & typeof VisualizationWidgetHeader.defaultProps;
|
||||||
|
|
||||||
|
function VisualizationWidgetHeader({ widget, refreshStartedAt, parameters, onParametersUpdate }: VisualizationWidgetHeaderProps) {
|
||||||
const canViewQuery = currentUser.hasPermission("view_query");
|
const canViewQuery = currentUser.hasPermission("view_query");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -93,9 +114,11 @@ function VisualizationWidgetHeader({ widget, refreshStartedAt, parameters, onPar
|
|||||||
<div className="t-header widget clearfix">
|
<div className="t-header widget clearfix">
|
||||||
<div className="th-title">
|
<div className="th-title">
|
||||||
<p>
|
<p>
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'. */}
|
||||||
<QueryLink query={widget.getQuery()} visualization={widget.visualization} readOnly={!canViewQuery} />
|
<QueryLink query={widget.getQuery()} visualization={widget.visualization} readOnly={!canViewQuery} />
|
||||||
</p>
|
</p>
|
||||||
{!isEmpty(widget.getQuery().description) && (
|
{!isEmpty(widget.getQuery().description) && (
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ children: any; className: string; }' is no... Remove this comment to see the full error message
|
||||||
<HtmlContent className="text-muted markdown query--description">
|
<HtmlContent className="text-muted markdown query--description">
|
||||||
{markdown.toHTML(widget.getQuery().description || "")}
|
{markdown.toHTML(widget.getQuery().description || "")}
|
||||||
</HtmlContent>
|
</HtmlContent>
|
||||||
@@ -111,27 +134,30 @@ function VisualizationWidgetHeader({ widget, refreshStartedAt, parameters, onPar
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
VisualizationWidgetHeader.propTypes = {
|
|
||||||
widget: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
|
||||||
refreshStartedAt: Moment,
|
|
||||||
parameters: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
onParametersUpdate: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
VisualizationWidgetHeader.defaultProps = {
|
VisualizationWidgetHeader.defaultProps = {
|
||||||
refreshStartedAt: null,
|
refreshStartedAt: null,
|
||||||
onParametersUpdate: () => {},
|
onParametersUpdate: () => {},
|
||||||
parameters: [],
|
parameters: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
function VisualizationWidgetFooter({ widget, isPublic, onRefresh, onExpand }) {
|
type OwnVisualizationWidgetFooterProps = {
|
||||||
|
widget: any;
|
||||||
|
isPublic?: boolean;
|
||||||
|
onRefresh: (...args: any[]) => any;
|
||||||
|
onExpand: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type VisualizationWidgetFooterProps = OwnVisualizationWidgetFooterProps & typeof VisualizationWidgetFooter.defaultProps;
|
||||||
|
|
||||||
|
function VisualizationWidgetFooter({ widget, isPublic, onRefresh, onExpand }: VisualizationWidgetFooterProps) {
|
||||||
const widgetQueryResult = widget.getQueryResult();
|
const widgetQueryResult = widget.getQueryResult();
|
||||||
const updatedAt = invoke(widgetQueryResult, "getUpdatedAt");
|
const updatedAt = invoke(widgetQueryResult, "getUpdatedAt");
|
||||||
const [refreshClickButtonId, setRefreshClickButtonId] = useState();
|
const [refreshClickButtonId, setRefreshClickButtonId] = useState();
|
||||||
|
|
||||||
const refreshWidget = buttonId => {
|
const refreshWidget = (buttonId: any) => {
|
||||||
if (!refreshClickButtonId) {
|
if (!refreshClickButtonId) {
|
||||||
setRefreshClickButtonId(buttonId);
|
setRefreshClickButtonId(buttonId);
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'null' is not assignable to param... Remove this comment to see the full error message
|
||||||
onRefresh().finally(() => setRefreshClickButtonId(null));
|
onRefresh().finally(() => setRefreshClickButtonId(null));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -173,28 +199,27 @@ function VisualizationWidgetFooter({ widget, isPublic, onRefresh, onExpand }) {
|
|||||||
) : null;
|
) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
VisualizationWidgetFooter.propTypes = {
|
|
||||||
widget: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
|
||||||
isPublic: PropTypes.bool,
|
|
||||||
onRefresh: PropTypes.func.isRequired,
|
|
||||||
onExpand: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
VisualizationWidgetFooter.defaultProps = { isPublic: false };
|
VisualizationWidgetFooter.defaultProps = { isPublic: false };
|
||||||
|
|
||||||
class VisualizationWidget extends React.Component {
|
type OwnVisualizationWidgetProps = {
|
||||||
static propTypes = {
|
widget: any;
|
||||||
widget: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
dashboard: any;
|
||||||
dashboard: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
// @ts-expect-error ts-migrate(2749) FIXME: 'FiltersType' refers to a value, but is being used... Remove this comment to see the full error message
|
||||||
filters: FiltersType,
|
filters?: FiltersType;
|
||||||
isPublic: PropTypes.bool,
|
isPublic?: boolean;
|
||||||
isLoading: PropTypes.bool,
|
isLoading?: boolean;
|
||||||
canEdit: PropTypes.bool,
|
canEdit?: boolean;
|
||||||
onLoad: PropTypes.func,
|
onLoad?: (...args: any[]) => any;
|
||||||
onRefresh: PropTypes.func,
|
onRefresh?: (...args: any[]) => any;
|
||||||
onDelete: PropTypes.func,
|
onDelete?: (...args: any[]) => any;
|
||||||
onParameterMappingsChange: PropTypes.func,
|
onParameterMappingsChange?: (...args: any[]) => any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type VisualizationWidgetState = any;
|
||||||
|
|
||||||
|
type VisualizationWidgetProps = OwnVisualizationWidgetProps & typeof VisualizationWidget.defaultProps;
|
||||||
|
|
||||||
|
class VisualizationWidget extends React.Component<VisualizationWidgetProps, VisualizationWidgetState> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
filters: [],
|
filters: [],
|
||||||
@@ -207,7 +232,7 @@ class VisualizationWidget extends React.Component {
|
|||||||
onParameterMappingsChange: () => {},
|
onParameterMappingsChange: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: VisualizationWidgetProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
localParameters: props.widget.getLocalParameters(),
|
localParameters: props.widget.getLocalParameters(),
|
||||||
@@ -222,7 +247,7 @@ class VisualizationWidget extends React.Component {
|
|||||||
onLoad();
|
onLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
onLocalFiltersChange = localFilters => {
|
onLocalFiltersChange = (localFilters: any) => {
|
||||||
this.setState({ localFilters });
|
this.setState({ localFilters });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -235,7 +260,7 @@ class VisualizationWidget extends React.Component {
|
|||||||
EditParameterMappingsDialog.showModal({
|
EditParameterMappingsDialog.showModal({
|
||||||
dashboard,
|
dashboard,
|
||||||
widget,
|
widget,
|
||||||
}).onClose(valuesChanged => {
|
}).onClose((valuesChanged: any) => {
|
||||||
// refresh widget if any parameter value has been updated
|
// refresh widget if any parameter value has been updated
|
||||||
if (valuesChanged) {
|
if (valuesChanged) {
|
||||||
onRefresh();
|
onRefresh();
|
||||||
@@ -267,6 +292,7 @@ class VisualizationWidget extends React.Component {
|
|||||||
visualization={widget.visualization}
|
visualization={widget.visualization}
|
||||||
queryResult={widgetQueryResult}
|
queryResult={widgetQueryResult}
|
||||||
filters={filters}
|
filters={filters}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(localFilters: any) => void' is not assignab... Remove this comment to see the full error message
|
||||||
onFiltersChange={this.onLocalFiltersChange}
|
onFiltersChange={this.onLocalFiltersChange}
|
||||||
context="widget"
|
context="widget"
|
||||||
/>
|
/>
|
||||||
@@ -290,6 +316,7 @@ class VisualizationWidget extends React.Component {
|
|||||||
const isRefreshing = isLoading && !!(widgetQueryResult && widgetQueryResult.getStatus());
|
const isRefreshing = isLoading && !!(widgetQueryResult && widgetQueryResult.getStatus());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||||
<Widget
|
<Widget
|
||||||
{...this.props}
|
{...this.props}
|
||||||
className="widget-visualization"
|
className="widget-visualization"
|
||||||
@@ -1,16 +1,24 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import { isEmpty } from "lodash";
|
import { isEmpty } from "lodash";
|
||||||
import Dropdown from "antd/lib/dropdown";
|
import Dropdown from "antd/lib/dropdown";
|
||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
import Menu from "antd/lib/menu";
|
import Menu from "antd/lib/menu";
|
||||||
import recordEvent from "@/services/recordEvent";
|
import recordEvent from "@/services/recordEvent";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'Moment' is declared but its value is never read.
|
||||||
import { Moment } from "@/components/proptypes";
|
import { Moment } from "@/components/proptypes";
|
||||||
|
|
||||||
import "./Widget.less";
|
import "./Widget.less";
|
||||||
|
|
||||||
function WidgetDropdownButton({ extraOptions, showDeleteOption, onDelete }) {
|
type OwnWidgetDropdownButtonProps = {
|
||||||
|
extraOptions?: React.ReactNode;
|
||||||
|
showDeleteOption?: boolean;
|
||||||
|
onDelete?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type WidgetDropdownButtonProps = OwnWidgetDropdownButtonProps & typeof WidgetDropdownButton.defaultProps;
|
||||||
|
|
||||||
|
function WidgetDropdownButton({ extraOptions, showDeleteOption, onDelete }: WidgetDropdownButtonProps) {
|
||||||
const WidgetMenu = (
|
const WidgetMenu = (
|
||||||
<Menu data-test="WidgetDropdownButtonMenu">
|
<Menu data-test="WidgetDropdownButtonMenu">
|
||||||
{extraOptions}
|
{extraOptions}
|
||||||
@@ -30,19 +38,19 @@ function WidgetDropdownButton({ extraOptions, showDeleteOption, onDelete }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
WidgetDropdownButton.propTypes = {
|
|
||||||
extraOptions: PropTypes.node,
|
|
||||||
showDeleteOption: PropTypes.bool,
|
|
||||||
onDelete: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
WidgetDropdownButton.defaultProps = {
|
WidgetDropdownButton.defaultProps = {
|
||||||
extraOptions: null,
|
extraOptions: null,
|
||||||
showDeleteOption: false,
|
showDeleteOption: false,
|
||||||
onDelete: () => {},
|
onDelete: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
function WidgetDeleteButton({ onClick }) {
|
type OwnWidgetDeleteButtonProps = {
|
||||||
|
onClick?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type WidgetDeleteButtonProps = OwnWidgetDeleteButtonProps & typeof WidgetDeleteButton.defaultProps;
|
||||||
|
|
||||||
|
function WidgetDeleteButton({ onClick }: WidgetDeleteButtonProps) {
|
||||||
return (
|
return (
|
||||||
<div className="widget-menu-remove">
|
<div className="widget-menu-remove">
|
||||||
<a className="action" title="Remove From Dashboard" onClick={onClick} data-test="WidgetDeleteButton">
|
<a className="action" title="Remove From Dashboard" onClick={onClick} data-test="WidgetDeleteButton">
|
||||||
@@ -51,24 +59,25 @@ function WidgetDeleteButton({ onClick }) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
WidgetDeleteButton.propTypes = { onClick: PropTypes.func };
|
|
||||||
WidgetDeleteButton.defaultProps = { onClick: () => {} };
|
WidgetDeleteButton.defaultProps = { onClick: () => {} };
|
||||||
|
|
||||||
class Widget extends React.Component {
|
type OwnWidgetProps = {
|
||||||
static propTypes = {
|
widget: any;
|
||||||
widget: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
|
className?: string;
|
||||||
className: PropTypes.string,
|
header?: React.ReactNode;
|
||||||
children: PropTypes.node,
|
footer?: React.ReactNode;
|
||||||
header: PropTypes.node,
|
canEdit?: boolean;
|
||||||
footer: PropTypes.node,
|
isPublic?: boolean;
|
||||||
canEdit: PropTypes.bool,
|
// @ts-expect-error ts-migrate(2749) FIXME: 'Moment' refers to a value, but is being used as a... Remove this comment to see the full error message
|
||||||
isPublic: PropTypes.bool,
|
refreshStartedAt?: Moment;
|
||||||
refreshStartedAt: Moment,
|
menuOptions?: React.ReactNode;
|
||||||
menuOptions: PropTypes.node,
|
tileProps?: any;
|
||||||
tileProps: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
onDelete?: (...args: any[]) => any;
|
||||||
onDelete: PropTypes.func,
|
};
|
||||||
};
|
|
||||||
|
type WidgetProps = OwnWidgetProps & typeof Widget.defaultProps;
|
||||||
|
|
||||||
|
class Widget extends React.Component<WidgetProps> {
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
className: "",
|
className: "",
|
||||||
@@ -85,6 +94,7 @@ class Widget extends React.Component {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { widget } = this.props;
|
const { widget } = this.props;
|
||||||
|
// @ts-expect-error ts-migrate(2554) FIXME: Expected 4 arguments, but got 3.
|
||||||
recordEvent("view", "widget", widget.id);
|
recordEvent("view", "widget", widget.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,19 +12,37 @@ import helper from "./dynamicFormHelper";
|
|||||||
|
|
||||||
import "./DynamicForm.less";
|
import "./DynamicForm.less";
|
||||||
|
|
||||||
const ActionType = PropTypes.shape({
|
type ActionType = {
|
||||||
name: PropTypes.string.isRequired,
|
name: string;
|
||||||
callback: PropTypes.func.isRequired,
|
callback: (...args: any[]) => any;
|
||||||
type: PropTypes.string,
|
type?: string;
|
||||||
pullRight: PropTypes.bool,
|
pullRight?: boolean;
|
||||||
disabledWhenDirty: PropTypes.bool,
|
disabledWhenDirty?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'Requireable<InferProps<{ name: Validator<str... Remove this comment to see the full error message
|
||||||
|
const ActionType: PropTypes.Requireable<ActionType> = PropTypes.shape({
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
callback: PropTypes.func.isRequired,
|
||||||
|
type: PropTypes.string,
|
||||||
|
pullRight: PropTypes.bool,
|
||||||
|
disabledWhenDirty: PropTypes.bool,
|
||||||
});
|
});
|
||||||
|
|
||||||
const AntdFormType = PropTypes.shape({
|
type AntdFormType = {
|
||||||
validateFieldsAndScroll: PropTypes.func,
|
validateFieldsAndScroll?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'Requireable<InferProps<{ validateFieldsAndSc... Remove this comment to see the full error message
|
||||||
|
const AntdFormType: PropTypes.Requireable<AntdFormType> = PropTypes.shape({
|
||||||
|
validateFieldsAndScroll: PropTypes.func,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fieldRules = ({ type, required, minLength }) => {
|
const fieldRules = ({
|
||||||
|
type,
|
||||||
|
required,
|
||||||
|
minLength
|
||||||
|
}: any) => {
|
||||||
const requiredRule = required;
|
const requiredRule = required;
|
||||||
const minLengthRule = minLength && includes(["text", "email", "password"], type);
|
const minLengthRule = minLength && includes(["text", "email", "password"], type);
|
||||||
const emailTypeRule = type === "email";
|
const emailTypeRule = type === "email";
|
||||||
@@ -36,7 +54,7 @@ const fieldRules = ({ type, required, minLength }) => {
|
|||||||
].filter(rule => rule);
|
].filter(rule => rule);
|
||||||
};
|
};
|
||||||
|
|
||||||
function normalizeEmptyValuesToNull(fields, values) {
|
function normalizeEmptyValuesToNull(fields: any, values: any) {
|
||||||
return mapValues(values, (value, key) => {
|
return mapValues(values, (value, key) => {
|
||||||
const { initialValue } = find(fields, { name: key }) || {};
|
const { initialValue } = find(fields, { name: key }) || {};
|
||||||
if ((initialValue === null || initialValue === undefined || initialValue === "") && value === "") {
|
if ((initialValue === null || initialValue === undefined || initialValue === "") && value === "") {
|
||||||
@@ -46,7 +64,15 @@ function normalizeEmptyValuesToNull(fields, values) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function DynamicFormFields({ fields, feedbackIcons, form }) {
|
type OwnDynamicFormFieldsProps = {
|
||||||
|
fields?: FieldType[];
|
||||||
|
feedbackIcons?: boolean;
|
||||||
|
form: AntdFormType;
|
||||||
|
};
|
||||||
|
|
||||||
|
type DynamicFormFieldsProps = OwnDynamicFormFieldsProps & typeof DynamicFormFields.defaultProps;
|
||||||
|
|
||||||
|
function DynamicFormFields({ fields, feedbackIcons, form }: DynamicFormFieldsProps) {
|
||||||
return fields.map(field => {
|
return fields.map(field => {
|
||||||
const { name, type, initialValue, contentAfter } = field;
|
const { name, type, initialValue, contentAfter } = field;
|
||||||
const fieldLabel = getFieldLabel(field);
|
const fieldLabel = getFieldLabel(field);
|
||||||
@@ -63,9 +89,11 @@ function DynamicFormFields({ fields, feedbackIcons, form }) {
|
|||||||
|
|
||||||
if (type === "file") {
|
if (type === "file") {
|
||||||
formItemProps.valuePropName = "data-value";
|
formItemProps.valuePropName = "data-value";
|
||||||
formItemProps.getValueFromEvent = e => {
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'getValueFromEvent' does not exist on typ... Remove this comment to see the full error message
|
||||||
|
formItemProps.getValueFromEvent = (e: any) => {
|
||||||
if (e && e.fileList[0]) {
|
if (e && e.fileList[0]) {
|
||||||
helper.getBase64(e.file).then(value => {
|
helper.getBase64(e.file).then(value => {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'setFieldsValue' does not exist on type '... Remove this comment to see the full error message
|
||||||
form.setFieldsValue({ [name]: value });
|
form.setFieldsValue({ [name]: value });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -76,26 +104,22 @@ function DynamicFormFields({ fields, feedbackIcons, form }) {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment key={name}>
|
<React.Fragment key={name}>
|
||||||
<Form.Item {...formItemProps}>
|
<Form.Item {...formItemProps}>
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type '{ field: FieldType; form: AntdFormType; }' i... Remove this comment to see the full error message */}
|
||||||
<DynamicFormField field={field} form={form} />
|
<DynamicFormField field={field} form={form} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'getFieldValue' does not exist on type 'A... Remove this comment to see the full error message */}
|
||||||
{isFunction(contentAfter) ? contentAfter(form.getFieldValue(name)) : contentAfter}
|
{isFunction(contentAfter) ? contentAfter(form.getFieldValue(name)) : contentAfter}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicFormFields.propTypes = {
|
|
||||||
fields: PropTypes.arrayOf(FieldType),
|
|
||||||
feedbackIcons: PropTypes.bool,
|
|
||||||
form: AntdFormType.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
DynamicFormFields.defaultProps = {
|
DynamicFormFields.defaultProps = {
|
||||||
fields: [],
|
fields: [],
|
||||||
feedbackIcons: false,
|
feedbackIcons: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const reducerForActionSet = (state, action) => {
|
const reducerForActionSet = (state: any, action: any) => {
|
||||||
if (action.inProgress) {
|
if (action.inProgress) {
|
||||||
state.add(action.actionName);
|
state.add(action.actionName);
|
||||||
} else {
|
} else {
|
||||||
@@ -104,7 +128,14 @@ const reducerForActionSet = (state, action) => {
|
|||||||
return new Set(state);
|
return new Set(state);
|
||||||
};
|
};
|
||||||
|
|
||||||
function DynamicFormActions({ actions, isFormDirty }) {
|
type OwnDynamicFormActionsProps = {
|
||||||
|
actions?: ActionType[];
|
||||||
|
isFormDirty?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type DynamicFormActionsProps = OwnDynamicFormActionsProps & typeof DynamicFormActions.defaultProps;
|
||||||
|
|
||||||
|
function DynamicFormActions({ actions, isFormDirty }: DynamicFormActionsProps) {
|
||||||
const [inProgressActions, setActionInProgress] = useReducer(reducerForActionSet, new Set());
|
const [inProgressActions, setActionInProgress] = useReducer(reducerForActionSet, new Set());
|
||||||
|
|
||||||
const handleAction = useCallback(action => {
|
const handleAction = useCallback(action => {
|
||||||
@@ -121,7 +152,9 @@ function DynamicFormActions({ actions, isFormDirty }) {
|
|||||||
key={action.name}
|
key={action.name}
|
||||||
htmlType="button"
|
htmlType="button"
|
||||||
className={cx("m-t-10", { "pull-right": action.pullRight })}
|
className={cx("m-t-10", { "pull-right": action.pullRight })}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string | undefined' is not assignable to typ... Remove this comment to see the full error message
|
||||||
type={action.type}
|
type={action.type}
|
||||||
|
// @ts-expect-error ts-migrate(2551) FIXME: Property 'disableWhenDirty' does not exist on type... Remove this comment to see the full error message
|
||||||
disabled={isFormDirty && action.disableWhenDirty}
|
disabled={isFormDirty && action.disableWhenDirty}
|
||||||
loading={inProgressActions.has(action.name)}
|
loading={inProgressActions.has(action.name)}
|
||||||
onClick={() => handleAction(action)}>
|
onClick={() => handleAction(action)}>
|
||||||
@@ -130,26 +163,25 @@ function DynamicFormActions({ actions, isFormDirty }) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicFormActions.propTypes = {
|
|
||||||
actions: PropTypes.arrayOf(ActionType),
|
|
||||||
isFormDirty: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
DynamicFormActions.defaultProps = {
|
DynamicFormActions.defaultProps = {
|
||||||
actions: [],
|
actions: [],
|
||||||
isFormDirty: false,
|
isFormDirty: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function DynamicForm({
|
type OwnDynamicFormProps = {
|
||||||
id,
|
id?: string;
|
||||||
fields,
|
fields?: FieldType[];
|
||||||
actions,
|
actions?: ActionType[];
|
||||||
feedbackIcons,
|
feedbackIcons?: boolean;
|
||||||
hideSubmitButton,
|
hideSubmitButton?: boolean;
|
||||||
defaultShowExtraFields,
|
defaultShowExtraFields?: boolean;
|
||||||
saveText,
|
saveText?: string;
|
||||||
onSubmit,
|
onSubmit?: (...args: any[]) => any;
|
||||||
}) {
|
};
|
||||||
|
|
||||||
|
type DynamicFormProps = OwnDynamicFormProps & typeof DynamicForm.defaultProps;
|
||||||
|
|
||||||
|
export default function DynamicForm({ id, fields, actions, feedbackIcons, hideSubmitButton, defaultShowExtraFields, saveText, onSubmit, }: DynamicFormProps) {
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [showExtraFields, setShowExtraFields] = useState(defaultShowExtraFields);
|
const [showExtraFields, setShowExtraFields] = useState(defaultShowExtraFields);
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
@@ -160,15 +192,16 @@ export default function DynamicForm({
|
|||||||
values => {
|
values => {
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
values = normalizeEmptyValuesToNull(fields, values);
|
values = normalizeEmptyValuesToNull(fields, values);
|
||||||
|
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
|
||||||
onSubmit(
|
onSubmit(
|
||||||
values,
|
values,
|
||||||
msg => {
|
(msg: any) => {
|
||||||
const { setFieldsValue, getFieldsValue } = form;
|
const { setFieldsValue, getFieldsValue } = form;
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
setFieldsValue(getFieldsValue()); // reset form touched state
|
setFieldsValue(getFieldsValue()); // reset form touched state
|
||||||
notification.success(msg);
|
notification.success(msg);
|
||||||
},
|
},
|
||||||
msg => {
|
(msg: any) => {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
notification.error(msg);
|
notification.error(msg);
|
||||||
}
|
}
|
||||||
@@ -192,6 +225,7 @@ export default function DynamicForm({
|
|||||||
layout="vertical"
|
layout="vertical"
|
||||||
onFinish={handleFinish}
|
onFinish={handleFinish}
|
||||||
onFinishFailed={handleFinishFailed}>
|
onFinishFailed={handleFinishFailed}>
|
||||||
|
{/* @ts-expect-error ts-migrate(2786) FIXME: 'DynamicFormFields' cannot be used as a JSX compon... Remove this comment to see the full error message */}
|
||||||
<DynamicFormFields fields={regularFields} feedbackIcons={feedbackIcons} form={form} />
|
<DynamicFormFields fields={regularFields} feedbackIcons={feedbackIcons} form={form} />
|
||||||
{!isEmpty(extraFields) && (
|
{!isEmpty(extraFields) && (
|
||||||
<div className="extra-options">
|
<div className="extra-options">
|
||||||
@@ -199,11 +233,13 @@ export default function DynamicForm({
|
|||||||
type="dashed"
|
type="dashed"
|
||||||
block
|
block
|
||||||
className="extra-options-button"
|
className="extra-options-button"
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean' is not assignable to type 'never'.
|
||||||
onClick={() => setShowExtraFields(currentShowExtraFields => !currentShowExtraFields)}>
|
onClick={() => setShowExtraFields(currentShowExtraFields => !currentShowExtraFields)}>
|
||||||
Additional Settings
|
Additional Settings
|
||||||
<i className={cx("fa m-l-5", { "fa-caret-up": showExtraFields, "fa-caret-down": !showExtraFields })} />
|
<i className={cx("fa m-l-5", { "fa-caret-up": showExtraFields, "fa-caret-down": !showExtraFields })} />
|
||||||
</Button>
|
</Button>
|
||||||
<Collapse collapsed={!showExtraFields} className="extra-options-content">
|
<Collapse collapsed={!showExtraFields} className="extra-options-content">
|
||||||
|
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'Element' is not assignable to type 'null | u... Remove this comment to see the full error message */}
|
||||||
<DynamicFormFields fields={extraFields} feedbackIcons={feedbackIcons} form={form} />
|
<DynamicFormFields fields={extraFields} feedbackIcons={feedbackIcons} form={form} />
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</div>
|
</div>
|
||||||
@@ -213,22 +249,12 @@ export default function DynamicForm({
|
|||||||
{saveText}
|
{saveText}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{/* @ts-expect-error ts-migrate(2786) FIXME: 'DynamicFormActions' cannot be used as a JSX compo... Remove this comment to see the full error message */}
|
||||||
<DynamicFormActions actions={actions} isFormDirty={form.isFieldsTouched()} />
|
<DynamicFormActions actions={actions} isFormDirty={form.isFieldsTouched()} />
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicForm.propTypes = {
|
|
||||||
id: PropTypes.string,
|
|
||||||
fields: PropTypes.arrayOf(FieldType),
|
|
||||||
actions: PropTypes.arrayOf(ActionType),
|
|
||||||
feedbackIcons: PropTypes.bool,
|
|
||||||
hideSubmitButton: PropTypes.bool,
|
|
||||||
defaultShowExtraFields: PropTypes.bool,
|
|
||||||
saveText: PropTypes.string,
|
|
||||||
onSubmit: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
DynamicForm.defaultProps = {
|
DynamicForm.defaultProps = {
|
||||||
id: null,
|
id: null,
|
||||||
fields: [],
|
fields: [],
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { get } from "lodash";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import getFieldLabel from "./getFieldLabel";
|
|
||||||
|
|
||||||
import {
|
|
||||||
AceEditorField,
|
|
||||||
CheckboxField,
|
|
||||||
ContentField,
|
|
||||||
FileField,
|
|
||||||
InputField,
|
|
||||||
NumberField,
|
|
||||||
SelectField,
|
|
||||||
TextAreaField,
|
|
||||||
} from "./fields";
|
|
||||||
|
|
||||||
export const FieldType = PropTypes.shape({
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
title: PropTypes.string,
|
|
||||||
type: PropTypes.oneOf([
|
|
||||||
"ace",
|
|
||||||
"text",
|
|
||||||
"textarea",
|
|
||||||
"email",
|
|
||||||
"password",
|
|
||||||
"number",
|
|
||||||
"checkbox",
|
|
||||||
"file",
|
|
||||||
"select",
|
|
||||||
"content",
|
|
||||||
]).isRequired,
|
|
||||||
initialValue: PropTypes.oneOfType([
|
|
||||||
PropTypes.string,
|
|
||||||
PropTypes.number,
|
|
||||||
PropTypes.bool,
|
|
||||||
PropTypes.arrayOf(PropTypes.string),
|
|
||||||
PropTypes.arrayOf(PropTypes.number),
|
|
||||||
]),
|
|
||||||
content: PropTypes.node,
|
|
||||||
mode: PropTypes.string,
|
|
||||||
required: PropTypes.bool,
|
|
||||||
extra: PropTypes.bool,
|
|
||||||
readOnly: PropTypes.bool,
|
|
||||||
autoFocus: PropTypes.bool,
|
|
||||||
minLength: PropTypes.number,
|
|
||||||
placeholder: PropTypes.string,
|
|
||||||
contentAfter: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
|
||||||
loading: PropTypes.bool,
|
|
||||||
props: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
|
||||||
});
|
|
||||||
|
|
||||||
const FieldTypeComponent = {
|
|
||||||
checkbox: CheckboxField,
|
|
||||||
file: FileField,
|
|
||||||
select: SelectField,
|
|
||||||
number: NumberField,
|
|
||||||
textarea: TextAreaField,
|
|
||||||
ace: AceEditorField,
|
|
||||||
content: ContentField,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function DynamicFormField({ form, field, ...otherProps }) {
|
|
||||||
const { name, type, readOnly, autoFocus } = field;
|
|
||||||
const fieldLabel = getFieldLabel(field);
|
|
||||||
|
|
||||||
const fieldProps = {
|
|
||||||
...field.props,
|
|
||||||
className: "w-100",
|
|
||||||
name,
|
|
||||||
type,
|
|
||||||
readOnly,
|
|
||||||
autoFocus,
|
|
||||||
placeholder: field.placeholder,
|
|
||||||
"data-test": fieldLabel,
|
|
||||||
...otherProps,
|
|
||||||
};
|
|
||||||
|
|
||||||
const FieldComponent = get(FieldTypeComponent, type, InputField);
|
|
||||||
return <FieldComponent {...fieldProps} form={form} field={field} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
DynamicFormField.propTypes = { field: FieldType.isRequired };
|
|
||||||
105
client/app/components/dynamic-form/DynamicFormField.tsx
Normal file
105
client/app/components/dynamic-form/DynamicFormField.tsx
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { get } from "lodash";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import getFieldLabel from "./getFieldLabel";
|
||||||
|
|
||||||
|
import {
|
||||||
|
AceEditorField,
|
||||||
|
CheckboxField,
|
||||||
|
ContentField,
|
||||||
|
FileField,
|
||||||
|
InputField,
|
||||||
|
NumberField,
|
||||||
|
SelectField,
|
||||||
|
TextAreaField,
|
||||||
|
} from "./fields";
|
||||||
|
|
||||||
|
type FieldType = {
|
||||||
|
name: string;
|
||||||
|
title?: string;
|
||||||
|
type: "ace" | "text" | "textarea" | "email" | "password" | "number" | "checkbox" | "file" | "select" | "content";
|
||||||
|
initialValue?: string | number | boolean | string[] | number[];
|
||||||
|
content?: React.ReactNode;
|
||||||
|
mode?: string;
|
||||||
|
required?: boolean;
|
||||||
|
extra?: boolean;
|
||||||
|
readOnly?: boolean;
|
||||||
|
autoFocus?: boolean;
|
||||||
|
minLength?: number;
|
||||||
|
placeholder?: string;
|
||||||
|
contentAfter?: React.ReactNode | ((...args: any[]) => any);
|
||||||
|
loading?: boolean;
|
||||||
|
props?: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'Requireable<InferProps<{ name: Validator<str... Remove this comment to see the full error message
|
||||||
|
const FieldType: PropTypes.Requireable<FieldType> = PropTypes.shape({
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.string,
|
||||||
|
type: PropTypes.oneOf([
|
||||||
|
"ace",
|
||||||
|
"text",
|
||||||
|
"textarea",
|
||||||
|
"email",
|
||||||
|
"password",
|
||||||
|
"number",
|
||||||
|
"checkbox",
|
||||||
|
"file",
|
||||||
|
"select",
|
||||||
|
"content",
|
||||||
|
]).isRequired,
|
||||||
|
initialValue: PropTypes.oneOfType([
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.number,
|
||||||
|
PropTypes.bool,
|
||||||
|
PropTypes.arrayOf(PropTypes.string),
|
||||||
|
PropTypes.arrayOf(PropTypes.number),
|
||||||
|
]),
|
||||||
|
content: PropTypes.node,
|
||||||
|
mode: PropTypes.string,
|
||||||
|
required: PropTypes.bool,
|
||||||
|
extra: PropTypes.bool,
|
||||||
|
readOnly: PropTypes.bool,
|
||||||
|
autoFocus: PropTypes.bool,
|
||||||
|
minLength: PropTypes.number,
|
||||||
|
placeholder: PropTypes.string,
|
||||||
|
contentAfter: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
||||||
|
loading: PropTypes.bool,
|
||||||
|
props: PropTypes.object,
|
||||||
|
});
|
||||||
|
export { FieldType };
|
||||||
|
|
||||||
|
const FieldTypeComponent = {
|
||||||
|
checkbox: CheckboxField,
|
||||||
|
file: FileField,
|
||||||
|
select: SelectField,
|
||||||
|
number: NumberField,
|
||||||
|
textarea: TextAreaField,
|
||||||
|
ace: AceEditorField,
|
||||||
|
content: ContentField,
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
field: FieldType;
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'form' does not exist on type 'Props'.
|
||||||
|
export default function DynamicFormField({ form, field, ...otherProps }: Props) {
|
||||||
|
const { name, type, readOnly, autoFocus } = field;
|
||||||
|
const fieldLabel = getFieldLabel(field);
|
||||||
|
|
||||||
|
const fieldProps = {
|
||||||
|
...field.props,
|
||||||
|
className: "w-100",
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
readOnly,
|
||||||
|
autoFocus,
|
||||||
|
placeholder: field.placeholder,
|
||||||
|
"data-test": fieldLabel,
|
||||||
|
...otherProps,
|
||||||
|
};
|
||||||
|
|
||||||
|
const FieldComponent = get(FieldTypeComponent, type, InputField);
|
||||||
|
return <FieldComponent {...fieldProps} form={form} field={field} />;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { each, includes, isUndefined, isEmpty, isNil, map, get, some } from "lodash";
|
import { each, includes, isUndefined, isEmpty, isNil, map, get, some } from "lodash";
|
||||||
|
|
||||||
function orderedInputs(properties, order, targetOptions) {
|
function orderedInputs(properties: any, order: any, targetOptions: any) {
|
||||||
const inputs = new Array(order.length);
|
const inputs = new Array(order.length);
|
||||||
Object.keys(properties).forEach(key => {
|
Object.keys(properties).forEach(key => {
|
||||||
const position = order.indexOf(key);
|
const position = order.indexOf(key);
|
||||||
@@ -17,6 +17,7 @@ function orderedInputs(properties, order, targetOptions) {
|
|||||||
|
|
||||||
if (input.type === "select") {
|
if (input.type === "select") {
|
||||||
input.placeholder = "Select an option";
|
input.placeholder = "Select an option";
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'options' does not exist on type '{ name:... Remove this comment to see the full error message
|
||||||
input.options = properties[key].options;
|
input.options = properties[key].options;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ function orderedInputs(properties, order, targetOptions) {
|
|||||||
return inputs;
|
return inputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeSchema(configurationSchema) {
|
function normalizeSchema(configurationSchema: any) {
|
||||||
each(configurationSchema.properties, (prop, name) => {
|
each(configurationSchema.properties, (prop, name) => {
|
||||||
if (name === "password" || name === "passwd") {
|
if (name === "password" || name === "passwd") {
|
||||||
prop.type = "password";
|
prop.type = "password";
|
||||||
@@ -64,23 +65,26 @@ function normalizeSchema(configurationSchema) {
|
|||||||
configurationSchema.order = configurationSchema.order || [];
|
configurationSchema.order = configurationSchema.order || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDefaultValueToFields(configurationSchema, options = {}) {
|
function setDefaultValueToFields(configurationSchema: any, options = {}) {
|
||||||
const properties = configurationSchema.properties;
|
const properties = configurationSchema.properties;
|
||||||
Object.keys(properties).forEach(key => {
|
Object.keys(properties).forEach(key => {
|
||||||
const property = properties[key];
|
const property = properties[key];
|
||||||
// set default value for checkboxes
|
// set default value for checkboxes
|
||||||
if (!isUndefined(property.default) && property.type === "checkbox") {
|
if (!isUndefined(property.default) && property.type === "checkbox") {
|
||||||
|
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
|
||||||
options[key] = property.default;
|
options[key] = property.default;
|
||||||
}
|
}
|
||||||
// set default or first value when value has predefined options
|
// set default or first value when value has predefined options
|
||||||
if (property.type === "select") {
|
if (property.type === "select") {
|
||||||
const optionValues = map(property.options, option => option.value);
|
const optionValues = map(property.options, option => option.value);
|
||||||
|
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
|
||||||
options[key] = includes(optionValues, property.default) ? property.default : optionValues[0];
|
options[key] = includes(optionValues, property.default) ? property.default : optionValues[0];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFields(type = {}, target = { options: {} }) {
|
function getFields(type = {}, target = { options: {} }) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'configuration_schema' does not exist on ... Remove this comment to see the full error message
|
||||||
const configurationSchema = type.configuration_schema;
|
const configurationSchema = type.configuration_schema;
|
||||||
normalizeSchema(configurationSchema);
|
normalizeSchema(configurationSchema);
|
||||||
const hasTargetObject = Object.keys(target.options).length > 0;
|
const hasTargetObject = Object.keys(target.options).length > 0;
|
||||||
@@ -88,6 +92,7 @@ function getFields(type = {}, target = { options: {} }) {
|
|||||||
setDefaultValueToFields(configurationSchema, target.options);
|
setDefaultValueToFields(configurationSchema, target.options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'id' does not exist on type '{ options: {... Remove this comment to see the full error message
|
||||||
const isNewTarget = !target.id;
|
const isNewTarget = !target.id;
|
||||||
const inputs = [
|
const inputs = [
|
||||||
{
|
{
|
||||||
@@ -95,8 +100,10 @@ function getFields(type = {}, target = { options: {} }) {
|
|||||||
title: "Name",
|
title: "Name",
|
||||||
type: "text",
|
type: "text",
|
||||||
required: true,
|
required: true,
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'name' does not exist on type '{ options:... Remove this comment to see the full error message
|
||||||
initialValue: target.name,
|
initialValue: target.name,
|
||||||
contentAfter: React.createElement("hr"),
|
contentAfter: React.createElement("hr"),
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'name' does not exist on type '{}'.
|
||||||
placeholder: `My ${type.name}`,
|
placeholder: `My ${type.name}`,
|
||||||
autoFocus: isNewTarget,
|
autoFocus: isNewTarget,
|
||||||
},
|
},
|
||||||
@@ -106,7 +113,7 @@ function getFields(type = {}, target = { options: {} }) {
|
|||||||
return inputs;
|
return inputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTargetWithValues(target, values) {
|
function updateTargetWithValues(target: any, values: any) {
|
||||||
target.name = values.name;
|
target.name = values.name;
|
||||||
Object.keys(values).forEach(key => {
|
Object.keys(values).forEach(key => {
|
||||||
if (key !== "name") {
|
if (key !== "name") {
|
||||||
@@ -115,16 +122,17 @@ function updateTargetWithValues(target, values) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBase64(file) {
|
function getBase64(file: any) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
|
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||||
reader.onload = () => resolve(reader.result.substr(reader.result.indexOf(",") + 1));
|
reader.onload = () => resolve(reader.result.substr(reader.result.indexOf(",") + 1));
|
||||||
reader.onerror = error => reject(error);
|
reader.onerror = error => reject(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasFilledExtraField(type, target) {
|
function hasFilledExtraField(type: any, target: any) {
|
||||||
const extraOptions = get(type, "configuration_schema.extra_options", []);
|
const extraOptions = get(type, "configuration_schema.extra_options", []);
|
||||||
return some(extraOptions, optionName => {
|
return some(extraOptions, optionName => {
|
||||||
const defaultOptionValue = get(type, ["configuration_schema", "properties", optionName, "default"]);
|
const defaultOptionValue = get(type, ["configuration_schema", "properties", optionName, "default"]);
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import AceEditorInput from "@/components/AceEditorInput";
|
import AceEditorInput from "@/components/AceEditorInput";
|
||||||
|
|
||||||
export default function AceEditorField({ form, field, ...otherProps }) {
|
export default function AceEditorField({
|
||||||
|
form,
|
||||||
|
field,
|
||||||
|
...otherProps
|
||||||
|
}: any) {
|
||||||
return <AceEditorInput {...otherProps} />;
|
return <AceEditorInput {...otherProps} />;
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,11 @@ import React from "react";
|
|||||||
import Checkbox from "antd/lib/checkbox";
|
import Checkbox from "antd/lib/checkbox";
|
||||||
import getFieldLabel from "../getFieldLabel";
|
import getFieldLabel from "../getFieldLabel";
|
||||||
|
|
||||||
export default function CheckboxField({ form, field, ...otherProps }) {
|
export default function CheckboxField({
|
||||||
|
form,
|
||||||
|
field,
|
||||||
|
...otherProps
|
||||||
|
}: any) {
|
||||||
const fieldLabel = getFieldLabel(field);
|
const fieldLabel = getFieldLabel(field);
|
||||||
return <Checkbox {...otherProps}>{fieldLabel}</Checkbox>;
|
return <Checkbox {...otherProps}>{fieldLabel}</Checkbox>;
|
||||||
}
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export default function ContentField({ field }) {
|
|
||||||
return field.content;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export default function ContentField({
|
||||||
|
field
|
||||||
|
}: any) {
|
||||||
|
return field.content;
|
||||||
|
}
|
||||||
@@ -3,7 +3,11 @@ import Button from "antd/lib/button";
|
|||||||
import Upload from "antd/lib/upload";
|
import Upload from "antd/lib/upload";
|
||||||
import UploadOutlinedIcon from "@ant-design/icons/UploadOutlined";
|
import UploadOutlinedIcon from "@ant-design/icons/UploadOutlined";
|
||||||
|
|
||||||
export default function FileField({ form, field, ...otherProps }) {
|
export default function FileField({
|
||||||
|
form,
|
||||||
|
field,
|
||||||
|
...otherProps
|
||||||
|
}: any) {
|
||||||
const { name, initialValue } = field;
|
const { name, initialValue } = field;
|
||||||
const { getFieldValue } = form;
|
const { getFieldValue } = form;
|
||||||
const disabled = getFieldValue(name) !== undefined && getFieldValue(name) !== initialValue;
|
const disabled = getFieldValue(name) !== undefined && getFieldValue(name) !== initialValue;
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Input from "antd/lib/input";
|
import Input from "antd/lib/input";
|
||||||
|
|
||||||
export default function InputField({ form, field, ...otherProps }) {
|
export default function InputField({
|
||||||
|
form,
|
||||||
|
field,
|
||||||
|
...otherProps
|
||||||
|
}: any) {
|
||||||
return <Input {...otherProps} />;
|
return <Input {...otherProps} />;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import InputNumber from "antd/lib/input-number";
|
import InputNumber from "antd/lib/input-number";
|
||||||
|
|
||||||
export default function NumberField({ form, field, ...otherProps }) {
|
export default function NumberField({
|
||||||
|
form,
|
||||||
|
field,
|
||||||
|
...otherProps
|
||||||
|
}: any) {
|
||||||
return <InputNumber {...otherProps} />;
|
return <InputNumber {...otherProps} />;
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Select from "antd/lib/select";
|
import Select from "antd/lib/select";
|
||||||
|
|
||||||
export default function SelectField({ form, field, ...otherProps }) {
|
export default function SelectField({
|
||||||
|
form,
|
||||||
|
field,
|
||||||
|
...otherProps
|
||||||
|
}: any) {
|
||||||
const { readOnly } = field;
|
const { readOnly } = field;
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
@@ -11,11 +15,9 @@ export default function SelectField({ form, field, ...otherProps }) {
|
|||||||
mode={field.mode}
|
mode={field.mode}
|
||||||
getPopupContainer={trigger => trigger.parentNode}>
|
getPopupContainer={trigger => trigger.parentNode}>
|
||||||
{field.options &&
|
{field.options &&
|
||||||
field.options.map(option => (
|
field.options.map((option: any) => <Select.Option key={`${option.value}`} value={option.value} disabled={readOnly}>
|
||||||
<Select.Option key={`${option.value}`} value={option.value} disabled={readOnly}>
|
{option.name || option.value}
|
||||||
{option.name || option.value}
|
</Select.Option>)}
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Input from "antd/lib/input";
|
import Input from "antd/lib/input";
|
||||||
|
|
||||||
export default function TextAreaField({ form, field, ...otherProps }) {
|
export default function TextAreaField({
|
||||||
|
form,
|
||||||
|
field,
|
||||||
|
...otherProps
|
||||||
|
}: any) {
|
||||||
return <Input.TextArea {...otherProps} />;
|
return <Input.TextArea {...otherProps} />;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { toHuman } from "@/lib/utils";
|
import { toHuman } from "@/lib/utils";
|
||||||
|
|
||||||
export default function getFieldLabel(field) {
|
export default function getFieldLabel(field: any) {
|
||||||
const { title, name } = field;
|
const { title, name } = field;
|
||||||
return title || toHuman(name);
|
return title || toHuman(name);
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { getDynamicDateFromString } from "@/services/parameters/DateParameter";
|
import { getDynamicDateFromString } from "@/services/parameters/DateParameter";
|
||||||
import DynamicDatePicker from "@/components/dynamic-parameters/DynamicDatePicker";
|
import DynamicDatePicker from "@/components/dynamic-parameters/DynamicDatePicker";
|
||||||
|
|
||||||
@@ -22,18 +21,21 @@ const DYNAMIC_DATE_OPTIONS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function DateParameter(props) {
|
type OwnProps = {
|
||||||
|
type?: string;
|
||||||
|
className?: string;
|
||||||
|
value?: any;
|
||||||
|
parameter?: any;
|
||||||
|
onSelect?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof DateParameter.defaultProps;
|
||||||
|
|
||||||
|
function DateParameter(props: Props) {
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ name: string; value: any; label: () => any... Remove this comment to see the full error message
|
||||||
return <DynamicDatePicker dynamicButtonOptions={{ options: DYNAMIC_DATE_OPTIONS }} {...props} />;
|
return <DynamicDatePicker dynamicButtonOptions={{ options: DYNAMIC_DATE_OPTIONS }} {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
DateParameter.propTypes = {
|
|
||||||
type: PropTypes.string,
|
|
||||||
className: PropTypes.string,
|
|
||||||
value: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
|
||||||
parameter: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
|
||||||
onSelect: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
DateParameter.defaultProps = {
|
DateParameter.defaultProps = {
|
||||||
type: "",
|
type: "",
|
||||||
className: "",
|
className: "",
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { includes } from "lodash";
|
import { includes } from "lodash";
|
||||||
import { getDynamicDateRangeFromString } from "@/services/parameters/DateRangeParameter";
|
import { getDynamicDateRangeFromString } from "@/services/parameters/DateRangeParameter";
|
||||||
import DynamicDateRangePicker from "@/components/dynamic-parameters/DynamicDateRangePicker";
|
import DynamicDateRangePicker from "@/components/dynamic-parameters/DynamicDateRangePicker";
|
||||||
@@ -128,19 +127,22 @@ const DYNAMIC_DATETIME_OPTIONS = [
|
|||||||
...DYNAMIC_DATE_OPTIONS,
|
...DYNAMIC_DATE_OPTIONS,
|
||||||
];
|
];
|
||||||
|
|
||||||
function DateRangeParameter(props) {
|
type OwnProps = {
|
||||||
|
type?: string;
|
||||||
|
className?: string;
|
||||||
|
value?: any;
|
||||||
|
parameter?: any;
|
||||||
|
onSelect?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof DateRangeParameter.defaultProps;
|
||||||
|
|
||||||
|
function DateRangeParameter(props: Props) {
|
||||||
const options = includes(props.type, "datetime-range") ? DYNAMIC_DATETIME_OPTIONS : DYNAMIC_DATE_OPTIONS;
|
const options = includes(props.type, "datetime-range") ? DYNAMIC_DATETIME_OPTIONS : DYNAMIC_DATE_OPTIONS;
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '({ name: string; value: any; label: () => an... Remove this comment to see the full error message
|
||||||
return <DynamicDateRangePicker {...props} dynamicButtonOptions={{ options }} />;
|
return <DynamicDateRangePicker {...props} dynamicButtonOptions={{ options }} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
DateRangeParameter.propTypes = {
|
|
||||||
type: PropTypes.string,
|
|
||||||
className: PropTypes.string,
|
|
||||||
value: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
|
||||||
parameter: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
|
||||||
onSelect: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
DateRangeParameter.defaultProps = {
|
DateRangeParameter.defaultProps = {
|
||||||
type: "",
|
type: "",
|
||||||
className: "",
|
className: "",
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import React, { useRef } from "react";
|
import React, { useRef } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { isFunction, get, findIndex } from "lodash";
|
import { isFunction, get, findIndex } from "lodash";
|
||||||
import Dropdown from "antd/lib/dropdown";
|
import Dropdown from "antd/lib/dropdown";
|
||||||
import Menu from "antd/lib/menu";
|
import Menu from "antd/lib/menu";
|
||||||
import Typography from "antd/lib/typography";
|
import Typography from "antd/lib/typography";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DynamicDateType' is declared but its value is nev... Remove this comment to see the full error message
|
||||||
import { DynamicDateType } from "@/services/parameters/DateParameter";
|
import { DynamicDateType } from "@/services/parameters/DateParameter";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DynamicDateRangeType' is declared but its value i... Remove this comment to see the full error message
|
||||||
import { DynamicDateRangeType } from "@/services/parameters/DateRangeParameter";
|
import { DynamicDateRangeType } from "@/services/parameters/DateRangeParameter";
|
||||||
|
|
||||||
import ArrowLeftOutlinedIcon from "@ant-design/icons/ArrowLeftOutlined";
|
import ArrowLeftOutlinedIcon from "@ant-design/icons/ArrowLeftOutlined";
|
||||||
@@ -15,11 +16,23 @@ import "./DynamicButton.less";
|
|||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
function DynamicButton({ options, selectedDynamicValue, onSelect, enabled, staticValueLabel }) {
|
type OwnProps = {
|
||||||
|
options?: any[];
|
||||||
|
// @ts-expect-error ts-migrate(2749) FIXME: 'DynamicDateType' refers to a value, but is being ... Remove this comment to see the full error message
|
||||||
|
selectedDynamicValue?: DynamicDateType | DynamicDateRangeType;
|
||||||
|
onSelect?: (...args: any[]) => any;
|
||||||
|
enabled?: boolean;
|
||||||
|
staticValueLabel?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof DynamicButton.defaultProps;
|
||||||
|
|
||||||
|
function DynamicButton({ options, selectedDynamicValue, onSelect, enabled, staticValueLabel }: Props) {
|
||||||
const menu = (
|
const menu = (
|
||||||
<Menu
|
<Menu
|
||||||
className="dynamic-menu"
|
className="dynamic-menu"
|
||||||
onClick={({ key }) => onSelect(get(options, key, "static"))}
|
onClick={({ key }) => onSelect(get(options, key, "static"))}
|
||||||
|
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type '{ value: any; }' is not assignab... Remove this comment to see the full error message
|
||||||
selectedKeys={[`${findIndex(options, { value: selectedDynamicValue })}`]}
|
selectedKeys={[`${findIndex(options, { value: selectedDynamicValue })}`]}
|
||||||
data-test="DynamicButtonMenu">
|
data-test="DynamicButtonMenu">
|
||||||
{options.map((option, index) => (
|
{options.map((option, index) => (
|
||||||
@@ -55,6 +68,7 @@ function DynamicButton({ options, selectedDynamicValue, onSelect, enabled, stati
|
|||||||
<ThunderboltOutlinedIcon className="dynamic-icon" />
|
<ThunderboltOutlinedIcon className="dynamic-icon" />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '() => null' is not assignable to type '(trig... Remove this comment to see the full error message
|
||||||
getPopupContainer={() => containerRef.current}
|
getPopupContainer={() => containerRef.current}
|
||||||
data-test="DynamicButton"
|
data-test="DynamicButton"
|
||||||
/>
|
/>
|
||||||
@@ -63,14 +77,6 @@ function DynamicButton({ options, selectedDynamicValue, onSelect, enabled, stati
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicButton.propTypes = {
|
|
||||||
options: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
|
|
||||||
selectedDynamicValue: PropTypes.oneOfType([DynamicDateType, DynamicDateRangeType]),
|
|
||||||
onSelect: PropTypes.func,
|
|
||||||
enabled: PropTypes.bool,
|
|
||||||
staticValueLabel: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
DynamicButton.defaultProps = {
|
DynamicButton.defaultProps = {
|
||||||
options: [],
|
options: [],
|
||||||
selectedDynamicValue: null,
|
selectedDynamicValue: null,
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { includes } from "lodash";
|
import { includes } from "lodash";
|
||||||
@@ -10,26 +9,26 @@ import DynamicButton from "@/components/dynamic-parameters/DynamicButton";
|
|||||||
|
|
||||||
import "./DynamicParameters.less";
|
import "./DynamicParameters.less";
|
||||||
|
|
||||||
class DynamicDatePicker extends React.Component {
|
type OwnProps = {
|
||||||
static propTypes = {
|
type?: string;
|
||||||
type: PropTypes.string,
|
className?: string;
|
||||||
className: PropTypes.string,
|
value?: any;
|
||||||
value: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
parameter?: any;
|
||||||
parameter: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
onSelect?: (...args: any[]) => any;
|
||||||
onSelect: PropTypes.func,
|
dynamicButtonOptions?: {
|
||||||
dynamicButtonOptions: PropTypes.shape({
|
staticValueLabel?: string;
|
||||||
staticValueLabel: PropTypes.string,
|
options?: {
|
||||||
options: PropTypes.arrayOf(
|
name?: string;
|
||||||
PropTypes.shape({
|
value?: any;
|
||||||
name: PropTypes.string,
|
label?: string | ((...args: any[]) => any);
|
||||||
value: PropTypes.object,
|
}[];
|
||||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
};
|
||||||
})
|
dateOptions?: any;
|
||||||
),
|
};
|
||||||
}),
|
|
||||||
dateOptions: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
|
||||||
};
|
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof DynamicDatePicker.defaultProps;
|
||||||
|
|
||||||
|
class DynamicDatePicker extends React.Component<Props> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
type: "",
|
type: "",
|
||||||
className: "",
|
className: "",
|
||||||
@@ -41,12 +40,14 @@ class DynamicDatePicker extends React.Component {
|
|||||||
onSelect: () => {},
|
onSelect: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
dateComponentRef: any;
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.dateComponentRef = React.createRef();
|
this.dateComponentRef = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDynamicValueSelect = dynamicValue => {
|
onDynamicValueSelect = (dynamicValue: any) => {
|
||||||
const { onSelect, parameter } = this.props;
|
const { onSelect, parameter } = this.props;
|
||||||
if (dynamicValue === "static") {
|
if (dynamicValue === "static") {
|
||||||
const parameterValue = parameter.getExecutionValue();
|
const parameterValue = parameter.getExecutionValue();
|
||||||
@@ -73,17 +74,21 @@ class DynamicDatePicker extends React.Component {
|
|||||||
if (isDateTime) {
|
if (isDateTime) {
|
||||||
DateComponent = DateTimeInput;
|
DateComponent = DateTimeInput;
|
||||||
if (includes(type, "with-seconds")) {
|
if (includes(type, "with-seconds")) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'withSeconds' does not exist on type '{}'... Remove this comment to see the full error message
|
||||||
additionalAttributes.withSeconds = true;
|
additionalAttributes.withSeconds = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moment.isMoment(value) || value === null) {
|
if (moment.isMoment(value) || value === null) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type '{}'.
|
||||||
additionalAttributes.value = value;
|
additionalAttributes.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasDynamicValue) {
|
if (hasDynamicValue) {
|
||||||
const dynamicDate = value;
|
const dynamicDate = value;
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'placeholder' does not exist on type '{}'... Remove this comment to see the full error message
|
||||||
additionalAttributes.placeholder = dynamicDate && dynamicDate.name;
|
additionalAttributes.placeholder = dynamicDate && dynamicDate.name;
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type '{}'.
|
||||||
additionalAttributes.value = null;
|
additionalAttributes.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +107,7 @@ class DynamicDatePicker extends React.Component {
|
|||||||
staticValueLabel={dynamicButtonOptions.staticValueLabel}
|
staticValueLabel={dynamicButtonOptions.staticValueLabel}
|
||||||
selectedDynamicValue={hasDynamicValue ? value : null}
|
selectedDynamicValue={hasDynamicValue ? value : null}
|
||||||
enabled={hasDynamicValue}
|
enabled={hasDynamicValue}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(dynamicValue: any) => void' is not assignab... Remove this comment to see the full error message
|
||||||
onSelect={this.onDynamicValueSelect}
|
onSelect={this.onDynamicValueSelect}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { includes, isArray, isObject } from "lodash";
|
import { includes, isArray, isObject } from "lodash";
|
||||||
@@ -10,30 +9,30 @@ import DynamicButton from "@/components/dynamic-parameters/DynamicButton";
|
|||||||
|
|
||||||
import "./DynamicParameters.less";
|
import "./DynamicParameters.less";
|
||||||
|
|
||||||
function isValidDateRangeValue(value) {
|
function isValidDateRangeValue(value: any) {
|
||||||
return isArray(value) && value.length === 2 && moment.isMoment(value[0]) && moment.isMoment(value[1]);
|
return isArray(value) && value.length === 2 && moment.isMoment(value[0]) && moment.isMoment(value[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DynamicDateRangePicker extends React.Component {
|
type OwnProps = {
|
||||||
static propTypes = {
|
type: "date-range" | "datetime-range" | "datetime-range-with-seconds";
|
||||||
type: PropTypes.oneOf(["date-range", "datetime-range", "datetime-range-with-seconds"]).isRequired,
|
className?: string;
|
||||||
className: PropTypes.string,
|
value?: any;
|
||||||
value: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
parameter?: any;
|
||||||
parameter: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
onSelect?: (...args: any[]) => any;
|
||||||
onSelect: PropTypes.func,
|
dynamicButtonOptions?: {
|
||||||
dynamicButtonOptions: PropTypes.shape({
|
staticValueLabel?: string;
|
||||||
staticValueLabel: PropTypes.string,
|
options?: {
|
||||||
options: PropTypes.arrayOf(
|
name?: string;
|
||||||
PropTypes.shape({
|
value?: any;
|
||||||
name: PropTypes.string,
|
label?: string | ((...args: any[]) => any);
|
||||||
value: PropTypes.object,
|
}[];
|
||||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
};
|
||||||
})
|
dateRangeOptions?: any;
|
||||||
),
|
};
|
||||||
}),
|
|
||||||
dateRangeOptions: PropTypes.any, // eslint-disable-line react/forbid-prop-types
|
|
||||||
};
|
|
||||||
|
|
||||||
|
type Props = OwnProps & typeof DynamicDateRangePicker.defaultProps;
|
||||||
|
|
||||||
|
class DynamicDateRangePicker extends React.Component<Props> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
type: "date-range",
|
type: "date-range",
|
||||||
className: "",
|
className: "",
|
||||||
@@ -45,16 +44,20 @@ class DynamicDateRangePicker extends React.Component {
|
|||||||
onSelect: () => {},
|
onSelect: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
dateRangeComponentRef: any;
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.dateRangeComponentRef = React.createRef();
|
this.dateRangeComponentRef = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDynamicValueSelect = dynamicValue => {
|
onDynamicValueSelect = (dynamicValue: any) => {
|
||||||
const { onSelect, parameter } = this.props;
|
const { onSelect, parameter } = this.props;
|
||||||
if (dynamicValue === "static") {
|
if (dynamicValue === "static") {
|
||||||
const parameterValue = parameter.getExecutionValue();
|
const parameterValue = parameter.getExecutionValue();
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'start' does not exist on type 'object'.
|
||||||
if (isObject(parameterValue) && parameterValue.start && parameterValue.end) {
|
if (isObject(parameterValue) && parameterValue.start && parameterValue.end) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'start' does not exist on type 'object'.
|
||||||
onSelect([moment(parameterValue.start), moment(parameterValue.end)]);
|
onSelect([moment(parameterValue.start), moment(parameterValue.end)]);
|
||||||
} else {
|
} else {
|
||||||
onSelect(null);
|
onSelect(null);
|
||||||
@@ -77,16 +80,20 @@ class DynamicDateRangePicker extends React.Component {
|
|||||||
if (isDateTimeRange) {
|
if (isDateTimeRange) {
|
||||||
DateRangeComponent = DateTimeRangeInput;
|
DateRangeComponent = DateTimeRangeInput;
|
||||||
if (includes(type, "with-seconds")) {
|
if (includes(type, "with-seconds")) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'withSeconds' does not exist on type '{}'... Remove this comment to see the full error message
|
||||||
additionalAttributes.withSeconds = true;
|
additionalAttributes.withSeconds = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isValidDateRangeValue(value) || value === null) {
|
if (isValidDateRangeValue(value) || value === null) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type '{}'.
|
||||||
additionalAttributes.value = value;
|
additionalAttributes.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasDynamicValue) {
|
if (hasDynamicValue) {
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'placeholder' does not exist on type '{}'... Remove this comment to see the full error message
|
||||||
additionalAttributes.placeholder = [value && value.name];
|
additionalAttributes.placeholder = [value && value.name];
|
||||||
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'value' does not exist on type '{}'.
|
||||||
additionalAttributes.value = null;
|
additionalAttributes.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +112,7 @@ class DynamicDateRangePicker extends React.Component {
|
|||||||
staticValueLabel={dynamicButtonOptions.staticValueLabel}
|
staticValueLabel={dynamicButtonOptions.staticValueLabel}
|
||||||
selectedDynamicValue={hasDynamicValue ? value : null}
|
selectedDynamicValue={hasDynamicValue ? value : null}
|
||||||
enabled={hasDynamicValue}
|
enabled={hasDynamicValue}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '(dynamicValue: any) => void' is not assignab... Remove this comment to see the full error message
|
||||||
onSelect={this.onDynamicValueSelect}
|
onSelect={this.onDynamicValueSelect}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { keys, some } from "lodash";
|
import { keys, some } from "lodash";
|
||||||
import React, { useCallback } from "react";
|
import React, { useCallback } from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import CloseOutlinedIcon from "@ant-design/icons/CloseOutlined";
|
import CloseOutlinedIcon from "@ant-design/icons/CloseOutlined";
|
||||||
import Link from "@/components/Link";
|
import Link from "@/components/Link";
|
||||||
@@ -10,7 +9,19 @@ import { currentUser } from "@/services/auth";
|
|||||||
import organizationStatus from "@/services/organizationStatus";
|
import organizationStatus from "@/services/organizationStatus";
|
||||||
import "./empty-state.less";
|
import "./empty-state.less";
|
||||||
|
|
||||||
export function Step({ show, completed, text, url, urlTarget, urlText, onClick }) {
|
type OwnStepProps = {
|
||||||
|
show: boolean;
|
||||||
|
completed: boolean;
|
||||||
|
text?: React.ReactNode;
|
||||||
|
url?: string;
|
||||||
|
urlTarget?: string;
|
||||||
|
urlText?: React.ReactNode;
|
||||||
|
onClick?: (...args: any[]) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type StepProps = OwnStepProps & typeof Step.defaultProps;
|
||||||
|
|
||||||
|
export function Step({ show, completed, text, url, urlTarget, urlText, onClick }: StepProps) {
|
||||||
if (!show) {
|
if (!show) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -25,16 +36,6 @@ export function Step({ show, completed, text, url, urlTarget, urlText, onClick }
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Step.propTypes = {
|
|
||||||
show: PropTypes.bool.isRequired,
|
|
||||||
completed: PropTypes.bool.isRequired,
|
|
||||||
text: PropTypes.node,
|
|
||||||
url: PropTypes.string,
|
|
||||||
urlTarget: PropTypes.string,
|
|
||||||
urlText: PropTypes.node,
|
|
||||||
onClick: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
Step.defaultProps = {
|
Step.defaultProps = {
|
||||||
url: null,
|
url: null,
|
||||||
urlTarget: null,
|
urlTarget: null,
|
||||||
@@ -43,10 +44,15 @@ Step.defaultProps = {
|
|||||||
onClick: null,
|
onClick: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EmptyStateHelpMessage({ helpTriggerType }) {
|
type EmptyStateHelpMessageProps = {
|
||||||
|
helpTriggerType: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function EmptyStateHelpMessage({ helpTriggerType }: EmptyStateHelpMessageProps) {
|
||||||
return (
|
return (
|
||||||
<p>
|
<p>
|
||||||
Need more support?{" "}
|
Need more support?{" "}
|
||||||
|
{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
|
||||||
<HelpTrigger className="f-14" type={helpTriggerType} showTooltip={false}>
|
<HelpTrigger className="f-14" type={helpTriggerType} showTooltip={false}>
|
||||||
See our Help
|
See our Help
|
||||||
</HelpTrigger>
|
</HelpTrigger>
|
||||||
@@ -54,26 +60,26 @@ export function EmptyStateHelpMessage({ helpTriggerType }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
EmptyStateHelpMessage.propTypes = {
|
type OwnEmptyStateProps = {
|
||||||
helpTriggerType: PropTypes.string.isRequired,
|
icon?: string;
|
||||||
|
header?: string;
|
||||||
|
description: string;
|
||||||
|
illustration: string;
|
||||||
|
illustrationPath?: string;
|
||||||
|
helpMessage?: React.ReactNode;
|
||||||
|
closable?: boolean;
|
||||||
|
onClose?: (...args: any[]) => any;
|
||||||
|
onboardingMode?: boolean;
|
||||||
|
showAlertStep?: boolean;
|
||||||
|
showDashboardStep?: boolean;
|
||||||
|
showDataSourceStep?: boolean;
|
||||||
|
showInviteStep?: boolean;
|
||||||
|
getStepItems?: (...args: any[]) => any;
|
||||||
};
|
};
|
||||||
|
|
||||||
function EmptyState({
|
type EmptyStateProps = OwnEmptyStateProps & typeof EmptyState.defaultProps;
|
||||||
icon,
|
|
||||||
header,
|
function EmptyState({ icon, header, description, illustration, helpMessage, closable, onClose, onboardingMode, showAlertStep, showDashboardStep, showDataSourceStep, showInviteStep, getStepsItems, illustrationPath, }: EmptyStateProps) {
|
||||||
description,
|
|
||||||
illustration,
|
|
||||||
helpMessage,
|
|
||||||
closable,
|
|
||||||
onClose,
|
|
||||||
onboardingMode,
|
|
||||||
showAlertStep,
|
|
||||||
showDashboardStep,
|
|
||||||
showDataSourceStep,
|
|
||||||
showInviteStep,
|
|
||||||
getStepsItems,
|
|
||||||
illustrationPath,
|
|
||||||
}) {
|
|
||||||
const isAvailable = {
|
const isAvailable = {
|
||||||
dataSource: showDataSourceStep,
|
dataSource: showDataSourceStep,
|
||||||
query: true,
|
query: true,
|
||||||
@@ -91,10 +97,12 @@ function EmptyState({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const showCreateDashboardDialog = useCallback(() => {
|
const showCreateDashboardDialog = useCallback(() => {
|
||||||
|
// @ts-expect-error ts-migrate(2554) FIXME: Expected 1 arguments, but got 0.
|
||||||
CreateDashboardDialog.showModal();
|
CreateDashboardDialog.showModal();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Show if `onboardingMode=false` or any requested step not completed
|
// Show if `onboardingMode=false` or any requested step not completed
|
||||||
|
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
|
||||||
const shouldShow = !onboardingMode || some(keys(isAvailable), step => isAvailable[step] && !isCompleted[step]);
|
const shouldShow = !onboardingMode || some(keys(isAvailable), step => isAvailable[step] && !isCompleted[step]);
|
||||||
|
|
||||||
if (!shouldShow) {
|
if (!shouldShow) {
|
||||||
@@ -105,10 +113,14 @@ function EmptyState({
|
|||||||
if (currentUser.isAdmin) {
|
if (currentUser.isAdmin) {
|
||||||
return (
|
return (
|
||||||
<Step
|
<Step
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
key="dataSources"
|
key="dataSources"
|
||||||
show={isAvailable.dataSource}
|
show={isAvailable.dataSource}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean' is not assignable to type 'never'.
|
||||||
completed={isCompleted.dataSource}
|
completed={isCompleted.dataSource}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
url="data_sources/new"
|
url="data_sources/new"
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
urlText="Connect a Data Source"
|
urlText="Connect a Data Source"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -116,9 +128,12 @@ function EmptyState({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Step
|
<Step
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
key="dataSources"
|
key="dataSources"
|
||||||
show={isAvailable.dataSource}
|
show={isAvailable.dataSource}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean' is not assignable to type 'never'.
|
||||||
completed={isCompleted.dataSource}
|
completed={isCompleted.dataSource}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
text="Ask an account admin to connect a data source"
|
text="Ask an account admin to connect a data source"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -133,10 +148,15 @@ function EmptyState({
|
|||||||
key: "queries",
|
key: "queries",
|
||||||
node: (
|
node: (
|
||||||
<Step
|
<Step
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
key="queries"
|
key="queries"
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean' is not assignable to type 'never'.
|
||||||
show={isAvailable.query}
|
show={isAvailable.query}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean' is not assignable to type 'never'.
|
||||||
completed={isCompleted.query}
|
completed={isCompleted.query}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
url="queries/new"
|
url="queries/new"
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
urlText="Create your first Query"
|
urlText="Create your first Query"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -145,10 +165,14 @@ function EmptyState({
|
|||||||
key: "alerts",
|
key: "alerts",
|
||||||
node: (
|
node: (
|
||||||
<Step
|
<Step
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
key="alerts"
|
key="alerts"
|
||||||
show={isAvailable.alert}
|
show={isAvailable.alert}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean' is not assignable to type 'never'.
|
||||||
completed={isCompleted.alert}
|
completed={isCompleted.alert}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
url="alerts/new"
|
url="alerts/new"
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
urlText="Create your first Alert"
|
urlText="Create your first Alert"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -157,10 +181,14 @@ function EmptyState({
|
|||||||
key: "dashboards",
|
key: "dashboards",
|
||||||
node: (
|
node: (
|
||||||
<Step
|
<Step
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
key="dashboards"
|
key="dashboards"
|
||||||
show={isAvailable.dashboard}
|
show={isAvailable.dashboard}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean' is not assignable to type 'never'.
|
||||||
completed={isCompleted.dashboard}
|
completed={isCompleted.dashboard}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type '() => void' is not assignable to type 'never... Remove this comment to see the full error message
|
||||||
onClick={showCreateDashboardDialog}
|
onClick={showCreateDashboardDialog}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
urlText="Create your first Dashboard"
|
urlText="Create your first Dashboard"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -169,16 +197,21 @@ function EmptyState({
|
|||||||
key: "users",
|
key: "users",
|
||||||
node: (
|
node: (
|
||||||
<Step
|
<Step
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
key="users"
|
key="users"
|
||||||
show={isAvailable.inviteUsers}
|
show={isAvailable.inviteUsers}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'boolean' is not assignable to type 'never'.
|
||||||
completed={isCompleted.inviteUsers}
|
completed={isCompleted.inviteUsers}
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
url="users/new"
|
url="users/new"
|
||||||
|
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
||||||
urlText="Invite your team members"
|
urlText="Invite your team members"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// @ts-expect-error ts-migrate(2349) FIXME: This expression is not callable.
|
||||||
const stepsItems = getStepsItems ? getStepsItems(defaultStepsItems) : defaultStepsItems;
|
const stepsItems = getStepsItems ? getStepsItems(defaultStepsItems) : defaultStepsItems;
|
||||||
const imageSource = illustrationPath ? illustrationPath : "static/images/illustrations/" + illustration + ".svg";
|
const imageSource = illustrationPath ? illustrationPath : "static/images/illustrations/" + illustration + ".svg";
|
||||||
|
|
||||||
@@ -195,7 +228,7 @@ function EmptyState({
|
|||||||
</div>
|
</div>
|
||||||
<div className="empty-state__steps">
|
<div className="empty-state__steps">
|
||||||
<h4>Let's get started</h4>
|
<h4>Let's get started</h4>
|
||||||
<ol>{stepsItems.map(item => item.node)}</ol>
|
<ol>{stepsItems.map((item: any) => item.node)}</ol>
|
||||||
{helpMessage}
|
{helpMessage}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -208,25 +241,6 @@ function EmptyState({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
EmptyState.propTypes = {
|
|
||||||
icon: PropTypes.string,
|
|
||||||
header: PropTypes.string,
|
|
||||||
description: PropTypes.string.isRequired,
|
|
||||||
illustration: PropTypes.string.isRequired,
|
|
||||||
illustrationPath: PropTypes.string,
|
|
||||||
helpMessage: PropTypes.node,
|
|
||||||
closable: PropTypes.bool,
|
|
||||||
onClose: PropTypes.func,
|
|
||||||
|
|
||||||
onboardingMode: PropTypes.bool,
|
|
||||||
showAlertStep: PropTypes.bool,
|
|
||||||
showDashboardStep: PropTypes.bool,
|
|
||||||
showDataSourceStep: PropTypes.bool,
|
|
||||||
showInviteStep: PropTypes.bool,
|
|
||||||
|
|
||||||
getStepItems: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
EmptyState.defaultProps = {
|
EmptyState.defaultProps = {
|
||||||
icon: null,
|
icon: null,
|
||||||
header: null,
|
header: null,
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
import Input from "antd/lib/input";
|
import Input from "antd/lib/input";
|
||||||
|
// @ts-expect-error ts-migrate(6133) FIXME: 'DialogPropType' is declared but its value is neve... Remove this comment to see the full error message
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
|
|
||||||
class CreateGroupDialog extends React.Component {
|
type Props = {
|
||||||
static propTypes = {
|
// @ts-expect-error ts-migrate(2749) FIXME: 'DialogPropType' refers to a value, but is being u... Remove this comment to see the full error message
|
||||||
dialog: DialogPropType.isRequired,
|
dialog: DialogPropType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type State = any;
|
||||||
|
|
||||||
|
class CreateGroupDialog extends React.Component<Props, State> {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
name: "",
|
name: "",
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user