Merge pull request from influxdata/polish/confirmation-buttons

Standardize Confirmation Buttons
pull/10616/head
Alex Paxton 2018-03-30 11:21:21 -07:00 committed by GitHub
commit 489a667d4f
34 changed files with 516 additions and 731 deletions

View File

@ -6,6 +6,9 @@
### UI Improvements ### UI Improvements
1. [#3096](https://github.com/influxdata/chronograf/pull/3096): Standardize delete confirmation interactions
1. [#3096](https://github.com/influxdata/chronograf/pull/3096): Standardize save & cancel interactions
### Bug Fixes ### Bug Fixes
1. [#2950](https://github.com/influxdata/chronograf/pull/2094): Always save template variables on first edit 1. [#2950](https://github.com/influxdata/chronograf/pull/2094): Always save template variables on first edit
@ -58,6 +61,7 @@
## v1.4.2.1 [2018-02-28] ## v1.4.2.1 [2018-02-28]
### Features ### Features
1. [#2837](https://github.com/influxdata/chronograf/pull/2837): Prevent execution of queries in cells that are not in view on the dashboard page 1. [#2837](https://github.com/influxdata/chronograf/pull/2837): Prevent execution of queries in cells that are not in view on the dashboard page
1. [#2829](https://github.com/influxdata/chronograf/pull/2829): Add an optional persistent legend which can toggle series visibility to dashboard cells 1. [#2829](https://github.com/influxdata/chronograf/pull/2829): Add an optional persistent legend which can toggle series visibility to dashboard cells
1. [#2846](https://github.com/influxdata/chronograf/pull/2846): Allow user to annotate graphs via UI or API 1. [#2846](https://github.com/influxdata/chronograf/pull/2846): Allow user to annotate graphs via UI or API

View File

@ -2,7 +2,7 @@ import React, {Component} from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import OnClickOutside from 'shared/components/OnClickOutside' import OnClickOutside from 'shared/components/OnClickOutside'
import ConfirmButtons from 'shared/components/ConfirmButtons' import ConfirmOrCancel from 'shared/components/ConfirmOrCancel'
class ChangePassRow extends Component { class ChangePassRow extends Component {
constructor(props) { constructor(props) {
@ -59,7 +59,7 @@ class ChangePassRow extends Component {
onKeyPress={this.handleKeyPress(user)} onKeyPress={this.handleKeyPress(user)}
autoFocus={true} autoFocus={true}
/> />
<ConfirmButtons <ConfirmOrCancel
onConfirm={this.handleSubmit} onConfirm={this.handleSubmit}
item={user} item={user}
onCancel={this.handleCancel} onCancel={this.handleCancel}

View File

@ -8,7 +8,7 @@ import onClickOutside from 'react-onclickoutside'
import {notify as notifyAction} from 'shared/actions/notifications' import {notify as notifyAction} from 'shared/actions/notifications'
import {formatRPDuration} from 'utils/formatting' import {formatRPDuration} from 'utils/formatting'
import YesNoButtons from 'shared/components/YesNoButtons' import ConfirmButton from 'shared/components/ConfirmButton'
import {DATABASE_TABLE} from 'src/admin/constants/tableSizing' import {DATABASE_TABLE} from 'src/admin/constants/tableSizing'
import {notifyRetentionPolicyCantHaveEmptyFields} from 'shared/copy/notifications' import {notifyRetentionPolicyCantHaveEmptyFields} from 'shared/copy/notifications'
@ -17,7 +17,6 @@ class DatabaseRow extends Component {
super(props) super(props)
this.state = { this.state = {
isEditing: false, isEditing: false,
isDeleting: false,
} }
} }
@ -39,7 +38,6 @@ class DatabaseRow extends Component {
} }
this.handleEndEdit() this.handleEndEdit()
this.handleEndDelete()
} }
handleStartEdit = () => { handleStartEdit = () => {
@ -50,14 +48,6 @@ class DatabaseRow extends Component {
this.setState({isEditing: false}) this.setState({isEditing: false})
} }
handleStartDelete = () => {
this.setState({isDeleting: true})
}
handleEndDelete = () => {
this.setState({isDeleting: false})
}
handleCreate = () => { handleCreate = () => {
const {database, retentionPolicy, onCreate} = this.props const {database, retentionPolicy, onCreate} = this.props
const validInputs = this.getInputValues() const validInputs = this.getInputValues()
@ -140,7 +130,7 @@ class DatabaseRow extends Component {
isDeletable, isDeletable,
isRFDisplayed, isRFDisplayed,
} = this.props } = this.props
const {isEditing, isDeleting} = this.state const {isEditing} = this.state
const formattedDuration = formatRPDuration(duration) const formattedDuration = formatRPDuration(duration)
@ -198,11 +188,18 @@ class DatabaseRow extends Component {
className="text-right" className="text-right"
style={{width: `${DATABASE_TABLE.colDelete}px`}} style={{width: `${DATABASE_TABLE.colDelete}px`}}
> >
<YesNoButtons <button
buttonSize="btn-xs" className="btn btn-xs btn-info"
onConfirm={isNew ? this.handleCreate : this.handleUpdate} onClick={isNew ? this.handleRemove : this.handleEndEdit}
onCancel={isNew ? this.handleRemove : this.handleEndEdit} >
/> Cancel
</button>
<button
className="btn btn-xs btn-success"
onClick={isNew ? this.handleCreate : this.handleUpdate}
>
Save
</button>
</td> </td>
</tr> </tr>
) )
@ -216,17 +213,11 @@ class DatabaseRow extends Component {
<span className="default-source-label">default</span> <span className="default-source-label">default</span>
) : null} ) : null}
</td> </td>
<td <td style={{width: `${DATABASE_TABLE.colDuration}px`}}>
onClick={this.handleStartEdit}
style={{width: `${DATABASE_TABLE.colDuration}px`}}
>
{formattedDuration} {formattedDuration}
</td> </td>
{isRFDisplayed ? ( {isRFDisplayed ? (
<td <td style={{width: `${DATABASE_TABLE.colReplication}px`}}>
onClick={this.handleStartEdit}
style={{width: `${DATABASE_TABLE.colReplication}px`}}
>
{replication} {replication}
</td> </td>
) : null} ) : null}
@ -234,21 +225,20 @@ class DatabaseRow extends Component {
className="text-right" className="text-right"
style={{width: `${DATABASE_TABLE.colDelete}px`}} style={{width: `${DATABASE_TABLE.colDelete}px`}}
> >
{isDeleting ? ( <button
<YesNoButtons className="btn btn-xs btn-info table--show-on-row-hover"
onConfirm={onDelete(database, retentionPolicy)} onClick={this.handleStartEdit}
onCancel={this.handleEndDelete} >
buttonSize="btn-xs" Edit
/> </button>
) : ( <ConfirmButton
<button text={`Delete ${name}`}
className="btn btn-danger btn-xs table--show-on-row-hover" confirmAction={onDelete(database, retentionPolicy)}
style={isDeletable ? {} : {visibility: 'hidden'}} size="btn-xs"
onClick={this.handleStartDelete} type="btn-danger"
> customClass="table--show-on-row-hover"
{`Delete ${name}`} disabled={!isDeletable}
</button> />
)}
</td> </td>
</tr> </tr>
) )

View File

@ -5,7 +5,7 @@ import {connect} from 'react-redux'
import {bindActionCreators} from 'redux' import {bindActionCreators} from 'redux'
import {notify as notifyAction} from 'shared/actions/notifications' import {notify as notifyAction} from 'shared/actions/notifications'
import ConfirmButtons from 'shared/components/ConfirmButtons' import ConfirmOrCancel from 'shared/components/ConfirmOrCancel'
import {notifyDatabaseDeleteConfirmationRequired} from 'shared/copy/notifications' import {notifyDatabaseDeleteConfirmationRequired} from 'shared/copy/notifications'
const DatabaseTableHeader = ({ const DatabaseTableHeader = ({
@ -101,7 +101,7 @@ const Header = ({
autoComplete={false} autoComplete={false}
spellCheck={false} spellCheck={false}
/> />
<ConfirmButtons <ConfirmOrCancel
item={database} item={database}
onConfirm={onConfirm} onConfirm={onConfirm}
onCancel={onCancel} onCancel={onCancel}
@ -132,7 +132,11 @@ const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => (
spellCheck={false} spellCheck={false}
autoComplete={false} autoComplete={false}
/> />
<ConfirmButtons item={database} onConfirm={onConfirm} onCancel={onCancel} /> <ConfirmOrCancel
item={database}
onConfirm={onConfirm}
onCancel={onCancel}
/>
</div> </div>
) )

View File

@ -1,72 +1,43 @@
import React, {Component} from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import ConfirmButtons from 'shared/components/ConfirmButtons' import ConfirmButton from 'shared/components/ConfirmButton'
import {QUERIES_TABLE} from 'src/admin/constants/tableSizing' import {QUERIES_TABLE} from 'src/admin/constants/tableSizing'
class QueryRow extends Component { const QueryRow = ({query, onKill}) => {
constructor(props) { const {database, duration} = query
super(props) const wrappedKill = () => {
onKill(query.id)
this.state = {
confirmingKill: false,
}
} }
handleInitiateKill = () => { return (
this.setState({confirmingKill: true}) <tr>
} <td
style={{width: `${QUERIES_TABLE.colDatabase}px`}}
handleFinishHim = () => { className="monotype"
this.props.onKill(this.props.query.id) >
} {database}
</td>
handleShowMercy = () => { <td>
this.setState({confirmingKill: false}) <code>{query.query}</code>
} </td>
<td style={{width: `${QUERIES_TABLE.colRunning}px`}} className="monotype">
render() { {duration}
const {query: {database, query, duration}} = this.props </td>
<td
return ( style={{width: `${QUERIES_TABLE.colKillQuery}px`}}
<tr> className="text-right"
<td >
style={{width: `${QUERIES_TABLE.colDatabase}px`}} <ConfirmButton
className="monotype" text="Kill"
> confirmAction={wrappedKill}
{database} size="btn-xs"
</td> type="btn-danger"
<td> customClass="table--show-on-row-hover"
<code>{query}</code> />
</td> </td>
<td </tr>
style={{width: `${QUERIES_TABLE.colRunning}px`}} )
className="monotype"
>
{duration}
</td>
<td
style={{width: `${QUERIES_TABLE.colKillQuery}px`}}
className="text-right"
>
{this.state.confirmingKill ? (
<ConfirmButtons
onConfirm={this.handleFinishHim}
onCancel={this.handleShowMercy}
buttonSize="btn-xs"
/>
) : (
<button
className="btn btn-xs btn-danger table--show-on-row-hover"
onClick={this.handleInitiateKill}
>
Kill
</button>
)}
</td>
</tr>
)
}
} }
const {func, shape} = PropTypes const {func, shape} = PropTypes

View File

@ -6,8 +6,8 @@ import classnames from 'classnames'
import RoleEditingRow from 'src/admin/components/RoleEditingRow' import RoleEditingRow from 'src/admin/components/RoleEditingRow'
import MultiSelectDropdown from 'shared/components/MultiSelectDropdown' import MultiSelectDropdown from 'shared/components/MultiSelectDropdown'
import ConfirmButtons from 'shared/components/ConfirmButtons' import ConfirmOrCancel from 'shared/components/ConfirmOrCancel'
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell' import ConfirmButton from 'shared/components/ConfirmButton'
import {ROLES_TABLE} from 'src/admin/constants/tableSizing' import {ROLES_TABLE} from 'src/admin/constants/tableSizing'
const RoleRow = ({ const RoleRow = ({
@ -51,7 +51,7 @@ const RoleRow = ({
className="text-right" className="text-right"
style={{width: `${ROLES_TABLE.colDelete}px`}} style={{width: `${ROLES_TABLE.colDelete}px`}}
> >
<ConfirmButtons <ConfirmOrCancel
item={role} item={role}
onConfirm={onSave} onConfirm={onSave}
onCancel={onCancel} onCancel={onCancel}
@ -62,6 +62,10 @@ const RoleRow = ({
) )
} }
const wrappedDelete = () => {
onDelete(role)
}
return ( return (
<tr> <tr>
<td style={{width: `${ROLES_TABLE.colName}px`}}>{roleName}</td> <td style={{width: `${ROLES_TABLE.colName}px`}}>{roleName}</td>
@ -97,11 +101,15 @@ const RoleRow = ({
/> />
) : null} ) : null}
</td> </td>
<DeleteConfirmTableCell <td className="text-right">
onDelete={onDelete} <ConfirmButton
item={role} customClass="table--show-on-row-hover"
buttonSize="btn-xs" size="btn-xs"
/> type="btn-danger"
text="Delete Role"
confirmAction={wrappedDelete}
/>
</td>
</tr> </tr>
) )
} }

View File

@ -7,8 +7,8 @@ import classnames from 'classnames'
import UserEditName from 'src/admin/components/UserEditName' import UserEditName from 'src/admin/components/UserEditName'
import UserNewPassword from 'src/admin/components/UserNewPassword' import UserNewPassword from 'src/admin/components/UserNewPassword'
import MultiSelectDropdown from 'shared/components/MultiSelectDropdown' import MultiSelectDropdown from 'shared/components/MultiSelectDropdown'
import ConfirmButtons from 'shared/components/ConfirmButtons' import ConfirmOrCancel from 'shared/components/ConfirmOrCancel'
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell' import ConfirmButton from 'shared/components/ConfirmButton'
import ChangePassRow from 'src/admin/components/ChangePassRow' import ChangePassRow from 'src/admin/components/ChangePassRow'
import {USERS_TABLE} from 'src/admin/constants/tableSizing' import {USERS_TABLE} from 'src/admin/constants/tableSizing'
@ -46,6 +46,10 @@ const UserRow = ({
const perms = _.get(permissions, ['0', 'allowed'], []) const perms = _.get(permissions, ['0', 'allowed'], [])
const wrappedDelete = () => {
onDelete(user)
}
if (isEditing) { if (isEditing) {
return ( return (
<tr className="admin-table--edit-row"> <tr className="admin-table--edit-row">
@ -62,7 +66,7 @@ const UserRow = ({
className="text-right" className="text-right"
style={{width: `${USERS_TABLE.colDelete}px`}} style={{width: `${USERS_TABLE.colDelete}px`}}
> >
<ConfirmButtons <ConfirmOrCancel
item={user} item={user}
onConfirm={onSave} onConfirm={onSave}
onCancel={onCancel} onCancel={onCancel}
@ -118,11 +122,15 @@ const UserRow = ({
/> />
) : null} ) : null}
</td> </td>
<DeleteConfirmTableCell <td className="text-right" style={{width: `${USERS_TABLE.colDelete}px`}}>
onDelete={onDelete} <ConfirmButton
item={user} size="btn-xs"
buttonSize="btn-xs" type="btn-danger"
/> text="Delete User"
confirmAction={wrappedDelete}
customClass="table--show-on-row-hover"
/>
</td>
</tr> </tr>
) )
} }

View File

@ -4,7 +4,7 @@ import {connect} from 'react-redux'
import {bindActionCreators} from 'redux' import {bindActionCreators} from 'redux'
import {withRouter} from 'react-router' import {withRouter} from 'react-router'
import ConfirmButtons from 'shared/components/ConfirmButtons' import ConfirmButton from 'shared/components/ConfirmButton'
import Dropdown from 'shared/components/Dropdown' import Dropdown from 'shared/components/Dropdown'
import InputClickToEdit from 'shared/components/InputClickToEdit' import InputClickToEdit from 'shared/components/InputClickToEdit'
@ -13,32 +13,7 @@ import {meChangeOrganizationAsync} from 'shared/actions/auth'
import {DEFAULT_ORG_ID} from 'src/admin/constants/chronografAdmin' import {DEFAULT_ORG_ID} from 'src/admin/constants/chronografAdmin'
import {USER_ROLES} from 'src/admin/constants/chronografAdmin' import {USER_ROLES} from 'src/admin/constants/chronografAdmin'
const OrganizationsTableRowDeleteButton = ({organization, onClickDelete}) =>
organization.id === DEFAULT_ORG_ID ? (
<button
className="btn btn-sm btn-default btn-square orgs-table--delete"
disabled={true}
>
<span className="icon trash" />
</button>
) : (
<button
className="btn btn-sm btn-default btn-square"
onClick={onClickDelete}
>
<span className="icon trash" />
</button>
)
class OrganizationsTableRow extends Component { class OrganizationsTableRow extends Component {
constructor(props) {
super(props)
this.state = {
isDeleting: false,
}
}
handleChangeCurrentOrganization = async () => { handleChangeCurrentOrganization = async () => {
const {router, links, meChangeOrganization, organization} = this.props const {router, links, meChangeOrganization, organization} = this.props
@ -49,13 +24,6 @@ class OrganizationsTableRow extends Component {
const {organization, onRename} = this.props const {organization, onRename} = this.props
onRename(organization, newName) onRename(organization, newName)
} }
handleDeleteClick = () => {
this.setState({isDeleting: true})
}
handleDismissDeleteConfirmation = () => {
this.setState({isDeleting: false})
}
handleDeleteOrg = organization => { handleDeleteOrg = organization => {
const {onDelete} = this.props const {onDelete} = this.props
@ -68,7 +36,6 @@ class OrganizationsTableRow extends Component {
} }
render() { render() {
const {isDeleting} = this.state
const {organization, currentOrganization} = this.props const {organization, currentOrganization} = this.props
const dropdownRolesItems = USER_ROLES.map(role => ({ const dropdownRolesItems = USER_ROLES.map(role => ({
@ -76,10 +43,6 @@ class OrganizationsTableRow extends Component {
text: role.name, text: role.name,
})) }))
const defaultRoleClassName = isDeleting
? 'fancytable--td orgs-table--default-role deleting'
: 'fancytable--td orgs-table--default-role'
return ( return (
<div className="fancytable--row"> <div className="fancytable--row">
<div className="fancytable--td orgs-table--active"> <div className="fancytable--td orgs-table--active">
@ -101,7 +64,7 @@ class OrganizationsTableRow extends Component {
wrapperClass="fancytable--td orgs-table--name" wrapperClass="fancytable--td orgs-table--name"
onBlur={this.handleUpdateOrgName} onBlur={this.handleUpdateOrgName}
/> />
<div className={defaultRoleClassName}> <div className="fancytable--td orgs-table--default-role">
<Dropdown <Dropdown
items={dropdownRolesItems} items={dropdownRolesItems}
onChoose={this.handleChooseDefaultRole} onChoose={this.handleChooseDefaultRole}
@ -109,21 +72,14 @@ class OrganizationsTableRow extends Component {
className="dropdown-stretch" className="dropdown-stretch"
/> />
</div> </div>
{isDeleting ? ( <ConfirmButton
<ConfirmButtons confirmAction={this.handleDeleteOrg}
item={organization} confirmText="Delete Organization?"
onCancel={this.handleDismissDeleteConfirmation} size="btn-sm"
onConfirm={this.handleDeleteOrg} square={true}
onClickOutside={this.handleDismissDeleteConfirmation} icon="trash"
confirmLeft={true} disabled={organization.id === DEFAULT_ORG_ID}
confirmTitle="Delete" />
/>
) : (
<OrganizationsTableRowDeleteButton
organization={organization}
onClickDelete={this.handleDeleteClick}
/>
)}
</div> </div>
) )
} }
@ -161,15 +117,6 @@ OrganizationsTableRow.propTypes = {
meChangeOrganization: func.isRequired, meChangeOrganization: func.isRequired,
} }
OrganizationsTableRowDeleteButton.propTypes = {
organization: shape({
id: string, // when optimistically created, organization will not have an id
name: string.isRequired,
defaultRole: string.isRequired,
}).isRequired,
onClickDelete: func.isRequired,
}
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
meChangeOrganization: bindActionCreators(meChangeOrganizationAsync, dispatch), meChangeOrganization: bindActionCreators(meChangeOrganizationAsync, dispatch),
}) })

View File

@ -1,7 +1,7 @@
import React, {Component} from 'react' import React, {Component} from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import ConfirmButtons from 'shared/components/ConfirmButtons' import ConfirmOrCancel from 'shared/components/ConfirmOrCancel'
import Dropdown from 'shared/components/Dropdown' import Dropdown from 'shared/components/Dropdown'
import {USER_ROLES} from 'src/admin/constants/chronografAdmin' import {USER_ROLES} from 'src/admin/constants/chronografAdmin'
@ -74,7 +74,7 @@ class OrganizationsTableRowNew extends Component {
ref={r => (this.inputRef = r)} ref={r => (this.inputRef = r)}
/> />
</div> </div>
<div className="fancytable--td orgs-table--default-role deleting"> <div className="fancytable--td orgs-table--default-role creating">
<Dropdown <Dropdown
items={dropdownRolesItems} items={dropdownRolesItems}
onChoose={this.handleChooseDefaultRole} onChoose={this.handleChooseDefaultRole}
@ -82,7 +82,7 @@ class OrganizationsTableRowNew extends Component {
className="dropdown-stretch" className="dropdown-stretch"
/> />
</div> </div>
<ConfirmButtons <ConfirmOrCancel
disabled={isSaveDisabled} disabled={isSaveDisabled}
onCancel={onCancelCreateOrganization} onCancel={onCancelCreateOrganization}
onConfirm={this.handleClickSave} onConfirm={this.handleClickSave}

View File

@ -1,7 +1,7 @@
import React, {Component} from 'react' import React, {Component} from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import ConfirmButtons from 'shared/components/ConfirmButtons' import ConfirmButton from 'shared/components/ConfirmButton'
import Dropdown from 'shared/components/Dropdown' import Dropdown from 'shared/components/Dropdown'
import InputClickToEdit from 'shared/components/InputClickToEdit' import InputClickToEdit from 'shared/components/InputClickToEdit'
@ -13,21 +13,11 @@ class ProvidersTableRow extends Component {
this.state = { this.state = {
...this.props.mapping, ...this.props.mapping,
isDeleting: false,
} }
} }
handleDeleteClick = () => {
this.setState({isDeleting: true})
}
handleDismissDeleteConfirmation = () => {
this.setState({isDeleting: false})
}
handleDeleteMap = mapping => { handleDeleteMap = mapping => {
const {onDelete} = this.props const {onDelete} = this.props
this.setState({isDeleting: false})
onDelete(mapping) onDelete(mapping)
} }
@ -106,22 +96,13 @@ class ProvidersTableRow extends Component {
disabled={isDefaultMapping} disabled={isDefaultMapping}
/> />
</div> </div>
{isDeleting ? ( <ConfirmButton
<ConfirmButtons square={true}
item={mapping} icon="trash"
onCancel={this.handleDismissDeleteConfirmation} confirmAction={this.handleDeleteMap}
onConfirm={this.handleDeleteMap} confirmText="Delete this Mapping?"
onClickOutside={this.handleDismissDeleteConfirmation} disabled={isDefaultMapping}
confirmTitle="Delete" />
/>
) : (
<button
className="btn btn-sm btn-default btn-square"
onClick={this.handleDeleteClick}
>
<span className="icon trash" />
</button>
)}
</div> </div>
) )
} }

View File

@ -1,6 +1,6 @@
import React, {PureComponent} from 'react' import React, {PureComponent} from 'react'
import ConfirmButtons from 'src/shared/components/ConfirmButtons' import ConfirmOrCancel from 'src/shared/components/ConfirmOrCancel'
import Dropdown from 'src/shared/components/Dropdown' import Dropdown from 'src/shared/components/Dropdown'
import InputClickToEdit from 'src/shared/components/InputClickToEdit' import InputClickToEdit from 'src/shared/components/InputClickToEdit'
@ -89,7 +89,7 @@ class ProvidersTableRowNew extends PureComponent<Props, State> {
<div className="fancytable--td provider--arrow"> <div className="fancytable--td provider--arrow">
<span /> <span />
</div> </div>
<div className="fancytable--td provider--redirect deleting"> <div className="fancytable--td provider--redirect creating">
<Dropdown <Dropdown
items={dropdownItems} items={dropdownItems}
onChoose={this.handleChooseOrganization} onChoose={this.handleChooseOrganization}
@ -97,7 +97,7 @@ class ProvidersTableRowNew extends PureComponent<Props, State> {
className="dropdown-stretch" className="dropdown-stretch"
/> />
</div> </div>
<ConfirmButtons <ConfirmOrCancel
onCancel={onCancel} onCancel={onCancel}
onConfirm={this.handleSaveNewMapping} onConfirm={this.handleSaveNewMapping}
isDisabled={preventCreate} isDisabled={preventCreate}

View File

@ -58,14 +58,16 @@ const UsersTableRow = ({
</td> </td>
<td style={{width: colProvider}}>{user.provider}</td> <td style={{width: colProvider}}>{user.provider}</td>
<td style={{width: colScheme}}>{user.scheme}</td> <td style={{width: colScheme}}>{user.scheme}</td>
<ConfirmButton <td className="text-right">
confirmText={removeWarning} <ConfirmButton
confirmAction={wrappedDelete} confirmText={removeWarning}
size="btn-xs" confirmAction={wrappedDelete}
type="btn-danger" size="btn-xs"
text="Remove" type="btn-danger"
customClass="table--show-on-row-hover" text="Remove"
/> customClass="table--show-on-row-hover"
/>
</td>
</tr> </tr>
) )
} }

View File

@ -3,13 +3,13 @@ export const USERS_TABLE = {
colPassword: 186, colPassword: 186,
colRoles: 190, colRoles: 190,
colPermissions: 190, colPermissions: 190,
colDelete: 70, colDelete: 110,
} }
export const ROLES_TABLE = { export const ROLES_TABLE = {
colName: 280, colName: 280,
colUsers: 200, colUsers: 200,
colPermissions: 200, colPermissions: 200,
colDelete: 70, colDelete: 110,
} }
export const QUERIES_TABLE = { export const QUERIES_TABLE = {
colDatabase: 160, colDatabase: 160,

View File

@ -5,7 +5,7 @@ import _ from 'lodash'
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized' import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell' import ConfirmButton from 'shared/components/ConfirmButton'
const AuthorizedEmptyState = ({onCreateDashboard}) => ( const AuthorizedEmptyState = ({onCreateDashboard}) => (
<div className="generic-empty-state"> <div className="generic-empty-state">
@ -35,6 +35,9 @@ const DashboardsTable = ({
onCreateDashboard, onCreateDashboard,
dashboardLink, dashboardLink,
}) => { }) => {
const wrappedDelete = dashboard => () => {
onDeleteDashboard(dashboard)
}
return dashboards && dashboards.length ? ( return dashboards && dashboards.length ? (
<table className="table v-center admin-table table-highlight"> <table className="table v-center admin-table table-highlight">
<thead> <thead>
@ -67,11 +70,15 @@ const DashboardsTable = ({
requiredRole={EDITOR_ROLE} requiredRole={EDITOR_ROLE}
replaceWithIfNotAuthorized={<td />} replaceWithIfNotAuthorized={<td />}
> >
<DeleteConfirmTableCell <td className="text-right">
onDelete={onDeleteDashboard} <ConfirmButton
item={dashboard} confirmAction={wrappedDelete}
buttonSize="btn-xs" size="btn-xs"
/> type="btn-danger"
text="Delete"
customClass="table--show-on-row-hover"
/>
</td>
</Authorized> </Authorized>
</tr> </tr>
))} ))}

View File

@ -2,7 +2,7 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import classnames from 'classnames' import classnames from 'classnames'
import ConfirmButtons from 'shared/components/ConfirmButtons' import ConfirmOrCancel from 'shared/components/ConfirmOrCancel'
import SourceSelector from 'src/dashboards/components/SourceSelector' import SourceSelector from 'src/dashboards/components/SourceSelector'
const OverlayControls = ({ const OverlayControls = ({
@ -44,7 +44,7 @@ const OverlayControls = ({
</li> </li>
</ul> </ul>
<div className="overlay-controls--right"> <div className="overlay-controls--right">
<ConfirmButtons <ConfirmOrCancel
onCancel={onCancel} onCancel={onCancel}
onConfirm={onSave} onConfirm={onSave}
isDisabled={!isSavable} isDisabled={!isSavable}

View File

@ -12,7 +12,7 @@ import Dropdown from 'shared/components/Dropdown'
import TemplateQueryBuilder from 'src/dashboards/components/template_variables/TemplateQueryBuilder' import TemplateQueryBuilder from 'src/dashboards/components/template_variables/TemplateQueryBuilder'
import TableInput from 'src/dashboards/components/template_variables/TableInput' import TableInput from 'src/dashboards/components/template_variables/TableInput'
import RowValues from 'src/dashboards/components/template_variables/RowValues' import RowValues from 'src/dashboards/components/template_variables/RowValues'
import RowButtons from 'src/dashboards/components/template_variables/RowButtons' import ConfirmButton from 'src/shared/components/ConfirmButton'
import {runTemplateVariableQuery as runTemplateVariableQueryAJAX} from 'src/dashboards/apis' import {runTemplateVariableQuery as runTemplateVariableQueryAJAX} from 'src/dashboards/apis'
@ -95,15 +95,31 @@ const TemplateVariableRow = ({
autoFocusTarget={autoFocusTarget} autoFocusTarget={autoFocusTarget}
/> />
</div> </div>
<div className="tvm--col-4"> <div className={`tvm--col-4${isEditing ? ' editing' : ''}`}>
<RowButtons {isEditing ? (
onStartEdit={onStartEdit} <div className="tvm-actions">
isEditing={isEditing} <button
onCancelEdit={onCancelEdit} className="btn btn-sm btn-info btn-square"
onDelete={onDeleteTempVar} type="button"
id={id} onClick={onCancelEdit}
selectedType={selectedType} >
/> <span className="icon remove" />
</button>
<button className="btn btn-sm btn-success btn-square" type="submit">
<span className="icon checkmark" />
</button>
</div>
) : (
<div className="tvm-actions">
<ConfirmButton
type="btn-danger"
confirmText="Delete template variable?"
confirmAction={onDeleteTempVar(id)}
icon="trash"
square={true}
/>
</div>
)}
</div> </div>
</form> </form>
) )

View File

@ -1,51 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import DeleteConfirmButtons from 'shared/components/DeleteConfirmButtons'
const RowButtons = ({onStartEdit, isEditing, onCancelEdit, onDelete, id}) => {
if (isEditing) {
return (
<div className="tvm-actions">
<button
className="btn btn-sm btn-info btn-square"
type="button"
onClick={onCancelEdit}
>
<span className="icon remove" />
</button>
<button className="btn btn-sm btn-success btn-square" type="submit">
<span className="icon checkmark" />
</button>
</div>
)
}
return (
<div className="tvm-actions">
<DeleteConfirmButtons
onDelete={onDelete(id)}
icon="remove"
square={true}
/>
<button
className="btn btn-sm btn-info btn-edit btn-square"
type="button"
onClick={onStartEdit('tempVar')}
>
<span className="icon pencil" />
</button>
</div>
)
}
const {bool, func, string} = PropTypes
RowButtons.propTypes = {
onStartEdit: func.isRequired,
isEditing: bool.isRequired,
onCancelEdit: func.isRequired,
onDelete: func.isRequired,
id: string.isRequired,
selectedType: string.isRequired,
}
export default RowButtons

View File

@ -18,16 +18,19 @@ interface CancelProps {
buttonSize: string buttonSize: string
onCancel: () => void onCancel: () => void
icon: string icon: string
title: string
} }
interface ConfirmButtonsProps {
interface ConfirmOrCancelProps {
onConfirm: (item: Item) => void onConfirm: (item: Item) => void
item: Item item: Item
onCancel: (item: Item) => void onCancel: (item: Item) => void
buttonSize?: string buttonSize?: string
isDisabled?: boolean isDisabled?: boolean
onClickOutside?: (item: Item) => void onClickOutside?: (item: Item) => void
confirmLeft?: boolean reversed?: boolean
confirmTitle?: string confirmTitle?: string
cancelTitle?: string
} }
export const Confirm: SFC<ConfirmProps> = ({ export const Confirm: SFC<ConfirmProps> = ({
@ -39,9 +42,12 @@ export const Confirm: SFC<ConfirmProps> = ({
}) => ( }) => (
<button <button
data-test="confirm" data-test="confirm"
className={classnames('btn btn-success btn-square', { className={classnames(
[buttonSize]: buttonSize, 'confirm-or-cancel--confirm btn btn-success btn-square',
})} {
[buttonSize]: buttonSize,
}
)}
disabled={isDisabled} disabled={isDisabled}
title={isDisabled ? `Cannot ${title}` : title} title={isDisabled ? `Cannot ${title}` : title}
onClick={onConfirm} onClick={onConfirm}
@ -50,22 +56,29 @@ export const Confirm: SFC<ConfirmProps> = ({
</button> </button>
) )
export const Cancel: SFC<CancelProps> = ({buttonSize, onCancel, icon}) => ( export const Cancel: SFC<CancelProps> = ({
buttonSize,
onCancel,
icon,
title,
}) => (
<button <button
data-test="cancel" data-test="cancel"
className={classnames('btn btn-info btn-square', { className={classnames('confirm-or-cancel--cancel btn btn-info btn-square', {
[buttonSize]: buttonSize, [buttonSize]: buttonSize,
})} })}
onClick={onCancel} onClick={onCancel}
title={title}
> >
<span className={icon} /> <span className={icon} />
</button> </button>
) )
class ConfirmButtons extends PureComponent<ConfirmButtonsProps, {}> { class ConfirmOrCancel extends PureComponent<ConfirmOrCancelProps, {}> {
public static defaultProps: Partial<ConfirmButtonsProps> = { public static defaultProps: Partial<ConfirmOrCancelProps> = {
buttonSize: 'btn-sm', buttonSize: 'btn-sm',
confirmTitle: 'Save', confirmTitle: 'Save',
cancelTitle: 'Cancel',
onClickOutside: () => {}, onClickOutside: () => {},
} }
@ -86,29 +99,23 @@ class ConfirmButtons extends PureComponent<ConfirmButtonsProps, {}> {
} }
public render() { public render() {
const {item, buttonSize, isDisabled, confirmLeft, confirmTitle} = this.props const {
item,
buttonSize,
isDisabled,
reversed,
confirmTitle,
cancelTitle,
} = this.props
return confirmLeft ? ( const className = `confirm-or-cancel${reversed ? ' reversed' : ''}`
<div className="confirm-buttons"> return (
<Confirm <div className={className}>
buttonSize={buttonSize}
isDisabled={isDisabled}
onConfirm={this.handleConfirm(item)}
icon="icon checkmark"
title={confirmTitle}
/>
<Cancel
buttonSize={buttonSize}
onCancel={this.handleCancel(item)}
icon="icon remove"
/>
</div>
) : (
<div className="confirm-buttons">
<Cancel <Cancel
buttonSize={buttonSize} buttonSize={buttonSize}
onCancel={this.handleCancel(item)} onCancel={this.handleCancel(item)}
icon="icon remove" icon="icon remove"
title={cancelTitle}
/> />
<Confirm <Confirm
buttonSize={buttonSize} buttonSize={buttonSize}
@ -122,4 +129,4 @@ class ConfirmButtons extends PureComponent<ConfirmButtonsProps, {}> {
} }
} }
export default OnClickOutside(ConfirmButtons) export default OnClickOutside(ConfirmOrCancel)

View File

@ -1,116 +0,0 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import OnClickOutside from 'shared/components/OnClickOutside'
import ConfirmButtons from 'shared/components/ConfirmButtons'
const DeleteButton = ({
onClickDelete,
buttonSize,
icon,
square,
text,
disabled,
}) => (
<button
className={classnames('btn btn-danger table--show-on-row-hover', {
[buttonSize]: buttonSize,
'btn-square': square,
disabled,
})}
onClick={onClickDelete}
>
{icon ? <span className={`icon ${icon}`} /> : null}
{square ? null : text}
</button>
)
class DeleteConfirmButtons extends Component {
constructor(props) {
super(props)
this.state = {
isConfirming: false,
}
}
handleClickDelete = () => {
this.setState({isConfirming: true})
}
handleCancel = () => {
this.setState({isConfirming: false})
}
handleClickOutside() {
this.setState({isConfirming: false})
}
render() {
const {
onDelete,
item,
buttonSize,
icon,
square,
text,
disabled,
} = this.props
const {isConfirming} = this.state
if (square && !icon) {
console.error(
'DeleteButton component requires both icon if passing in square.'
)
}
return isConfirming ? (
<ConfirmButtons
onConfirm={onDelete}
item={item}
onCancel={this.handleCancel}
buttonSize={buttonSize}
/>
) : (
<DeleteButton
text={text}
onClickDelete={disabled ? () => {} : this.handleClickDelete}
buttonSize={buttonSize}
icon={icon}
square={square}
disabled={disabled}
/>
)
}
}
const {bool, func, oneOfType, shape, string} = PropTypes
DeleteButton.propTypes = {
onClickDelete: func.isRequired,
buttonSize: string,
icon: string,
square: bool,
disabled: bool,
text: string.isRequired,
}
DeleteButton.defaultProps = {
text: 'Delete',
}
DeleteConfirmButtons.propTypes = {
text: string,
item: oneOfType([(string, shape())]),
onDelete: func.isRequired,
buttonSize: string,
square: bool,
icon: string,
disabled: bool,
}
DeleteConfirmButtons.defaultProps = {
buttonSize: 'btn-sm',
}
export default OnClickOutside(DeleteConfirmButtons)

View File

@ -1,12 +0,0 @@
import React from 'react'
import DeleteConfirmButtons from 'shared/components/DeleteConfirmButtons'
import {ADMIN_TABLE} from 'src/admin/constants/tableSizing'
const DeleteConfirmTableCell = props => (
<td className="text-right" style={{width: `${ADMIN_TABLE.colDelete}px`}}>
<DeleteConfirmButtons {...props} />
</td>
)
export default DeleteConfirmTableCell

View File

@ -1,37 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
const YesNoButtons = ({onConfirm, onCancel, buttonSize}) => (
<div>
<button
className={classnames('btn btn-square btn-info', {
[buttonSize]: buttonSize,
})}
onClick={onCancel}
>
<span className="icon remove" />
</button>
<button
className={classnames('btn btn-square btn-success', {
[buttonSize]: buttonSize,
})}
onClick={onConfirm}
>
<span className="icon checkmark" />
</button>
</div>
)
const {func, string} = PropTypes
YesNoButtons.propTypes = {
onConfirm: func.isRequired,
onCancel: func.isRequired,
buttonSize: string,
}
YesNoButtons.defaultProps = {
buttonSize: 'btn-sm',
}
export default YesNoButtons

View File

@ -7,6 +7,7 @@ import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
import Dropdown from 'shared/components/Dropdown' import Dropdown from 'shared/components/Dropdown'
import QuestionMarkTooltip from 'shared/components/QuestionMarkTooltip' import QuestionMarkTooltip from 'shared/components/QuestionMarkTooltip'
import ConfirmButton from 'shared/components/ConfirmButton'
const kapacitorDropdown = ( const kapacitorDropdown = (
kapacitors, kapacitors,
@ -96,117 +97,124 @@ const InfluxTable = ({
handleDeleteKapacitor, handleDeleteKapacitor,
isUsingAuth, isUsingAuth,
me, me,
}) => ( }) => {
<div className="row"> const wrappedDelete = s => () => {
<div className="col-md-12"> handleDeleteSource(s)
<div className="panel"> }
<div className="panel-heading"> return (
<h2 className="panel-title"> <div className="row">
{isUsingAuth ? ( <div className="col-md-12">
<span> <div className="panel">
Connections for <em>{me.currentOrganization.name}</em> <div className="panel-heading">
</span> <h2 className="panel-title">
) : ( {isUsingAuth ? (
<span>Connections</span> <span>
)} Connections for <em>{me.currentOrganization.name}</em>
</h2> </span>
<Authorized requiredRole={EDITOR_ROLE}> ) : (
<Link <span>Connections</span>
to={`/sources/${source.id}/manage-sources/new`} )}
className="btn btn-sm btn-primary" </h2>
> <Authorized requiredRole={EDITOR_ROLE}>
<span className="icon plus" /> Add Connection <Link
</Link> to={`/sources/${source.id}/manage-sources/new`}
</Authorized> className="btn btn-sm btn-primary"
</div> >
<div className="panel-body"> <span className="icon plus" /> Add Connection
<table className="table v-center margin-bottom-zero table-highlight"> </Link>
<thead> </Authorized>
<tr> </div>
<th className="source-table--connect-col" /> <div className="panel-body">
<th>InfluxDB Connection</th> <table className="table v-center margin-bottom-zero table-highlight">
<th className="text-right" /> <thead>
<th> <tr>
Kapacitor Connection{' '} <th className="source-table--connect-col" />
<QuestionMarkTooltip <th>InfluxDB Connection</th>
tipID="kapacitor-node-helper" <th className="text-right" />
tipContent={ <th>
'<p>Kapacitor Connections are<br/>scoped per InfluxDB Connection.<br/>Only one can be active at a time.</p>' Kapacitor Connection{' '}
} <QuestionMarkTooltip
/> tipID="kapacitor-node-helper"
</th> tipContent={
</tr> '<p>Kapacitor Connections are<br/>scoped per InfluxDB Connection.<br/>Only one can be active at a time.</p>'
</thead> }
<tbody> />
{sources.map(s => { </th>
return ( </tr>
<tr </thead>
key={s.id} <tbody>
className={s.id === source.id ? 'highlight' : null} {sources.map(s => {
> return (
<td> <tr
{s.id === source.id ? ( key={s.id}
<div className="btn btn-success btn-xs source-table--connect"> className={s.id === source.id ? 'highlight' : null}
Connected >
</div> <td>
) : ( {s.id === source.id ? (
<Link <div className="btn btn-success btn-xs source-table--connect">
className="btn btn-default btn-xs source-table--connect" Connected
to={`/sources/${s.id}/hosts`} </div>
> ) : (
Connect
</Link>
)}
</td>
<td>
<h5 className="margin-zero">
<Authorized
requiredRole={EDITOR_ROLE}
replaceWithIfNotAuthorized={<strong>{s.name}</strong>}
>
<Link <Link
to={`${location.pathname}/${s.id}/edit`} className="btn btn-default btn-xs source-table--connect"
className={ to={`/sources/${s.id}/hosts`}
s.id === source.id ? 'link-success' : null >
Connect
</Link>
)}
</td>
<td>
<h5 className="margin-zero">
<Authorized
requiredRole={EDITOR_ROLE}
replaceWithIfNotAuthorized={
<strong>{s.name}</strong>
} }
> >
<strong>{s.name}</strong> <Link
{s.default ? ' (Default)' : null} to={`${location.pathname}/${s.id}/edit`}
</Link> className={
s.id === source.id ? 'link-success' : null
}
>
<strong>{s.name}</strong>
{s.default ? ' (Default)' : null}
</Link>
</Authorized>
</h5>
<span>{s.url}</span>
</td>
<td className="text-right">
<Authorized requiredRole={EDITOR_ROLE}>
<ConfirmButton
customClass="delete-source table--show-on-row-hover"
type="btn-danger"
size="btn-xs"
text="Delete Connection"
confirmAction={wrappedDelete(s)}
/>
</Authorized> </Authorized>
</h5> </td>
<span>{s.url}</span> <td className="source-table--kapacitor">
</td> {kapacitorDropdown(
<td className="text-right"> s.kapacitors,
<Authorized requiredRole={EDITOR_ROLE}> s,
<a router,
className="btn btn-xs btn-danger table--show-on-row-hover" setActiveKapacitor,
href="#" handleDeleteKapacitor
onClick={handleDeleteSource(s)} )}
> </td>
Delete Connection </tr>
</a> )
</Authorized> })}
</td> </tbody>
<td className="source-table--kapacitor"> </table>
{kapacitorDropdown( </div>
s.kapacitors,
s,
router,
setActiveKapacitor,
handleDeleteKapacitor
)}
</td>
</tr>
)
})}
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
</div> )
) }
const {array, bool, func, shape, string} = PropTypes const {array, bool, func, shape, string} = PropTypes

View File

@ -35,11 +35,9 @@
@import 'components/crosshairs'; @import 'components/crosshairs';
@import 'components/ceo-display-options'; @import 'components/ceo-display-options';
@import 'components/confirm-button'; @import 'components/confirm-button';
@import 'components/confirm-buttons'; @import 'components/confirm-or-cancel';
@import 'components/code-mirror-theme'; @import 'components/code-mirror-theme';
@import 'components/color-dropdown'; @import 'components/color-dropdown';
@import 'components/confirm-button';
@import 'components/confirm-buttons';
@import 'components/custom-time-range'; @import 'components/custom-time-range';
@import 'components/customize-fields'; @import 'components/customize-fields';
@import 'components/dygraphs'; @import 'components/dygraphs';

View File

@ -1,18 +0,0 @@
/*
Confirmation Buttons
------------------------------------------------------
*/
.confirm-buttons {
display: flex !important;
align-items: center;
justify-content: flex-end;
flex-wrap: nowrap;
& > * {
margin: 0 0 0 4px !important;
&:first-child {
margin: 0 !important;
}
}
}

View File

@ -0,0 +1,30 @@
/*
"Confirm or Cancel" Buttons
----------------------------------------------------------------------------
*/
.confirm-or-cancel {
display: flex;
align-items: center;
justify-content: flex-end;
flex-wrap: nowrap;
.confirm-or-cancel--confirm {
order: 2;
margin-left: 4px;
}
.confirm-or-cancel--cancel {
order: 1;
}
&.reversed {
.confirm-or-cancel--confirm {
order: 1;
margin-left: 0;
}
.confirm-or-cancel--cancel {
order: 2;
margin-left: 4px;
}
}
}

View File

@ -12,7 +12,8 @@ $fancytable--table--margin: 4px;
flex-wrap: nowrap; flex-wrap: nowrap;
align-items: center; align-items: center;
> div:not(.confirm-buttons) { > div:not(.confirm-or-cancel),
> div:not(.confirm-button) {
margin-right: $fancytable--table--margin; margin-right: $fancytable--table--margin;
} }
} }

View File

@ -26,10 +26,12 @@ $orgs-table--delete-width: 30px;
width: $orgs-table--active-width; width: $orgs-table--active-width;
justify-content: center; justify-content: center;
@include no-user-select(); @include no-user-select();
.btn {width: 100%;} .btn {
width: 100%;
}
} }
.orgs-table--default-role.deleting { .orgs-table--default-role.creating {
width: ( width: (
$orgs-table--default-role-width - $fancytable--table--margin - $orgs-table--default-role-width - $fancytable--table--margin -
$orgs-table--delete-width $orgs-table--delete-width

View File

@ -28,22 +28,35 @@ $tvmp-table-gutter: 8px;
} }
} }
.template-variable-manager--body { .template-variable-manager--body {
padding: 18px ($tvmp-gutter - $tvmp-table-gutter) $tvmp-gutter ($tvmp-gutter - $tvmp-table-gutter); padding: 18px ($tvmp-gutter - $tvmp-table-gutter) $tvmp-gutter
($tvmp-gutter - $tvmp-table-gutter);
border-radius: 0 0 $radius $radius; border-radius: 0 0 $radius $radius;
min-height: $tvmp-min-height; min-height: $tvmp-min-height;
max-height: $tvmp-max-height; max-height: $tvmp-max-height;
@include gradient-v($g2-kevlar,$g0-obsidian); @include gradient-v($g2-kevlar, $g0-obsidian);
@include custom-scrollbar-round($g0-obsidian,$g3-castle); @include custom-scrollbar-round($g0-obsidian, $g3-castle);
} }
.template-variable-manager--table, .template-variable-manager--table,
.template-variable-manager--table-container { .template-variable-manager--table-container {
width: 100%; width: 100%;
} }
/* Column Widths */ /* Column Widths */
.tvm--col-1 {flex: 0 0 140px;} .tvm--col-1 {
.tvm--col-2 {flex: 0 0 140px;} flex: 0 0 140px;
.tvm--col-3 {flex: 1 0 0;} }
.tvm--col-4 {flex: 0 0 68px;} .tvm--col-2 {
flex: 0 0 140px;
}
.tvm--col-3 {
flex: 1 0 0;
}
.tvm--col-4 {
flex: 0 0 30px;
&.editing {
flex: 0 0 68px;
}
}
/* Table Column Labels */ /* Table Column Labels */
.template-variable-manager--table-heading { .template-variable-manager--table-heading {
@ -61,7 +74,9 @@ $tvmp-table-gutter: 8px;
@include no-user-select(); @include no-user-select();
padding-left: 6px; padding-left: 6px;
margin-right: $tvmp-table-gutter; margin-right: $tvmp-table-gutter;
&:last-child {margin-right: 0;} &:last-child {
margin-right: 0;
}
} }
} }
@ -84,7 +99,9 @@ $tvmp-table-gutter: 8px;
> * { > * {
margin-right: $tvmp-table-gutter; margin-right: $tvmp-table-gutter;
&:last-child {margin-right: 0;} &:last-child {
margin-right: 0;
}
} }
} }
@ -110,8 +127,7 @@ $tvmp-table-gutter: 8px;
border: 2px solid $g5-pepper; border: 2px solid $g5-pepper;
background-color: $g2-kevlar; background-color: $g2-kevlar;
overflow: hidden; overflow: hidden;
transition: transition: border-color 0.25s ease;
border-color 0.25s ease;
&:hover { &:hover {
cursor: text; cursor: text;
@ -167,12 +183,16 @@ $tvmp-table-gutter: 8px;
margin-bottom: 2px; margin-bottom: 2px;
margin-right: 2px; margin-right: 2px;
} }
> *:last-child {margin-right: 0;} > *:last-child {
margin-right: 0;
}
.dropdown { .dropdown {
flex: 1 0 0%; flex: 1 0 0;
& > .dropdown-toggle {width: 100%;} & > .dropdown-toggle {
width: 100%;
}
} }
} }
.tvm-query-builder--text { .tvm-query-builder--text {
@ -188,29 +208,13 @@ $tvmp-table-gutter: 8px;
font-weight: 600; font-weight: 600;
font-family: $code-font; font-family: $code-font;
} }
.tvm-actions { .tvm-actions {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
.btn-edit { & > .btn:nth-child(2) {
order: 1;
}
> .btn:first-child {
margin-left: $tvmp-table-gutter; margin-left: $tvmp-table-gutter;
} }
/* Override confirm buttons styles */
/* Janky, but doing this quick & dirty for now */
.btn-danger {
order: 2;
}
.confirm-buttons > .btn {
margin-left: $tvmp-table-gutter !important;
}
/* Hide the edit button when confirming a delete */
.confirm-buttons + .btn-edit {
display: none;
}
} }

View File

@ -10,24 +10,38 @@ $provider--providerorg-width: 210px;
$provider--redirect-width: 220px; $provider--redirect-width: 220px;
$provider--delete-width: 30px; $provider--delete-width: 30px;
.provider--id {
.provider--id {width: $provider--id-width;} width: $provider--id-width;
.provider--scheme {width: $provider--scheme-width;} }
.provider--provider {width: $provider--provider-width;} .provider--scheme {
.provider--providerorg {width: $provider--providerorg-width;} width: $provider--scheme-width;
.provider--redirect {width: $provider--redirect-width;} }
.provider--provider {
width: $provider--provider-width;
}
.provider--providerorg {
width: $provider--providerorg-width;
}
.provider--redirect {
width: $provider--redirect-width;
}
.provider--delete { .provider--delete {
width: $provider--delete-width; width: $provider--delete-width;
min-width: $provider--delete-width; min-width: $provider--delete-width;
} }
.provider--arrow {flex: 1 0 0;} .provider--arrow {
flex: 1 0 0;
}
.fancytable--td.provider--id, .fancytable--td.provider--id,
.fancytable--th.provider--id { .fancytable--th.provider--id {
padding: 0 8px; padding: 0 8px;
} }
.provider--redirect.deleting { .provider--redirect.creating {
width: ($provider--redirect-width - $fancytable--table--margin - $provider--delete-width); width: (
$provider--redirect-width - $fancytable--table--margin -
$provider--delete-width
);
} }
.provider--arrow { .provider--arrow {
@ -43,7 +57,7 @@ $provider--delete-width: 30px;
position: relative; position: relative;
height: 2px; height: 2px;
width: 100%; width: 100%;
@include gradient-h($c-pool,$c-star); @include gradient-h($c-pool, $c-star);
&:before, &:before,
&:after { &:after {

View File

@ -88,7 +88,7 @@ $overlay-z: 100;
.overlay-technology--editor .query-maker--empty { .overlay-technology--editor .query-maker--empty {
margin-bottom: 8px; margin-bottom: 8px;
} }
.overlay-controls .confirm-buttons { .overlay-controls .confirm-or-cancel {
margin-left: 32px; margin-left: 32px;
} }

View File

@ -13,7 +13,8 @@ $table--highlight-color: $g4-onyx;
border-collapse: collapse; border-collapse: collapse;
margin: 0; margin: 0;
th, td { th,
td {
font-size: 13px; font-size: 13px;
text-align: left; text-align: left;
@ -22,14 +23,20 @@ $table--highlight-color: $g4-onyx;
letter-spacing: 0; letter-spacing: 0;
} }
&.text-left {text-align: left;} &.text-left {
&.text-center {text-align: center;} text-align: left;
&.text-right {text-align: right;} }
&.text-center {
text-align: center;
}
&.text-right {
text-align: right;
}
} }
// Header // Header
> thead > tr > th, > thead > tr > th,
> thead > tr > td, { > thead > tr > td {
color: $g17-whisper; color: $g17-whisper;
font-weight: 600; font-weight: 600;
padding: 8px; padding: 8px;
@ -39,7 +46,7 @@ $table--highlight-color: $g4-onyx;
} }
// Body // Body
> tbody > tr > td, { > tbody > tr > td {
text-align: left; text-align: left;
color: $g13-mist; color: $g13-mist;
font-weight: 500; font-weight: 500;
@ -71,17 +78,26 @@ $table--highlight-color: $g4-onyx;
.table.table-striped { .table.table-striped {
> tbody > tr { > tbody > tr {
&:nth-child(odd) {background-color: $table--highlight-color;} &:nth-child(odd) {
&:nth-child(even) {background-color: transparent;} background-color: $table--highlight-color;
}
&:nth-child(even) {
background-color: transparent;
}
} }
} }
.table.table-highlight > tbody > tr:hover {background-color: $table--highlight-color;} .table.table-highlight > tbody > tr:hover {
background-color: $table--highlight-color;
}
.table > tbody > tr .table--show-on-row-hover {visibility: hidden;} .table > tbody > tr .table--show-on-row-hover {
visibility: hidden;
}
.table > tbody > tr:hover .table--show-on-row-hover, .table > tbody > tr:hover .table--show-on-row-hover,
.table > tbody > tr .table--show-on-row-hover.active {visibility: visible;} .table > tbody > tr .table--show-on-row-hover.active {
visibility: visible;
}
// For use in a Status column // For use in a Status column
.table-dot { .table-dot {
@ -91,9 +107,25 @@ $table--highlight-color: $g4-onyx;
border-radius: 50%; border-radius: 50%;
background-color: $g17-whisper; background-color: $g17-whisper;
&.dot-success {background-color: $c-rainforest;} &.dot-success {
&.dot-primary {background-color: $c-pool;} background-color: $c-rainforest;
&.dot-warning {background-color: $c-pineapple;} }
&.dot-danger {background-color: $c-dreamsicle;} &.dot-primary {
&.dot-critical {background-color: $c-fire;} background-color: $c-pool;
}
&.dot-warning {
background-color: $c-pineapple;
}
&.dot-danger {
background-color: $c-dreamsicle;
}
&.dot-critical {
background-color: $c-fire;
}
}
// Ensuring buttons inside tables don't get huge
.table th .btn,
.table td .btn {
display: inline-block;
} }

View File

@ -203,7 +203,7 @@ br {
margin-bottom: 4px; margin-bottom: 4px;
&.intro { &.intro {
@include gradient-h($c-pool,$c-star); @include gradient-h($c-pool, $c-star);
color: $g20-white; color: $g20-white;
} }
@ -306,7 +306,7 @@ $tick-script-overlay-margin: 30px;
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
*/ */
.icon.download.dlcsv:before { .icon.download.dlcsv:before {
content: "\e91d"; content: '\e91d';
font-weight: bold; font-weight: bold;
color: #bec2cc; color: #bec2cc;
} }
@ -403,23 +403,23 @@ $dash-editable-header-padding: 7px;
.page-header__left.page-header__dash-editable, .page-header__left.page-header__dash-editable,
.dashboard-title, .dashboard-title,
.dashboard-title input[type="text"].form-control.dashboard-title--input, .dashboard-title input[type='text'].form-control.dashboard-title--input,
.dashboard-title h1 { .dashboard-title h1 {
flex: 1 0 0; flex: 1 0 0;
} }
.dashboard-title { .dashboard-title {
display: flex; display: flex;
input[type="text"].form-control.dashboard-title--input, input[type='text'].form-control.dashboard-title--input,
input[type="text"].form-control.dashboard-title--input:focus, input[type='text'].form-control.dashboard-title--input:focus,
h1 { h1 {
font-size: $page-header-size; font-size: $page-header-size;
font-weight: $page-header-weight; font-weight: $page-header-weight;
padding: 0 $dash-editable-header-padding; padding: 0 $dash-editable-header-padding;
} }
input[type="text"].form-control.dashboard-title--input, input[type='text'].form-control.dashboard-title--input,
input[type="text"].form-control.dashboard-title--input:focus { input[type='text'].form-control.dashboard-title--input:focus {
font-size: $page-header-size; font-size: $page-header-size;
font-weight: $page-header-weight; font-weight: $page-header-weight;
} }
@ -439,7 +439,7 @@ $dash-editable-header-padding: 7px;
border-color 0.25s ease; border-color 0.25s ease;
&:after { &:after {
content: "\f058"; content: '\f058';
font-family: 'icomoon'; font-family: 'icomoon';
position: absolute; position: absolute;
font-size: 15px; font-size: 15px;
@ -550,3 +550,11 @@ div.dropdown.dropdown-stretch > div.dropdown-toggle,
div.dropdown.dropdown-stretch > button.dropdown-toggle { div.dropdown.dropdown-stretch > button.dropdown-toggle {
width: 100%; width: 100%;
} }
/*
Delete Source
-----------------------------------------------------------------------------
*/
.delete-source {
display: inline-block;
}

View File

@ -1,16 +1,17 @@
import React from 'react' import React from 'react'
import ConfirmButtons, { import ConfirmOrCancel, {
Cancel, Cancel,
Confirm, Confirm,
} from 'src/shared/components/ConfirmButtons' } from 'src/shared/components/ConfirmOrCancel'
import {shallow} from 'enzyme' import {shallow} from 'enzyme'
const setup = (override = {}) => { const setup = (override = {}) => {
const props = { const props = {
buttonSize: '', buttonSize: '',
confirmLeft: false, reversed: false,
confirmTitle: '', confirmTitle: '',
cancelTitle: '',
isDisabled: false, isDisabled: false,
item: '', item: '',
onCancel: () => {}, onCancel: () => {},
@ -19,7 +20,7 @@ const setup = (override = {}) => {
...override, ...override,
} }
const wrapper = shallow(<ConfirmButtons {...props} />) const wrapper = shallow(<ConfirmOrCancel {...props} />)
return { return {
props, props,
@ -27,7 +28,7 @@ const setup = (override = {}) => {
} }
} }
describe('Componenets.Shared.ConfirmButtons', () => { describe('Componenets.Shared.ConfirmOrCancel', () => {
describe('rendering', () => { describe('rendering', () => {
it('has a confirm and cancel button', () => { it('has a confirm and cancel button', () => {
const {wrapper} = setup() const {wrapper} = setup()
@ -38,30 +39,6 @@ describe('Componenets.Shared.ConfirmButtons', () => {
expect(cancel.exists()).toBe(true) expect(cancel.exists()).toBe(true)
}) })
describe('confirmLeft is true', () => {
it('has a confirm button to the left of the cancel button', () => {
const {wrapper} = setup({confirmLeft: true})
const buttons = wrapper.dive().children()
const firstButton = buttons.first()
const lastButton = buttons.last()
expect(firstButton.find(Confirm).exists()).toBe(true)
expect(lastButton.find(Cancel).exists()).toBe(true)
})
})
describe('confirmLeft is false', () => {
it('has a confirm button to the right of the cancel button', () => {
const {wrapper} = setup({confirmLeft: false})
const buttons = wrapper.dive().children()
const firstButton = buttons.first()
const lastButton = buttons.last()
expect(firstButton.find(Cancel).exists()).toBe(true)
expect(lastButton.find(Confirm).exists()).toBe(true)
})
})
describe('confirmTitle', () => { describe('confirmTitle', () => {
describe('is not defined', () => { describe('is not defined', () => {
it('has a default title', () => { it('has a default title', () => {

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import ProvidersTableRowNew from 'src/admin/components/chronograf/ProvidersTableRowNew' import ProvidersTableRowNew from 'src/admin/components/chronograf/ProvidersTableRowNew'
import ConfirmButtons from 'src/shared/components/ConfirmButtons' import ConfirmOrCancel from 'src/shared/components/ConfirmOrCancel'
import InputClickToEdit from 'src/shared/components/InputClickToEdit' import InputClickToEdit from 'src/shared/components/InputClickToEdit'
import {shallow} from 'enzyme' import {shallow} from 'enzyme'
@ -37,10 +37,10 @@ describe('Components.Shared.ProvidersTableRowNew', () => {
const organizations = [{id: 'default', name: 'default'}] const organizations = [{id: 'default', name: 'default'}]
const {row} = setup({organizations}) const {row} = setup({organizations})
const confirmButtons = row.find(ConfirmButtons) const confirmOrCancel = row.find(ConfirmOrCancel)
const confirmButtonsDisabled = confirmButtons.prop('isDisabled') const confirmOrCancelDisabled = confirmOrCancel.prop('isDisabled')
expect(confirmButtonsDisabled).toBe(true) expect(confirmOrCancelDisabled).toBe(true)
}) })
}) })
@ -54,10 +54,10 @@ describe('Components.Shared.ProvidersTableRowNew', () => {
providerInput.simulate('click') providerInput.simulate('click')
providerInput.simulate('change', '*') providerInput.simulate('change', '*')
const confirmButtons = row.find(ConfirmButtons) const confirmOrCancel = row.find(ConfirmOrCancel)
const confirmButtonsDisabled = confirmButtons.prop('isDisabled') const confirmOrCancelDisabled = confirmOrCancel.prop('isDisabled')
expect(confirmButtonsDisabled).toBe(true) expect(confirmOrCancelDisabled).toBe(true)
}) })
}) })
@ -71,10 +71,10 @@ describe('Components.Shared.ProvidersTableRowNew', () => {
providerOrganizationInput.simulate('click') providerOrganizationInput.simulate('click')
providerOrganizationInput.simulate('change', '*') providerOrganizationInput.simulate('change', '*')
const confirmButtons = row.find(ConfirmButtons) const confirmOrCancel = row.find(ConfirmOrCancel)
const confirmButtonsDisabled = confirmButtons.prop('isDisabled') const confirmOrCancelDisabled = confirmOrCancel.prop('isDisabled')
expect(confirmButtonsDisabled).toBe(true) expect(confirmOrCancelDisabled).toBe(true)
}) })
}) })
@ -92,10 +92,10 @@ describe('Components.Shared.ProvidersTableRowNew', () => {
providerOrganizationInput.simulate('click') providerOrganizationInput.simulate('click')
providerOrganizationInput.simulate('change', '*') providerOrganizationInput.simulate('change', '*')
const confirmButtons = row.find(ConfirmButtons) const confirmOrCancel = row.find(ConfirmOrCancel)
const confirmButtonsDisabled = confirmButtons.prop('isDisabled') const confirmOrCancelDisabled = confirmOrCancel.prop('isDisabled')
expect(confirmButtonsDisabled).toBe(false) expect(confirmOrCancelDisabled).toBe(false)
}) })
}) })
}) })