diff options
author | Viktor Vorona <viktor.vorona@sonarsource.com> | 2024-09-12 17:02:57 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-09-13 20:02:35 +0000 |
commit | 4aa45bbc641827cf55b1c01170a405f2d73c1182 (patch) | |
tree | 050c7321f044f34d8b2b318b9918a93e83169736 /server/sonar-web/src/main/js/apps | |
parent | 4674af96f13e9f48a6a371ddd07c21ae2859c66d (diff) | |
download | sonarqube-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')
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'); |