From ab2a5de55004f8ac50a99fae9840da26ce125088 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Wed, 29 Nov 2017 14:27:13 +0100 Subject: [PATCH] Rewrite part of the permissions app to Typescript --- .../sonar-web/src/main/js/api/permissions.ts | 23 ++++- server/sonar-web/src/main/js/app/types.ts | 6 +- ...issions.js => OrganizationPermissions.tsx} | 19 ++-- ...vigation.js => OrganizationNavigation.tsx} | 24 +++-- ...est.js => OrganizationNavigation-test.tsx} | 47 +++++---- ...p => OrganizationNavigation-test.tsx.snap} | 0 .../{AllHoldersList.js => AllHoldersList.tsx} | 89 ++++++----------- .../components/AllHoldersListContainer.tsx | 95 +++++++++++++++++++ .../global/components/{App.js => App.tsx} | 16 ++-- .../{GroupHolder.js => GroupHolder.tsx} | 46 ++++----- .../{HoldersList.js => HoldersList.tsx} | 84 +++++----------- .../{PageError.js => PageError.tsx} | 25 ++--- .../shared/components/PermissionHeader.tsx | 87 +++++++++++++++++ .../{SearchForm.js => SearchForm.tsx} | 14 ++- .../{UserHolder.js => UserHolder.tsx} | 88 +++++++++-------- .../projectsManagement/__tests__/App-test.tsx | 3 +- .../__tests__/ChangeVisibilityForm-test.tsx | 3 +- .../__tests__/CreateProjectForm-test.tsx | 3 +- .../__tests__/Header-test.tsx | 2 +- .../__tests__/Search-test.tsx | 3 +- 20 files changed, 405 insertions(+), 272 deletions(-) rename server/sonar-web/src/main/js/apps/organizations/components/{OrganizationPermissions.js => OrganizationPermissions.tsx} (77%) rename server/sonar-web/src/main/js/apps/organizations/navigation/{OrganizationNavigation.js => OrganizationNavigation.tsx} (94%) rename server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/{OrganizationNavigation-test.js => OrganizationNavigation-test.tsx} (60%) rename server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/{OrganizationNavigation-test.js.snap => OrganizationNavigation-test.tsx.snap} (100%) rename server/sonar-web/src/main/js/apps/permissions/global/components/{AllHoldersList.js => AllHoldersList.tsx} (55%) create mode 100644 server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersListContainer.tsx rename server/sonar-web/src/main/js/apps/permissions/global/components/{App.js => App.tsx} (76%) rename server/sonar-web/src/main/js/apps/permissions/shared/components/{GroupHolder.js => GroupHolder.tsx} (62%) rename server/sonar-web/src/main/js/apps/permissions/shared/components/{HoldersList.js => HoldersList.tsx} (52%) rename server/sonar-web/src/main/js/apps/permissions/shared/components/{PageError.js => PageError.tsx} (74%) create mode 100644 server/sonar-web/src/main/js/apps/permissions/shared/components/PermissionHeader.tsx rename server/sonar-web/src/main/js/apps/permissions/shared/components/{SearchForm.js => SearchForm.tsx} (86%) rename server/sonar-web/src/main/js/apps/permissions/shared/components/{UserHolder.js => UserHolder.tsx} (50%) diff --git a/server/sonar-web/src/main/js/api/permissions.ts b/server/sonar-web/src/main/js/api/permissions.ts index 8d2cf3e5596..39d77276f02 100644 --- a/server/sonar-web/src/main/js/api/permissions.ts +++ b/server/sonar-web/src/main/js/api/permissions.ts @@ -178,12 +178,20 @@ export function removeProjectCreatorFromTemplate( return post('/api/permissions/remove_project_creator_from_template', { templateId, permission }); } +export interface PermissionUser { + login: string; + name: string; + email?: string; + permissions: string[]; + avatar?: string; +} + export function getPermissionsUsersForComponent( projectKey: string, query?: string, permission?: string, organization?: string -): Promise { +): Promise { const data: RequestData = { projectKey, ps: PAGE_SIZE }; if (query) { data.q = query; @@ -197,12 +205,19 @@ export function getPermissionsUsersForComponent( return getJSON('/api/permissions/users', data).then(r => r.users); } +export interface PermissionGroup { + id: string; + name: string; + description?: string; + permissions: string[]; +} + export function getPermissionsGroupsForComponent( projectKey: string, query: string = '', permission?: string, organization?: string -): Promise { +): Promise { const data: RequestData = { projectKey, ps: PAGE_SIZE }; if (query) { data.q = query; @@ -220,7 +235,7 @@ export function getGlobalPermissionsUsers( query?: string, permission?: string, organization?: string -): Promise { +): Promise { const data: RequestData = { ps: PAGE_SIZE }; if (query) { data.q = query; @@ -238,7 +253,7 @@ export function getGlobalPermissionsGroups( query?: string, permission?: string, organization?: string -): Promise { +): Promise { const data: RequestData = { ps: PAGE_SIZE }; if (query) { data.q = query; diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index f18764601e4..a180856c47a 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -108,7 +108,7 @@ export interface Metric { } export interface Organization { - adminPages?: Array<{ key: string; name: string }>; + adminPages?: { key: string; name: string }[]; avatar?: string; canAdmin?: boolean; canDelete?: boolean; @@ -118,8 +118,8 @@ export interface Organization { isDefault?: boolean; key: string; name: string; - pages?: Array<{ key: string; name: string }>; - projectVisibility: string; + pages?: { key: string; name: string }[]; + projectVisibility: Visibility; url?: string; } diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPermissions.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPermissions.tsx similarity index 77% rename from server/sonar-web/src/main/js/apps/organizations/components/OrganizationPermissions.js rename to server/sonar-web/src/main/js/apps/organizations/components/OrganizationPermissions.tsx index cab087b3e55..b2136ea2f35 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPermissions.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationPermissions.tsx @@ -17,24 +17,21 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { connect } from 'react-redux'; import GlobalPermissionsApp from '../../permissions/global/components/App'; import { getOrganizationByKey } from '../../../store/rootReducer'; -/*:: import type { Organization } from '../../../store/organizations/duck'; */ +import { Organization } from '../../../app/types'; -/*:: -type Props = { - organization: Organization -}; -*/ +interface Props { + organization: Organization; +} -function OrganizationPermissions(props /*: Props */) { - return ; +function OrganizationPermissions({ organization }: Props) { + return ; } -const mapStateToProps = (state, ownProps) => ({ +const mapStateToProps = (state: any, ownProps: any) => ({ organization: getOrganizationByKey(state, ownProps.params.organizationKey) }); diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.tsx similarity index 94% rename from server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js rename to server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.tsx index 507d43bbfb0..4cffcc47d80 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.tsx @@ -17,17 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; +import * as classNames from 'classnames'; import { Link } from 'react-router'; -import classNames from 'classnames'; import * as theme from '../../../app/theme'; import { translate } from '../../../helpers/l10n'; import ContextNavBar from '../../../components/nav/ContextNavBar'; import NavBarTabs from '../../../components/nav/NavBarTabs'; import OrganizationAvatar from '../../../components/common/OrganizationAvatar'; import { getQualityGatesUrl } from '../../../helpers/urls'; -/*:: import type { Organization } from '../../../store/organizations/duck'; */ +import { Extension, Organization } from '../../../app/types'; const ADMIN_PATHS = [ 'edit', @@ -38,14 +37,13 @@ const ADMIN_PATHS = [ 'projects_management' ]; -export default class OrganizationNavigation extends React.PureComponent { - /*:: props: { - location: { pathname: string }, - organization: Organization - }; -*/ +interface Props { + location: { pathname: string }; + organization: Organization; +} - renderAdministration(adminActive /*: boolean */) { +export default class OrganizationNavigation extends React.PureComponent { + renderAdministration(adminActive: boolean) { const { organization } = this.props; return ( @@ -105,7 +103,7 @@ export default class OrganizationNavigation extends React.PureComponent { return extensions.map(this.renderExtension); } - renderExtension = (extension /*: { key: string, name: string } */) => { + renderExtension = (extension: Extension) => { const { organization } = this.props; const pathname = `/organizations/${organization.key}/extension/${extension.key}`; return ( @@ -117,7 +115,7 @@ export default class OrganizationNavigation extends React.PureComponent { ); }; - renderExtensions(moreActive /*: boolean */) { + renderExtensions(moreActive: boolean) { const extensions = this.props.organization.pages || []; if (extensions.length > 0) { return ( diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/OrganizationNavigation-test.js b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/OrganizationNavigation-test.tsx similarity index 60% rename from server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/OrganizationNavigation-test.js rename to server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/OrganizationNavigation-test.tsx index 3f337c032d5..e4884f83545 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/OrganizationNavigation-test.js +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/OrganizationNavigation-test.tsx @@ -17,46 +17,45 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; +import * as React from 'react'; import { shallow } from 'enzyme'; import OrganizationNavigation from '../OrganizationNavigation'; +import { Visibility } from '../../../../app/types'; jest.mock('../../../issues/utils', () => ({ isMySet: () => false })); +const organization = { + key: 'foo', + name: 'Foo', + canAdmin: false, + canDelete: false, + projectVisibility: Visibility.Public +}; + it('regular user', () => { - const organization = { key: 'foo', name: 'Foo', canAdmin: false, canDelete: false }; - expect( - shallow( - - ) - ).toMatchSnapshot(); + expect(getWrapper()).toMatchSnapshot(); }); it('admin', () => { - const organization = { key: 'foo', name: 'Foo', canAdmin: true, canDelete: true }; expect( - shallow( - - ) + getWrapper({ organization: { ...organization, canAdmin: true, canDelete: true } }) ).toMatchSnapshot(); }); it('undeletable org', () => { - const organization = { key: 'foo', name: 'Foo', canAdmin: true, canDelete: false }; expect( - shallow( - - ) + getWrapper({ organization: { ...organization, canAdmin: true, canDelete: false } }) ).toMatchSnapshot(); }); + +function getWrapper(props = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap rename to server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.js b/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx similarity index 55% rename from server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.js rename to server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx index f340d7e27c1..b9ee74940d0 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.js +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx @@ -17,40 +17,39 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; -import { connect } from 'react-redux'; +import * as React from 'react'; import SearchForm from '../../shared/components/SearchForm'; import HoldersList from '../../shared/components/HoldersList'; -import { - loadHolders, - grantToUser, - revokeFromUser, - grantToGroup, - revokeFromGroup, - updateFilter, - updateQuery, - selectPermission -} from '../store/actions'; import { translate } from '../../../../helpers/l10n'; -import { - getPermissionsAppUsers, - getPermissionsAppGroups, - getPermissionsAppQuery, - getPermissionsAppFilter, - getPermissionsAppSelectedPermission -} from '../../../../store/rootReducer'; +import { Organization } from '../../../../app/types'; +import { PermissionUser, PermissionGroup } from '../../../../api/permissions'; const PERMISSIONS_ORDER = ['admin', 'profileadmin', 'gateadmin', 'scan', 'provisioning']; - const PERMISSIONS_FOR_CUSTOM_ORG = ['admin', 'profileadmin', 'scan', 'provisioning']; -class AllHoldersList extends React.PureComponent { +interface Props { + filter: string; + grantPermissionToGroup: (groupName: string, permission: string) => void; + grantPermissionToUser: (login: string, permission: string) => void; + groups: PermissionGroup[]; + loadHolders: () => void; + onFilter: (filter: string) => void; + onSearch: (query: string) => void; + onSelectPermission: (permission: string) => void; + organization?: Organization; + query: string; + revokePermissionFromGroup: (groupName: string, permission: string) => void; + revokePermissionFromUser: (login: string, permission: string) => void; + selectedPermission?: string; + users: PermissionUser[]; +} + +export default class AllHoldersList extends React.PureComponent { componentDidMount() { this.props.loadHolders(); } - handleToggleUser(user, permission) { + handleToggleUser = (user: PermissionUser, permission: string) => { const hasPermission = user.permissions.includes(permission); if (hasPermission) { @@ -58,9 +57,9 @@ class AllHoldersList extends React.PureComponent { } else { this.props.grantPermissionToUser(user.login, permission); } - } + }; - handleToggleGroup(group, permission) { + handleToggleGroup = (group: PermissionGroup, permission: string) => { const hasPermission = group.permissions.includes(permission); if (hasPermission) { @@ -68,7 +67,7 @@ class AllHoldersList extends React.PureComponent { } else { this.props.grantPermissionToGroup(group.name, permission); } - } + }; render() { const order = @@ -91,8 +90,8 @@ class AllHoldersList extends React.PureComponent { users={this.props.users} groups={this.props.groups} onSelectPermission={this.props.onSelectPermission} - onToggleUser={this.handleToggleUser.bind(this)} - onToggleGroup={this.handleToggleGroup.bind(this)}> + onToggleUser={this.handleToggleUser} + onToggleGroup={this.handleToggleGroup}> ({ - users: getPermissionsAppUsers(state), - groups: getPermissionsAppGroups(state), - query: getPermissionsAppQuery(state), - filter: getPermissionsAppFilter(state), - selectedPermission: getPermissionsAppSelectedPermission(state) -}); - -/*:: -type OwnProps = { - organization?: { key: string } -}; -*/ - -const mapDispatchToProps = (dispatch /*: Function */, ownProps /*: OwnProps */) => { - const organizationKey = ownProps.organization ? ownProps.organization.key : undefined; - return { - loadHolders: () => dispatch(loadHolders(organizationKey)), - onSearch: query => dispatch(updateQuery(query, organizationKey)), - onFilter: filter => dispatch(updateFilter(filter, organizationKey)), - onSelectPermission: permission => dispatch(selectPermission(permission, organizationKey)), - grantPermissionToUser: (login, permission) => - dispatch(grantToUser(login, permission, organizationKey)), - revokePermissionFromUser: (login, permission) => - dispatch(revokeFromUser(login, permission, organizationKey)), - grantPermissionToGroup: (groupName, permission) => - dispatch(grantToGroup(groupName, permission, organizationKey)), - revokePermissionFromGroup: (groupName, permission) => - dispatch(revokeFromGroup(groupName, permission, organizationKey)) - }; -}; - -export default connect(mapStateToProps, mapDispatchToProps)(AllHoldersList); diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersListContainer.tsx b/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersListContainer.tsx new file mode 100644 index 00000000000..bb0647549eb --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersListContainer.tsx @@ -0,0 +1,95 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { connect } from 'react-redux'; +import { + loadHolders, + grantToUser, + revokeFromUser, + grantToGroup, + revokeFromGroup, + updateFilter, + updateQuery, + selectPermission +} from '../store/actions'; +import { + getPermissionsAppUsers, + getPermissionsAppGroups, + getPermissionsAppQuery, + getPermissionsAppFilter, + getPermissionsAppSelectedPermission +} from '../../../../store/rootReducer'; +import AllHoldersList from './AllHoldersList'; +import { Organization } from '../../../../app/types'; +import { PermissionUser, PermissionGroup } from '../../../../api/permissions'; + +interface OwnProps { + organization?: Organization; +} + +interface StateToProps { + filter: string; + groups: PermissionGroup[]; + query: string; + selectedPermission?: string; + users: PermissionUser[]; +} + +interface DispatchToProps { + grantPermissionToGroup: (groupName: string, permission: string) => void; + grantPermissionToUser: (login: string, permission: string) => void; + loadHolders: () => void; + onFilter: (filter: string) => void; + onSearch: (query: string) => void; + onSelectPermission: (permission: string) => void; + revokePermissionFromGroup: (groupName: string, permission: string) => void; + revokePermissionFromUser: (login: string, permission: string) => void; +} + +const mapStateToProps = (state: any) => ({ + filter: getPermissionsAppFilter(state), + groups: getPermissionsAppGroups(state), + query: getPermissionsAppQuery(state), + selectedPermission: getPermissionsAppSelectedPermission(state), + users: getPermissionsAppUsers(state) +}); + +const mapDispatchToProps = (dispatch: Function, ownProps: OwnProps) => { + const organizationKey = ownProps.organization ? ownProps.organization.key : undefined; + return { + grantPermissionToGroup: (groupName: string, permission: string) => + dispatch(grantToGroup(groupName, permission, organizationKey)), + grantPermissionToUser: (login: string, permission: string) => + dispatch(grantToUser(login, permission, organizationKey)), + loadHolders: () => dispatch(loadHolders(organizationKey)), + onFilter: (filter: string) => dispatch(updateFilter(filter, organizationKey)), + onSearch: (query: string) => dispatch(updateQuery(query, organizationKey)), + onSelectPermission: (permission: string) => + dispatch(selectPermission(permission, organizationKey)), + revokePermissionFromGroup: (groupName: string, permission: string) => + dispatch(revokeFromGroup(groupName, permission, organizationKey)), + revokePermissionFromUser: (login: string, permission: string) => + dispatch(revokeFromUser(login, permission, organizationKey)) + }; +}; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(AllHoldersList); diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/App.js b/server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx similarity index 76% rename from server/sonar-web/src/main/js/apps/permissions/global/components/App.js rename to server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx index 4b7ea9f15e1..fdd4aec3dcf 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/App.js +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx @@ -17,22 +17,26 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import Helmet from 'react-helmet'; import PageHeader from './PageHeader'; -import AllHoldersList from './AllHoldersList'; +import AllHoldersListContainer from './AllHoldersListContainer'; import PageError from '../../shared/components/PageError'; import { translate } from '../../../../helpers/l10n'; +import { Organization } from '../../../../app/types'; import '../../styles.css'; -export default function App(props /*: { organization?: {} } */) { +interface Props { + organization?: Organization; +} + +export default function App({ organization }: Props) { return (
- + - +
); } diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/components/GroupHolder.js b/server/sonar-web/src/main/js/apps/permissions/shared/components/GroupHolder.tsx similarity index 62% rename from server/sonar-web/src/main/js/apps/permissions/shared/components/GroupHolder.js rename to server/sonar-web/src/main/js/apps/permissions/shared/components/GroupHolder.tsx index 12a9c2af1ba..3fc076c002e 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/components/GroupHolder.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/components/GroupHolder.tsx @@ -17,39 +17,35 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import * as React from 'react'; +import Checkbox from '../../../../components/controls/Checkbox'; import GroupIcon from '../../../../components/icons-components/GroupIcon'; +import { PermissionGroup } from '../../../../api/permissions'; -export default class GroupHolder extends React.PureComponent { - static propTypes = { - group: PropTypes.object.isRequired, - permissions: PropTypes.array.isRequired, - selectedPermission: PropTypes.string, - permissionsOrder: PropTypes.array.isRequired, - onToggle: PropTypes.func.isRequired - }; +interface Props { + group: PermissionGroup; + permissions: string[]; + selectedPermission?: string; + permissionsOrder: string[]; + onToggle: (group: PermissionGroup, permission: string) => void; +} - handleClick(permission, e) { - e.preventDefault(); - e.target.blur(); - this.props.onToggle(this.props.group, permission); - } +export default class GroupHolder extends React.PureComponent { + handleCheck = (_checked: boolean, permission?: string) => + permission && this.props.onToggle(this.props.group, permission); render() { const { selectedPermission } = this.props; - const permissionCells = this.props.permissionsOrder.map(p => ( + const permissionCells = this.props.permissionsOrder.map(permission => ( - + style={{ backgroundColor: permission === selectedPermission ? '#d9edf7' : 'transparent' }}> + )); diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/components/HoldersList.js b/server/sonar-web/src/main/js/apps/permissions/shared/components/HoldersList.tsx similarity index 52% rename from server/sonar-web/src/main/js/apps/permissions/shared/components/HoldersList.js rename to server/sonar-web/src/main/js/apps/permissions/shared/components/HoldersList.tsx index da8bc9aa11f..189351ba346 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/components/HoldersList.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/components/HoldersList.tsx @@ -17,69 +17,35 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import * as React from 'react'; import UserHolder from './UserHolder'; import GroupHolder from './GroupHolder'; -import Tooltip from '../../../../components/controls/Tooltip'; -import { translate, translateWithParameters } from '../../../../helpers/l10n'; +import PermissionHeader, { Permission } from './PermissionHeader'; +import { PermissionUser, PermissionGroup } from '../../../../api/permissions'; +import { translate } from '../../../../helpers/l10n'; -export default class HoldersList extends React.PureComponent { - static propTypes = { - permissions: PropTypes.array.isRequired, - users: PropTypes.array.isRequired, - groups: PropTypes.array.isRequired, - selectedPermission: PropTypes.string, - showPublicProjectsWarning: PropTypes.bool, - onSelectPermission: PropTypes.func.isRequired, - onToggleUser: PropTypes.func.isRequired, - onToggleGroup: PropTypes.func.isRequired - }; - - static defaultProps = { - showPublicProjectsWarning: false - }; - - handlePermissionClick = event => { - event.preventDefault(); - event.currentTarget.blur(); - this.props.onSelectPermission(event.currentTarget.dataset.key); - }; - - renderTooltip = permission => - this.props.showPublicProjectsWarning && - (permission.key === 'user' || permission.key === 'codeviewer') ? ( -
- {permission.description} -
- {translate('projects_role.public_projects_warning')} -
-
- ) : ( - permission.description - ); +interface Props { + permissions: Permission[]; + users: PermissionUser[]; + groups: PermissionGroup[]; + selectedPermission?: string; + showPublicProjectsWarning?: boolean; + onSelectPermission: (permission: string) => void; + onToggleUser: (user: PermissionUser, permission: string) => void; + onToggleGroup: (group: PermissionGroup, permission: string) => void; +} +export default class HoldersList extends React.PureComponent { renderTableHeader() { - const { selectedPermission } = this.props; + const { onSelectPermission, selectedPermission, showPublicProjectsWarning } = this.props; const cells = this.props.permissions.map(p => ( - -
- - - {p.name} - - - - - -
- + onSelectPermission={onSelectPermission} + permission={p} + selectedPermission={selectedPermission} + showPublicProjectsWarning={showPublicProjectsWarning} + /> )); return ( @@ -101,13 +67,15 @@ export default class HoldersList extends React.PureComponent { } render() { + const permissionsOrder = this.props.permissions.map(p => p.key); + const users = this.props.users.map(user => ( )); @@ -118,7 +86,7 @@ export default class HoldersList extends React.PureComponent { group={group} permissions={group.permissions} selectedPermission={this.props.selectedPermission} - permissionsOrder={this.props.permissions} + permissionsOrder={permissionsOrder} onToggle={this.props.onToggleGroup} /> )); diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/components/PageError.js b/server/sonar-web/src/main/js/apps/permissions/shared/components/PageError.tsx similarity index 74% rename from server/sonar-web/src/main/js/apps/permissions/shared/components/PageError.js rename to server/sonar-web/src/main/js/apps/permissions/shared/components/PageError.tsx index e3d6266d94b..4f3878329c3 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/components/PageError.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/components/PageError.tsx @@ -17,28 +17,23 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import * as React from 'react'; import { connect } from 'react-redux'; import { getPermissionsAppError } from '../../../../store/rootReducer'; -class PageError extends React.PureComponent { - static propTypes = { - message: PropTypes.string - }; - - render() { - const { message } = this.props; - - if (!message) { - return null; - } +interface Props { + message: string; +} - return
{message}
; +function PageError({ message }: Props) { + if (!message) { + return null; } + + return
{message}
; } -const mapStateToProps = state => ({ +const mapStateToProps = (state: any) => ({ message: getPermissionsAppError(state) }); diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/components/PermissionHeader.tsx b/server/sonar-web/src/main/js/apps/permissions/shared/components/PermissionHeader.tsx new file mode 100644 index 00000000000..96d9e40d1c2 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permissions/shared/components/PermissionHeader.tsx @@ -0,0 +1,87 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import Tooltip from '../../../../components/controls/Tooltip'; +import { translate, translateWithParameters } from '../../../../helpers/l10n'; + +export interface Permission { + key: string; + name: string; + description: string; +} + +interface Props { + onSelectPermission: (permission: string) => void; + permission: Permission; + selectedPermission?: string; + showPublicProjectsWarning?: boolean; +} + +export default class PermissionHeader extends React.PureComponent { + static defaultProps = { + showPublicProjectsWarning: false + }; + + handlePermissionClick = (event: React.SyntheticEvent) => { + event.preventDefault(); + event.currentTarget.blur(); + this.props.onSelectPermission(this.props.permission.key); + }; + + renderTooltip = (permission: Permission) => { + if (this.props.showPublicProjectsWarning && ['user', 'codeviewer'].includes(permission.key)) { + return ( +
+ {permission.description} +
+ {translate('projects_role.public_projects_warning')} +
+
+ ); + } + return permission.description; + }; + + render() { + const { permission, selectedPermission } = this.props; + return ( + + + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/components/SearchForm.js b/server/sonar-web/src/main/js/apps/permissions/shared/components/SearchForm.tsx similarity index 86% rename from server/sonar-web/src/main/js/apps/permissions/shared/components/SearchForm.js rename to server/sonar-web/src/main/js/apps/permissions/shared/components/SearchForm.tsx index b680b491fbd..903d3be562d 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/components/SearchForm.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/components/SearchForm.tsx @@ -17,13 +17,19 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import * as React from 'react'; import RadioToggle from '../../../../components/controls/RadioToggle'; import SearchBox from '../../../../components/controls/SearchBox'; -import { translate, translateWithParameters } from '../../../../helpers/l10n'; +import { translate } from '../../../../helpers/l10n'; -export default function SearchForm(props) { +interface Props { + filter: string; + onFilter: (value: string) => void; + onSearch: (value: string) => void; + query: string; +} + +export default function SearchForm(props: Props) { const filterOptions = [ { value: 'all', label: translate('all') }, { value: 'users', label: translate('users.page') }, diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/components/UserHolder.js b/server/sonar-web/src/main/js/apps/permissions/shared/components/UserHolder.tsx similarity index 50% rename from server/sonar-web/src/main/js/apps/permissions/shared/components/UserHolder.js rename to server/sonar-web/src/main/js/apps/permissions/shared/components/UserHolder.tsx index d0b97ae588b..068c98f67af 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/components/UserHolder.js +++ b/server/sonar-web/src/main/js/apps/permissions/shared/components/UserHolder.tsx @@ -17,69 +17,73 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import * as React from 'react'; import Avatar from '../../../../components/ui/Avatar'; +import Checkbox from '../../../../components/controls/Checkbox'; +import { PermissionUser } from '../../../../api/permissions'; import { translate } from '../../../../helpers/l10n'; -export default class UserHolder extends React.PureComponent { - static propTypes = { - user: PropTypes.object.isRequired, - permissions: PropTypes.array.isRequired, - selectedPermission: PropTypes.string, - permissionsOrder: PropTypes.array.isRequired, - onToggle: PropTypes.func.isRequired - }; +interface Props { + user: PermissionUser; + permissions: string[]; + selectedPermission?: string; + permissionsOrder: string[]; + onToggle: (user: PermissionUser, permission: string) => void; +} - handleClick(permission, e) { - e.preventDefault(); - e.target.blur(); - this.props.onToggle(this.props.user, permission); - } +export default class UserHolder extends React.PureComponent { + handleCheck = (_checked: boolean, permission?: string) => + permission && this.props.onToggle(this.props.user, permission); render() { const { selectedPermission } = this.props; - const permissionCells = this.props.permissionsOrder.map(p => ( + const permissionCells = this.props.permissionsOrder.map(permission => ( - + style={{ backgroundColor: permission === selectedPermission ? '#d9edf7' : 'transparent' }}> + )); const { user } = this.props; - - const isCreator = user.login === ''; + if (user.login === '') { + return ( + + +
+
+ {user.name} +
+
+ {translate('permission_templates.project_creators.explanation')} +
+
+ + {permissionCells} + + ); + } return ( - {!isCreator && ( - - )} +
{user.name} - {!isCreator && {user.login}} + {user.login}
- {!isCreator &&
{user.email}
} - {isCreator && ( -
- {translate('permission_templates.project_creators.explanation')} -
- )} +
{user.email}
{permissionCells} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx index 00014d3b253..e17f7697ac1 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx @@ -21,6 +21,7 @@ import * as React from 'react'; import { mount } from 'enzyme'; import App, { Props } from '../App'; +import { Visibility } from '../../../app/types'; jest.mock('react-dom'); @@ -42,7 +43,7 @@ jest.mock('../../../api/components', () => ({ getComponents: jest.fn() })); const getComponents = require('../../../api/components').getComponents as jest.Mock; -const organization = { key: 'org', name: 'org', projectVisibility: 'public' }; +const organization = { key: 'org', name: 'org', projectVisibility: Visibility.Public }; const defaultSearchParameters = { organization: 'org', diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ChangeVisibilityForm-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ChangeVisibilityForm-test.tsx index 61dce6dbfe4..4d3505afd22 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ChangeVisibilityForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ChangeVisibilityForm-test.tsx @@ -21,12 +21,13 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import ChangeVisibilityForm, { Props } from '../ChangeVisibilityForm'; import { click } from '../../../helpers/testUtils'; +import { Visibility } from '../../../app/types'; const organization = { canUpdateProjectsVisibilityToPrivate: true, key: 'org', name: 'org', - projectVisibility: 'public' + projectVisibility: Visibility.Public }; it('renders disabled', () => { diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx index c514345b78a..2682a771619 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/CreateProjectForm-test.tsx @@ -28,10 +28,11 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import CreateProjectForm from '../CreateProjectForm'; import { change, submit, click } from '../../../helpers/testUtils'; +import { Visibility } from '../../../app/types'; const createProject = require('../../../api/components').createProject as jest.Mock; -const organization = { key: 'org', name: 'org', projectVisibility: 'public' }; +const organization = { key: 'org', name: 'org', projectVisibility: Visibility.Public }; it('creates project', async () => { const wrapper = shallow( diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Header-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Header-test.tsx index b7cb62ab9a4..7d5b637949b 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Header-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Header-test.tsx @@ -23,7 +23,7 @@ import Header, { Props } from '../Header'; import { Visibility } from '../../../app/types'; import { click } from '../../../helpers/testUtils'; -const organization = { key: 'org', name: 'org', projectVisibility: 'public' }; +const organization = { key: 'org', name: 'org', projectVisibility: Visibility.Public }; it('renders', () => { expect(shallowRender()).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx index 824c6ab2a5e..71cf3dcbe80 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx @@ -21,8 +21,9 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import Search, { Props } from '../Search'; import { click } from '../../../helpers/testUtils'; +import { Visibility } from '../../../app/types'; -const organization = { key: 'org', name: 'org', projectVisibility: 'public' }; +const organization = { key: 'org', name: 'org', projectVisibility: Visibility.Public }; it('renders', () => { expect(shallowRender()).toMatchSnapshot(); -- 2.39.5