]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21019 - Project Settings - Quality Profile adopts MIUI
authorKevin Silva <kevin.silva@sonarsource.com>
Sat, 18 Nov 2023 18:12:46 +0000 (19:12 +0100)
committersonartech <sonartech@sonarsource.com>
Mon, 20 Nov 2023 20:02:38 +0000 (20:02 +0000)
server/sonar-web/src/main/js/app/components/GlobalContainer.tsx
server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesApp.tsx
server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesAppRenderer.tsx
server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/projectQualityProfilesApp-it.tsx
server/sonar-web/src/main/js/apps/projectQualityProfiles/components/AddLanguageModal.tsx
server/sonar-web/src/main/js/apps/projectQualityProfiles/components/LanguageProfileSelectOption.tsx
server/sonar-web/src/main/js/apps/projectQualityProfiles/components/SetQualityProfileModal.tsx

index aa2277eb8dd5cd5b98b97af1f88438f5d5487201..e1fd1f5f38c4f6d5723704b5a6778197129a69c4 100644 (file)
@@ -64,6 +64,7 @@ const TEMP_PAGELIST_WITH_NEW_BACKGROUND_WHITE = [
   '/project/links',
   '/project/import_export',
   '/project/quality_gate',
+  '/project/quality_profiles',
 ];
 
 export default function GlobalContainer() {
index b527f81c962eecd3d0d46262c32be2107cbd2834..4f9247d5a90049d79b0aef101e0055cce6df84d6 100644 (file)
@@ -17,6 +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 { addGlobalSuccessMessage } from 'design-system';
 import { differenceBy } from 'lodash';
 import * as React from 'react';
 import {
@@ -28,7 +29,6 @@ import {
 } from '../../api/quality-profiles';
 import withComponentContext from '../../app/components/componentContext/withComponentContext';
 import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization';
-import { addGlobalSuccessMessage } from '../../helpers/globalMessages';
 import { translateWithParameters } from '../../helpers/l10n';
 import { isDefined } from '../../helpers/types';
 import { Component } from '../../types/types';
index 3af9417a960a144f5bcffd6b4a171b6b75747f0c..ce39f10023352a5db6bcc83623167132b02591a5 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import {
+  ActionCell,
+  ButtonPrimary,
+  ContentCell,
+  HelperHintIcon,
+  InteractiveIcon,
+  LargeCenteredLayout,
+  Link,
+  PageContentFontWrapper,
+  PencilIcon,
+  Spinner,
+  Table,
+  TableRow,
+  TableRowInteractive,
+  Title,
+} from 'design-system';
 import { groupBy, orderBy } from 'lodash';
 import * as React from 'react';
 import { Helmet } from 'react-helmet-async';
 import { Profile } from '../../api/quality-profiles';
 import A11ySkipTarget from '../../components/a11y/A11ySkipTarget';
-import Link from '../../components/common/Link';
 import HelpTooltip from '../../components/controls/HelpTooltip';
-import { Button } from '../../components/controls/buttons';
 import Suggestions from '../../components/embed-docs-modal/Suggestions';
-import EditIcon from '../../components/icons/EditIcon';
-import PlusCircleIcon from '../../components/icons/PlusCircleIcon';
 import { translate } from '../../helpers/l10n';
 import { getRulesUrl } from '../../helpers/urls';
 import { Component } from '../../types/types';
@@ -65,124 +77,124 @@ export default function ProjectQualityProfilesAppRenderer(
   const profilesByLanguage = groupBy(allProfiles, 'language');
   const orderedProfiles = orderBy(projectProfiles, (p) => p.profile.languageName);
 
+  const COLUMN_WIDTHS_WITH_PURGE_SETTING = ['auto', 'auto', 'auto', '5%'];
+
+  const header = (
+    <TableRow>
+      <ContentCell>{translate('language')}</ContentCell>
+      <ContentCell>{translate('project_quality_profile.current')}</ContentCell>
+      <ContentCell>{translate('coding_rules.filters.activation.active_rules')}</ContentCell>
+      <ActionCell>{translate('actions')}</ActionCell>
+    </TableRow>
+  );
+
   return (
-    <div className="page page-limited" id="project-quality-profiles">
-      <Suggestions suggestions="project_quality_profiles" />
-      <Helmet defer={false} title={translate('project_quality_profiles.page')} />
-      <A11ySkipTarget anchor="profiles_main" />
+    <LargeCenteredLayout id="project-quality-profiles">
+      <PageContentFontWrapper className="sw-my-8 sw-body-sm">
+        <Suggestions suggestions="project_quality_profiles" />
+        <Helmet defer={false} title={translate('project_quality_profiles.page')} />
+        <A11ySkipTarget anchor="profiles_main" />
 
-      <header className="page-header">
-        <div className="page-title display-flex-center">
-          <h1>{translate('project_quality_profiles.page')} </h1>
+        <header className="sw-mb-2 sw-flex sw-items-center">
+          <Title>{translate('project_quality_profiles.page')}</Title>
           <HelpTooltip
-            className="spacer-left"
-            overlay={
-              <div className="big-padded-top big-padded-bottom">
-                {translate('quality_profiles.list.projects.help')}
-              </div>
-            }
-          />
-        </div>
-      </header>
+            className="sw-ml-2 sw-mb-4"
+            overlay={translate('quality_profiles.list.projects.help')}
+          >
+            <HelperHintIcon aria-label="help-tooltip" />
+          </HelpTooltip>
+        </header>
 
-      <div className="boxed-group">
-        <h2 className="boxed-group-header">{translate('project_quality_profile.subtitle')}</h2>
+        <div>
+          <p>{translate('project_quality_profiles.page.description')}</p>
+          <div className="sw-mt-16">
+            <Spinner loading={loading}>
+              {!loading && orderedProfiles.length > 0 && (
+                <Table
+                  noHeaderTopBorder
+                  className="sw-w-[60%]"
+                  columnCount={COLUMN_WIDTHS_WITH_PURGE_SETTING.length}
+                  columnWidths={COLUMN_WIDTHS_WITH_PURGE_SETTING}
+                  header={header}
+                >
+                  {orderedProfiles.map((projectProfile) => {
+                    const { profile, selected } = projectProfile;
 
-        <div className="boxed-group-inner">
-          <p className="big-spacer-bottom">
-            {translate('project_quality_profiles.page.description')}
-          </p>
+                    return (
+                      <TableRowInteractive key={profile.language}>
+                        <ContentCell>
+                          <span>{profile.languageName}</span>
+                        </ContentCell>
+                        <ContentCell>
+                          <span>
+                            {!selected && profile.isDefault ? (
+                              <em>{translate('project_quality_profile.instance_default')}</em>
+                            ) : (
+                              <>
+                                {profile.name}
+                                {profile.isBuiltIn && (
+                                  <BuiltInQualityProfileBadge className="spacer-left" />
+                                )}
+                              </>
+                            )}
+                          </span>
+                        </ContentCell>
+                        <ContentCell>
+                          <Link to={getRulesUrl({ activation: 'true', qprofile: profile.key })}>
+                            {profile.activeRuleCount}
+                          </Link>
+                        </ContentCell>
 
-          {loading && <i className="spinner spacer-left" />}
+                        <ActionCell>
+                          <InteractiveIcon
+                            Icon={PencilIcon}
+                            aria-label={translate('project_quality_profile.change_profile')}
+                            onClick={() => {
+                              props.onOpenSetProfileModal(projectProfile);
+                            }}
+                            size="small"
+                            stopPropagation={false}
+                          />
+                        </ActionCell>
+                      </TableRowInteractive>
+                    );
+                  })}
+                </Table>
+              )}
 
-          {!loading && orderedProfiles.length > 0 && (
-            <table className="data zebra">
-              <thead>
-                <tr>
-                  <th>{translate('language')}</th>
-                  <th className="thin nowrap">{translate('project_quality_profile.current')}</th>
-                  <th className="thin nowrap text-right">
-                    {translate('coding_rules.filters.activation.active_rules')}
-                  </th>
-                  <th aria-label={translate('actions')} />
-                </tr>
-              </thead>
-              <tbody>
-                {orderedProfiles.map((projectProfile) => {
-                  const { profile, selected } = projectProfile;
-                  return (
-                    <tr key={profile.language}>
-                      <td>{profile.languageName}</td>
-                      <td className="thin nowrap">
-                        <span className="display-inline-flex-center">
-                          {!selected && profile.isDefault ? (
-                            <em>{translate('project_quality_profile.instance_default')}</em>
-                          ) : (
-                            <>
-                              {profile.name}
-                              {profile.isBuiltIn && (
-                                <BuiltInQualityProfileBadge className="spacer-left" />
-                              )}
-                            </>
-                          )}
-                        </span>
-                      </td>
-                      <td className="nowrap text-right">
-                        <Link to={getRulesUrl({ activation: 'true', qprofile: profile.key })}>
-                          {profile.activeRuleCount}
-                        </Link>
-                      </td>
-                      <td className="text-right">
-                        <Button
-                          onClick={() => {
-                            props.onOpenSetProfileModal(projectProfile);
-                          }}
-                        >
-                          <EditIcon className="spacer-right" />
-                          {translate('project_quality_profile.change_profile')}
-                        </Button>
-                      </td>
-                    </tr>
-                  );
-                })}
-              </tbody>
-            </table>
-          )}
+              <div className="sw-mt-8">
+                <div className="sw-mb-4">
+                  {translate('project_quality_profile.add_language.description')}
+                </div>
 
-          <div className="big-spacer-top">
-            <h2>{translate('project_quality_profile.add_language.title')}</h2>
+                <ButtonPrimary disabled={loading} onClick={props.onOpenAddLanguageModal}>
+                  {translate('project_quality_profile.add_language.action')}
+                </ButtonPrimary>
+              </div>
 
-            <p className="spacer-top big-spacer-bottom">
-              {translate('project_quality_profile.add_language.description')}
-            </p>
+              {showProjectProfileInModal && (
+                <SetQualityProfileModal
+                  availableProfiles={profilesByLanguage[showProjectProfileInModal.profile.language]}
+                  component={component}
+                  currentProfile={showProjectProfileInModal.profile}
+                  onClose={props.onCloseModal}
+                  onSubmit={props.onSetProfile}
+                  usesDefault={!showProjectProfileInModal.selected}
+                />
+              )}
 
-            <Button disabled={loading} onClick={props.onOpenAddLanguageModal}>
-              <PlusCircleIcon className="little-spacer-right" />
-              {translate('project_quality_profile.add_language.action')}
-            </Button>
+              {showAddLanguageModal && projectProfiles && (
+                <AddLanguageModal
+                  profilesByLanguage={profilesByLanguage}
+                  onClose={props.onCloseModal}
+                  onSubmit={props.onAddLanguage}
+                  unavailableLanguages={projectProfiles.map((p) => p.profile.language)}
+                />
+              )}
+            </Spinner>
           </div>
-
-          {showProjectProfileInModal && (
-            <SetQualityProfileModal
-              availableProfiles={profilesByLanguage[showProjectProfileInModal.profile.language]}
-              component={component}
-              currentProfile={showProjectProfileInModal.profile}
-              onClose={props.onCloseModal}
-              onSubmit={props.onSetProfile}
-              usesDefault={!showProjectProfileInModal.selected}
-            />
-          )}
-
-          {showAddLanguageModal && projectProfiles && (
-            <AddLanguageModal
-              profilesByLanguage={profilesByLanguage}
-              onClose={props.onCloseModal}
-              onSubmit={props.onAddLanguage}
-              unavailableLanguages={projectProfiles.map((p) => p.profile.language)}
-            />
-          )}
         </div>
-      </div>
-    </div>
+      </PageContentFontWrapper>
+    </LargeCenteredLayout>
   );
 }
index 0ff50093a04b974499c56ec98e0f7ae343de489a..205b02204820cabe3ba49b0e78acb66b090e0952 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-
 import userEvent from '@testing-library/user-event';
+import { addGlobalSuccessMessage } from 'design-system';
 import selectEvent from 'react-select-event';
 import {
   ProfileProject,
@@ -27,7 +27,6 @@ import {
   searchQualityProfiles,
 } from '../../../api/quality-profiles';
 import handleRequiredAuthorization from '../../../app/utils/handleRequiredAuthorization';
-import { addGlobalSuccessMessage } from '../../../helpers/globalMessages';
 import { mockComponent } from '../../../helpers/mocks/component';
 import {
   RenderContext,
@@ -100,13 +99,10 @@ jest.mock('../../../api/quality-profiles', () => {
   };
 });
 
-jest.mock('../../../helpers/globalMessages', () => {
-  const globalMessages = jest.requireActual('../../../helpers/globalMessages');
-  return {
-    ...globalMessages,
-    addGlobalSuccessMessage: jest.fn(),
-  };
-});
+jest.mock('design-system', () => ({
+  ...jest.requireActual('design-system'),
+  addGlobalSuccessMessage: jest.fn(),
+}));
 
 jest.mock('../../../app/utils/handleRequiredAuthorization', () => jest.fn());
 
@@ -116,7 +112,7 @@ const ui = {
   pageTitle: byText('project_quality_profiles.page'),
   pageSubTitle: byText('project_quality_profile.subtitle'),
   pageDescription: byText('project_quality_profiles.page.description'),
-  helpTooltip: byLabelText('help'),
+  helpTooltip: byLabelText('help-tooltip'),
   profileRows: byRole('row'),
   addLanguageButton: byRole('button', { name: 'project_quality_profile.add_language.action' }),
   modalAddLanguageTitle: byText('project_quality_profile.add_language_modal.title'),
@@ -158,9 +154,8 @@ it('should be able to add and change profile for languages', async () => {
   });
 
   expect(ui.pageTitle.get()).toBeInTheDocument();
-  expect(ui.pageSubTitle.get()).toBeInTheDocument();
   expect(ui.pageDescription.get()).toBeInTheDocument();
-  expect(ui.addLanguageButton.get()).toBeInTheDocument();
+  expect(await ui.addLanguageButton.find()).toBeInTheDocument();
   await expect(ui.helpTooltip.get()).toHaveATooltipWithContent(
     'quality_profiles.list.projects.help',
   );
@@ -190,7 +185,7 @@ it('should be able to add and change profile for languages', async () => {
 
   // Updates the page after API call
   const htmlRow = byRole('row', {
-    name: 'HTML html profile 10 project_quality_profile.change_profile',
+    name: 'HTML html profile 10',
   });
 
   expect(ui.htmlLanguage.get()).toBeInTheDocument();
@@ -231,15 +226,14 @@ it('should call authorization api when permissions is not proper', () => {
   expect(handleRequiredAuthorization).toHaveBeenCalled();
 });
 
-it('should still show page with add language button when api fails', () => {
+it('should still show page with add language button when api fails', async () => {
   jest.mocked(searchQualityProfiles).mockRejectedValueOnce(null);
   jest.mocked(getProfileProjects).mockRejectedValueOnce(null);
 
   renderProjectQualityProfilesApp();
   expect(ui.pageTitle.get()).toBeInTheDocument();
-  expect(ui.pageSubTitle.get()).toBeInTheDocument();
   expect(ui.pageDescription.get()).toBeInTheDocument();
-  expect(ui.addLanguageButton.get()).toBeInTheDocument();
+  expect(await ui.addLanguageButton.find()).toBeInTheDocument();
 });
 
 function renderProjectQualityProfilesApp(
index d09f755fc04b0043e61a1c66f72c25dc6d28f0f4..887e4a35bf6826c3054aa1a07dd6e154e0d7f137 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { ButtonPrimary, FormField, InputSelect, Modal } from 'design-system';
 import { difference } from 'lodash';
 import * as React from 'react';
 import { Profile } from '../../../api/quality-profiles';
 import withLanguagesContext from '../../../app/components/languages/withLanguagesContext';
-import Select, { LabelValueSelectOption } from '../../../components/controls/Select';
-import SimpleModal from '../../../components/controls/SimpleModal';
-import { ButtonLink, SubmitButton } from '../../../components/controls/buttons';
+import { LabelValueSelectOption } from '../../../components/controls/Select';
 import { translate } from '../../../helpers/l10n';
 import { Languages } from '../../../types/languages';
 import { Dict } from '../../../types/types';
@@ -62,80 +61,71 @@ export function AddLanguageModal(props: AddLanguageModalProps) {
         }))
       : [];
 
-  return (
-    <SimpleModal
-      header={header}
-      onClose={props.onClose}
-      onSubmit={() => {
-        if (language && key) {
-          props.onSubmit(key);
-        }
-      }}
-    >
-      {({ onCloseClick, onFormSubmit, submitting }) => (
-        <>
-          <div className="modal-head">
-            <h2>{header}</h2>
-          </div>
+  const onFormSubmit = (event: React.FormEvent<HTMLFormElement>) => {
+    event.preventDefault();
+    if (language && key) {
+      props.onSubmit(key);
+    }
+  };
 
-          <form onSubmit={onFormSubmit}>
-            <div className="modal-body">
-              <div className="big-spacer-bottom">
-                <div className="little-spacer-bottom">
-                  <label className="text-bold" htmlFor="language">
-                    {translate('project_quality_profile.add_language_modal.choose_language')}
-                  </label>
-                </div>
-                <Select
-                  className="abs-width-300"
-                  isDisabled={submitting}
-                  id="language"
-                  aria-label={translate(
-                    'project_quality_profile.add_language_modal.choose_language',
-                  )}
-                  onChange={({ value }: LabelValueSelectOption) => {
-                    setSelected({ language: value, key: undefined });
-                  }}
-                  options={languageOptions}
-                />
-              </div>
+  const renderForm = (
+    <form id="add-language-quality-profile" onSubmit={onFormSubmit}>
+      <div>
+        <FormField
+          className="sw-mb-4"
+          label={translate('project_quality_profile.add_language_modal.choose_language')}
+          htmlFor="language"
+        >
+          <InputSelect
+            size="full"
+            id="language"
+            aria-label={translate('project_quality_profile.add_language_modal.choose_language')}
+            onChange={({ value }: LabelValueSelectOption) => {
+              setSelected({ language: value, key: undefined });
+            }}
+            options={languageOptions}
+          />
+        </FormField>
 
-              <div className="big-spacer-bottom">
-                <div className="little-spacer-bottom">
-                  <label className="text-bold" htmlFor="profiles">
-                    {translate('project_quality_profile.add_language_modal.choose_profile')}
-                  </label>
-                </div>
-                <Select
-                  className="abs-width-300"
-                  isDisabled={submitting || !language}
-                  id="profiles"
-                  aria-label={translate(
-                    'project_quality_profile.add_language_modal.choose_profile',
-                  )}
-                  onChange={({ value }: ProfileOption) => setSelected({ language, key: value })}
-                  options={profileOptions}
-                  components={{
-                    Option: LanguageProfileSelectOption,
-                  }}
-                  value={profileOptions.find((o) => o.value === key) ?? null}
-                />
-              </div>
-            </div>
+        <FormField
+          className="sw-mb-4"
+          label={translate('project_quality_profile.add_language_modal.choose_profile')}
+          htmlFor="profiles"
+        >
+          <InputSelect
+            size="full"
+            isDisabled={!language}
+            id="profiles"
+            aria-label={translate('project_quality_profile.add_language_modal.choose_profile')}
+            onChange={({ value }: ProfileOption) => setSelected({ language, key: value })}
+            options={profileOptions}
+            components={{
+              Option: LanguageProfileSelectOption,
+            }}
+            value={profileOptions.find((o) => o.value === key) ?? null}
+          />
+        </FormField>
+      </div>
+    </form>
+  );
 
-            <div className="modal-foot">
-              {submitting && <i className="spinner spacer-right" />}
-              <SubmitButton disabled={submitting || !language || !key}>
-                {translate('save')}
-              </SubmitButton>
-              <ButtonLink disabled={submitting} onClick={onCloseClick}>
-                {translate('cancel')}
-              </ButtonLink>
-            </div>
-          </form>
-        </>
-      )}
-    </SimpleModal>
+  return (
+    <Modal
+      onClose={props.onClose}
+      headerTitle={header}
+      isOverflowVisible
+      body={renderForm}
+      primaryButton={
+        <ButtonPrimary
+          disabled={!language || !key}
+          form="add-language-quality-profile"
+          type="submit"
+        >
+          {translate('save')}
+        </ButtonPrimary>
+      }
+      secondaryButtonLabel={translate('cancel')}
+    />
   );
 }
 
index 6e83258278ed8f451a1dda5a9efd8f460e8d9e5f..572e5f96016126e24ed4eddb4ce2967f170ee8ad 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { Link } from 'design-system';
 import * as React from 'react';
 import { components, OptionProps } from 'react-select';
 import DisableableSelectOption from '../../../components/common/DisableableSelectOption';
-import Link from '../../../components/common/Link';
 import { LabelValueSelectOption } from '../../../components/controls/Select';
 import { translate } from '../../../helpers/l10n';
 import { getQualityProfileUrl } from '../../../helpers/urls';
index b79bc7c10fc36a477ec7517b5923d1a2390e6f3a..de4b10b22b20a93f1f1c01c839a96a02af016542 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import {
+  ButtonPrimary,
+  FlagMessage,
+  InputSelect,
+  LightLabel,
+  Modal,
+  RadioButton,
+} from 'design-system';
 import * as React from 'react';
 import { Profile } from '../../../api/quality-profiles';
-import Radio from '../../../components/controls/Radio';
-import Select from '../../../components/controls/Select';
-import SimpleModal from '../../../components/controls/SimpleModal';
-import { ButtonLink, SubmitButton } from '../../../components/controls/buttons';
-import { Alert } from '../../../components/ui/Alert';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { Component } from '../../../types/types';
 import BuiltInQualityProfileBadge from '../../quality-profiles/components/BuiltInQualityProfileBadge';
@@ -68,99 +71,79 @@ export default function SetQualityProfileModal(props: SetQualityProfileModalProp
     hasSelectedSysDefault ? p.key === defaultProfile.key : p.key === selected,
   );
 
-  return (
-    <SimpleModal
-      header={header}
-      onClose={props.onClose}
-      onSubmit={() =>
-        props.onSubmit(hasSelectedSysDefault ? undefined : selected, currentProfile.key)
-      }
-    >
-      {({ onCloseClick, onFormSubmit, submitting }) => (
-        <>
-          <div className="modal-head">
-            <h2>{header}</h2>
+  const handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
+    event.preventDefault();
+    props.onSubmit(hasSelectedSysDefault ? undefined : selected, currentProfile.key);
+  };
+
+  const renderForm = (
+    <form id="change-quality-profile" onSubmit={handleFormSubmit}>
+      <div>
+        <RadioButton
+          className="sw-mb-4"
+          checked={hasSelectedSysDefault}
+          onCheck={() => setSelected(USE_SYSTEM_DEFAULT)}
+          value={USE_SYSTEM_DEFAULT}
+        >
+          <div className="sw-ml-2">
+            <div>{translate('project_quality_profile.always_use_default')}</div>
+            <LightLabel>
+              <span>
+                {translate('current_noun')}: {defaultProfile?.name}
+              </span>
+              {defaultProfile?.isBuiltIn && <BuiltInQualityProfileBadge className="sw-ml-2" />}
+            </LightLabel>
           </div>
+        </RadioButton>
 
-          <form onSubmit={onFormSubmit}>
-            <div className="modal-body">
-              <div className="big-spacer-bottom">
-                <Radio
-                  className="display-flex-start"
-                  checked={hasSelectedSysDefault}
-                  disabled={submitting}
-                  onCheck={() => setSelected(USE_SYSTEM_DEFAULT)}
-                  value={USE_SYSTEM_DEFAULT}
-                >
-                  <div className="spacer-left">
-                    <div className="little-spacer-bottom">
-                      {translate('project_quality_profile.always_use_default')}
-                    </div>
-                    <div className="display-flex-center">
-                      <span className="text-muted spacer-right">{translate('current_noun')}:</span>
-                      {defaultProfile.name}
-                      {defaultProfile.isBuiltIn && (
-                        <BuiltInQualityProfileBadge className="spacer-left" />
-                      )}
-                    </div>
-                  </div>
-                </Radio>
-              </div>
+        <RadioButton
+          className="sw-mb-2"
+          checked={!hasSelectedSysDefault}
+          onCheck={(value) => {
+            if (hasSelectedSysDefault) {
+              setSelected(value);
+            }
+          }}
+          value={currentProfile.key}
+        >
+          <div className="sw-ml-2">{translate('project_quality_profile.always_use_specific')}</div>
+        </RadioButton>
 
-              <div className="big-spacer-bottom">
-                <Radio
-                  className="display-flex-start"
-                  checked={!hasSelectedSysDefault}
-                  disabled={submitting}
-                  onCheck={(value) => {
-                    if (hasSelectedSysDefault) {
-                      setSelected(value);
-                    }
-                  }}
-                  value={currentProfile.key}
-                >
-                  <div className="spacer-left">
-                    <div className="little-spacer-bottom">
-                      {translate('project_quality_profile.always_use_specific')}
-                    </div>
-                    <div className="display-flex-center">
-                      <Select
-                        className="abs-width-300"
-                        aria-label={translate('project_quality_profile.always_use_specific')}
-                        isDisabled={submitting || hasSelectedSysDefault}
-                        onChange={({ value }: ProfileOption) => setSelected(value)}
-                        options={profileOptions}
-                        components={{
-                          Option: LanguageProfileSelectOption,
-                        }}
-                        value={profileOptions.find(
-                          (option) =>
-                            option.value ===
-                            (!hasSelectedSysDefault ? selected : currentProfile.key),
-                        )}
-                      />
-                    </div>
-                  </div>
-                </Radio>
-              </div>
+        <InputSelect
+          className="sw-ml-8"
+          aria-label={translate('project_quality_profile.always_use_specific')}
+          isDisabled={hasSelectedSysDefault}
+          onChange={({ value }: ProfileOption) => setSelected(value)}
+          options={profileOptions}
+          components={{
+            Option: LanguageProfileSelectOption,
+          }}
+          value={profileOptions.find(
+            (option) => option.value === (!hasSelectedSysDefault ? selected : currentProfile.key),
+          )}
+        />
 
-              {needsReanalysis && (
-                <Alert variant="warning">
-                  {translate('project_quality_profile.requires_new_analysis')}
-                </Alert>
-              )}
-            </div>
+        {needsReanalysis && (
+          <FlagMessage className="sw-w-full sw-mt-4" variant="warning">
+            {translate('project_quality_profile.requires_new_analysis')}
+          </FlagMessage>
+        )}
+      </div>
+    </form>
+  );
 
-            <div className="modal-foot">
-              {submitting && <i className="spinner spacer-right" />}
-              <SubmitButton disabled={submitting || !hasChanged}>{translate('save')}</SubmitButton>
-              <ButtonLink disabled={submitting} onClick={onCloseClick}>
-                {translate('cancel')}
-              </ButtonLink>
-            </div>
-          </form>
-        </>
-      )}
-    </SimpleModal>
+  return (
+    <Modal
+      onClose={props.onClose}
+      headerTitle={header}
+      isOverflowVisible
+      body={renderForm}
+      primaryButton={
+        <ButtonPrimary disabled={!hasChanged} form="change-quality-profile" type="submit">
+          {translate('save')}
+        </ButtonPrimary>
+      }
+      secondaryButtonLabel={translate('cancel')}
+    />
   );
 }