diff --git a/ui/package.json b/ui/package.json index 7680f053c7..dc4f751778 100644 --- a/ui/package.json +++ b/ui/package.json @@ -40,7 +40,7 @@ "@types/node": "^9.4.6", "@types/prop-types": "^15.5.2", "@types/react": "^16.0.38", - "@types/react-router": "3", + "@types/react-router": "^3.0.15", "@types/text-encoding": "^0.0.32", "autoprefixer": "^6.3.1", "babel-core": "^6.5.1", diff --git a/ui/src/admin/components/chronograf/OrganizationsTable.js b/ui/src/admin/components/chronograf/OrganizationsTable.js index 65c7a30b52..aca6866f36 100644 --- a/ui/src/admin/components/chronograf/OrganizationsTable.js +++ b/ui/src/admin/components/chronograf/OrganizationsTable.js @@ -2,6 +2,7 @@ import React, {Component} from 'react' import PropTypes from 'prop-types' import uuid from 'uuid' +import _ from 'lodash' import OrganizationsTableRow from 'src/admin/components/chronograf/OrganizationsTableRow' import OrganizationsTableRowNew from 'src/admin/components/chronograf/OrganizationsTableRowNew' @@ -15,6 +16,12 @@ class OrganizationsTable extends Component { } } + shouldComponentUpdate(nextProps, nextState) { + return ( + !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState) + ) + } + handleClickCreateOrganization = () => { this.setState({isCreatingOrganization: true}) } diff --git a/ui/src/admin/components/chronograf/OrganizationsTableRow.js b/ui/src/admin/components/chronograf/OrganizationsTableRow.tsx similarity index 62% rename from ui/src/admin/components/chronograf/OrganizationsTableRow.js rename to ui/src/admin/components/chronograf/OrganizationsTableRow.tsx index 5d6cf6e648..091146a38f 100644 --- a/ui/src/admin/components/chronograf/OrganizationsTableRow.js +++ b/ui/src/admin/components/chronograf/OrganizationsTableRow.tsx @@ -1,42 +1,54 @@ -import React, {Component} from 'react' -import PropTypes from 'prop-types' +import React, {PureComponent} from 'react' import {connect} from 'react-redux' import {bindActionCreators} from 'redux' -import {withRouter} from 'react-router' +import {withRouter, InjectedRouter} from 'react-router' -import ConfirmButton from 'shared/components/ConfirmButton' -import Dropdown from 'shared/components/Dropdown' -import InputClickToEdit from 'shared/components/InputClickToEdit' +import _ from 'lodash' -import {meChangeOrganizationAsync} from 'shared/actions/auth' +import ConfirmButton from 'src/shared/components/ConfirmButton' +import Dropdown from 'src/shared/components/Dropdown' +import InputClickToEdit from 'src/shared/components/InputClickToEdit' + +import {meChangeOrganizationAsync} from 'src/shared/actions/auth' import {DEFAULT_ORG_ID} from 'src/admin/constants/chronografAdmin' import {USER_ROLES} from 'src/admin/constants/chronografAdmin' +import {Organization} from 'src/types' -class OrganizationsTableRow extends Component { - handleChangeCurrentOrganization = async () => { - const {router, links, meChangeOrganization, organization} = this.props +interface CurrentOrganization { + name: string + id: string +} - await meChangeOrganization(links.me, {organization: organization.id}) - router.push('') +interface ExternalLink { + name: string + url: string +} +interface ExternalLinks { + custom: ExternalLink[] +} +interface Links { + me: string + external: ExternalLinks +} + +interface Props { + organization: Organization + currentOrganization: CurrentOrganization + onDelete: (Organization) => void + onRename: (Organization, newName: string) => void + onChooseDefaultRole: (Organization, roleName: string) => void + meChangeOrganization: (me: string, id) => void + links: Links + router: InjectedRouter +} + +class OrganizationsTableRow extends PureComponent { + public shouldComponentUpdate(nextProps) { + return !_.isEqual(this.props, nextProps) } - handleUpdateOrgName = newName => { - const {organization, onRename} = this.props - onRename(organization, newName) - } - - handleDeleteOrg = () => { - const {onDelete, organization} = this.props - onDelete(organization) - } - - handleChooseDefaultRole = role => { - const {organization, onChooseDefaultRole} = this.props - onChooseDefaultRole(organization, role.name) - } - - render() { + public render() { const {organization, currentOrganization} = this.props const dropdownRolesItems = USER_ROLES.map(role => ({ @@ -84,38 +96,28 @@ class OrganizationsTableRow extends Component { ) } -} -const {arrayOf, func, shape, string} = PropTypes + public handleChangeCurrentOrganization = async () => { + const {router, links, meChangeOrganization, organization} = this.props -OrganizationsTableRow.propTypes = { - organization: shape({ - id: string, // when optimistically created, organization will not have an id - name: string.isRequired, - defaultRole: string.isRequired, - }).isRequired, - onDelete: func.isRequired, - onRename: func.isRequired, - onChooseDefaultRole: func.isRequired, - currentOrganization: shape({ - name: string.isRequired, - id: string.isRequired, - }), - router: shape({ - push: func.isRequired, - }).isRequired, - links: shape({ - me: string, - external: shape({ - custom: arrayOf( - shape({ - name: string.isRequired, - url: string.isRequired, - }) - ), - }), - }), - meChangeOrganization: func.isRequired, + await meChangeOrganization(links.me, {organization: organization.id}) + router.push('') + } + + public handleUpdateOrgName = newName => { + const {organization, onRename} = this.props + onRename(organization, newName) + } + + public handleDeleteOrg = () => { + const {onDelete, organization} = this.props + onDelete(organization) + } + + public handleChooseDefaultRole = role => { + const {organization, onChooseDefaultRole} = this.props + onChooseDefaultRole(organization, role.name) + } } const mapDispatchToProps = dispatch => ({ @@ -126,6 +128,6 @@ const mapStateToProps = ({links}) => ({ links, }) -export default connect(mapStateToProps, mapDispatchToProps)( - withRouter(OrganizationsTableRow) +export default withRouter( + connect(mapStateToProps, mapDispatchToProps)(OrganizationsTableRow) ) diff --git a/ui/src/admin/components/chronograf/UsersTable.js b/ui/src/admin/components/chronograf/UsersTable.js index 6fb3a0893d..f02be5f736 100644 --- a/ui/src/admin/components/chronograf/UsersTable.js +++ b/ui/src/admin/components/chronograf/UsersTable.js @@ -2,6 +2,7 @@ import React, {Component} from 'react' import PropTypes from 'prop-types' import uuid from 'uuid' +import _ from 'lodash' import UsersTableHeader from 'src/admin/components/chronograf/UsersTableHeader' import UsersTableRowNew from 'src/admin/components/chronograf/UsersTableRowNew' @@ -18,6 +19,12 @@ class UsersTable extends Component { } } + shouldComponentUpdate(nextProps, nextState) { + return ( + !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState) + ) + } + handleChangeUserRole = (user, currentRole) => newRole => { this.props.onUpdateUserRole(user, currentRole, newRole) } diff --git a/ui/src/admin/components/chronograf/UsersTableRow.js b/ui/src/admin/components/chronograf/UsersTableRow.js deleted file mode 100644 index 7a23bbf38f..0000000000 --- a/ui/src/admin/components/chronograf/UsersTableRow.js +++ /dev/null @@ -1,88 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' - -import Dropdown from 'shared/components/Dropdown' -import ConfirmButton from 'shared/components/ConfirmButton' - -import {USER_ROLES} from 'src/admin/constants/chronografAdmin' -import {USERS_TABLE} from 'src/admin/constants/chronografTableSizing' - -const UsersTableRow = ({ - user, - organization, - onChangeUserRole, - onDelete, - meID, -}) => { - const {colRole, colProvider, colScheme} = USERS_TABLE - - const dropdownRolesItems = USER_ROLES.map(r => ({ - ...r, - text: r.name, - })) - const currentRole = user.roles.find( - role => role.organization === organization.id - ) - - const userIsMe = user.id === meID - - const wrappedDelete = () => { - onDelete(user) - } - - const removeWarning = 'Remove this user\nfrom Current Org?' - - return ( - - - {userIsMe ? ( - - - {user.name} - - ) : ( - {user.name} - )} - - - - - - - {user.provider} - {user.scheme} - - - - - ) -} - -const {func, shape, string} = PropTypes - -UsersTableRow.propTypes = { - user: shape(), - organization: shape({ - name: string.isRequired, - id: string.isRequired, - }), - onChangeUserRole: func.isRequired, - onDelete: func.isRequired, - meID: string.isRequired, -} - -export default UsersTableRow diff --git a/ui/src/admin/components/chronograf/UsersTableRow.tsx b/ui/src/admin/components/chronograf/UsersTableRow.tsx new file mode 100644 index 0000000000..d0fce70bd4 --- /dev/null +++ b/ui/src/admin/components/chronograf/UsersTableRow.tsx @@ -0,0 +1,103 @@ +import React, {PureComponent} from 'react' + +import Dropdown from 'src/shared/components/Dropdown' +import ConfirmButton from 'src/shared/components/ConfirmButton' + +import {USER_ROLES} from 'src/admin/constants/chronografAdmin' +import {USERS_TABLE} from 'src/admin/constants/chronografTableSizing' +import {User, Role} from 'src/types' + +interface Organization { + name: string + id: string +} + +interface DropdownRole { + text: string + name: string +} + +interface Props { + user: User + organization: Organization + onChangeUserRole: (User, Role) => void + onDelete: (User) => void + meID: string +} + +class UsersTableRow extends PureComponent { + public render() { + const {user, onChangeUserRole} = this.props + const {colRole, colProvider, colScheme} = USERS_TABLE + + return ( + + + {this.isMe ? ( + + + {user.name} + + ) : ( + {user.name} + )} + + + + + + + {user.provider} + {user.scheme} + + + + + ) + } + + private handleDelete = (): void => { + const {user, onDelete} = this.props + + onDelete(user) + } + + private get rolesDropdownItems(): DropdownRole[] { + return USER_ROLES.map(r => ({ + ...r, + text: r.name, + })) + } + + private get currentRole(): Role { + const {user, organization} = this.props + + return user.roles.find(role => role.organization === organization.id) + } + + private get isMe(): boolean { + const {user, meID} = this.props + + return user.id === meID + } + + private get confirmationText(): string { + return 'Remove this user\nfrom Current Org?' + } +} + +export default UsersTableRow diff --git a/ui/src/admin/containers/chronograf/UsersPage.js b/ui/src/admin/containers/chronograf/UsersPage.js index 0d79e31b66..44fe06d1aa 100644 --- a/ui/src/admin/containers/chronograf/UsersPage.js +++ b/ui/src/admin/containers/chronograf/UsersPage.js @@ -1,4 +1,4 @@ -import React, {Component} from 'react' +import React, {PureComponent} from 'react' import PropTypes from 'prop-types' import {connect} from 'react-redux' import {bindActionCreators} from 'redux' @@ -8,7 +8,7 @@ import {notify as notifyAction} from 'shared/actions/notifications' import UsersTable from 'src/admin/components/chronograf/UsersTable' -class UsersPage extends Component { +class UsersPage extends PureComponent { constructor(props) { super(props) diff --git a/ui/src/shared/components/ConfirmButton.tsx b/ui/src/shared/components/ConfirmButton.tsx index e3db54be05..a28a6cd1b3 100644 --- a/ui/src/shared/components/ConfirmButton.tsx +++ b/ui/src/shared/components/ConfirmButton.tsx @@ -37,37 +37,18 @@ class ConfirmButton extends PureComponent { } public render() { - const { - text, - confirmText, - type, - size, - square, - icon, - disabled, - customClass, - } = this.props - const {expanded} = this.state - - const customClassString = customClass ? ` ${customClass}` : '' - const squareString = square ? ' btn-square' : '' - const expandedString = expanded ? ' active' : '' - const disabledString = disabled ? ' disabled' : '' - - const classname = `confirm-button btn ${type} ${size}${customClassString}${squareString}${expandedString}${disabledString}` + const {text, confirmText, icon} = this.props return (
(this.buttonDiv = r)} > {icon && } {text && text} -
+
{ ) } + private get className() { + const {type, size, square, disabled, customClass} = this.props + const {expanded} = this.state + + const customClassString = customClass ? ` ${customClass}` : '' + const squareString = square ? ' btn-square' : '' + const expandedString = expanded ? ' active' : '' + const disabledString = disabled ? ' disabled' : '' + + return `confirm-button btn ${type} ${size}${customClassString}${squareString}${expandedString}${disabledString}` + } + private handleButtonClick = () => { if (this.props.disabled) { return @@ -97,7 +90,7 @@ class ConfirmButton extends PureComponent { this.setState({expanded: false}) } - private calculatePosition = () => { + private get calculatedPosition() { if (!this.buttonDiv || !this.tooltipDiv) { return '' } diff --git a/ui/yarn.lock b/ui/yarn.lock index 17b0ed4bd3..ef2683edb7 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -61,7 +61,7 @@ version "15.5.2" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.2.tgz#3c6b8dceb2906cc87fe4358e809f9d20c8d59be1" -"@types/react-router@3": +"@types/react-router@^3.0.15": version "3.0.15" resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-3.0.15.tgz#b55b0dc5ad8f6fa66b609f0efc390b191381d082" dependencies: