aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps
diff options
context:
space:
mode:
authorViktor Vorona <viktor.vorona@sonarsource.com>2024-09-12 17:02:57 +0200
committersonartech <sonartech@sonarsource.com>2024-09-13 20:02:35 +0000
commit4aa45bbc641827cf55b1c01170a405f2d73c1182 (patch)
tree050c7321f044f34d8b2b318b9918a93e83169736 /server/sonar-web/src/main/js/apps
parent4674af96f13e9f48a6a371ddd07c21ae2859c66d (diff)
downloadsonarqube-4aa45bbc641827cf55b1c01170a405f2d73c1182.tar.gz
sonarqube-4aa45bbc641827cf55b1c01170a405f2d73c1182.zip
SONAR-22831 Migrate Change Rule details to echoes components
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts7
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx80
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx6
4 files changed, 76 insertions, 44 deletions
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts
index 6fdf75b1707..7e57a1be36d 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts
+++ b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts
@@ -669,9 +669,10 @@ describe('Rule app details', () => {
// Change rule details in quality profile
await user.click(ui.changeButton('QP FooBaz').get());
+ await user.clear(ui.paramInput('1').get());
await user.type(ui.paramInput('1').get(), 'New');
await user.click(ui.saveButton.get(ui.changeQPDialog.get()));
- expect(screen.getByText('New')).toBeInTheDocument();
+ expect(await screen.findByText('New')).toBeInTheDocument();
// Revert rule details in quality profile
await user.click(ui.revertToParentDefinitionButton.get());
@@ -687,7 +688,7 @@ describe('Rule app details', () => {
it('can deactivate an inherrited rule', async () => {
const { ui, user } = getPageObjects();
rulesHandler.setIsAdmin();
- renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule1');
+ renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule11');
await ui.detailsloaded();
// Should show 2 deactivate buttons: one for the parent, one for the child profile.
@@ -704,7 +705,7 @@ describe('Rule app details', () => {
const { ui } = getPageObjects();
rulesHandler.setIsAdmin();
settingsHandler.set(SettingsKey.QPAdminCanDisableInheritedRules, 'false');
- renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule1');
+ renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule11');
await ui.detailsloaded();
// Should show 1 deactivate button: one for the parent, none for the child profile.
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx
index 995cb49fd19..e88e698bc7c 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx
@@ -17,7 +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.
*/
-import { ButtonSecondary } from 'design-system';
+import { Button, ButtonVariety } from '@sonarsource/echoes-react';
import * as React from 'react';
import { Profile as BaseProfile } from '../../../api/quality-profiles';
import { Rule, RuleActivation, RuleDetails } from '../../../types/types';
@@ -40,25 +40,26 @@ export default function ActivationButton(props: Props) {
return (
<>
- <ButtonSecondary
+ <Button
+ variety={ButtonVariety.Default}
aria-label={ariaLabel}
className={className}
id="coding-rules-quality-profile-activate"
onClick={() => setModalOpen(true)}
>
{buttonText}
- </ButtonSecondary>
+ </Button>
- {modalOpen && (
- <ActivationFormModal
- activation={activation}
- modalHeader={modalHeader}
- onClose={() => setModalOpen(false)}
- onDone={props.onDone}
- profiles={profiles}
- rule={rule}
- />
- )}
+ <ActivationFormModal
+ activation={activation}
+ modalHeader={modalHeader}
+ isOpen={modalOpen}
+ onOpenChange={setModalOpen}
+ onClose={() => setModalOpen(false)}
+ onDone={props.onDone}
+ profiles={profiles}
+ rule={rule}
+ />
</>
);
}
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 94487afb689..b6a26714588 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
@@ -18,15 +18,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Button, ButtonVariety, Modal } from '@sonarsource/echoes-react';
import {
- ButtonPrimary,
FlagMessage,
FormField,
InputField,
InputSelect,
InputTextArea,
LabelValueSelectOption,
- Modal,
Note,
Switch,
} from 'design-system';
@@ -46,9 +45,11 @@ import { SeveritySelect } from './SeveritySelect';
interface Props {
activation?: RuleActivation;
+ isOpen: boolean;
modalHeader: string;
onClose: () => void;
onDone?: (severity: string, prioritizedRule: boolean) => Promise<void> | void;
+ onOpenChange: (isOpen: boolean) => void;
profiles: Profile[];
rule: Rule | RuleDetails;
}
@@ -61,29 +62,50 @@ const MIN_PROFILES_TO_ENABLE_SELECT = 2;
const FORM_ID = 'rule-activation-modal-form';
export default function ActivationFormModal(props: Readonly<Props>) {
- const { activation, rule, profiles, modalHeader } = props;
+ const { activation, rule, profiles, modalHeader, isOpen, onOpenChange } = props;
const { mutate: activateRule, isPending: submitting } = useActivateRuleMutation((data) => {
props.onDone?.(data.severity as string, data.prioritizedRule as boolean);
props.onClose();
});
const { hasFeature } = useAvailableFeatures();
const intl = useIntl();
- const [prioritizedRule, setPrioritizedRule] = React.useState(
- activation ? activation.prioritizedRule : false,
+ const [changedPrioritizedRule, setChangedPrioritizedRule] = React.useState<boolean | undefined>(
+ undefined,
);
-
- const profilesWithDepth = getQualityProfilesWithDepth(profiles, rule.lang);
- const [profile, setProfile] = React.useState(profilesWithDepth[0]);
- const [params, setParams] = React.useState(getRuleParams({ activation, rule }));
- const [severity, setSeverity] = React.useState(
- (activation ? activation.severity : rule.severity) as IssueSeverity,
+ const [changedProfile, setChangedProfile] = React.useState<ProfileWithDepth | undefined>(
+ undefined,
+ );
+ const [changedParams, setChangedParams] = React.useState<Record<string, string> | undefined>(
+ undefined,
);
+ const [changedSeverity, setChangedSeverity] = React.useState<IssueSeverity | undefined>(
+ undefined,
+ );
+
+ const profilesWithDepth = React.useMemo(() => {
+ return getQualityProfilesWithDepth(profiles, rule.lang);
+ }, [profiles, rule.lang]);
+ const prioritizedRule =
+ changedPrioritizedRule ?? (activation ? activation.prioritizedRule : false);
+ const profile = changedProfile ?? profilesWithDepth[0];
+ const params = changedParams ?? getRuleParams({ activation, rule });
+ const severity =
+ changedSeverity ?? ((activation ? activation.severity : rule.severity) as IssueSeverity);
const profileOptions = profilesWithDepth.map((p) => ({ label: p.name, value: p }));
const isCustomRule = !!(rule as RuleDetails).templateKey;
const activeInAllProfiles = profilesWithDepth.length <= 0;
const isUpdateMode = !!activation;
+ React.useEffect(() => {
+ if (!isOpen) {
+ setChangedPrioritizedRule(undefined);
+ setChangedProfile(undefined);
+ setChangedParams(undefined);
+ setChangedSeverity(undefined);
+ }
+ }, [isOpen]);
+
const handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
const data = {
@@ -100,27 +122,33 @@ export default function ActivationFormModal(props: Readonly<Props>) {
event: React.SyntheticEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
const { name, value } = event.currentTarget;
- setParams({ ...params, [name]: value });
+ setChangedParams({ ...params, [name]: value });
};
- const makeScrollable = (rule.params?.length ?? 0) > 1;
-
return (
<Modal
- headerTitle={modalHeader}
- onClose={props.onClose}
- loading={submitting}
- isOverflowVisible={!makeScrollable}
- isScrollable={makeScrollable}
+ title={modalHeader}
+ isOpen={isOpen}
+ onOpenChange={onOpenChange}
primaryButton={
- <ButtonPrimary disabled={submitting || activeInAllProfiles} form={FORM_ID} type="submit">
+ <Button
+ variety={ButtonVariety.Primary}
+ isDisabled={submitting || activeInAllProfiles}
+ isLoading={submitting}
+ form={FORM_ID}
+ type="submit"
+ >
{isUpdateMode
? intl.formatMessage({ id: 'save' })
: intl.formatMessage({ id: 'coding_rules.activate' })}
- </ButtonPrimary>
+ </Button>
+ }
+ secondaryButton={
+ <Button variety={ButtonVariety.Default} isDisabled={submitting} onClick={props.onClose}>
+ {intl.formatMessage({ id: 'cancel' })}
+ </Button>
}
- secondaryButtonLabel={intl.formatMessage({ id: 'cancel' })}
- body={
+ content={
<form className="sw-pb-10" id={FORM_ID} onSubmit={handleFormSubmit}>
{!isUpdateMode && activeInAllProfiles && (
<FlagMessage className="sw-mb-2" variant="info">
@@ -139,7 +167,7 @@ export default function ActivationFormModal(props: Readonly<Props>) {
isClearable={false}
isDisabled={submitting || profilesWithDepth.length < MIN_PROFILES_TO_ENABLE_SELECT}
onChange={({ value }: LabelValueSelectOption<ProfileWithDepth>) => {
- setProfile(value);
+ setChangedProfile(value);
}}
getOptionLabel={({ value }: LabelValueSelectOption<ProfileWithDepth>) =>
' '.repeat(value.depth) + value.name
@@ -184,7 +212,7 @@ export default function ActivationFormModal(props: Readonly<Props>) {
{ state: 'off' },
),
}}
- onChange={setPrioritizedRule}
+ onChange={setChangedPrioritizedRule}
value={prioritizedRule}
/>
<span className="sw-text-xs">
@@ -202,7 +230,7 @@ export default function ActivationFormModal(props: Readonly<Props>) {
<SeveritySelect
isDisabled={submitting}
onChange={({ value }: LabelValueSelectOption<IssueSeverity>) => {
- setSeverity(value);
+ setChangedSeverity(value);
}}
severity={severity}
/>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx
index e12115032a1..8bb71661a4d 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx
@@ -39,7 +39,7 @@ export default function ComparisonResultActivation(props: React.PropsWithChildre
const [rule, setRule] = React.useState<RuleDetails>();
const intl = useIntl();
- const isOpen = state === 'open' && rule;
+ const isOpen = state === 'open' && !!rule;
const activateRuleMsg = intl.formatMessage(
{ id: 'quality_profiles.comparison.activate_rule' },
@@ -71,8 +71,10 @@ export default function ComparisonResultActivation(props: React.PropsWithChildre
</Button>
</Tooltip>
- {isOpen && (
+ {rule && (
<ActivationFormModal
+ isOpen={isOpen}
+ onOpenChange={(open) => setState(open ? 'open' : 'closed')}
modalHeader={intl.formatMessage({ id: 'coding_rules.activate_in_quality_profile' })}
onClose={() => {
setState('closed');