Skip to content
Snippets Groups Projects
Unverified Commit eb6708b5 authored by Gustavo Saiani's avatar Gustavo Saiani Committed by GitHub
Browse files

Extract sidebar component from DatabaseEditingApp (#17597)

parent f16bf7b0
No related branches found
No related tags found
No related merge requests found
import React, { useRef } from "react";
import PropTypes from "prop-types";
import { Box } from "grid-styled";
import { t } from "ttag";
import DeleteDatabaseModal from "metabase/admin/databases/components/DeleteDatabaseModal.jsx";
import ActionButton from "metabase/components/ActionButton";
import ModalWithTrigger from "metabase/components/ModalWithTrigger";
import ConfirmContent from "metabase/components/ConfirmContent";
const propTypes = {
database: PropTypes.object.isRequired,
deleteDatabase: PropTypes.func.isRequired,
syncDatabaseSchema: PropTypes.func.isRequired,
rescanDatabaseFields: PropTypes.func.isRequired,
discardSavedFieldValues: PropTypes.func.isRequired,
};
const DatabaseEditAppSidebar = ({
database,
deleteDatabase,
syncDatabaseSchema,
rescanDatabaseFields,
discardSavedFieldValues,
}) => {
const discardSavedFieldValuesModal = useRef();
const deleteDatabaseModal = useRef();
return (
<Box ml={[2, 3]} width={420}>
<div className="Actions bg-light rounded p3">
<div className="Actions-group">
<label className="Actions-groupLabel block text-bold">{t`Actions`}</label>
<ol>
<li>
<ActionButton
actionFn={() => syncDatabaseSchema(database.id)}
className="Button Button--syncDbSchema"
normalText={t`Sync database schema now`}
activeText={t`Starting…`}
failedText={t`Failed to sync`}
successText={t`Sync triggered!`}
/>
</li>
<li className="mt2">
<ActionButton
actionFn={() => rescanDatabaseFields(database.id)}
className="Button Button--rescanFieldValues"
normalText={t`Re-scan field values now`}
activeText={t`Starting…`}
failedText={t`Failed to start scan`}
successText={t`Scan triggered!`}
/>
</li>
</ol>
</div>
<div className="Actions-group">
<label className="Actions-groupLabel block text-bold">{t`Danger Zone`}</label>
<ol>
<li>
<ModalWithTrigger
ref={discardSavedFieldValuesModal}
triggerClasses="Button Button--danger Button--discardSavedFieldValues"
triggerElement={t`Discard saved field values`}
>
<ConfirmContent
title={t`Discard saved field values`}
onClose={() => discardSavedFieldValuesModal.current.toggle()}
onAction={() => discardSavedFieldValues(database.id)}
/>
</ModalWithTrigger>
</li>
<li className="mt2">
<ModalWithTrigger
ref={deleteDatabaseModal}
triggerClasses="Button Button--deleteDatabase Button--danger"
triggerElement={t`Remove this database`}
>
<DeleteDatabaseModal
database={database}
onClose={() => deleteDatabaseModal.current.toggle()}
onDelete={() => deleteDatabase(database.id, true)}
/>
</ModalWithTrigger>
</li>
</ol>
</div>
</div>
</Box>
);
};
DatabaseEditAppSidebar.propTypes = propTypes;
export default DatabaseEditAppSidebar;
import React from "react";
import { fireEvent, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Sidebar from "./Sidebar";
it("syncs database schema", () => {
const databaseId = 1;
const database = { id: databaseId };
const syncDatabaseSchema = jest.fn();
render(
<Sidebar database={database} syncDatabaseSchema={syncDatabaseSchema} />,
);
const syncButton = screen.getByText("Sync database schema now");
fireEvent.click(syncButton);
expect(syncDatabaseSchema).toHaveBeenCalledWith(databaseId);
});
it("rescans database field values", () => {
const databaseId = 1;
const database = { id: databaseId };
const rescanDatabaseFields = jest.fn();
render(
<Sidebar database={database} rescanDatabaseFields={rescanDatabaseFields} />,
);
const rescanButton = screen.getByText("Re-scan field values now");
fireEvent.click(rescanButton);
expect(rescanDatabaseFields).toHaveBeenCalledWith(databaseId);
});
it("discards saved field values", () => {
const databaseId = 1;
const database = { id: databaseId };
const discardSavedFieldValues = jest.fn();
render(
<Sidebar
database={database}
discardSavedFieldValues={discardSavedFieldValues}
/>,
);
const discardButton = screen.getByText("Discard saved field values");
fireEvent.click(discardButton);
expect(screen.getAllByText("Discard saved field values").length).toBe(2);
const cancelButton = screen.getByText("Cancel");
fireEvent.click(cancelButton);
fireEvent.click(discardButton);
const yesButton = screen.getByText("Yes");
fireEvent.click(yesButton);
expect(discardSavedFieldValues).toHaveBeenCalledWith(databaseId);
});
it("removes database", () => {
const databaseId = 1;
const name = "DB Name";
const database = { id: databaseId, name };
const deleteDatabase = jest.fn();
render(<Sidebar database={database} deleteDatabase={deleteDatabase} />);
const removeDBButton = screen.getByText("Remove this database");
fireEvent.click(removeDBButton);
screen.getByText(`Delete the ${name} database?`);
const cancelButton = screen.getByText("Cancel");
fireEvent.click(cancelButton);
fireEvent.click(removeDBButton);
const input = screen.getByRole("textbox");
userEvent.type(input, name);
const deleteButton = screen.getByText("Delete");
fireEvent.click(deleteButton);
expect(deleteDatabase).toHaveBeenCalled();
});
......@@ -10,14 +10,12 @@ import { Box, Flex } from "grid-styled";
import title from "metabase/hoc/Title";
import DeleteDatabaseModal from "../components/DeleteDatabaseModal";
import ActionButton from "metabase/components/ActionButton";
import AddDatabaseHelpCard from "metabase/components/AddDatabaseHelpCard";
import Button from "metabase/components/Button";
import Breadcrumbs from "metabase/components/Breadcrumbs";
import DriverWarning from "metabase/components/DriverWarning";
import Radio from "metabase/components/Radio";
import ModalWithTrigger from "metabase/components/ModalWithTrigger";
import Sidebar from "metabase/admin/databases/components/DatabaseEditApp/Sidebar/Sidebar";
import Databases from "metabase/entities/databases";
......@@ -38,7 +36,6 @@ import {
deleteDatabase,
selectEngine,
} from "../database";
import ConfirmContent from "metabase/components/ConfirmContent";
import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper";
import { getIn } from "icepick";
......@@ -72,10 +69,7 @@ const mapDispatchToProps = {
selectEngine,
};
type TabName = "connection" | "scheduling";
type TabOption = { name: string, value: TabName };
const TABS: TabOption[] = [
const TABS = [
{
name: t`Connection`,
value: "connection",
......@@ -92,19 +86,12 @@ const TABS: TabOption[] = [
)
@title(({ database }) => database && database.name)
export default class DatabaseEditApp extends Component {
state: {
currentTab: TabName,
};
constructor(props, context) {
super(props, context);
this.state = {
currentTab: TABS[0].value,
};
this.discardSavedFieldValuesModal = React.createRef();
this.deleteDatabaseModal = React.createRef();
}
static propTypes = {
......@@ -139,26 +126,30 @@ export default class DatabaseEditApp extends Component {
render() {
const {
database,
deleteDatabase,
discardSavedFieldValues,
selectedEngine,
letUserControlSchedulingSaved,
letUserControlSchedulingForm,
initializeError,
rescanDatabaseFields,
syncDatabaseSchema,
} = this.props;
const { currentTab } = this.state;
const editingExistingDatabase = database && database.id != null;
const editingExistingDatabase = database?.id != null;
const addingNewDatabase = !editingExistingDatabase;
const showTabs = editingExistingDatabase && letUserControlSchedulingSaved;
const crumbs = [
[t`Databases`, "/admin/databases"],
[addingNewDatabase ? t`Add Database` : database.name],
];
return (
<Box px={[3, 4, 5]} mt={[1, 2, 3]}>
<Breadcrumbs
className="py4"
crumbs={[
[t`Databases`, "/admin/databases"],
[addingNewDatabase ? t`Add Database` : database.name],
]}
/>
<Breadcrumbs className="py4" crumbs={crumbs} />
<Flex pb={2}>
<Box>
<div className="pt0">
......@@ -258,82 +249,14 @@ export default class DatabaseEditApp extends Component {
</div>
</Box>
{/* Sidebar Actions */}
{editingExistingDatabase && (
<Box ml={[2, 3]} width={420}>
<div className="Actions bg-light rounded p3">
<div className="Actions-group">
<label className="Actions-groupLabel block text-bold">{t`Actions`}</label>
<ol>
<li>
<ActionButton
actionFn={() =>
this.props.syncDatabaseSchema(database.id)
}
className="Button Button--syncDbSchema"
normalText={t`Sync database schema now`}
activeText={t`Starting…`}
failedText={t`Failed to sync`}
successText={t`Sync triggered!`}
/>
</li>
<li className="mt2">
<ActionButton
actionFn={() =>
this.props.rescanDatabaseFields(database.id)
}
className="Button Button--rescanFieldValues"
normalText={t`Re-scan field values now`}
activeText={t`Starting…`}
failedText={t`Failed to start scan`}
successText={t`Scan triggered!`}
/>
</li>
</ol>
</div>
<div className="Actions-group">
<label className="Actions-groupLabel block text-bold">{t`Danger Zone`}</label>
<ol>
<li>
<ModalWithTrigger
ref={this.discardSavedFieldValuesModal}
triggerClasses="Button Button--danger Button--discardSavedFieldValues"
triggerElement={t`Discard saved field values`}
>
<ConfirmContent
title={t`Discard saved field values`}
onClose={() =>
this.discardSavedFieldValuesModal.current.toggle()
}
onAction={() =>
this.props.discardSavedFieldValues(database.id)
}
/>
</ModalWithTrigger>
</li>
<li className="mt2">
<ModalWithTrigger
ref={this.deleteDatabaseModal}
triggerClasses="Button Button--deleteDatabase Button--danger"
triggerElement={t`Remove this database`}
>
<DeleteDatabaseModal
database={database}
onClose={() =>
this.deleteDatabaseModal.current.toggle()
}
onDelete={() =>
this.props.deleteDatabase(database.id, true)
}
/>
</ModalWithTrigger>
</li>
</ol>
</div>
</div>
</Box>
<Sidebar
database={database}
deleteDatabase={deleteDatabase}
discardSavedFieldValues={discardSavedFieldValues}
rescanDatabaseFields={rescanDatabaseFields}
syncDatabaseSchema={syncDatabaseSchema}
/>
)}
</Flex>
</Box>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment