diff options
15 files changed, 529 insertions, 124 deletions
diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index bcca9babe86..8774f66a0ec 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -57,6 +57,11 @@ export interface AnalysisEvent { description?: string; key: string; name: string; + qualityGate?: { + failing: Array<{ branch: string; key: string; name: string }>; + status: string; + stillFailing: boolean; + }; } export interface AppState { diff --git a/server/sonar-web/src/main/js/apps/overview/events/Event.tsx b/server/sonar-web/src/main/js/apps/overview/events/Event.tsx index 9090864acca..bd1498eb292 100644 --- a/server/sonar-web/src/main/js/apps/overview/events/Event.tsx +++ b/server/sonar-web/src/main/js/apps/overview/events/Event.tsx @@ -18,8 +18,11 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { translate } from '../../../helpers/l10n'; +import { FormattedMessage } from 'react-intl'; import { AnalysisEvent } from '../../../app/types'; +import { isRichQualityGateEvent } from '../../projectActivity/components/RichQualityGateEventInner'; +import Level from '../../../components/ui/Level'; +import { translate } from '../../../helpers/l10n'; interface Props { event: AnalysisEvent; @@ -36,6 +39,23 @@ export default function Event({ event }: Props) { ); } + if (isRichQualityGateEvent(event)) { + return ( + <div className="overview-analysis-event"> + <span className="note">{translate('event.category', event.category)}:</span>{' '} + {event.qualityGate.stillFailing ? ( + <FormattedMessage + defaultMessage={translate('event.quality_gate.still_x')} + id="event.quality_gate.still_x" + values={{ status: <Level level={event.qualityGate.status} small={true} /> }} + /> + ) : ( + <Level level={event.qualityGate.status} small={true} /> + )} + </div> + ); + } + return ( <div className="overview-analysis-event"> <span className="note">{translate('event.category', event.category)}:</span>{' '} diff --git a/server/sonar-web/src/main/js/apps/overview/events/__tests__/Event-test.tsx b/server/sonar-web/src/main/js/apps/overview/events/__tests__/Event-test.tsx index e15be764817..05b9bcc6693 100644 --- a/server/sonar-web/src/main/js/apps/overview/events/__tests__/Event-test.tsx +++ b/server/sonar-web/src/main/js/apps/overview/events/__tests__/Event-test.tsx @@ -20,6 +20,7 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import Event from '../Event'; +import { RichQualityGateEvent } from '../../../projectActivity/components/RichQualityGateEventInner'; const EVENT = { key: '1', category: 'OTHER', name: 'test' }; const VERSION = { key: '2', category: 'VERSION', name: '6.5-SNAPSHOT' }; @@ -31,3 +32,17 @@ it('should render an event correctly', () => { it('should render a version correctly', () => { expect(shallow(<Event event={VERSION} />)).toMatchSnapshot(); }); + +it('should render rich quality gate event', () => { + const event: RichQualityGateEvent = { + category: 'QUALITY_GATE', + key: 'foo1234', + name: '', + qualityGate: { + failing: [{ branch: 'master', key: 'foo', name: 'Foo' }], + status: 'ERROR', + stillFailing: true + } + }; + expect(shallow(<Event event={event} />)).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Event-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Event-test.tsx.snap index 5af2a4e8b9b..eb026e1a3e7 100644 --- a/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Event-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Event-test.tsx.snap @@ -25,3 +25,29 @@ exports[`should render an event correctly 1`] = ` </strong> </div> `; + +exports[`should render rich quality gate event 1`] = ` +<div + className="overview-analysis-event" +> + <span + className="note" + > + event.category.QUALITY_GATE + : + </span> + + <FormattedMessage + defaultMessage="event.quality_gate.still_x" + id="event.quality_gate.still_x" + values={ + Object { + "status": <Level + level="ERROR" + small={true} + />, + } + } + /> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx index 53455dd8439..80fe882f3d9 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx @@ -86,7 +86,7 @@ export class Meta extends React.PureComponent<Props> { } return ( - <div className="overview-meta-card"> + <div className="overview-meta-card" id="overview-meta-quality-gate"> {qualityGate && ( <MetaQualityGate organization={organizationsEnabled ? component.organization : undefined} diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.tsx index fa50f913600..4d66330e64e 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.tsx @@ -18,6 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import * as classNames from 'classnames'; +import { isRichQualityGateEvent, RichQualityGateEventInner } from './RichQualityGateEventInner'; import { AnalysisEvent } from '../../../app/types'; import ProjectEventIcon from '../../../components/icons-components/ProjectEventIcon'; import { translate } from '../../../helpers/l10n'; @@ -27,17 +29,28 @@ interface Props { } export default function EventInner({ event }: Props) { - return ( - <div className="project-activity-event-inner"> - <div className="project-activity-event-inner-icon little-spacer-right"> - <ProjectEventIcon - className={'project-activity-event-icon margin-align ' + event.category} - /> + if (isRichQualityGateEvent(event)) { + return <RichQualityGateEventInner event={event} />; + } else { + return ( + <div className="project-activity-event-inner"> + <div className="project-activity-event-inner-main"> + <ProjectEventIcon + className={classNames( + 'project-activity-event-icon', + 'little-spacer-right', + event.category + )} + /> + + <span className="project-activity-event-inner-text"> + <span className="note little-spacer-right"> + {translate('event.category', event.category)}: + </span> + <strong title={event.description}>{event.name}</strong> + </span> + </div> </div> - <span className="project-activity-event-inner-text"> - <span className="note">{translate('event.category', event.category)}:</span>{' '} - <strong title={event.description}>{event.name}</strong> - </span> - </div> - ); + ); + } } diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx index e98701a25cb..7db07883772 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.tsx @@ -108,7 +108,6 @@ export default class ProjectActivityAnalysis extends React.PureComponent<Props, render() { const { analysis, isFirst, canAdmin } = this.props; const { date, events } = analysis; - const analysisTitle = translate('project_activity.analysis'); const hasVersion = events.find(event => event.category === 'VERSION') != null; const canAddVersion = canAdmin && !hasVersion && this.props.canCreateVersion; @@ -117,16 +116,13 @@ export default class ProjectActivityAnalysis extends React.PureComponent<Props, return ( <li - className={classNames('project-activity-analysis clearfix', { - selected: this.props.selected - })} + className={classNames('project-activity-analysis', { selected: this.props.selected })} data-date={date.valueOf()} onClick={this.handleClick} tabIndex={0}> <div className="project-activity-time spacer-right"> <TimeTooltipFormatter className="text-middle" date={date} /> </div> - <div className="project-activity-analysis-icon spacer-right" title={analysisTitle} /> {(canAddVersion || canAddEvent || canDeleteAnalyses) && ( <div className="project-activity-analysis-actions big-spacer-right"> diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/RichQualityGateEventInner.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/RichQualityGateEventInner.tsx new file mode 100644 index 00000000000..780668cf39d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/RichQualityGateEventInner.tsx @@ -0,0 +1,117 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { Link } from 'react-router'; +import * as classNames from 'classnames'; +import { AnalysisEvent } from '../../../app/types'; +import DropdownIcon from '../../../components/icons-components/DropdownIcon'; +import ProjectEventIcon from '../../../components/icons-components/ProjectEventIcon'; +import { ResetButtonLink } from '../../../components/ui/buttons'; +import Level from '../../../components/ui/Level'; +import { translate } from '../../../helpers/l10n'; +import { getProjectUrl } from '../../../helpers/urls'; + +export type RichQualityGateEvent = Exclude<AnalysisEvent, 'qualityGate'> & + Required<Pick<AnalysisEvent, 'qualityGate'>>; + +export function isRichQualityGateEvent(event: AnalysisEvent): event is RichQualityGateEvent { + return event.category === 'QUALITY_GATE' && event.qualityGate !== undefined; +} + +interface Props { + event: RichQualityGateEvent; +} + +interface State { + expanded: boolean; +} + +export class RichQualityGateEventInner extends React.PureComponent<Props, State> { + state: State = { expanded: false }; + + toggleProjectsList = () => { + this.setState(state => ({ expanded: !state.expanded })); + }; + + render() { + const { event } = this.props; + const { expanded } = this.state; + return ( + <div className="project-activity-event-inner"> + <div className="project-activity-event-inner-main"> + <ProjectEventIcon + className={classNames( + 'project-activity-event-icon', + 'little-spacer-right', + event.category + )} + /> + + <div className="project-activity-event-inner-text flex-1"> + <span className="note little-spacer-right"> + {translate('event.category', event.category)}: + </span> + {event.qualityGate.stillFailing ? ( + <FormattedMessage + defaultMessage={translate('event.quality_gate.still_x')} + id="event.quality_gate.still_x" + values={{ status: <Level level={event.qualityGate.status} small={true} /> }} + /> + ) : ( + <Level level={event.qualityGate.status} small={true} /> + )} + </div> + + {event.qualityGate.failing.length > 0 && ( + <ResetButtonLink + className="project-activity-event-inner-more-link" + onClick={this.toggleProjectsList} + stopPropagation={true}> + {expanded ? translate('hide') : translate('more')} + <DropdownIcon className="little-spacer-left" turned={expanded} /> + </ResetButtonLink> + )} + </div> + + {expanded && ( + <ul> + {event.qualityGate.failing.map(project => ( + <li className="display-flex-center little-spacer-top" key={project.key}> + <Level + className="little-spacer-right" + level={event.qualityGate.status} + small={true} + /> + <div className="flex-1 text-ellipsis"> + <Link + onClick={e => e.stopPropagation()} + to={getProjectUrl(project.key, project.branch)}> + {project.name} + </Link> + </div> + </li> + ))} + </ul> + )} + </div> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/RichQualityGateEventInner-test.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/RichQualityGateEventInner-test.tsx new file mode 100644 index 00000000000..1bccc563006 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/RichQualityGateEventInner-test.tsx @@ -0,0 +1,55 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 * as React from 'react'; +import { shallow } from 'enzyme'; +import { RichQualityGateEventInner, RichQualityGateEvent } from '../RichQualityGateEventInner'; +import { click } from '../../../../helpers/testUtils'; + +const event: RichQualityGateEvent = { + category: 'QUALITY_GATE', + key: 'foo1234', + name: '', + qualityGate: { + failing: [ + { branch: 'master', key: 'foo', name: 'Foo' }, + { branch: 'master', key: 'bar', name: 'Bar' } + ], + status: 'ERROR', + stillFailing: true + } +}; + +it('should render', () => { + const wrapper = shallow(<RichQualityGateEventInner event={event} />); + expect(wrapper).toMatchSnapshot(); + + click(wrapper.find('.project-activity-event-inner-more-link')); + wrapper.update(); + expect(wrapper).toMatchSnapshot(); +}); + +it('should not expand', () => { + const wrapper = shallow( + <RichQualityGateEventInner + event={{ ...event, qualityGate: { ...event.qualityGate, failing: [] } }} + /> + ); + expect(wrapper.find('.project-activity-event-inner-more-link').exists()).toBe(false); +}); diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/RichQualityGateEventInner-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/RichQualityGateEventInner-test.tsx.snap new file mode 100644 index 00000000000..1127c310212 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/RichQualityGateEventInner-test.tsx.snap @@ -0,0 +1,157 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render 1`] = ` +<div + className="project-activity-event-inner" +> + <div + className="project-activity-event-inner-main" + > + <ProjectEventIcon + className="project-activity-event-icon little-spacer-right QUALITY_GATE" + /> + <div + className="project-activity-event-inner-text flex-1" + > + <span + className="note little-spacer-right" + > + event.category.QUALITY_GATE + : + </span> + <FormattedMessage + defaultMessage="event.quality_gate.still_x" + id="event.quality_gate.still_x" + values={ + Object { + "status": <Level + level="ERROR" + small={true} + />, + } + } + /> + </div> + <ResetButtonLink + className="project-activity-event-inner-more-link" + onClick={[Function]} + stopPropagation={true} + > + more + <DropdownIcon + className="little-spacer-left" + turned={false} + /> + </ResetButtonLink> + </div> +</div> +`; + +exports[`should render 2`] = ` +<div + className="project-activity-event-inner" +> + <div + className="project-activity-event-inner-main" + > + <ProjectEventIcon + className="project-activity-event-icon little-spacer-right QUALITY_GATE" + /> + <div + className="project-activity-event-inner-text flex-1" + > + <span + className="note little-spacer-right" + > + event.category.QUALITY_GATE + : + </span> + <FormattedMessage + defaultMessage="event.quality_gate.still_x" + id="event.quality_gate.still_x" + values={ + Object { + "status": <Level + level="ERROR" + small={true} + />, + } + } + /> + </div> + <ResetButtonLink + className="project-activity-event-inner-more-link" + onClick={[Function]} + stopPropagation={true} + > + hide + <DropdownIcon + className="little-spacer-left" + turned={true} + /> + </ResetButtonLink> + </div> + <ul> + <li + className="display-flex-center little-spacer-top" + key="foo" + > + <Level + className="little-spacer-right" + level="ERROR" + small={true} + /> + <div + className="flex-1 text-ellipsis" + > + <Link + onClick={[Function]} + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/dashboard", + "query": Object { + "branch": "master", + "id": "foo", + }, + } + } + > + Foo + </Link> + </div> + </li> + <li + className="display-flex-center little-spacer-top" + key="bar" + > + <Level + className="little-spacer-right" + level="ERROR" + small={true} + /> + <div + className="flex-1 text-ellipsis" + > + <Link + onClick={[Function]} + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/dashboard", + "query": Object { + "branch": "master", + "id": "bar", + }, + } + } + > + Bar + </Link> + </div> + </li> + </ul> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css b/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css index e8953145857..ca57363cb5b 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/projectActivity.css @@ -61,78 +61,6 @@ flex-shrink: 0; } -.project-activity-graphs { - flex-grow: 1; - display: flex; - flex-direction: column; - align-items: stretch; - justify-content: center; -} - -.project-activity-graph-container { - padding: 10px 0; - flex-grow: 1; - display: flex; - flex-direction: column; - align-items: stretch; - justify-content: center; -} - -.project-activity-graph { - flex: 1; - overflow: hidden; -} - -.project-activity-graph-legends { - flex-grow: 0; - padding-bottom: 16px; - text-align: center; -} - -.project-activity-graph-legend-actionable { - display: inline-block; - padding: 4px 8px 4px 12px; - border-width: 1px; - border-style: solid; - border-radius: 12px; -} - -.project-activity-graph-tooltip { - padding: 8px; -} - -.project-activity-graph-tooltip-line { - height: 20px; -} - -.project-activity-graph-tooltip-line + .project-activity-graph-tooltip-line { - padding-top: 4px; -} - -.project-activity-graph-tooltip-line .project-activity-event-icon { - margin-top: 1px; -} - -.project-activity-graph-tooltip-issues-line { - height: 26px; - padding-bottom: 4px; -} - -.project-activity-graph-tooltip-separator { - padding-left: 16px; - padding-right: 16px; -} - -.project-activity-graph-tooltip-separator hr { - margin-top: 8px; - margin-bottom: 8px; -} - -.project-activity-graph-tooltip-title, -.project-activity-graph-tooltip-value { - font-weight: bold; -} - .project-activity-days-list { } @@ -156,8 +84,9 @@ .project-activity-analysis { position: relative; - min-height: 20px; - padding: 4px; + display: flex; + min-height: var(--smallControlHeight); + padding: calc(0.5 * var(--gridSize)); border-top: 1px solid var(--barBorderColor); border-bottom: 1px solid var(--barBorderColor); cursor: pointer; @@ -180,36 +109,30 @@ } .project-activity-analysis-actions { - float: left; + flex-shrink: 0; + flex-grow: 0; + height: var(--smallControlHeight); } .project-activity-time { - float: left; + flex-shrink: 0; + flex-grow: 0; width: 54px; - line-height: 20px; + height: var(--smallControlHeight); + line-height: var(--smallControlHeight); box-sizing: border-box; font-size: var(--smallFontSize); font-weight: bold; text-align: right; } -.project-activity-analysis-icon { - float: left; - width: 10px; - height: 10px; - margin-top: 5px; - border: 2px solid var(--blue); - border-radius: 10px; - box-sizing: border-box; - content: ''; -} - .project-activity-events { - overflow: hidden; + flex: 1; + min-width: 0; } .project-activity-event { - line-height: 18px; + line-height: var(--smallControlHeight); display: flex; } @@ -219,28 +142,32 @@ .project-activity-event-inner { flex: 1; + min-width: 0; +} + +.project-activity-event-inner-main { display: flex; - flex-direction: row; - overflow: hidden; + align-items: flex-start; } -.project-activity-event-inner-icon { +.project-activity-event-icon { flex-shrink: 0; + flex-grow: 0; + margin-top: calc(0.5 * var(--smallControlHeight) - 7px); } .project-activity-event-inner-text { - flex: 1; - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; + line-height: var(--smallControlHeight); } -.project-activity-event-actions { - display: inline-block; +.project-activity-event-inner-more-link { + line-height: 16px; + margin-top: 2px; } -.project-activity-event-inner-icon .project-activity-event-icon { - margin-top: 3px; +.project-activity-event-actions { + flex-shrink: 0; + flex-grow: 0; } .project-activity-event-icon.VERSION { @@ -289,3 +216,76 @@ overflow: hidden; text-overflow: ellipsis; } + +.project-activity-graphs { + flex-grow: 1; + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: center; +} + +.project-activity-graph-container { + padding: 10px 0; + flex-grow: 1; + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: center; +} + +.project-activity-graph { + flex: 1; + overflow: hidden; +} + +.project-activity-graph-legends { + flex-grow: 0; + padding-bottom: 16px; + text-align: center; +} + +.project-activity-graph-legend-actionable { + display: inline-block; + padding: 4px 8px 4px 12px; + border-width: 1px; + border-style: solid; + border-radius: 12px; +} + +.project-activity-graph-tooltip { + padding: 8px; +} + +.project-activity-graph-tooltip-line { + height: 20px; +} + +.project-activity-graph-tooltip-line + .project-activity-graph-tooltip-line { + padding-top: 4px; +} + +.Select .project-activity-event-icon, +.project-activity-graph-tooltip-line .project-activity-event-icon { + margin-top: 1px; +} + +.project-activity-graph-tooltip-issues-line { + height: 26px; + padding-bottom: 4px; +} + +.project-activity-graph-tooltip-separator { + padding-left: 16px; + padding-right: 16px; +} + +.project-activity-graph-tooltip-separator hr { + margin-top: 8px; + margin-bottom: 8px; +} + +.project-activity-graph-tooltip-title, +.project-activity-graph-tooltip-value { + font-weight: bold; +} diff --git a/server/sonar-web/src/main/js/components/ui/Level.css b/server/sonar-web/src/main/js/components/ui/Level.css index d402b5a4d3d..45bc0a4783b 100644 --- a/server/sonar-web/src/main/js/components/ui/Level.css +++ b/server/sonar-web/src/main/js/components/ui/Level.css @@ -42,8 +42,8 @@ padding-right: 9px; margin-top: -1px; margin-bottom: -1px; - height: 18px; - line-height: 18px; + height: var(--smallControlHeight); + line-height: var(--smallControlHeight); font-size: var(--smallFontSize); } diff --git a/server/sonar-web/src/main/js/components/ui/Level.tsx b/server/sonar-web/src/main/js/components/ui/Level.tsx index 684ea75b97a..6f74c198fa1 100644 --- a/server/sonar-web/src/main/js/components/ui/Level.tsx +++ b/server/sonar-web/src/main/js/components/ui/Level.tsx @@ -30,7 +30,7 @@ interface Props { } export default function Level(props: Props) { - const formatted = formatMeasure(props.level, 'LEVEL', null); + const formatted = formatMeasure(props.level, 'LEVEL'); const className = classNames(props.className, 'level', 'level-' + props.level, { 'level-small': props.small, 'level-muted': props.muted diff --git a/server/sonar-web/src/main/js/components/ui/buttons.css b/server/sonar-web/src/main/js/components/ui/buttons.css index c5e8be870f9..ed7a6499ecb 100644 --- a/server/sonar-web/src/main/js/components/ui/buttons.css +++ b/server/sonar-web/src/main/js/components/ui/buttons.css @@ -119,6 +119,7 @@ .button-link { display: inline; height: auto; /* Keep this to not inherit the height from .button */ + line-height: 1; margin: 0; padding: 0; border: none; @@ -128,7 +129,6 @@ border-bottom: 1px solid var(--lightBlue); font-weight: 400; font-size: inherit; - line-height: inherit; transition: all 0.2s ease; } 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 93e061c69ac..eb795a57ec0 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -437,6 +437,7 @@ event.category.VERSION=Version event.category.QUALITY_GATE=Quality Gate event.category.QUALITY_PROFILE=Quality Profile event.category.OTHER=Other +event.quality_gate.still_x=Still {status} #------------------------------------------------------------------------------ |