Просмотр исходного кода

Merge pull request #12665 from grafana/9863-animated-delete-button

built a component for delete button in tables
Marcus Efraimsson 7 лет назад
Родитель
Сommit
0550ec6de1

+ 3 - 17
public/app/containers/Teams/TeamList.tsx

@@ -5,7 +5,7 @@ import PageHeader from 'app/core/components/PageHeader/PageHeader';
 import { NavStore } from 'app/stores/NavStore/NavStore';
 import { TeamsStore, ITeam } from 'app/stores/TeamsStore/TeamsStore';
 import { BackendSrv } from 'app/core/services/backend_srv';
-import appEvents from 'app/core/app_events';
+import DeleteButton from 'app/core/components/DeleteButton/DeleteButton';
 
 interface Props {
   nav: typeof NavStore.Type;
@@ -28,18 +28,6 @@ export class TeamList extends React.Component<Props, any> {
   }
 
   deleteTeam(team: ITeam) {
-    appEvents.emit('confirm-modal', {
-      title: 'Delete',
-      text: 'Are you sure you want to delete Team ' + team.name + '?',
-      yesText: 'Delete',
-      icon: 'fa-warning',
-      onConfirm: () => {
-        this.deleteTeamConfirmed(team);
-      },
-    });
-  }
-
-  deleteTeamConfirmed(team) {
     this.props.backendSrv.delete('/api/teams/' + team.id).then(this.fetchTeams.bind(this));
   }
 
@@ -67,9 +55,7 @@ export class TeamList extends React.Component<Props, any> {
           <a href={teamUrl}>{team.memberCount}</a>
         </td>
         <td className="text-right">
-          <a onClick={() => this.deleteTeam(team)} className="btn btn-danger btn-small">
-            <i className="fa fa-remove" />
-          </a>
+          <DeleteButton onConfirmDelete={() => this.deleteTeam(team)} />
         </td>
       </tr>
     );
@@ -102,7 +88,7 @@ export class TeamList extends React.Component<Props, any> {
             </a>
           </div>
 
-          <div className="admin-list-table">
+          <div className="admin-list-table tr-overflow">
             <table className="filter-table filter-table--hover form-inline">
               <thead>
                 <tr>

+ 44 - 0
public/app/core/components/DeleteButton/DeleteButton.jest.tsx

@@ -0,0 +1,44 @@
+import React from 'react';
+import DeleteButton from './DeleteButton';
+import { shallow } from 'enzyme';
+
+describe('DeleteButton', () => {
+  let wrapper;
+  let deleted;
+
+  beforeAll(() => {
+    deleted = false;
+
+    function deleteItem() {
+      deleted = true;
+    }
+    wrapper = shallow(<DeleteButton onConfirmDelete={() => deleteItem()} />);
+  });
+
+  it('should show confirm delete when clicked', () => {
+    expect(wrapper.state().showConfirm).toBe(false);
+    wrapper.find('.delete-button').simulate('click');
+    expect(wrapper.state().showConfirm).toBe(true);
+  });
+
+  it('should hide confirm delete when clicked', () => {
+    wrapper.find('.delete-button').simulate('click');
+    expect(wrapper.state().showConfirm).toBe(true);
+    wrapper
+      .find('.confirm-delete')
+      .find('.btn')
+      .at(0)
+      .simulate('click');
+    expect(wrapper.state().showConfirm).toBe(false);
+  });
+
+  it('should show confirm delete when clicked', () => {
+    expect(deleted).toBe(false);
+    wrapper
+      .find('.confirm-delete')
+      .find('.btn')
+      .at(1)
+      .simulate('click');
+    expect(deleted).toBe(true);
+  });
+});

+ 66 - 0
public/app/core/components/DeleteButton/DeleteButton.tsx

@@ -0,0 +1,66 @@
+import React, { PureComponent } from 'react';
+
+export interface DeleteButtonProps {
+  onConfirmDelete();
+}
+
+export interface DeleteButtonStates {
+  showConfirm: boolean;
+}
+
+export default class DeleteButton extends PureComponent<DeleteButtonProps, DeleteButtonStates> {
+  state: DeleteButtonStates = {
+    showConfirm: false,
+  };
+
+  onClickDelete = event => {
+    if (event) {
+      event.preventDefault();
+    }
+
+    this.setState({
+      showConfirm: true,
+    });
+  };
+
+  onClickCancel = event => {
+    if (event) {
+      event.preventDefault();
+    }
+    this.setState({
+      showConfirm: false,
+    });
+  };
+
+  render() {
+    const onClickConfirm = this.props.onConfirmDelete;
+    let showConfirm;
+    let showDeleteButton;
+
+    if (this.state.showConfirm) {
+      showConfirm = 'show';
+      showDeleteButton = 'hide';
+    } else {
+      showConfirm = 'hide';
+      showDeleteButton = 'show';
+    }
+
+    return (
+      <span className="delete-button-container">
+        <a className={'delete-button ' + showDeleteButton + ' btn btn-danger btn-small'} onClick={this.onClickDelete}>
+          <i className="fa fa-remove" />
+        </a>
+        <span className="confirm-delete-container">
+          <span className={'confirm-delete ' + showConfirm}>
+            <a className="btn btn-small" onClick={this.onClickCancel}>
+              Cancel
+            </a>
+            <a className="btn btn-danger btn-small" onClick={onClickConfirm}>
+              Confirm Delete
+            </a>
+          </span>
+        </span>
+      </span>
+    );
+  }
+}

+ 1 - 0
public/sass/_grafana.scss

@@ -93,6 +93,7 @@
 @import 'components/form_select_box';
 @import 'components/user-picker';
 @import 'components/description-picker';
+@import 'components/delete_button';
 
 // PAGES
 @import 'pages/login';

+ 50 - 0
public/sass/components/_delete_button.scss

@@ -0,0 +1,50 @@
+// sets a fixed width so that the rest of the table
+// isn't affected by the animation
+.delete-button-container {
+  width: 24px;
+  direction: rtl;
+  display: flex;
+  align-items: center;
+}
+
+//this container is used to make sure confirm-delete isn't
+//shown outside of table
+.confirm-delete-container {
+  overflow: hidden;
+  width: 145px;
+  position: absolute;
+  z-index: 1;
+}
+
+.delete-button {
+  position: absolute;
+
+  &.show {
+    opacity: 1;
+    transition: opacity 0.1s ease;
+    z-index: 2;
+  }
+
+  &.hide {
+    opacity: 0;
+    transition: opacity 0.1s ease;
+    z-index: 0;
+  }
+}
+
+.confirm-delete {
+  display: flex;
+  align-items: flex-start;
+
+  &.show {
+    opacity: 1;
+    transition: opacity 0.08s ease-out, transform 0.1s ease-out;
+    transform: translateX(0);
+  }
+
+  &.hide {
+    opacity: 0;
+    transition: opacity 0.12s ease-in, transform 0.14s ease-in;
+    transform: translateX(100px);
+  }
+}