From 4aa45bbc641827cf55b1c01170a405f2d73c1182 Mon Sep 17 00:00:00 2001 From: Viktor Vorona Date: Thu, 12 Sep 2024 17:02:57 +0200 Subject: [PATCH] SONAR-22831 Migrate Change Rule details to echoes components --- .../src/main/js/api/mocks/data/rules.ts | 11 ++- .../coding-rules/__tests__/CodingRules-it.ts | 7 +- .../components/ActivationButton.tsx | 27 ++++--- .../components/ActivationFormModal.tsx | 80 +++++++++++++------ .../compare/ComparisonResultActivation.tsx | 6 +- 5 files changed, 81 insertions(+), 50 deletions(-) diff --git a/server/sonar-web/src/main/js/api/mocks/data/rules.ts b/server/sonar-web/src/main/js/api/mocks/data/rules.ts index 37acd30ad07..12d827391a8 100644 --- a/server/sonar-web/src/main/js/api/mocks/data/rules.ts +++ b/server/sonar-web/src/main/js/api/mocks/data/rules.ts @@ -265,12 +265,7 @@ export function mockRuleDetailsList() { export function mockRulesActivationsInQP() { return { - [RULE_1]: [ - mockRuleActivation({ qProfile: QP_1 }), - mockRuleActivation({ qProfile: QP_4 }), - mockRuleActivation({ qProfile: QP_5, inherit: 'INHERITED' }), - mockRuleActivation({ qProfile: QP_6 }), - ], + [RULE_1]: [mockRuleActivation({ qProfile: QP_1 }), mockRuleActivation({ qProfile: QP_6 })], [RULE_7]: [mockRuleActivation({ qProfile: QP_2 })], [RULE_8]: [mockRuleActivation({ qProfile: QP_2 })], [RULE_9]: [ @@ -287,5 +282,9 @@ export function mockRulesActivationsInQP() { mockRuleActivation({ qProfile: QP_2, inherit: 'OVERRIDES', prioritizedRule: true }), mockRuleActivation({ qProfile: QP_2_Parent, severity: 'MINOR' }), ], + [RULE_11]: [ + mockRuleActivation({ qProfile: QP_4 }), + mockRuleActivation({ qProfile: QP_5, inherit: 'INHERITED' }), + ], }; } 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 ( <> - setModalOpen(true)} > {buttonText} - + - {modalOpen && ( - setModalOpen(false)} - onDone={props.onDone} - profiles={profiles} - rule={rule} - /> - )} + 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; + 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) { - 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( + 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( + undefined, + ); + const [changedParams, setChangedParams] = React.useState | undefined>( + undefined, ); + const [changedSeverity, setChangedSeverity] = React.useState( + 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) => { event.preventDefault(); const data = { @@ -100,27 +122,33 @@ export default function ActivationFormModal(props: Readonly) { event: React.SyntheticEvent, ) => { const { name, value } = event.currentTarget; - setParams({ ...params, [name]: value }); + setChangedParams({ ...params, [name]: value }); }; - const makeScrollable = (rule.params?.length ?? 0) > 1; - return ( + + } + secondaryButton={ + } - secondaryButtonLabel={intl.formatMessage({ id: 'cancel' })} - body={ + content={
{!isUpdateMode && activeInAllProfiles && ( @@ -139,7 +167,7 @@ export default function ActivationFormModal(props: Readonly) { isClearable={false} isDisabled={submitting || profilesWithDepth.length < MIN_PROFILES_TO_ENABLE_SELECT} onChange={({ value }: LabelValueSelectOption) => { - setProfile(value); + setChangedProfile(value); }} getOptionLabel={({ value }: LabelValueSelectOption) => ' '.repeat(value.depth) + value.name @@ -184,7 +212,7 @@ export default function ActivationFormModal(props: Readonly) { { state: 'off' }, ), }} - onChange={setPrioritizedRule} + onChange={setChangedPrioritizedRule} value={prioritizedRule} /> @@ -202,7 +230,7 @@ export default function ActivationFormModal(props: Readonly) { ) => { - 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(); 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 - {isOpen && ( + {rule && ( setState(open ? 'open' : 'closed')} modalHeader={intl.formatMessage({ id: 'coding_rules.activate_in_quality_profile' })} onClose={() => { setState('closed'); -- 2.39.5