diff options
author | Kevin Silva <kevin.silva@sonarsource.com> | 2023-11-18 19:12:46 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-11-20 20:02:38 +0000 |
commit | 175c2eaef5237a6371adc9eda13b23cae1886229 (patch) | |
tree | 21350f082e1b254fb7abcf03d0f18c08437c529d /server | |
parent | a9ef47fe7a52a378936ff4be9ffd3fb724f00ee9 (diff) | |
download | sonarqube-175c2eaef5237a6371adc9eda13b23cae1886229.tar.gz sonarqube-175c2eaef5237a6371adc9eda13b23cae1886229.zip |
SONAR-21019 - Project Settings - Quality Profile adopts MIUI
Diffstat (limited to 'server')
7 files changed, 277 insertions, 297 deletions
diff --git a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx index aa2277eb8dd..e1fd1f5f38c 100644 --- a/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/GlobalContainer.tsx @@ -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() { diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesApp.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesApp.tsx index b527f81c962..4f9247d5a90 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesApp.tsx +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesApp.tsx @@ -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'; diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesAppRenderer.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesAppRenderer.tsx index 3af9417a960..ce39f100233 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesAppRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/ProjectQualityProfilesAppRenderer.tsx @@ -17,17 +17,29 @@ * 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> ); } diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/projectQualityProfilesApp-it.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/projectQualityProfilesApp-it.tsx index 0ff50093a04..205b0220482 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/projectQualityProfilesApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/projectQualityProfilesApp-it.tsx @@ -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( diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/AddLanguageModal.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/AddLanguageModal.tsx index d09f755fc04..887e4a35bf6 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/AddLanguageModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/AddLanguageModal.tsx @@ -17,13 +17,12 @@ * 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')} + /> ); } diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/LanguageProfileSelectOption.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/LanguageProfileSelectOption.tsx index 6e83258278e..572e5f96016 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/LanguageProfileSelectOption.tsx +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/LanguageProfileSelectOption.tsx @@ -17,10 +17,10 @@ * 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'; diff --git a/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/SetQualityProfileModal.tsx b/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/SetQualityProfileModal.tsx index b79bc7c10fc..de4b10b22b2 100644 --- a/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/SetQualityProfileModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectQualityProfiles/components/SetQualityProfileModal.tsx @@ -17,13 +17,16 @@ * 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')} + /> ); } |