diff options
author | Mathieu Suen <mathieu.suen@sonarsource.com> | 2022-03-25 12:04:10 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-03-28 20:02:53 +0000 |
commit | b5cbc1d5b142b64cd406badfb15a011ec344fecf (patch) | |
tree | 63154ceaa653bca17973be813bddc0fa3587b587 /server/sonar-web | |
parent | 63ad64c7c2b365736eb4c9c91a68d9580c549d69 (diff) | |
download | sonarqube-b5cbc1d5b142b64cd406badfb15a011ec344fecf.tar.gz sonarqube-b5cbc1d5b142b64cd406badfb15a011ec344fecf.zip |
SONAR-16085 Migrating custom rule modal to new react-select
Diffstat (limited to 'server/sonar-web')
6 files changed, 227 insertions, 392 deletions
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx index 97a0c93be8e..d405bbd13eb 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx @@ -19,18 +19,17 @@ */ import classNames from 'classnames'; import * as React from 'react'; -import { components, OptionProps, OptionTypeBase, SingleValueProps } from 'react-select'; +import { OptionTypeBase } from 'react-select'; import { activateRule, Profile } from '../../../api/quality-profiles'; import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import Modal from '../../../components/controls/Modal'; import Select from '../../../components/controls/Select'; -import SeverityHelper from '../../../components/shared/SeverityHelper'; import { Alert } from '../../../components/ui/Alert'; -import { SEVERITIES } from '../../../helpers/constants'; import { translate } from '../../../helpers/l10n'; import { sanitizeString } from '../../../helpers/sanitize'; import { Dict, Rule, RuleActivation, RuleDetails } from '../../../types/types'; import { sortProfiles } from '../../quality-profiles/utils'; +import { SeveritySelect } from './SeveritySelect'; interface Props { activation?: RuleActivation; @@ -153,26 +152,6 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat const isCustomRule = !!(rule as RuleDetails).templateKey; const activeInAllProfiles = profilesWithDepth.length <= 0; const isUpdateMode = !!activation; - const serverityOption = SEVERITIES.map(severity => ({ - label: translate('severity', severity), - value: severity - })); - - function Option(props: OptionProps<OptionTypeBase, false>) { - return ( - <components.Option {...props}> - <SeverityHelper severity={props.data.value} /> - </components.Option> - ); - } - - function SingleValue(props: SingleValueProps<OptionTypeBase>) { - return ( - <components.SingleValue {...props}> - <SeverityHelper className="coding-rules-severity-value" severity={props.data.value} /> - </components.SingleValue> - ); - } return ( <Modal contentLabel={this.props.modalHeader} onRequestClose={this.props.onClose} size="small"> @@ -203,16 +182,11 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat </div> <div className="modal-field"> <label id="coding-rules-severity-select">{translate('severity')}</label> - <Select - className="js-severity" - isClearable={false} + <SeveritySelect isDisabled={submitting} - aria-labelledby="coding-rules-severity-select" + ariaLabelledby="coding-rules-severity-select" onChange={this.handleSeverityChange} - components={{ Option, SingleValue }} - options={serverityOption} - isSearchable={false} - value={serverityOption.find(s => s.value === severity)} + severity={severity} /> </div> {isCustomRule ? ( diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx index f819781b368..697183bbaa5 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx @@ -18,22 +18,23 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { components, OptionProps, OptionTypeBase, SingleValueProps } from 'react-select'; import { createRule, updateRule } from '../../../api/rules'; import FormattingTips from '../../../components/common/FormattingTips'; import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import Modal from '../../../components/controls/Modal'; -import SelectLegacy from '../../../components/controls/SelectLegacy'; -import SeverityHelper from '../../../components/shared/SeverityHelper'; +import Select from '../../../components/controls/Select'; import TypeHelper from '../../../components/shared/TypeHelper'; import { Alert } from '../../../components/ui/Alert'; import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker'; import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation'; -import { RULE_STATUSES, RULE_TYPES, SEVERITIES } from '../../../helpers/constants'; +import { RULE_STATUSES, RULE_TYPES } from '../../../helpers/constants'; import { csvEscape } from '../../../helpers/csv'; import { translate } from '../../../helpers/l10n'; import { sanitizeString } from '../../../helpers/sanitize'; import { latinize } from '../../../helpers/strings'; -import { Dict, RuleDetails, RuleParameter, RuleType } from '../../../types/types'; +import { Dict, RuleDetails, RuleParameter } from '../../../types/types'; +import { SeveritySelect } from './SeveritySelect'; interface Props { customRule?: RuleDetails; @@ -212,71 +213,81 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat </div> ); - renderTypeOption = ({ value }: { value: RuleType }) => { - return <TypeHelper type={value} />; + renderTypeOption = (props: OptionProps<OptionTypeBase, false>) => { + return ( + <components.Option {...props}> + <TypeHelper type={props.data.value} /> + </components.Option> + ); }; - renderTypeField = () => ( - <div className="modal-field flex-1 spacer-right"> - <label htmlFor="coding-rules-custom-rule-type">{translate('type')}</label> - <SelectLegacy - clearable={false} - disabled={this.state.submitting} - id="coding-rules-custom-rule-type" - onChange={this.handleTypeChange} - optionRenderer={this.renderTypeOption} - options={RULE_TYPES.map(type => ({ - label: translate('issue.type', type), - value: type - }))} - searchable={false} - value={this.state.type} - valueRenderer={this.renderTypeOption} - /> - </div> - ); + renderTypeSingleValue = (props: SingleValueProps<OptionTypeBase>) => { + return ( + <components.SingleValue {...props}> + <TypeHelper className="display-flex-center" type={props.data.value} /> + </components.SingleValue> + ); + }; - renderSeverityOption = ({ value }: { value: string }) => <SeverityHelper severity={value} />; + renderTypeField = () => { + const ruleTypeOption = RULE_TYPES.map(type => ({ + label: translate('issue.type', type), + value: type + })); + return ( + <div className="modal-field flex-1 spacer-right"> + <label id="coding-rules-custom-rule-type">{translate('type')}</label> + <Select + aria-labelledby="coding-rules-custom-rule-type" + isClearable={false} + isDisabled={this.state.submitting} + isSearchable={false} + onChange={this.handleTypeChange} + components={{ + Option: this.renderTypeOption, + SingleValue: this.renderTypeSingleValue + }} + options={ruleTypeOption} + value={ruleTypeOption.find(t => t.value === this.state.type)} + /> + </div> + ); + }; renderSeverityField = () => ( <div className="modal-field flex-1 spacer-right"> - <label htmlFor="coding-rules-custom-rule-severity">{translate('severity')}</label> - <SelectLegacy - clearable={false} - disabled={this.state.submitting} - id="coding-rules-custom-rule-severity" + <label id="coding-rules-custom-rule-severity">{translate('severity')}</label> + <SeveritySelect + ariaLabelledby="coding-rules-custom-rule-severity" + isDisabled={this.state.submitting} onChange={this.handleSeverityChange} - optionRenderer={this.renderSeverityOption} - options={SEVERITIES.map(severity => ({ - label: translate('severity', severity), - value: severity - }))} - searchable={false} - value={this.state.severity} - valueRenderer={this.renderSeverityOption} + severity={this.state.severity} /> </div> ); - renderStatusField = () => ( - <div className="modal-field flex-1"> - <label htmlFor="coding-rules-custom-rule-status"> - {translate('coding_rules.filters.status')} - </label> - <SelectLegacy - clearable={false} - disabled={this.state.submitting} - id="coding-rules-custom-rule-status" - onChange={this.handleStatusChange} - options={RULE_STATUSES.map(status => ({ - label: translate('rules.status', status), - value: status - }))} - searchable={false} - value={this.state.status} - /> - </div> - ); + renderStatusField = () => { + const statusesOptions = RULE_STATUSES.map(status => ({ + label: translate('rules.status', status), + value: status + })); + return ( + <div className="modal-field flex-1"> + <label id="coding-rules-custom-rule-status"> + {translate('coding_rules.filters.status')} + </label> + <Select + isClearable={false} + isDisabled={this.state.submitting} + aria-labelledby="coding-rules-custom-rule-status" + onChange={this.handleStatusChange} + options={statusesOptions} + searchable={false} + value={statusesOptions.find(s => s.value === this.state.status)} + /> + </div> + ); + }; renderParameterField = (param: RuleParameter) => ( <div className="modal-field" key={param.key}> diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/SeveritySelect.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/SeveritySelect.tsx new file mode 100644 index 00000000000..def50c057d9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/SeveritySelect.tsx @@ -0,0 +1,69 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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 { components, OptionProps, OptionTypeBase, SingleValueProps } from 'react-select'; +import Select from '../../../components/controls/Select'; +import SeverityHelper from '../../../components/shared/SeverityHelper'; +import { SEVERITIES } from '../../../helpers/constants'; +import { translate } from '../../../helpers/l10n'; + +export interface SeveritySelectProps { + isDisabled: boolean; + severity: string; + ariaLabelledby: string; + onChange: (value: OptionTypeBase) => void; +} + +export function SeveritySelect(props: SeveritySelectProps) { + const { isDisabled, severity, ariaLabelledby } = props; + const serverityOption = SEVERITIES.map(severity => ({ + label: translate('severity', severity), + value: severity + })); + + function Option(props: OptionProps<OptionTypeBase, false>) { + return ( + <components.Option {...props}> + <SeverityHelper className="display-flex-center" severity={props.data.value} /> + </components.Option> + ); + } + + function SingleValue(props: SingleValueProps<OptionTypeBase>) { + return ( + <components.SingleValue {...props}> + <SeverityHelper className="display-flex-center" severity={props.data.value} /> + </components.SingleValue> + ); + } + + return ( + <Select + className="js-severity" + aria-labelledby={ariaLabelledby} + isDisabled={isDisabled} + onChange={props.onChange} + components={{ Option, SingleValue }} + options={serverityOption} + isSearchable={false} + value={serverityOption.find(s => s.value === severity)} + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap index ccfb4ebf108..8806948181a 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap @@ -50,49 +50,11 @@ exports[`should render correctly: custom rule 1`] = ` > severity </label> - <Select - aria-labelledby="coding-rules-severity-select" - className="js-severity" - components={ - Object { - "Option": [Function], - "SingleValue": [Function], - } - } - isClearable={false} + <SeveritySelect + ariaLabelledby="coding-rules-severity-select" isDisabled={false} - isSearchable={false} onChange={[Function]} - options={ - Array [ - Object { - "label": "severity.BLOCKER", - "value": "BLOCKER", - }, - Object { - "label": "severity.CRITICAL", - "value": "CRITICAL", - }, - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - }, - Object { - "label": "severity.MINOR", - "value": "MINOR", - }, - Object { - "label": "severity.INFO", - "value": "INFO", - }, - ] - } - value={ - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - } - } + severity="MAJOR" /> </div> <div @@ -174,49 +136,11 @@ exports[`should render correctly: default 1`] = ` > severity </label> - <Select - aria-labelledby="coding-rules-severity-select" - className="js-severity" - components={ - Object { - "Option": [Function], - "SingleValue": [Function], - } - } - isClearable={false} + <SeveritySelect + ariaLabelledby="coding-rules-severity-select" isDisabled={false} - isSearchable={false} onChange={[Function]} - options={ - Array [ - Object { - "label": "severity.BLOCKER", - "value": "BLOCKER", - }, - Object { - "label": "severity.CRITICAL", - "value": "CRITICAL", - }, - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - }, - Object { - "label": "severity.MINOR", - "value": "MINOR", - }, - Object { - "label": "severity.INFO", - "value": "INFO", - }, - ] - } - value={ - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - } - } + severity="MAJOR" /> </div> <div @@ -333,49 +257,11 @@ exports[`should render correctly: submitting 1`] = ` > severity </label> - <Select - aria-labelledby="coding-rules-severity-select" - className="js-severity" - components={ - Object { - "Option": [Function], - "SingleValue": [Function], - } - } - isClearable={false} + <SeveritySelect + ariaLabelledby="coding-rules-severity-select" isDisabled={true} - isSearchable={false} onChange={[Function]} - options={ - Array [ - Object { - "label": "severity.BLOCKER", - "value": "BLOCKER", - }, - Object { - "label": "severity.CRITICAL", - "value": "CRITICAL", - }, - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - }, - Object { - "label": "severity.MINOR", - "value": "MINOR", - }, - Object { - "label": "severity.INFO", - "value": "INFO", - }, - ] - } - value={ - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - } - } + severity="MAJOR" /> </div> <div @@ -490,49 +376,11 @@ exports[`should render correctly: update mode 1`] = ` > severity </label> - <Select - aria-labelledby="coding-rules-severity-select" - className="js-severity" - components={ - Object { - "Option": [Function], - "SingleValue": [Function], - } - } - isClearable={false} + <SeveritySelect + ariaLabelledby="coding-rules-severity-select" isDisabled={false} - isSearchable={false} onChange={[Function]} - options={ - Array [ - Object { - "label": "severity.BLOCKER", - "value": "BLOCKER", - }, - Object { - "label": "severity.CRITICAL", - "value": "CRITICAL", - }, - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - }, - Object { - "label": "severity.MINOR", - "value": "MINOR", - }, - Object { - "label": "severity.INFO", - "value": "INFO", - }, - ] - } - value={ - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - } - } + severity="MAJOR" /> </div> <div @@ -683,49 +531,11 @@ exports[`should render correctly: with deep profiles 1`] = ` > severity </label> - <Select - aria-labelledby="coding-rules-severity-select" - className="js-severity" - components={ - Object { - "Option": [Function], - "SingleValue": [Function], - } - } - isClearable={false} + <SeveritySelect + ariaLabelledby="coding-rules-severity-select" isDisabled={false} - isSearchable={false} onChange={[Function]} - options={ - Array [ - Object { - "label": "severity.BLOCKER", - "value": "BLOCKER", - }, - Object { - "label": "severity.CRITICAL", - "value": "CRITICAL", - }, - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - }, - Object { - "label": "severity.MINOR", - "value": "MINOR", - }, - Object { - "label": "severity.INFO", - "value": "INFO", - }, - ] - } - value={ - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - } - } + severity="MAJOR" /> </div> <div diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap index fb6cf7e8194..1af1f3d81d7 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap @@ -72,16 +72,22 @@ exports[`should handle re-activation 1`] = ` className="modal-field flex-1 spacer-right" > <label - htmlFor="coding-rules-custom-rule-type" + id="coding-rules-custom-rule-type" > type </label> - <SelectLegacy - clearable={false} - disabled={false} - id="coding-rules-custom-rule-type" + <Select + aria-labelledby="coding-rules-custom-rule-type" + components={ + Object { + "Option": [Function], + "SingleValue": [Function], + } + } + isClearable={false} + isDisabled={false} + isSearchable={false} onChange={[Function]} - optionRenderer={[Function]} options={ Array [ Object { @@ -102,66 +108,41 @@ exports[`should handle re-activation 1`] = ` }, ] } - searchable={false} - value="CODE_SMELL" - valueRenderer={[Function]} + value={ + Object { + "label": "issue.type.CODE_SMELL", + "value": "CODE_SMELL", + } + } /> </div> <div className="modal-field flex-1 spacer-right" > <label - htmlFor="coding-rules-custom-rule-severity" + id="coding-rules-custom-rule-severity" > severity </label> - <SelectLegacy - clearable={false} - disabled={false} - id="coding-rules-custom-rule-severity" + <SeveritySelect + ariaLabelledby="coding-rules-custom-rule-severity" + isDisabled={false} onChange={[Function]} - optionRenderer={[Function]} - options={ - Array [ - Object { - "label": "severity.BLOCKER", - "value": "BLOCKER", - }, - Object { - "label": "severity.CRITICAL", - "value": "CRITICAL", - }, - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - }, - Object { - "label": "severity.MINOR", - "value": "MINOR", - }, - Object { - "label": "severity.INFO", - "value": "INFO", - }, - ] - } - searchable={false} - value="MAJOR" - valueRenderer={[Function]} + severity="MAJOR" /> </div> <div className="modal-field flex-1" > <label - htmlFor="coding-rules-custom-rule-status" + id="coding-rules-custom-rule-status" > coding_rules.filters.status </label> - <SelectLegacy - clearable={false} - disabled={false} - id="coding-rules-custom-rule-status" + <Select + aria-labelledby="coding-rules-custom-rule-status" + isClearable={false} + isDisabled={false} onChange={[Function]} options={ Array [ @@ -180,7 +161,12 @@ exports[`should handle re-activation 1`] = ` ] } searchable={false} - value="READY" + value={ + Object { + "label": "rules.status.READY", + "value": "READY", + } + } /> </div> </div> @@ -341,16 +327,22 @@ exports[`should render correctly: default 1`] = ` className="modal-field flex-1 spacer-right" > <label - htmlFor="coding-rules-custom-rule-type" + id="coding-rules-custom-rule-type" > type </label> - <SelectLegacy - clearable={false} - disabled={false} - id="coding-rules-custom-rule-type" + <Select + aria-labelledby="coding-rules-custom-rule-type" + components={ + Object { + "Option": [Function], + "SingleValue": [Function], + } + } + isClearable={false} + isDisabled={false} + isSearchable={false} onChange={[Function]} - optionRenderer={[Function]} options={ Array [ Object { @@ -371,66 +363,41 @@ exports[`should render correctly: default 1`] = ` }, ] } - searchable={false} - value="CODE_SMELL" - valueRenderer={[Function]} + value={ + Object { + "label": "issue.type.CODE_SMELL", + "value": "CODE_SMELL", + } + } /> </div> <div className="modal-field flex-1 spacer-right" > <label - htmlFor="coding-rules-custom-rule-severity" + id="coding-rules-custom-rule-severity" > severity </label> - <SelectLegacy - clearable={false} - disabled={false} - id="coding-rules-custom-rule-severity" + <SeveritySelect + ariaLabelledby="coding-rules-custom-rule-severity" + isDisabled={false} onChange={[Function]} - optionRenderer={[Function]} - options={ - Array [ - Object { - "label": "severity.BLOCKER", - "value": "BLOCKER", - }, - Object { - "label": "severity.CRITICAL", - "value": "CRITICAL", - }, - Object { - "label": "severity.MAJOR", - "value": "MAJOR", - }, - Object { - "label": "severity.MINOR", - "value": "MINOR", - }, - Object { - "label": "severity.INFO", - "value": "INFO", - }, - ] - } - searchable={false} - value="MAJOR" - valueRenderer={[Function]} + severity="MAJOR" /> </div> <div className="modal-field flex-1" > <label - htmlFor="coding-rules-custom-rule-status" + id="coding-rules-custom-rule-status" > coding_rules.filters.status </label> - <SelectLegacy - clearable={false} - disabled={false} - id="coding-rules-custom-rule-status" + <Select + aria-labelledby="coding-rules-custom-rule-status" + isClearable={false} + isDisabled={false} onChange={[Function]} options={ Array [ @@ -449,7 +416,12 @@ exports[`should render correctly: default 1`] = ` ] } searchable={false} - value="READY" + value={ + Object { + "label": "rules.status.READY", + "value": "READY", + } + } /> </div> </div> diff --git a/server/sonar-web/src/main/js/apps/coding-rules/styles.css b/server/sonar-web/src/main/js/apps/coding-rules/styles.css index d8f26b52468..f3d5897218d 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/styles.css +++ b/server/sonar-web/src/main/js/apps/coding-rules/styles.css @@ -101,8 +101,7 @@ line-height: 1; } -.coding-rules-details-tag-edit-cancel, -.coding-rules-severity-value svg { +.coding-rules-details-tag-edit-cancel { vertical-align: middle; } |