diff options
Diffstat (limited to 'server/sonar-web/src/main/js/apps/quality-profiles/home')
13 files changed, 349 insertions, 412 deletions
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.tsx index d95984ce033..8f6d589cff8 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.tsx @@ -17,39 +17,33 @@ * 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 Modal from 'react-modal'; -import Select from 'react-select'; +import * as Select from 'react-select'; import { sortBy } from 'lodash'; import { getImporters, createQualityProfile } from '../../../api/quality-profiles'; import { translate } from '../../../helpers/l10n'; -/*:: -type Props = { - languages: Array<{ key: string, name: string }>, - onClose: () => void, - onCreate: Function, - onRequestFail: Object => void, - organization: ?string -}; -*/ - -/*:: -type State = { - importers: Array<{ key: string, languages: Array<string>, name: string }>, - language?: string, - loading: boolean, - name: string, - preloading: boolean -}; -*/ - -export default class CreateProfileForm extends React.PureComponent { +interface Props { + languages: Array<{ key: string; name: string }>; + onClose: () => void; + onCreate: Function; + onRequestFail: (reasong: any) => void; + organization: string | null; +} + +interface State { + importers: Array<{ key: string; languages: Array<string>; name: string }>; + language?: string; + loading: boolean; + name: string; + preloading: boolean; +} + +export default class CreateProfileForm extends React.PureComponent<Props, State> { /*:: form: HTMLFormElement; */ - /*:: mounted: boolean; */ - /*:: props: Props; */ - state /*: State */ = { importers: [], loading: false, name: '', preloading: true }; + mounted: boolean; + state: State = { importers: [], loading: false, name: '', preloading: true }; componentDidMount() { this.mounted = true; @@ -61,39 +55,41 @@ export default class CreateProfileForm extends React.PureComponent { } fetchImporters() { - getImporters().then(importers => { - if (this.mounted) { - this.setState({ importers, preloading: false }); + getImporters().then( + (importers: Array<{ key: string; languages: Array<string>; name: string }>) => { + if (this.mounted) { + this.setState({ importers, preloading: false }); + } } - }); + ); } - handleCancelClick = (event /*: Event */) => { + handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => { event.preventDefault(); this.props.onClose(); }; - handleNameChange = (event /*: { currentTarget: HTMLInputElement } */) => { + handleNameChange = (event: React.SyntheticEvent<HTMLInputElement>) => { this.setState({ name: event.currentTarget.value }); }; - handleLanguageChange = (option /*: { value: string } */) => { + handleLanguageChange = (option: { value: string }) => { this.setState({ language: option.value }); }; - handleFormSubmit = (event /*: Event */) => { + handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => { event.preventDefault(); this.setState({ loading: true }); - const data = new FormData(this.form); + const data = new FormData(event.currentTarget); if (this.props.organization) { data.append('organization', this.props.organization); } createQualityProfile(data).then( - response => this.props.onCreate(response.profile), - error => { + (response: any) => this.props.onCreate(response.profile), + (error: any) => { if (this.mounted) { this.setState({ loading: false }); } @@ -118,10 +114,7 @@ export default class CreateProfileForm extends React.PureComponent { className="modal" overlayClassName="modal-overlay" onRequestClose={this.props.onClose}> - <form - id="create-profile-form" - onSubmit={this.handleFormSubmit} - ref={node => (this.form = node)}> + <form id="create-profile-form" onSubmit={this.handleFormSubmit}> <div className="modal-head"> <h2> {header} @@ -141,11 +134,11 @@ export default class CreateProfileForm extends React.PureComponent { <input autoFocus={true} id="create-profile-name" - maxLength="100" + maxLength={100} name="name" onChange={this.handleNameChange} required={true} - size="50" + size={50} type="text" value={this.state.name} /> @@ -157,7 +150,6 @@ export default class CreateProfileForm extends React.PureComponent { </label> <Select clearable={false} - id="create-profile-language" name="language" onChange={this.handleLanguageChange} options={languages.map(language => ({ diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.tsx index 4be28f3dd04..f238580d1e2 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.tsx @@ -17,32 +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. */ -// @flow -import React from 'react'; +import * as React from 'react'; import EvolutionDeprecated from './EvolutionDeprecated'; import EvolutionStagnant from './EvolutionStagnant'; import EvolutionRules from './EvolutionRules'; -/*:: import type { Profile } from '../propTypes'; */ +import { IProfile } from '../types'; -/*:: -type Props = { - organization: ?string, - profiles: Array<Profile> -}; -*/ - -export default class Evolution extends React.PureComponent { - /*:: props: Props; */ - - render() { - const { organization, profiles } = this.props; +interface Props { + organization: string | null; + profiles: IProfile[]; +} - return ( - <div className="quality-profiles-evolution"> - <EvolutionDeprecated organization={organization} profiles={profiles} /> - <EvolutionStagnant organization={organization} profiles={profiles} /> - <EvolutionRules organization={organization} /> - </div> - ); - } +export default function Evolution({ organization, profiles }: Props) { + return ( + <div className="quality-profiles-evolution"> + <EvolutionDeprecated organization={organization} profiles={profiles} /> + <EvolutionStagnant organization={organization} profiles={profiles} /> + <EvolutionRules organization={organization} /> + </div> + ); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js deleted file mode 100644 index a3eaef27bd2..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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. - */ -// @flow -import React from 'react'; -import { Link } from 'react-router'; -import { sortBy } from 'lodash'; -import ProfileLink from '../components/ProfileLink'; -import { getDeprecatedActiveRulesUrl } from '../../../helpers/urls'; -import { translateWithParameters, translate } from '../../../helpers/l10n'; -/*:: import type { Profile } from '../propTypes'; */ - -/*:: -type Props = { - organization: ?string, - profiles: Array<Profile> -}; -*/ - -export default class EvolutionDeprecated extends React.PureComponent { - /*:: props: Props; */ - - render() { - const profilesWithDeprecations = this.props.profiles.filter( - profile => profile.activeDeprecatedRuleCount > 0 - ); - - if (profilesWithDeprecations.length === 0) { - return null; - } - - const sortedProfiles = sortBy(profilesWithDeprecations, p => -p.activeDeprecatedRuleCount); - - return ( - <div className="quality-profile-box quality-profiles-evolution-deprecated"> - <div className="spacer-bottom"> - <strong> - {translate('quality_profiles.deprecated_rules')} - </strong> - </div> - <div className="spacer-bottom"> - {translateWithParameters( - 'quality_profiles.deprecated_rules_are_still_activated', - profilesWithDeprecations.length - )} - </div> - <ul> - {sortedProfiles.map(profile => - <li key={profile.key} className="spacer-top"> - <div className="text-ellipsis"> - <ProfileLink - className="link-no-underline" - language={profile.language} - name={profile.name} - organization={this.props.organization}> - {profile.name} - </ProfileLink> - </div> - <div className="note"> - {profile.languageName} - {', '} - <Link - to={getDeprecatedActiveRulesUrl( - { qprofile: profile.key }, - this.props.organization - )} - className="text-muted"> - {translateWithParameters( - 'quality_profile.x_rules', - profile.activeDeprecatedRuleCount - )} - </Link> - </div> - </li> - )} - </ul> - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx new file mode 100644 index 00000000000..0f8da8a3e9a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx @@ -0,0 +1,86 @@ +/* + * 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 { Link } from 'react-router'; +import { sortBy } from 'lodash'; +import ProfileLink from '../components/ProfileLink'; +import { getDeprecatedActiveRulesUrl } from '../../../helpers/urls'; +import { translateWithParameters, translate } from '../../../helpers/l10n'; +import { IProfile } from '../types'; + +interface Props { + organization: string | null; + profiles: IProfile[]; +} + +export default function EvolutionDeprecated(props: Props) { + const profilesWithDeprecations = props.profiles.filter( + profile => profile.activeDeprecatedRuleCount > 0 + ); + + if (profilesWithDeprecations.length === 0) { + return null; + } + + const sortedProfiles = sortBy(profilesWithDeprecations, p => -p.activeDeprecatedRuleCount); + + return ( + <div className="quality-profile-box quality-profiles-evolution-deprecated"> + <div className="spacer-bottom"> + <strong> + {translate('quality_profiles.deprecated_rules')} + </strong> + </div> + <div className="spacer-bottom"> + {translateWithParameters( + 'quality_profiles.deprecated_rules_are_still_activated', + profilesWithDeprecations.length + )} + </div> + <ul> + {sortedProfiles.map(profile => + <li key={profile.key} className="spacer-top"> + <div className="text-ellipsis"> + <ProfileLink + className="link-no-underline" + language={profile.language} + name={profile.name} + organization={props.organization}> + {profile.name} + </ProfileLink> + </div> + <div className="note"> + {profile.languageName} + {', '} + <Link + to={getDeprecatedActiveRulesUrl({ qprofile: profile.key }, props.organization)} + className="text-muted"> + {translateWithParameters( + 'quality_profile.x_rules', + profile.activeDeprecatedRuleCount + )} + </Link> + </div> + </li> + )} + </ul> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx index 042c04a73b6..c9e3e1f46e1 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx @@ -17,10 +17,9 @@ * 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 { Link } from 'react-router'; -import moment from 'moment'; +import * as moment from 'moment'; import { sortBy } from 'lodash'; import { searchRules } from '../../../api/rules'; import { translateWithParameters, translate } from '../../../helpers/l10n'; @@ -31,24 +30,33 @@ const RULES_LIMIT = 10; const PERIOD_START_MOMENT = moment().subtract(1, 'year'); -function parseRules(r) { +function parseRules(r: any) { const { rules, actives } = r; - return rules.map(rule => { + return rules.map((rule: any) => { const activations = actives[rule.key]; return { ...rule, activations: activations ? activations.length : 0 }; }); } -/*:: -type Props = { - organization: ?string -}; -*/ +interface Props { + organization: string | null; +} + +interface IRule { + activations: number; + key: string; + langName: string; + name: string; +} + +interface State { + latestRules?: Array<IRule>; + latestRulesTotal?: number; +} -export default class EvolutionRules extends React.PureComponent { - /*:: mounted: boolean; */ - /*:: props: Props; */ - state = {}; +export default class EvolutionRules extends React.PureComponent<Props, State> { + mounted: boolean; + state: State = {}; componentDidMount() { this.mounted = true; @@ -68,10 +76,10 @@ export default class EvolutionRules extends React.PureComponent { f: 'name,langName,actives' }; - searchRules(data).then(r => { + searchRules(data).then((r: any) => { if (this.mounted) { this.setState({ - latestRules: sortBy(parseRules(r), 'langName'), + latestRules: sortBy<IRule>(parseRules(r), 'langName'), latestRulesTotal: r.total }); } @@ -79,7 +87,7 @@ export default class EvolutionRules extends React.PureComponent { } render() { - if (!this.state.latestRulesTotal) { + if (!this.state.latestRulesTotal || !this.state.latestRules) { return null; } @@ -125,7 +133,7 @@ export default class EvolutionRules extends React.PureComponent { {this.state.latestRulesTotal > RULES_LIMIT && <div className="spacer-top"> <Link to={newRulesUrl} className="small"> - {translate('see_all')} {formatMeasure(this.state.latestRulesTotal, 'SHORT_INT')} + {translate('see_all')} {formatMeasure(this.state.latestRulesTotal, 'SHORT_INT', null)} </Link> </div>} </div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js deleted file mode 100644 index 1eda4c0b8e3..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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. - */ -// @flow -import React from 'react'; -import moment from 'moment'; -import ProfileLink from '../components/ProfileLink'; -import { translate } from '../../../helpers/l10n'; -import { isStagnant } from '../utils'; -/*:: import type { Profile } from '../propTypes'; */ - -/*:: -type Props = { - organization: ?string, - profiles: Array<Profile> -}; -*/ - -export default class EvolutionStagnant extends React.PureComponent { - /*:: props: Props; */ - - render() { - // TODO filter built-in out - - const outdated = this.props.profiles.filter(isStagnant); - - if (outdated.length === 0) { - return null; - } - - return ( - <div className="quality-profile-box quality-profiles-evolution-stagnant"> - <div className="spacer-bottom"> - <strong> - {translate('quality_profiles.stagnant_profiles')} - </strong> - </div> - <div className="spacer-bottom"> - {translate('quality_profiles.not_updated_more_than_year')} - </div> - <ul> - {outdated.map(profile => - <li key={profile.key} className="spacer-top"> - <div className="text-ellipsis"> - <ProfileLink - className="link-no-underline" - language={profile.language} - name={profile.name} - organization={this.props.organization}> - {profile.name} - </ProfileLink> - </div> - <div className="note"> - {profile.languageName} - {', '} - updated on {moment(profile.rulesUpdatedAt).format('LL')} - </div> - </li> - )} - </ul> - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx new file mode 100644 index 00000000000..8b3fc2eae90 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx @@ -0,0 +1,73 @@ +/* + * 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 * as moment from 'moment'; +import ProfileLink from '../components/ProfileLink'; +import { translate } from '../../../helpers/l10n'; +import { isStagnant } from '../utils'; +import { IProfile } from '../types'; + +interface Props { + organization: string | null; + profiles: IProfile[]; +} + +export default function EvolutionStagnan(props: Props) { + // TODO filter built-in out + + const outdated = props.profiles.filter(isStagnant); + + if (outdated.length === 0) { + return null; + } + + return ( + <div className="quality-profile-box quality-profiles-evolution-stagnant"> + <div className="spacer-bottom"> + <strong> + {translate('quality_profiles.stagnant_profiles')} + </strong> + </div> + <div className="spacer-bottom"> + {translate('quality_profiles.not_updated_more_than_year')} + </div> + <ul> + {outdated.map(profile => + <li key={profile.key} className="spacer-top"> + <div className="text-ellipsis"> + <ProfileLink + className="link-no-underline" + language={profile.language} + name={profile.name} + organization={props.organization}> + {profile.name} + </ProfileLink> + </div> + <div className="note"> + {profile.languageName} + {', '} + updated on {moment(profile.rulesUpdatedAt).format('LL')} + </div> + </li> + )} + </ul> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.tsx index 95f63d8cd4c..cd3b21502a5 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.tsx @@ -17,42 +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. */ -// @flow -import React from 'react'; +import * as React from 'react'; import PageHeader from './PageHeader'; import Evolution from './Evolution'; import ProfilesList from './ProfilesList'; -/*:: import type { Profile } from '../propTypes'; */ +import { IProfile } from '../types'; -/*:: -type Props = { - canAdmin: boolean, - languages: Array<{ key: string, name: string }>, - location: { query: { [string]: string } }, - onRequestFail: Object => void, - organization?: string, - profiles: Array<Profile>, - updateProfiles: () => Promise<*> -}; -*/ - -export default class HomeContainer extends React.PureComponent { - /*:: props: Props; */ +interface Props { + canAdmin: boolean; + languages: Array<{ key: string; name: string }>; + location: { query: { [p: string]: string } }; + onRequestFail: (reason: any) => void; + organization: string | null; + profiles: Array<IProfile>; + updateProfiles: () => Promise<void>; +} - render() { - return ( - <div> - <PageHeader {...this.props} /> +export default function HomeContainer(props: Props) { + return ( + <div> + <PageHeader {...props} /> - <div className="page-with-sidebar"> - <div className="page-main"> - <ProfilesList {...this.props} /> - </div> - <div className="page-sidebar"> - <Evolution {...this.props} /> - </div> + <div className="page-with-sidebar"> + <div className="page-main"> + <ProfilesList {...props} /> + </div> + <div className="page-sidebar"> + <Evolution {...props} /> </div> </div> - ); - } + </div> + ); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx index 44ba30feeb7..0f3b2b9c19f 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx @@ -17,51 +17,44 @@ * 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 PropTypes from 'prop-types'; +import * as React from 'react'; +import * as PropTypes from 'prop-types'; import CreateProfileForm from './CreateProfileForm'; import RestoreProfileForm from './RestoreProfileForm'; -/*:: import type { Profile } from '../propTypes'; */ import { getProfilePath } from '../utils'; import { translate } from '../../../helpers/l10n'; +import { IProfile } from '../types'; -/*:: -type Props = { - canAdmin: boolean, - languages: Array<{ key: string, name: string }>, - onRequestFail: Object => void, - organization: ?string, - updateProfiles: () => Promise<*> -}; -*/ - -/*:: -type State = { - createFormOpen: boolean, - restoreFormOpen: boolean -}; -*/ +interface Props { + canAdmin: boolean; + languages: Array<{ key: string; name: string }>; + onRequestFail: (reason: any) => void; + organization: string | null; + updateProfiles: () => Promise<void>; +} -export default class PageHeader extends React.PureComponent { - /*:: props: Props; */ +interface State { + createFormOpen: boolean; + restoreFormOpen: boolean; +} +export default class PageHeader extends React.PureComponent<Props, State> { static contextTypes = { router: PropTypes.object }; - state /*: State */ = { + state = { createFormOpen: false, restoreFormOpen: false }; - handleCreateClick = (event /*: Event & { currentTarget: HTMLButtonElement } */) => { + handleCreateClick = (event: React.SyntheticEvent<HTMLElement>) => { event.preventDefault(); event.currentTarget.blur(); this.setState({ createFormOpen: true }); }; - handleCreate = (profile /*: Profile */) => { + handleCreate = (profile: IProfile) => { this.props.updateProfiles().then(() => { this.context.router.push( getProfilePath(profile.name, profile.language, this.props.organization) @@ -73,7 +66,7 @@ export default class PageHeader extends React.PureComponent { this.setState({ createFormOpen: false }); }; - handleRestoreClick = (event /*: Event */) => { + handleRestoreClick = (event: React.SyntheticEvent<HTMLElement>) => { event.preventDefault(); this.setState({ restoreFormOpen: true }); }; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.tsx index b912a9662a5..6f4c8f8c17b 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.tsx @@ -17,31 +17,25 @@ * 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 { groupBy, pick, sortBy } from 'lodash'; import ProfilesListRow from './ProfilesListRow'; import ProfilesListHeader from './ProfilesListHeader'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin'; -/*:: import type { Profile } from '../propTypes'; */ +import { IProfile } from '../types'; -/*:: -type Props = { - canAdmin: boolean, - languages: Array<{ key: string, name: string }>, - location: { query: { [string]: string } }, - onRequestFail: Object => void, - organization: ?string, - profiles: Array<Profile>, - updateProfiles: () => Promise<*> -}; -*/ - -export default class ProfilesList extends React.PureComponent { - /*:: props: Props; */ +interface Props { + canAdmin: boolean; + languages: Array<{ key: string; name: string }>; + location: { query: { [p: string]: string } }; + onRequestFail: (reason: any) => void; + organization: string | null; + profiles: IProfile[]; + updateProfiles: () => Promise<void>; +} - renderProfiles(profiles /*: Array<Profile> */) { +export default class ProfilesList extends React.PureComponent<Props> { + renderProfiles(profiles: IProfile[]) { return profiles.map(profile => <ProfilesListRow canAdmin={this.props.canAdmin} @@ -54,7 +48,7 @@ export default class ProfilesList extends React.PureComponent { ); } - renderHeader(languageKey /*: string */, profilesCount /*: number */) { + renderHeader(languageKey: string, profilesCount: number) { const language = this.props.languages.find(l => l.key === languageKey); if (!language) { @@ -91,8 +85,14 @@ export default class ProfilesList extends React.PureComponent { const { profiles, languages } = this.props; const { language } = this.props.location.query; - const profilesIndex = groupBy(profiles, profile => profile.language); - const profilesToShow = language ? pick(profilesIndex, language) : profilesIndex; + const profilesIndex: { [language: string]: IProfile[] } = groupBy<IProfile>( + profiles, + profile => profile.language + ); + + const profilesToShow: { [language: string]: IProfile[] } = language + ? pick(profilesIndex, language) + : profilesIndex; const languagesToShow = sortBy(Object.keys(profilesToShow)); @@ -115,12 +115,10 @@ export default class ProfilesList extends React.PureComponent { {profilesToShow[languageKey] != null && this.renderHeader(languageKey, profilesToShow[languageKey].length)} - <TooltipsContainer> - <tbody> - {profilesToShow[languageKey] != null && - this.renderProfiles(profilesToShow[languageKey])} - </tbody> - </TooltipsContainer> + <tbody> + {profilesToShow[languageKey] != null && + this.renderProfiles(profilesToShow[languageKey])} + </tbody> </table> </div> )} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.tsx index ab76d1a4df3..15b98d6a2d4 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.tsx @@ -17,23 +17,18 @@ * 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 { IndexLink } from 'react-router'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { getProfilesPath, getProfilesForLanguagePath } from '../utils'; -/*:: -type Props = { - currentFilter?: string, - languages: Array<{ key: string, name: string }>, - organization: ?string -}; -*/ - -export default class ProfilesListHeader extends React.PureComponent { - /*:: props: Props; */ +interface Props { + currentFilter?: string; + languages: Array<{ key: string; name: string }>; + organization: string | null; +} +export default class ProfilesListHeader extends React.PureComponent<Props> { renderFilterToggle() { const { languages, currentFilter } = this.props; const currentLanguage = currentFilter && languages.find(l => l.key === currentFilter); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx index f9d18056d2e..2d982c34db4 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx @@ -17,8 +17,7 @@ * 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 { Link } from 'react-router'; import ProfileLink from '../components/ProfileLink'; import ProfileDate from '../components/ProfileDate'; @@ -27,21 +26,18 @@ import BuiltInBadge from '../components/BuiltInBadge'; import { translate } from '../../../helpers/l10n'; import { getRulesUrl } from '../../../helpers/urls'; import { isStagnant } from '../utils'; -/*:: import type { Profile } from '../propTypes'; */ +import { IProfile } from '../types'; +import Tooltip from '../../../components/controls/Tooltip'; -/*:: -type Props = { - canAdmin: boolean, - onRequestFail: Object => void, - organization: ?string, - profile: Profile, - updateProfiles: () => Promise<*> -}; -*/ - -export default class ProfilesListRow extends React.PureComponent { - /*:: props: Props; */ +interface Props { + canAdmin: boolean; + onRequestFail: (reason: any) => void; + organization: string | null; + profile: IProfile; + updateProfiles: () => Promise<void>; +} +export default class ProfilesListRow extends React.PureComponent<Props> { renderName() { const { profile } = this.props; const offset = 25 * (profile.depth - 1); @@ -100,13 +96,11 @@ export default class ProfilesListRow extends React.PureComponent { <div> {profile.activeDeprecatedRuleCount > 0 && <span className="spacer-right"> - <Link - to={deprecatedRulesUrl} - className="badge badge-normal-size badge-danger-light" - title={translate('quality_profiles.deprecated_rules')} - data-toggle="tooltip"> - {profile.activeDeprecatedRuleCount} - </Link> + <Tooltip overlay={translate('quality_profiles.deprecated_rules')}> + <Link to={deprecatedRulesUrl} className="badge badge-normal-size badge-danger-light"> + {profile.activeDeprecatedRuleCount} + </Link> + </Tooltip> </span>} <Link to={activeRulesUrl}> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx index 26411abb34c..5f12d61f662 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx @@ -17,35 +17,28 @@ * 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 Modal from 'react-modal'; import { restoreQualityProfile } from '../../../api/quality-profiles'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -/*:: -type Props = { - onClose: () => void, - onRequestFail: Object => void, - onRestore: Function, - organization: ?string -}; -*/ +interface Props { + onClose: () => void; + onRequestFail: (reason: any) => void; + onRestore: () => void; + organization: string | null; +} -/*:: -type State = { - loading: boolean, - profile?: { name: string }, - ruleFailures?: number, - ruleSuccesses?: number -}; -*/ +interface State { + loading: boolean; + profile?: { name: string }; + ruleFailures?: number; + ruleSuccesses?: number; +} -export default class RestoreProfileForm extends React.PureComponent { - /*:: form: HTMLFormElement; */ - /*:: mounted: boolean; */ - /*:: props: Props; */ - state /*: State */ = { loading: false }; +export default class RestoreProfileForm extends React.PureComponent<Props, State> { + mounted: boolean; + state: State = { loading: false }; componentDidMount() { this.mounted = true; @@ -55,23 +48,23 @@ export default class RestoreProfileForm extends React.PureComponent { this.mounted = false; } - handleCancelClick = (event /*: Event */) => { + handleCancelClick = (event: React.SyntheticEvent<HTMLElement>) => { event.preventDefault(); this.props.onClose(); }; - handleFormSubmit = (event /*: Event */) => { + handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => { event.preventDefault(); this.setState({ loading: true }); - const data = new FormData(this.form); + const data = new FormData(event.currentTarget); if (this.props.organization) { data.append('organization', this.props.organization); } restoreQualityProfile(data).then( - response => { + (response: any) => { if (this.mounted) { this.setState({ loading: false, @@ -82,7 +75,7 @@ export default class RestoreProfileForm extends React.PureComponent { } this.props.onRestore(); }, - error => { + (error: any) => { if (this.mounted) { this.setState({ loading: false }); } @@ -103,10 +96,7 @@ export default class RestoreProfileForm extends React.PureComponent { className="modal" overlayClassName="modal-overlay" onRequestClose={this.props.onClose}> - <form - id="restore-profile-form" - onSubmit={this.handleFormSubmit} - ref={node => (this.form = node)}> + <form id="restore-profile-form" onSubmit={this.handleFormSubmit}> <div className="modal-head"> <h2> {header} |