Refactor organizations table to use fancy-table styles and InputClickToEdit

pull/10616/head
Alex P 2017-11-14 11:52:40 -08:00 committed by Jared Scheib
parent f402faa936
commit 21cbd433c7
8 changed files with 178 additions and 263 deletions

View File

@ -67,15 +67,17 @@ class OrganizationsTable extends Component {
</button>
</div>
<div className="panel-body">
<div className="orgs-table--org-labels">
<div className="orgs-table--active" />
<div className="orgs-table--name">Name</div>
<div className="orgs-table--public">
<div className="fancytable--labels">
<div className="fancytable--th orgs-table--active" />
<div className="fancytable--th orgs-table--name">Name</div>
<div className="fancytable--th orgs-table--public">
Public{' '}
<QuestionMarkTooltip tipID="public" tipContent={PUBLIC_TOOLTIP} />
</div>
<div className="orgs-table--default-role">Default Role</div>
<div className="orgs-table--delete" />
<div className="fancytable--th orgs-table--default-role">
Default Role
</div>
<div className="fancytable--th orgs-table--delete" />
</div>
{isCreatingOrganization
? <OrganizationsTableRowNew

View File

@ -6,6 +6,7 @@ import {withRouter} from 'react-router'
import SlideToggle from 'shared/components/SlideToggle'
import ConfirmButtons from 'shared/components/ConfirmButtons'
import Dropdown from 'shared/components/Dropdown'
import InputClickToEdit from 'shared/components/InputClickToEdit'
import {meChangeOrganizationAsync} from 'shared/actions/auth'
@ -32,9 +33,7 @@ class OrganizationsTableRow extends Component {
super(props)
this.state = {
isEditing: false,
isDeleting: false,
workingName: this.props.organization.name,
}
}
@ -44,55 +43,10 @@ class OrganizationsTableRow extends Component {
await meChangeOrganization(links.me, {organization: organization.id})
router.push('')
}
handleNameClick = () => {
this.setState({isEditing: true})
handleUpdateOrgName = newName => {
const {organization, onRename} = this.props
onRename(organization, newName)
}
handleConfirmRename = () => {
const {onRename, organization} = this.props
const {workingName} = this.state
onRename(organization, workingName)
this.setState({workingName, isEditing: false})
}
handleCancelRename = () => {
const {organization} = this.props
this.setState({
workingName: organization.name,
isEditing: false,
})
}
handleInputChange = e => {
this.setState({workingName: e.target.value})
}
handleInputBlur = () => {
const {organization} = this.props
const {workingName} = this.state
if (organization.name === workingName) {
this.handleCancelRename()
} else {
this.handleConfirmRename()
}
}
handleKeyDown = e => {
if (e.key === 'Enter') {
this.handleInputBlur()
} else if (e.key === 'Escape') {
this.handleCancelRename()
}
}
handleFocus = e => {
e.target.select()
}
handleDeleteClick = () => {
this.setState({isDeleting: true})
}
@ -117,7 +71,7 @@ class OrganizationsTableRow extends Component {
}
render() {
const {workingName, isEditing, isDeleting} = this.state
const {isDeleting} = this.state
const {organization, currentOrganization} = this.props
const dropdownRolesItems = USER_ROLES.map(role => ({
@ -126,12 +80,12 @@ class OrganizationsTableRow extends Component {
}))
const defaultRoleClassName = isDeleting
? 'orgs-table--default-role editing'
: 'orgs-table--default-role'
? 'fancytable--td orgs-table--default-role deleting'
: 'fancytable--td orgs-table--default-role'
return (
<div className="orgs-table--org">
<div className="orgs-table--active">
<div className="fancytable--row">
<div className="fancytable--td orgs-table--active">
{organization.id === currentOrganization.id
? <button className="btn btn-sm btn-success">
<span className="icon checkmark" /> Current
@ -143,32 +97,22 @@ class OrganizationsTableRow extends Component {
<span className="icon shuffle" /> Switch to
</button>}
</div>
{isEditing
? <input
type="text"
className="form-control input-sm orgs-table--input"
defaultValue={workingName}
onChange={this.handleInputChange}
onBlur={this.handleInputBlur}
onKeyDown={this.handleKeyDown}
placeholder="Name this Organization..."
autoFocus={true}
onFocus={this.handleFocus}
ref={r => (this.inputRef = r)}
/>
: <div className="orgs-table--name" onClick={this.handleNameClick}>
{workingName}
<span className="icon pencil" />
</div>}
{organization.id === DEFAULT_ORG_ID
? <div className="orgs-table--public">
<SlideToggle
size="xs"
active={organization.public}
onToggle={this.handleTogglePublic}
/>
</div>
: <div className="orgs-table--public disabled">&mdash;</div>}
<InputClickToEdit
value={organization.name}
wrapperClass="fancytable--td orgs-table--name"
onUpdate={this.handleUpdateOrgName}
/>
<div className="fancytable--td orgs-table--public">
{organization.id === DEFAULT_ORG_ID
? <div className="orgs-table--public-toggle">
<SlideToggle
size="xs"
active={organization.public}
onToggle={this.handleTogglePublic}
/>
</div>
: <div className="orgs-table--public-toggle disabled">&mdash;</div>}
</div>
<div className={defaultRoleClassName}>
<Dropdown
items={dropdownRolesItems}

View File

@ -0,0 +1,75 @@
import React, {PropTypes, Component} from 'react'
import SlideToggle from 'shared/components/SlideToggle'
import Dropdown from 'shared/components/Dropdown'
import {USER_ROLES} from 'src/admin/constants/dummyUsers'
// This is a non-editable organization row, used currently for DEFAULT_ORG
class OrganizationsTableRowDefault extends Component {
togglePublic = () => {
const {organization, onTogglePublic} = this.props
onTogglePublic(organization)
}
handleChooseDefaultRole = role => {
const {organization, onChooseDefaultRole} = this.props
onChooseDefaultRole(organization, role.name)
}
render() {
const {organization} = this.props
const dropdownRolesItems = USER_ROLES.map(role => ({
...role,
text: role.name,
}))
return (
<div className="fancytable--row">
<div className="fancytable--td orgs-table--id">
{organization.id}
</div>
<div className="fancytable--td orgs-table--name">
{organization.name}
</div>
<div className="fancytable--td orgs-table--public">
<div className="orgs-table--public-toggle">
<SlideToggle
size="xs"
active={organization.public}
onToggle={this.togglePublic}
/>
</div>
</div>
<div className="fancytable--td orgs-table--default-role">
<Dropdown
items={dropdownRolesItems}
onChoose={this.handleChooseDefaultRole}
selected={organization.defaultRole}
className="dropdown-stretch"
/>
</div>
<button
className="btn btn-sm btn-default btn-square orgs-table--delete"
disabled={true}
>
<span className="icon trash" />
</button>
</div>
)
}
}
const {func, shape, string} = PropTypes
OrganizationsTableRowDefault.propTypes = {
organization: shape({
id: string,
name: string.isRequired,
}).isRequired,
onTogglePublic: func.isRequired,
onChooseDefaultRole: func.isRequired,
}
export default OrganizationsTableRowDefault

View File

@ -58,20 +58,22 @@ class OrganizationsTableRowNew extends Component {
}))
return (
<div className="orgs-table--org orgs-table--new-org">
<div className="orgs-table--active">&mdash;</div>
<input
type="text"
className="form-control input-sm orgs-table--input"
value={name}
onKeyDown={this.handleKeyDown}
onChange={this.handleInputChange}
onFocus={this.handleInputFocus}
placeholder="Name this Organization..."
autoFocus={true}
ref={r => (this.inputRef = r)}
/>
<div className="orgs-table--default-role editing">
<div className="fancytable--row">
<div className="fancytable--td orgs-table--active">&mdash;</div>
<div className="fancytable--td orgs-table--name">
<input
type="text"
className="form-control input-sm"
value={name}
onKeyDown={this.handleKeyDown}
onChange={this.handleInputChange}
onFocus={this.handleInputFocus}
placeholder="Name this Organization..."
autoFocus={true}
ref={r => (this.inputRef = r)}
/>
</div>
<div className="fancytable--td orgs-table--default-role deleting">
<Dropdown
items={dropdownRolesItems}
onChoose={this.handleChooseDefaultRole}

View File

@ -1,4 +1,4 @@
import {DEFAULT_ORG} from 'src/admin/constants/dummyUsers'
import {DEFAULT_ORG_ID} from 'src/admin/constants/chronografAdmin'
export const DEFAULT_PROVIDER_MAP_ID = '0'
export const PROVIDER_MAPS = [
@ -7,7 +7,7 @@ export const PROVIDER_MAPS = [
scheme: '*',
provider: '*',
providerOrganization: '*',
redirectOrg: {id: DEFAULT_ORG.id, name: DEFAULT_ORG.name},
redirectOrg: {id: DEFAULT_ORG_ID, name: 'Default'},
},
{
id: '1',

View File

@ -17,30 +17,35 @@ $fancytable--table--margin: 4px;
}
}
.fancytable--row {
margin-bottom: 8px;
margin-bottom: $fancytable--table--margin;
position: relative;
&:last-of-type {
margin-bottom: 0;
}
}
.fancytable--td {
height: 30px;
}
.fancytable--labels {
border-bottom: 2px solid $g5-pepper;
margin-bottom: 10px;
@include no-user-select();
}
.fancytable--th {
color: $g17-whisper;
.fancytable--th,
.fancytable--td {
font-weight: 500;
height: 34px;
line-height: 34px;
font-size: 13px;
}
.fancytable--th {
color: $g17-whisper;
padding: 0 11px;
&:last-of-type {
margin-right: 0;
}
}
.fancytable--td {
display: flex;
align-items: center;
color: $g13-mist;
}

View File

@ -4,91 +4,41 @@
Is not actually a table
*/
.orgs-table--org {
$orgs-table--active-width: 102px;
$orgs-table--public-width: 90px;
$orgs-table--default-role-width: 130px;
$orgs-table--delete-width: 30px;
.orgs-table--name {
flex: 1 0 0;
}
.orgs-table--public {
width: $orgs-table--public-width;
text-align: center;
}
.orgs-table--default-role {
width: $orgs-table--default-role-width;
}
.orgs-table--delete {
width: $orgs-table--delete-width;
}
.orgs-table--active {
width: $orgs-table--active-width;
}
.orgs-table--default-role.deleting {
width: (
$orgs-table--default-role-width - $fancytable--table--margin -
$orgs-table--delete-width
);
}
.orgs-table--public-toggle {
height: 30px;
width: 100%;
display: flex;
align-items: center;
margin-bottom: 8px;
position: relative;
&:last-of-type {
margin-bottom: 0;
}
}
.orgs-table--id {
padding: 0 11px;
width: 60px;
height: 30px;
line-height: 30px;
font-size: 13px;
color: $g13-mist;
font-weight: 500;
}
.orgs-table--active {
padding: 0 4px 0 0;
width: 102px;
height: 30px;
button.btn {
width: 100%;
}
}
.orgs-table--name,
.orgs-table--name-disabled,
input[type="text"].form-control.orgs-table--input {
flex: 1 0 0;
font-weight: 600;
font-size: 13px;
margin-right: 4px;
}
.orgs-table--name,
.orgs-table--name-disabled {
@include no-user-select();
padding: 0 11px;
border-radius: 4px;
height: 30px;
line-height: 28px;
border-style: solid;
border-width: 2px;
}
.orgs-table--name {
border-color: $g2-kevlar;
background-color: $g2-kevlar;
color: $g13-mist;
position: relative;
transition: color 0.4s ease, background-color 0.4s ease,
border-color 0.4s ease;
> span.icon {
position: absolute;
top: 50%;
right: 11px;
transform: translateY(-50%);
color: $g8-storm;
opacity: 0;
transition: opacity 0.25s ease;
}
&:hover {
color: $g20-white;
background-color: $g5-pepper;
border-color: $g5-pepper;
cursor: text;
> span.icon {
opacity: 1;
}
}
}
.orgs-table--public {
height: 30px;
margin-right: 4px;
text-align: center;
width: 88px;
justify-content: center;
background-color: $g4-onyx;
border-radius: 4px;
line-height: 30px;
position: relative;
> .slide-toggle {
@ -103,69 +53,3 @@ input[type="text"].form-control.orgs-table--input {
@include no-user-select();
}
}
.orgs-table--default-role,
.orgs-table--default-role-disabled {
width: 100px;
height: 30px;
margin-right: 4px;
}
.orgs-table--default-role.editing {
width: 96px;
}
.orgs-table--default-role-disabled {
background-color: $g4-onyx;
font-style: italic;
color: $g9-mountain;
padding: 0 11px;
line-height: 30px;
font-size: 13px;
font-weight: 600;
@include no-user-select();
}
.orgs-table--delete {
height: 30px;
width: 30px;
}
/* Table Headers */
.orgs-table--org-labels {
display: flex;
align-items: center;
border-bottom: 2px solid $g5-pepper;
margin-bottom: 10px;
width: 100%;
@include no-user-select();
> .orgs-table--name,
> .orgs-table--name:hover,
> .orgs-table--public,
> .orgs-table--active {
transition: none;
background-color: transparent;
border-color: transparent;
}
> .orgs-table--id,
> .orgs-table--name,
> .orgs-table--name:hover,
> .orgs-table--default-role,
> .orgs-table--public,
> .orgs-table--active {
color: $g17-whisper;
font-weight: 500;
}
> .orgs-table--default-role,
> .orgs-table--public,
> .orgs-table--active {
line-height: 30px;
font-size: 13px;
padding: 0 11px;
}
}
/* Config table beneath organizations table */
.panel .panel-body table.table.superadmin-config {
margin-top: 60px;
}

View File

@ -4,25 +4,28 @@
*/
$provider--id-width: 60px;
$provider--scheme-width: 170px;
$provider--provider-width: 170px;
$provider--providerorg-width: 220px;
$provider--scheme-width: 150px;
$provider--provider-width: 150px;
$provider--providerorg-width: 210px;
$provider--redirect-width: 220px;
$provider--delete-width: 30px;
.fancytable--td.provider--id,
.fancytable--th.provider--id {
padding: 0 8px;
}
.provider--id {width: $provider--id-width;}
.provider--scheme {width: $provider--scheme-width;}
.provider--provider {width: $provider--provider-width;}
.provider--providerorg {width: $provider--providerorg-width;}
.provider--redirect {width: $provider--redirect-width;}
.provider--delete {width: $provider--delete-width;}
.provider--delete {
width: $provider--delete-width;
min-width: $provider--delete-width;
}
.provider--arrow {flex: 1 0 0;}
.fancytable--td.provider--id,
.fancytable--th.provider--id {
padding: 0 8px;
}
.provider--redirect.deleting {
width: ($provider--redirect-width - $fancytable--table--margin - $provider--delete-width);
}