From 5a5718ef3e3867d06091ca8eca59fde9f1d673ec Mon Sep 17 00:00:00 2001 From: Kevin Silva Date: Fri, 20 Oct 2023 13:29:42 +0200 Subject: [PATCH] SONAR-20814 - Update layout when SPECIFIC_ANALYSIS is selected --- .../components/BranchAnalysisList.tsx | 160 -------------- .../components/BranchAnalysisListRenderer.tsx | 207 ------------------ .../BranchNewCodeDefinitionSettingModal.tsx | 16 +- .../NewCodeDefinitionSettingAnalysis.tsx | 63 +++++- ...ewCodeDefinitionSettingReferenceBranch.tsx | 51 +++-- .../ProjectNewCodeDefinitionSelector.tsx | 148 ++++++------- .../ProjectNewCodeDefinitionApp-it.tsx | 18 +- .../main/js/apps/projectNewCode/styles.css | 93 -------- .../NewCodeDefinitionAnalysisWarning.tsx | 36 +-- .../resources/org/sonar/l10n/core.properties | 5 - 10 files changed, 182 insertions(+), 615 deletions(-) delete mode 100644 server/sonar-web/src/main/js/apps/projectNewCode/components/BranchAnalysisList.tsx delete mode 100644 server/sonar-web/src/main/js/apps/projectNewCode/components/BranchAnalysisListRenderer.tsx diff --git a/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchAnalysisList.tsx b/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchAnalysisList.tsx deleted file mode 100644 index 03f39eae6e9..00000000000 --- a/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchAnalysisList.tsx +++ /dev/null @@ -1,160 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { subDays } from 'date-fns'; -import { throttle } from 'lodash'; -import * as React from 'react'; -import { getProjectActivity } from '../../../api/projectActivity'; -import { parseDate, toShortISO8601String } from '../../../helpers/dates'; -import { Analysis, ParsedAnalysis } from '../../../types/project-activity'; -import { Dict } from '../../../types/types'; -import BranchAnalysisListRenderer from './BranchAnalysisListRenderer'; - -interface Props { - analysis: string; - branch: string; - component: string; - onSelectAnalysis: (analysis: ParsedAnalysis) => void; -} - -interface State { - analyses: ParsedAnalysis[]; - loading: boolean; - range: number; - scroll: number; -} - -const STICKY_BADGE_SCROLL_OFFSET = 10; - -export default class BranchAnalysisList extends React.PureComponent { - mounted = false; - badges: Dict = {}; - state: State = { - analyses: [], - loading: true, - range: 30, - scroll: 0, - }; - - constructor(props: Props) { - super(props); - this.updateScroll = throttle(this.updateScroll, 20); - } - - componentDidMount() { - this.mounted = true; - this.fetchAnalyses(true); - } - - componentWillUnmount() { - this.mounted = false; - } - - scrollToSelected() { - document.querySelector('.branch-analysis.selected')?.scrollIntoView({ - block: 'center', - behavior: 'smooth', - }); - } - - fetchAnalyses(initial = false) { - const { analysis, branch, component } = this.props; - const { range } = this.state; - this.setState({ loading: true }); - - return getProjectActivity({ - branch, - project: component, - from: range ? toShortISO8601String(subDays(new Date(), range)) : undefined, - }).then((result: { analyses: Analysis[] }) => { - // If the selected analysis wasn't found in the default 30 days range, redo the search - if (initial && analysis && !result.analyses.find((a) => a.key === analysis)) { - this.handleRangeChange({ value: 0 }); - return; - } - - this.setState( - { - analyses: result.analyses.map((analysis) => ({ - ...analysis, - date: parseDate(analysis.date), - })) as ParsedAnalysis[], - loading: false, - }, - () => { - this.scrollToSelected(); - }, - ); - }); - } - - handleScroll = (e: React.SyntheticEvent) => { - if (e.currentTarget) { - this.updateScroll(e.currentTarget.scrollTop); - } - }; - - updateScroll = (scroll: number) => { - this.setState({ scroll }); - }; - - registerBadgeNode = (version: string) => (el: HTMLDivElement) => { - if (el) { - if (!el.getAttribute('originOffsetTop')) { - el.setAttribute('originOffsetTop', String(el.offsetTop)); - } - this.badges[version] = el; - } - }; - - shouldStick = (version: string) => { - const badge = this.badges[version]; - return ( - !!badge && - Number(badge.getAttribute('originOffsetTop')) < this.state.scroll + STICKY_BADGE_SCROLL_OFFSET - ); - }; - - handleRangeChange = ({ value }: { value: number }) => { - this.setState({ range: value }, () => { - this.fetchAnalyses().catch(() => { - /* noop */ - }); - }); - }; - - render() { - const { analysis, onSelectAnalysis } = this.props; - const { analyses, loading, range } = this.state; - - return ( - - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchAnalysisListRenderer.tsx b/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchAnalysisListRenderer.tsx deleted file mode 100644 index d4d0c66530d..00000000000 --- a/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchAnalysisListRenderer.tsx +++ /dev/null @@ -1,207 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import classNames from 'classnames'; -import * as React from 'react'; -import { injectIntl, IntlShape, WrappedComponentProps } from 'react-intl'; -import Radio from '../../../components/controls/Radio'; -import Select from '../../../components/controls/Select'; -import Tooltip from '../../../components/controls/Tooltip'; -import DateFormatter, { longFormatterOption } from '../../../components/intl/DateFormatter'; -import TimeFormatter from '../../../components/intl/TimeFormatter'; -import Spinner from '../../../components/ui/Spinner'; -import { parseDate, toShortISO8601String } from '../../../helpers/dates'; -import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { ParsedAnalysis } from '../../../types/project-activity'; -import Events from '../../projectActivity/components/Events'; -import { getAnalysesByVersionByDay } from '../../projectActivity/utils'; - -export interface BranchAnalysisListRendererProps { - analyses: ParsedAnalysis[]; - handleRangeChange: ({ value }: { value: number }) => void; - handleScroll: (e: React.SyntheticEvent) => void; - loading: boolean; - onSelectAnalysis: (analysis: ParsedAnalysis) => void; - range: number; - registerBadgeNode: (version: string) => (el: HTMLDivElement) => void; - selectedAnalysisKey: string; - shouldStick: (version: string) => boolean; -} - -function renderAnalysis(args: { - analysis: ParsedAnalysis; - isFirst: boolean; - onSelectAnalysis: (analysis: ParsedAnalysis) => void; - selectedAnalysisKey: string; - intl: IntlShape; -}) { - const { analysis, isFirst, selectedAnalysisKey, intl } = args; - return ( -
  • -
    - - {(formattedTime) => ( - - )} - -
    - - {analysis.events.length > 0 && ( - - )} - -
    - {}} - value="" - disabled - /> -
    -
  • - ); -} - -function BranchAnalysisListRenderer( - props: BranchAnalysisListRendererProps & WrappedComponentProps, -) { - const { analyses, loading, range, selectedAnalysisKey, intl } = props; - - const byVersionByDay = React.useMemo( - () => - getAnalysesByVersionByDay(analyses, { - category: '', - }), - [analyses], - ); - - const hasFilteredData = - byVersionByDay.length > 1 || - (byVersionByDay.length === 1 && Object.keys(byVersionByDay[0].byDay).length > 0); - - const options = [ - { - label: translate('baseline.branch_analyses.ranges.30days'), - value: 30, - }, - { - label: translate('baseline.branch_analyses.ranges.allTime'), - value: 0, - }, - ]; - - return ( - <> -
    - - props.onChangeReferenceBranch(option.value)} - value={currentBranch} - components={{ - Option: renderBranchOption, - }} - /> +
    + + + + props.onChangeReferenceBranch(option.value)} + value={currentBranch} + components={{ + Option: renderBranchOption, + }} + /> +
    )} diff --git a/server/sonar-web/src/main/js/apps/projectNewCode/components/ProjectNewCodeDefinitionSelector.tsx b/server/sonar-web/src/main/js/apps/projectNewCode/components/ProjectNewCodeDefinitionSelector.tsx index a9cbced11c4..8ef34130d1b 100644 --- a/server/sonar-web/src/main/js/apps/projectNewCode/components/ProjectNewCodeDefinitionSelector.tsx +++ b/server/sonar-web/src/main/js/apps/projectNewCode/components/ProjectNewCodeDefinitionSelector.tsx @@ -17,23 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import classNames from 'classnames'; -import { RadioButton } from 'design-system'; +import { ButtonPrimary, ButtonSecondary, FlagMessage, RadioButton, Spinner } from 'design-system'; import { noop } from 'lodash'; import * as React from 'react'; -import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import GlobalNewCodeDefinitionDescription from '../../../components/new-code-definition/GlobalNewCodeDefinitionDescription'; -import NewCodeDefinitionAnalysisWarning from '../../../components/new-code-definition/NewCodeDefinitionAnalysisWarning'; import NewCodeDefinitionDaysOption from '../../../components/new-code-definition/NewCodeDefinitionDaysOption'; import NewCodeDefinitionPreviousVersionOption from '../../../components/new-code-definition/NewCodeDefinitionPreviousVersionOption'; import { NewCodeDefinitionLevels } from '../../../components/new-code-definition/utils'; -import { Alert } from '../../../components/ui/Alert'; -import Spinner from '../../../components/ui/Spinner'; import { translate } from '../../../helpers/l10n'; import { Branch } from '../../../types/branch-like'; import { NewCodeDefinition, NewCodeDefinitionType } from '../../../types/new-code-definition'; import { validateSetting } from '../utils'; -import BranchAnalysisList from './BranchAnalysisList'; import NewCodeDefinitionSettingAnalysis from './NewCodeDefinitionSettingAnalysis'; import NewCodeDefinitionSettingReferenceBranch from './NewCodeDefinitionSettingReferenceBranch'; @@ -100,11 +94,11 @@ export default function ProjectNewCodeDefinitionSelector( } return ( -
    -
    + +
    props.onToggleSpecificSetting(false)} value="general" > @@ -117,7 +111,7 @@ export default function ProjectNewCodeDefinitionSelector( props.onToggleSpecificSetting(true)} value="specific" > @@ -125,87 +119,81 @@ export default function ProjectNewCodeDefinitionSelector(
    -
    - {newCodeDefinitionType === NewCodeDefinitionType.SpecificAnalysis && ( - - )} -
    - + + + {branchesEnabled && ( + - - {branchesEnabled && ( - - )} - {!branchesEnabled && newCodeDefinitionType === NewCodeDefinitionType.SpecificAnalysis && ( - - )} -
    - {!branchesEnabled && - overrideGlobalNewCodeDefinition && - selectedNewCodeDefinitionType === NewCodeDefinitionType.SpecificAnalysis && ( - - )} + )}
    -
    - - {translate('baseline.next_analysis_notice')} - - - {!saving && ( - <> - {translate('save')} - - {translate('cancel')} - - +
    + {isChanged && ( + + {translate('baseline.next_analysis_notice')} + )} +
    + + {translate('save')} + + + {translate('cancel')} + + +
    ); diff --git a/server/sonar-web/src/main/js/apps/projectNewCode/components/__tests__/ProjectNewCodeDefinitionApp-it.tsx b/server/sonar-web/src/main/js/apps/projectNewCode/components/__tests__/ProjectNewCodeDefinitionApp-it.tsx index 7025d1bb8d5..1a633c223e2 100644 --- a/server/sonar-web/src/main/js/apps/projectNewCode/components/__tests__/ProjectNewCodeDefinitionApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/projectNewCode/components/__tests__/ProjectNewCodeDefinitionApp-it.tsx @@ -28,6 +28,7 @@ import NewCodeDefinitionServiceMock from '../../../../api/mocks/NewCodeDefinitio import { ProjectActivityServiceMock } from '../../../../api/mocks/ProjectActivityServiceMock'; import { mockComponent } from '../../../../helpers/mocks/component'; import { mockNewCodePeriodBranch } from '../../../../helpers/mocks/new-code-definition'; +import { mockAnalysis } from '../../../../helpers/mocks/project-activity'; import { mockAppState } from '../../../../helpers/testMocks'; import { RenderContext, @@ -157,16 +158,21 @@ it('cannot set specific analysis setting', async () => { value: 'analysis_id', }), ]); + projectActivityMock.setAnalysesList([ + mockAnalysis({ + key: `analysis_id`, + date: '2018-01-11T00:00:00+0200', + }), + ]); renderProjectNewCodeDefinitionApp(); await ui.appIsLoaded(); expect(await ui.specificAnalysisRadio.find()).toBeChecked(); + expect(ui.baselineSpecificAnalysisDate.get()).toBeInTheDocument(); + expect(ui.specificAnalysisRadio.get()).toHaveClass('disabled'); expect(ui.specificAnalysisWarning.get()).toBeInTheDocument(); - await selectEvent.select(ui.analysisFromSelect.get(), 'baseline.branch_analyses.ranges.allTime'); - - expect(first(ui.analysisListItem.getAll())).toHaveClass('disabled'); expect(ui.saveButton.get()).toBeDisabled(); }); @@ -245,9 +251,6 @@ it('cannot set a specific analysis setting for branch', async () => { expect(ui.specificAnalysisRadio.get()).toHaveClass('disabled'); expect(ui.specificAnalysisWarning.get()).toBeInTheDocument(); - await selectEvent.select(ui.analysisFromSelect.get(), 'baseline.branch_analyses.ranges.allTime'); - - expect(first(ui.analysisListItem.getAll())).toHaveClass('disabled'); expect(last(ui.saveButton.getAll())).toBeDisabled(); }); @@ -402,8 +405,6 @@ function getPageObjects() { chooseBranchSelect: byRole('combobox', { name: 'baseline.reference_branch.choose' }), specificAnalysisRadio: byRole('radio', { name: /baseline.specific_analysis.description/ }), specificAnalysisWarning: byText('baseline.specific_analysis.compliance_warning.title'), - analysisFromSelect: byRole('combobox', { name: 'baseline.analysis_from' }), - analysisListItem: byRole('radio', { name: /baseline.branch_analyses.analysis_for_x/ }), saveButton: byRole('button', { name: 'save' }), cancelButton: byRole('button', { name: 'cancel' }), branchActionsButton: () => byRole('button', { name: `menu` }), @@ -411,6 +412,7 @@ function getPageObjects() { resetToDefaultButton: byRole('menuitem', { name: 'reset_to_default' }), branchNCDsBanner: byText(/new_code_definition.auto_update.branch.message/), dismissButton: byLabelText('dismiss'), + baselineSpecificAnalysisDate: byText(/January 10, 2018/), }; async function appIsLoaded() { diff --git a/server/sonar-web/src/main/js/apps/projectNewCode/styles.css b/server/sonar-web/src/main/js/apps/projectNewCode/styles.css index 5e09ae45cbc..665f3fdf466 100644 --- a/server/sonar-web/src/main/js/apps/projectNewCode/styles.css +++ b/server/sonar-web/src/main/js/apps/projectNewCode/styles.css @@ -21,13 +21,6 @@ padding: calc(4 * var(--gridSize)); } -.project-baseline-setting { - display: flex; - flex-direction: column; - max-height: 60vh; - padding-top: 2px; -} - .branch-baseline-selector > hr { margin: 0 calc(-4 * var(--gridSize)) calc(4 * var(--gridSize)); } @@ -38,92 +31,6 @@ flex-direction: column; } -.branch-analysis-list-wrapper { - display: flex; - flex-direction: column; - overflow: hidden; - position: relative; - min-height: 200px; -} - -.branch-analysis-list { - overflow-y: auto; - padding-left: 12px; - padding-right: 15px; - min-height: 50px; -} - -.branch-analysis-list > ul { - padding-top: 18px; -} - -.branch-analysis-date { - margin-bottom: 16px; - font-size: 15px; - font-weight: bold; -} - -.branch-analysis-day { - margin-top: var(--gridSize); - margin-bottom: calc(3 * var(--gridSize)); -} - -.branch-analysis { - display: flex; - justify-content: space-between; - padding: var(--gridSize); - border-top: 1px solid var(--barBorderColor); - border-bottom: 1px solid var(--barBorderColor); - cursor: not-allowed; -} - -.branch-analysis + .branch-analysis { - border-top: none; -} - -.branch-analysis:hover { - background-color: var(--disableGrayBg); -} - -.branch-analysis > .project-activity-events { - flex: 1 0 50%; -} - -.branch-analysis-time { - width: 150px; -} - -.branch-analysis-version-badge { - margin-left: -12px; - padding-top: var(--gridSize); - padding-bottom: var(--gridSize); - background-color: white; -} - -.branch-analysis-version-badge.sticky + .branch-analysis-days-list { - padding-top: 36px; -} - -.branch-analysis-version-badge.sticky, -.branch-analysis-version-badge.first { - position: absolute; - top: 1px; - left: 13px; - right: 16px; - padding-top: calc(3 * var(--gridSize)); - z-index: var(--belowNormalZIndex); -} - -.branch-analysis-version-badge .badge { - max-width: 385px; - border-radius: 0 2px 2px 0; - font-weight: bold; - font-size: var(--smallFontSize); - letter-spacing: 0; - overflow: hidden; - text-overflow: ellipsis; -} - .branch-setting-warning { background-color: var(--alertBackgroundWarning) !important; } diff --git a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionAnalysisWarning.tsx b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionAnalysisWarning.tsx index 44b830290dd..375121bdf9c 100644 --- a/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionAnalysisWarning.tsx +++ b/server/sonar-web/src/main/js/components/new-code-definition/NewCodeDefinitionAnalysisWarning.tsx @@ -17,27 +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 { FlagMessage, Link } from 'design-system'; import * as React from 'react'; +import { useDocUrl } from '../../helpers/docs'; import { translate } from '../../helpers/l10n'; -import DocLink from '../common/DocLink'; -import { Alert } from '../ui/Alert'; export default function NewCodeDefinitionAnalysisWarning() { + const toStatic = useDocUrl('/project-administration/defining-new-code/'); return ( - -

    - {translate('baseline.specific_analysis.compliance_warning.title')} -

    -

    - {translate('baseline.specific_analysis.compliance_warning.explanation')} -

    -

    - {translate('learn_more')}:  - - {translate('baseline.specific_analysis.compliance_warning.link')} - -

    -
    + +
    +

    + {translate('baseline.specific_analysis.compliance_warning.title')} +

    +

    + {translate('baseline.specific_analysis.compliance_warning.explanation')} +

    +

    + {translate('learn_more')}: + + {translate('learn_more')} + +

    +
    +
    ); } diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index e57f0450734..f5e0132f773 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -695,11 +695,6 @@ branch_list.default_setting=Project setting baseline.new_code_period_for_branch_x=New Code for {0} baseline.new_code_period_for_branch_x.question=Choose the baseline for new code for this branch -baseline.analysis_from=Analysis from: -baseline.branch_analyses.ranges.30days=Last 30 days -baseline.branch_analyses.ranges.allTime=All time -baseline.branch_analyses.analysis_for_x=Analysis for {0} -baseline.no_analyses=No analyses regulatory_report.page=Regulatory Report regulatory_report.description1=The regulatory report is a zip file containing a snapshot of the selected branch. It contains: -- 2.39.5