* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { HttpStatusCode } from 'axios';
+import {
+ ButtonPrimary,
+ FlagMessage,
+ FormField,
+ InputField,
+ InputSelect,
+ InputTextArea,
+ LabelValueSelectOption,
+ LightLabel,
+ Modal,
+} from 'design-system';
import * as React from 'react';
-import { components, OptionProps, SingleValueProps } from 'react-select';
+import { OptionProps, SingleValueProps, components } 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 Select, { LabelValueSelectOption } 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 } 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, RuleType, Status } from '../../../types/types';
import { SeveritySelect } from './SeveritySelect';
interface Props {
type: RuleType;
}
+const FORM_ID = 'custom-rule-form';
+
export default class CustomRuleFormModal extends React.PureComponent<Props, State> {
mounted = false;
constructor(props: Props) {
super(props);
const params: Dict<string> = {};
- if (props.customRule && props.customRule.params) {
+ if (props.customRule?.params) {
for (const param of props.customRule.params) {
- params[param.key] = param.defaultValue || '';
+ params[param.key] = param.defaultValue ?? '';
}
}
this.state = {
- description: (props.customRule && props.customRule.mdDesc) || '',
+ description: props.customRule?.mdDesc ?? '',
key: '',
keyModifiedByUser: false,
- name: (props.customRule && props.customRule.name) || '',
+ name: props.customRule?.name ?? '',
params,
reactivating: false,
- severity: (props.customRule && props.customRule.severity) || props.templateRule.severity,
- status: (props.customRule && props.customRule.status) || props.templateRule.status,
+ severity: props.customRule?.severity ?? props.templateRule.severity,
+ status: props.customRule?.status ?? props.templateRule.status,
submitting: false,
- type: (props.customRule && props.customRule.type) || props.templateRule.type,
+ type: props.customRule?.type ?? props.templateRule.type,
};
}
},
(response: Response) => {
if (this.mounted) {
- this.setState({ reactivating: response.status === 409, submitting: false });
+ this.setState({
+ reactivating: response.status === HttpStatusCode.Conflict,
+ submitting: false,
+ });
}
},
);
handleSeverityChange = ({ value }: { value: string }) => this.setState({ severity: value });
- handleStatusChange = ({ value }: LabelValueSelectOption) => this.setState({ status: value });
+ handleStatusChange = ({ value }: LabelValueSelectOption<Status>) =>
+ this.setState({ status: value });
handleParameterChange = (event: React.SyntheticEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = event.currentTarget;
};
renderNameField = () => (
- <div className="modal-field">
- <label htmlFor="coding-rules-custom-rule-creation-name">
- {translate('name')} <MandatoryFieldMarker />
- </label>
- <input
+ <FormField
+ ariaLabel={translate('name')}
+ label={translate('name')}
+ htmlFor="coding-rules-custom-rule-creation-name"
+ required
+ >
+ <InputField
autoFocus
disabled={this.state.submitting}
id="coding-rules-custom-rule-creation-name"
onChange={this.handleNameChange}
required
+ size="full"
type="text"
value={this.state.name}
/>
- </div>
+ </FormField>
);
renderKeyField = () => (
- <div className="modal-field">
- <label htmlFor="coding-rules-custom-rule-creation-key">
- {translate('key')} {!this.props.customRule && <MandatoryFieldMarker />}
- </label>
-
+ <FormField
+ ariaLabel={translate('key')}
+ label={translate('key')}
+ htmlFor="coding-rules-custom-rule-creation-key"
+ required
+ >
{this.props.customRule ? (
- <span className="coding-rules-detail-custom-rule-key" title={this.props.customRule.key}>
- {this.props.customRule.key}
- </span>
+ <span title={this.props.customRule.key}>{this.props.customRule.key}</span>
) : (
- <input
+ <InputField
disabled={this.state.submitting}
id="coding-rules-custom-rule-creation-key"
onChange={this.handleKeyChange}
required
+ size="full"
type="text"
value={this.state.key}
/>
)}
- </div>
+ </FormField>
);
renderDescriptionField = () => (
- <div className="modal-field">
- <label htmlFor="coding-rules-custom-rule-creation-html-description">
- {translate('description')}
- <MandatoryFieldMarker />
- </label>
- <textarea
+ <FormField
+ ariaLabel={translate('description')}
+ label={translate('description')}
+ htmlFor="coding-rules-custom-rule-creation-html-description"
+ required
+ >
+ <InputTextArea
disabled={this.state.submitting}
id="coding-rules-custom-rule-creation-html-description"
onChange={this.handleDescriptionChange}
required
rows={5}
+ size="full"
value={this.state.description}
/>
- <FormattingTips className="modal-field-descriptor text-right" />
- </div>
+ <FormattingTips />
+ </FormField>
);
renderTypeOption = (props: OptionProps<LabelValueSelectOption<RuleType>, false>) => {
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"
+ <FormField
+ ariaLabel={translate('type')}
+ label={translate('type')}
+ htmlFor="coding-rules-custom-rule-type"
+ >
+ <InputSelect
+ inputId="coding-rules-custom-rule-type"
isClearable={false}
isDisabled={this.state.submitting}
isSearchable={false}
options={ruleTypeOption}
value={ruleTypeOption.find((t) => t.value === this.state.type)}
/>
- </div>
+ </FormField>
);
};
renderSeverityField = () => (
- <div className="modal-field flex-1 spacer-right">
- <label id="coding-rules-custom-rule-severity">{translate('severity')}</label>
+ <FormField
+ ariaLabel={translate('severity')}
+ label={translate('severity')}
+ htmlFor="coding-rules-severity-select"
+ >
<SeveritySelect
- ariaLabelledby="coding-rules-custom-rule-severity"
isDisabled={this.state.submitting}
onChange={this.handleSeverityChange}
severity={this.state.severity}
/>
- </div>
+ </FormField>
);
renderStatusField = () => {
value: status,
}));
return (
- <div className="modal-field flex-1">
- <label id="coding-rules-custom-rule-status">
- {translate('coding_rules.filters.status')}
- </label>
- <Select
+ <FormField
+ ariaLabel={translate('coding_rules.filters.status')}
+ label={translate('coding_rules.filters.status')}
+ htmlFor="coding-rules-custom-rule-status"
+ >
+ <InputSelect
+ inputId="coding-rules-custom-rule-status"
isClearable={false}
isDisabled={this.state.submitting}
aria-labelledby="coding-rules-custom-rule-status"
isSearchable={false}
value={statusesOptions.find((s) => s.value === this.state.status)}
/>
- </div>
+ </FormField>
);
};
renderParameterField = (param: RuleParameter) => (
- <div className="modal-field" key={param.key}>
- <label className="capitalize" htmlFor={param.key}>
- {param.key}
- </label>
-
+ <FormField
+ ariaLabel={param.key}
+ className="sw-capitalize"
+ label={param.key}
+ htmlFor={`coding-rule-custom-rule-${param.key}`}
+ key={param.key}
+ >
{param.type === 'TEXT' ? (
- <textarea
+ <InputTextArea
disabled={this.state.submitting}
- id={param.key}
+ id={`coding-rule-custom-rule-${param.key}`}
name={param.key}
onChange={this.handleParameterChange}
placeholder={param.defaultValue}
+ size="full"
rows={3}
value={this.state.params[param.key] || ''}
/>
) : (
- <input
+ <InputField
disabled={this.state.submitting}
- id={param.key}
+ id={`coding-rule-custom-rule-${param.key}`}
name={param.key}
onChange={this.handleParameterChange}
placeholder={param.defaultValue}
+ size="full"
type="text"
- value={this.state.params[param.key] || ''}
+ value={this.state.params[param.key] ?? ''}
/>
)}
{param.htmlDesc !== undefined && (
- <div
- className="modal-field-description"
+ <LightLabel
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: sanitizeString(param.htmlDesc) }}
/>
)}
- </div>
+ </FormField>
);
render() {
submit = translate('coding_rules.reactivate');
}
return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
- <form onSubmit={this.handleFormSubmit}>
- <div className="modal-head">
- <h2>{header}</h2>
- </div>
-
- <div className="modal-body modal-container">
+ <Modal
+ headerTitle={header}
+ onClose={this.props.onClose}
+ body={
+ <form
+ className="sw-flex sw-flex-col sw-justify-stretch sw-pb-4"
+ id={FORM_ID}
+ onSubmit={this.handleFormSubmit}
+ >
{reactivating && (
- <Alert variant="warning">{translate('coding_rules.reactivate.help')}</Alert>
+ <FlagMessage variant="warning" className="sw-mb-6">
+ {translate('coding_rules.reactivate.help')}
+ </FlagMessage>
)}
- <MandatoryFieldsExplanation className="spacer-bottom" />
+ <MandatoryFieldsExplanation className="sw-mb-4" />
{this.renderNameField()}
{this.renderKeyField()}
- <div className="display-flex-space-between">
- {/* do not allow to change the type of existing rule */}
- {!customRule && this.renderTypeField()}
- {this.renderSeverityField()}
- {this.renderStatusField()}
- </div>
+ {/* do not allow to change the type of existing rule */}
+ {!customRule && this.renderTypeField()}
+ {this.renderSeverityField()}
+ {this.renderStatusField()}
{this.renderDescriptionField()}
{params.map(this.renderParameterField)}
- </div>
-
- <div className="modal-foot">
- {submitting && <i className="spinner spacer-right" />}
- <SubmitButton disabled={this.state.submitting}>{submit}</SubmitButton>
- <ResetButtonLink
- disabled={submitting}
- id="coding-rules-custom-rule-creation-cancel"
- onClick={this.props.onClose}
- >
- {translate('cancel')}
- </ResetButtonLink>
- </div>
- </form>
- </Modal>
+ </form>
+ }
+ primaryButton={
+ <ButtonPrimary disabled={submitting} type="submit" form={FORM_ID}>
+ {submit}
+ </ButtonPrimary>
+ }
+ loading={submitting}
+ secondaryButtonLabel={translate('cancel')}
+ />
);
}
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { InputSelect, LabelValueSelectOption } from 'design-system';
import * as React from 'react';
-import { components, OptionProps, SingleValueProps } from 'react-select';
-import Select, { LabelValueSelectOption } from '../../../components/controls/Select';
+import { OptionProps, SingleValueProps, components } from 'react-select';
import SeverityHelper from '../../../components/shared/SeverityHelper';
import { SEVERITIES } from '../../../helpers/constants';
import { translate } from '../../../helpers/l10n';
+import { IssueSeverity } from '../../../types/issues';
export interface SeveritySelectProps {
isDisabled: boolean;
severity: string;
- ariaLabelledby: string;
- onChange: (value: LabelValueSelectOption) => void;
+ onChange: (value: LabelValueSelectOption<IssueSeverity>) => void;
}
-function Option(props: OptionProps<LabelValueSelectOption, false>) {
+function Option(props: Readonly<OptionProps<LabelValueSelectOption<IssueSeverity>, false>>) {
return (
<components.Option {...props}>
<SeverityHelper className="display-flex-center" severity={props.data.value} />
);
}
-function SingleValue(props: SingleValueProps<LabelValueSelectOption, false>) {
+function SingleValue(
+ props: Readonly<SingleValueProps<LabelValueSelectOption<IssueSeverity>, false>>,
+) {
return (
<components.SingleValue {...props}>
<SeverityHelper className="display-flex-center" severity={props.data.value} />
}
export function SeveritySelect(props: SeveritySelectProps) {
- const { isDisabled, severity, ariaLabelledby } = props;
+ const { isDisabled, severity } = props;
const serverityOption = SEVERITIES.map((severity) => ({
label: translate('severity', severity),
value: severity,
}));
return (
- <Select
- id="coding-rules-severity-select"
- aria-labelledby={ariaLabelledby}
+ <InputSelect
+ aria-label={translate('severity')}
+ inputId="coding-rules-severity-select"
isDisabled={isDisabled}
onChange={props.onChange}
components={{ Option, SingleValue }}