aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx
diff options
context:
space:
mode:
authorStas Vilchik <stas.vilchik@sonarsource.com>2018-01-29 14:21:28 +0100
committerGitHub <noreply@github.com>2018-01-29 14:21:28 +0100
commitcebce15815204aa189f63f9e1b86143b258898d2 (patch)
tree5a3a773405e86a42e29c12c3e447951052bec6e9 /server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx
parentad504279d97bd55d8c191b1ffb793c6f005ffa5a (diff)
downloadsonarqube-cebce15815204aa189f63f9e1b86143b258898d2.tar.gz
sonarqube-cebce15815204aa189f63f9e1b86143b258898d2.zip
rewrite rules app with react (#2982)
Diffstat (limited to 'server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx')
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx216
1 files changed, 216 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx
new file mode 100644
index 00000000000..c5e31bcb5a5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx
@@ -0,0 +1,216 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 RemoveExtendedDescriptionModal from './RemoveExtendedDescriptionModal';
+import { updateRule } from '../../../api/rules';
+import { RuleDetails } from '../../../app/types';
+import MarkdownTips from '../../../components/common/MarkdownTips';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+ canWrite: boolean | undefined;
+ onChange: (newRuleDetails: RuleDetails) => void;
+ organization: string | undefined;
+ ruleDetails: RuleDetails;
+}
+
+interface State {
+ description: string;
+ descriptionForm: boolean;
+ removeDescriptionModal: boolean;
+ submitting: boolean;
+}
+
+export default class RuleDetailsDescription extends React.PureComponent<Props, State> {
+ mounted: boolean;
+ state: State = {
+ description: '',
+ descriptionForm: false,
+ submitting: false,
+ removeDescriptionModal: false
+ };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleDescriptionChange = (event: React.SyntheticEvent<HTMLTextAreaElement>) =>
+ this.setState({ description: event.currentTarget.value });
+
+ handleCancelClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState({ descriptionForm: false });
+ };
+
+ handleSaveClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.updateDescription(this.state.description);
+ };
+
+ handleRemoveDescriptionClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState({ removeDescriptionModal: true });
+ };
+
+ handleCancelRemoving = () => this.setState({ removeDescriptionModal: false });
+
+ handleConfirmRemoving = () => {
+ this.setState({ removeDescriptionModal: false });
+ this.updateDescription('');
+ };
+
+ updateDescription = (text: string) => {
+ this.setState({ submitting: true });
+
+ updateRule({
+ key: this.props.ruleDetails.key,
+ /* eslint-disable camelcase */
+ markdown_note: text,
+ /* eslint-enable camelcase*/
+ organization: this.props.organization
+ }).then(
+ ruleDetails => {
+ this.props.onChange(ruleDetails);
+ if (this.mounted) {
+ this.setState({ submitting: false, descriptionForm: false });
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ submitting: false });
+ }
+ }
+ );
+ };
+
+ handleExtendDescriptionClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.setState({
+ // set description` to the current `mdNote` each time the form is open
+ description: this.props.ruleDetails.mdNote || '',
+ descriptionForm: true
+ });
+ };
+
+ renderDescription = () => (
+ <div id="coding-rules-detail-description-extra">
+ {this.props.ruleDetails.htmlNote !== undefined && (
+ <div
+ className="rule-desc spacer-bottom markdown"
+ dangerouslySetInnerHTML={{ __html: this.props.ruleDetails.htmlNote }}
+ />
+ )}
+ {this.props.canWrite && (
+ <button
+ id="coding-rules-detail-extend-description"
+ onClick={this.handleExtendDescriptionClick}>
+ {translate('coding_rules.extend_description')}
+ </button>
+ )}
+ </div>
+ );
+
+ renderForm = () => (
+ <div className="coding-rules-detail-extend-description-form">
+ <table className="width100">
+ <tbody>
+ <tr>
+ <td className="width100" colSpan={2}>
+ <textarea
+ autoFocus={true}
+ id="coding-rules-detail-extend-description-text"
+ onChange={this.handleDescriptionChange}
+ rows={4}
+ style={{ width: '100%', marginBottom: 4 }}
+ value={this.state.description}
+ />
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <button
+ disabled={this.state.submitting}
+ id="coding-rules-detail-extend-description-submit"
+ onClick={this.handleSaveClick}>
+ {translate('save')}
+ </button>
+ {this.props.ruleDetails.mdNote !== undefined && (
+ <>
+ <button
+ className="button-red spacer-left"
+ disabled={this.state.submitting}
+ id="coding-rules-detail-extend-description-remove"
+ onClick={this.handleRemoveDescriptionClick}>
+ {translate('remove')}
+ </button>
+ {this.state.removeDescriptionModal && (
+ <RemoveExtendedDescriptionModal
+ onCancel={this.handleCancelRemoving}
+ onSubmit={this.handleConfirmRemoving}
+ />
+ )}
+ </>
+ )}
+ <button
+ className="spacer-left button-link"
+ disabled={this.state.submitting}
+ id="coding-rules-detail-extend-description-cancel"
+ onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </button>
+ {this.state.submitting && <i className="spinner spacer-left" />}
+ </td>
+ <td className="text-right">
+ <MarkdownTips />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ );
+
+ render() {
+ const { ruleDetails } = this.props;
+
+ return (
+ <div className="js-rule-description">
+ <div
+ className="coding-rules-detail-description rule-desc markdown"
+ dangerouslySetInnerHTML={{ __html: ruleDetails.htmlDesc || '' }}
+ />
+
+ {!ruleDetails.templateKey && (
+ <div className="coding-rules-detail-description coding-rules-detail-description-extra">
+ {!this.state.descriptionForm && this.renderDescription()}
+ {this.state.descriptionForm && this.props.canWrite && this.renderForm()}
+ </div>
+ )}
+ </div>
+ );
+ }
+}