diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2018-08-30 12:19:33 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2018-10-10 09:23:03 +0200 |
commit | 58ef20e45102167223529f7df26f4434e8039045 (patch) | |
tree | c512ee2b76b847f8af543a1c0a3a9146b1c7a676 /server/sonar-web/src/main/js | |
parent | 6a08bacfe5b6d692e83e493246efad11f22ff70e (diff) | |
download | sonarqube-58ef20e45102167223529f7df26f4434e8039045.tar.gz sonarqube-58ef20e45102167223529f7df26f4434e8039045.zip |
SONAR-11207 Display analysis warnings in the web app
Diffstat (limited to 'server/sonar-web/src/main/js')
16 files changed, 449 insertions, 8 deletions
diff --git a/server/sonar-web/src/main/js/api/ce.ts b/server/sonar-web/src/main/js/api/ce.ts index b72d148873e..3d61fbc6589 100644 --- a/server/sonar-web/src/main/js/api/ce.ts +++ b/server/sonar-web/src/main/js/api/ce.ts @@ -33,7 +33,7 @@ export function getStatus(componentId?: string): Promise<any> { return getJSON('/api/ce/activity_status', data); } -export function getTask(id: string, additionalFields?: string[]): Promise<any> { +export function getTask(id: string, additionalFields?: string[]): Promise<Task> { return getJSON('/api/ce/task', { id, additionalFields }).then(r => r.task); } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx index b99e3f10d61..46ecd76a630 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx @@ -96,6 +96,7 @@ export default class ComponentNav extends React.PureComponent<Props> { branchLike={currentBranchLike} branchMeasures={this.props.branchMeasures} component={component} + currentTask={currentTask} /> </div> <ComponentNavMenu diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx index a1e503abd3d..f6c5487409c 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx @@ -19,6 +19,7 @@ */ import * as React from 'react'; import { connect } from 'react-redux'; +import ComponentNavWarnings from './ComponentNavWarnings'; import { BranchLike, Component, @@ -26,7 +27,8 @@ import { isLoggedIn, HomePageType, HomePage, - Measure + Measure, + Task } from '../../../types'; import BranchMeasures from '../../../../components/common/BranchMeasures'; import BranchStatus from '../../../../components/common/BranchStatus'; @@ -52,9 +54,10 @@ interface Props extends StateProps { branchLike?: BranchLike; branchMeasures?: Measure[]; component: Component; + currentTask?: Task; } -export function ComponentNavMeta({ branchLike, branchMeasures, component, currentUser }: Props) { +export function ComponentNavMeta({ branchLike, branchMeasures, component, currentTask, currentUser }: Props) { const mainBranch = !branchLike || isMainBranch(branchLike); const longBranch = isLongLivingBranch(branchLike); const currentPage = getCurrentPage(component, branchLike); @@ -62,6 +65,8 @@ export function ComponentNavMeta({ branchLike, branchMeasures, component, curren return ( <div className="navbar-context-meta"> + {currentTask && + Boolean(currentTask.warningCount) && <ComponentNavWarnings task={currentTask} />} {component.analysisDate && ( <div className="spacer-left text-ellipsis"> <DateTimeFormatter date={component.analysisDate} /> diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavWarnings.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavWarnings.tsx new file mode 100644 index 00000000000..192d562afd7 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavWarnings.tsx @@ -0,0 +1,78 @@ +/* + * 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 { lazyLoad } from '../../../../components/lazyLoad'; +import WarningIcon from '../../../../components/icons-components/WarningIcon'; +import { Task } from '../../../types'; +import { translate, translateWithParameters } from '../../../../helpers/l10n'; + +const AnalysisWarningsModal = lazyLoad(() => + import('../../../../components/common/AnalysisWarningsModal') +); + +interface Props { + task: Pick<Task, 'id' | 'warningCount'>; +} + +interface State { + modal: boolean; +} + +export default class ComponentNavWarnings extends React.PureComponent<Props, State> { + state: State = { modal: false }; + + handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => { + event.preventDefault(); + event.currentTarget.blur(); + this.setState({ modal: true }); + }; + + handleCloseModal = () => { + this.setState({ modal: false }); + }; + + render() { + return ( + <> + <div className="badge badge-focus badge-medium display-inline-flex-center js-component-analysis-warnings"> + <WarningIcon className="spacer-right" /> + <FormattedMessage + defaultMessage={translate('component_navigation.last_analsys_had_warnings')} + id="component_navigation.last_analsys_had_warnings" + values={{ + warnings: ( + <a href="#" onClick={this.handleClick}> + {translateWithParameters( + 'component_navigation.x_warnings', + String(this.props.task.warningCount) + )} + </a> + ) + }} + /> + </div> + {this.state.modal && ( + <AnalysisWarningsModal onClose={this.handleCloseModal} taskId={this.props.task.id} /> + )} + </> + ); + } +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavWarnings-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavWarnings-test.tsx new file mode 100644 index 00000000000..9eba3b71968 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavWarnings-test.tsx @@ -0,0 +1,28 @@ +/* + * 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 ComponentNavWarnings from '../ComponentNavWarnings'; + +it('should render', () => { + const wrapper = shallow(<ComponentNavWarnings task={{ id: 'abcd1234' }} />); + wrapper.setState({ modal: true }); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavWarnings-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavWarnings-test.tsx.snap new file mode 100644 index 00000000000..1f672b50096 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavWarnings-test.tsx.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render 1`] = ` +<React.Fragment> + <div + className="badge badge-focus badge-medium display-inline-flex-center js-component-analysis-warnings" + > + <WarningIcon + className="spacer-right" + /> + <FormattedMessage + defaultMessage="component_navigation.last_analsys_had_warnings" + id="component_navigation.last_analsys_had_warnings" + values={ + Object { + "warnings": <a + href="#" + onClick={[Function]} + > + component_navigation.x_warnings.undefined + </a>, + } + } + /> + </div> + <LazyLoader + onClose={[Function]} + taskId="abcd1234" + /> +</React.Fragment> +`; diff --git a/server/sonar-web/src/main/js/app/styles/components/badges.css b/server/sonar-web/src/main/js/app/styles/components/badges.css index 05d1f81238f..8ec56d49e69 100644 --- a/server/sonar-web/src/main/js/app/styles/components/badges.css +++ b/server/sonar-web/src/main/js/app/styles/components/badges.css @@ -53,6 +53,12 @@ a.badge { border-radius: 50px; } +.badge-medium { + height: var(--controlHeight); + line-height: calc(var(--controlHeight)); + letter-spacing: 0.01em; +} + .list-group-item > .badge, .list-group-item-heading > .badge { float: right; diff --git a/server/sonar-web/src/main/js/app/theme.js b/server/sonar-web/src/main/js/app/theme.js index bc1731dd834..ccaff6a1bf0 100644 --- a/server/sonar-web/src/main/js/app/theme.js +++ b/server/sonar-web/src/main/js/app/theme.js @@ -56,6 +56,9 @@ module.exports = { snippetFontColor: '#f0f0f0', + // alerts + warningIconColor: '#e2bf41', + // sizes grid, gridSize: `${grid}px`, diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index 6d6965e1fce..109e08c56ed 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -728,6 +728,7 @@ export interface Task { componentName?: string; componentQualifier?: string; errorMessage?: string; + errorStacktrace?: string; errorType?: string; executedAt?: string; executionTimeMs?: number; @@ -738,11 +739,14 @@ export interface Task { organization: string; pullRequest?: string; pullRequestTitle?: string; + scannerContext?: string; startedAt?: string; status: string; submittedAt: string; submitterLogin?: string; type: string; + warningCount?: number; + warnings?: string[]; } export interface TestCase { diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx index 68587cd0ad9..119d8ac7d61 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx @@ -24,6 +24,12 @@ import { STATUSES } from '../constants'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import ActionsDropdown, { ActionsDropdownItem } from '../../../components/controls/ActionsDropdown'; import { Task } from '../../../app/types'; +import { lazyLoad } from '../../../components/lazyLoad'; + +const AnalysisWarningsModal = lazyLoad( + () => import('../../../components/common/AnalysisWarningsModal'), + 'AnalysisWarningsModal' +); interface Props { component?: {}; @@ -35,12 +41,14 @@ interface Props { interface State { scannerContextOpen: boolean; stacktraceOpen: boolean; + warningsOpen: boolean; } export default class TaskActions extends React.PureComponent<Props, State> { state: State = { scannerContextOpen: false, - stacktraceOpen: false + stacktraceOpen: false, + warningsOpen: false }; handleFilterClick = () => { @@ -55,13 +63,25 @@ export default class TaskActions extends React.PureComponent<Props, State> { this.setState({ scannerContextOpen: true }); }; - closeScannerContext = () => this.setState({ scannerContextOpen: false }); + closeScannerContext = () => { + this.setState({ scannerContextOpen: false }); + }; handleShowStacktraceClick = () => { this.setState({ stacktraceOpen: true }); }; - closeStacktrace = () => this.setState({ stacktraceOpen: false }); + closeStacktrace = () => { + this.setState({ stacktraceOpen: false }); + }; + + handleShowWarningsClick = () => { + this.setState({ warningsOpen: true }); + }; + + closeWarnings = () => { + this.setState({ warningsOpen: false }); + }; render() { const { component, task } = this.props; @@ -69,7 +89,9 @@ export default class TaskActions extends React.PureComponent<Props, State> { const canFilter = component === undefined; const canCancel = task.status === STATUSES.PENDING; const canShowStacktrace = task.errorMessage !== undefined; - const hasActions = canFilter || canCancel || task.hasScannerContext || canShowStacktrace; + const canShowWarnings = task.warningCount !== undefined && task.warningCount > 0; + const hasActions = + canFilter || canCancel || task.hasScannerContext || canShowStacktrace || canShowWarnings; if (!hasActions) { return <td> </td>; @@ -109,6 +131,13 @@ export default class TaskActions extends React.PureComponent<Props, State> { {translate('background_tasks.show_stacktrace')} </ActionsDropdownItem> )} + {canShowWarnings && ( + <ActionsDropdownItem + className="js-task-show-warnings" + onClick={this.handleShowWarningsClick}> + {translate('background_tasks.show_warnings')} + </ActionsDropdownItem> + )} </ActionsDropdown> {this.state.scannerContextOpen && ( @@ -116,6 +145,10 @@ export default class TaskActions extends React.PureComponent<Props, State> { )} {this.state.stacktraceOpen && <Stacktrace onClose={this.closeStacktrace} task={task} />} + + {this.state.warningsOpen && ( + <AnalysisWarningsModal onClose={this.closeWarnings} taskId={task.id} /> + )} </td> ); } diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/TaskActions-test.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/TaskActions-test.tsx index 456f1f0e07a..5ce2e4bcf58 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/TaskActions-test.tsx +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/TaskActions-test.tsx @@ -21,6 +21,7 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import TaskActions from '../TaskActions'; import { click } from '../../../../helpers/testUtils'; +import { Task } from '../../../../app/types'; it('renders', () => { expect(shallowRender()).toMatchSnapshot(); @@ -48,7 +49,16 @@ it('shows scanner context', () => { expect(wrapper.find('ScannerContext').exists()).toBeFalsy(); }); -function shallowRender(fields?: any, props?: any) { +it('shows warnings', () => { + const wrapper = shallowRender({ warningCount: 2 }); + click(wrapper.find('.js-task-show-warnings')); + expect(wrapper.find('AnalysisWarningsModal')).toMatchSnapshot(); + wrapper.find('AnalysisWarningsModal').prop<Function>('onClose')(); + wrapper.update(); + expect(wrapper.find('AnalysisWarningsModal').exists()).toBeFalsy(); +}); + +function shallowRender(fields?: Partial<Task>, props?: Partial<TaskActions['props']>) { return shallow( <TaskActions onCancelTask={jest.fn()} @@ -57,6 +67,7 @@ function shallowRender(fields?: any, props?: any) { componentName: 'foo', status: 'PENDING', id: '123', + organization: 'org', submittedAt: '2017-01-01', type: 'REPORT', ...fields diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskActions-test.tsx.snap index 84a4e16e597..0d460cfddc0 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskActions-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskActions-test.tsx.snap @@ -127,6 +127,7 @@ exports[`shows scanner context 1`] = ` "componentName": "foo", "hasScannerContext": true, "id": "123", + "organization": "org", "status": "PENDING", "submittedAt": "2017-01-01", "type": "REPORT", @@ -143,6 +144,7 @@ exports[`shows stack trace 1`] = ` "componentName": "foo", "errorMessage": "error!", "id": "123", + "organization": "org", "status": "PENDING", "submittedAt": "2017-01-01", "type": "REPORT", @@ -150,3 +152,10 @@ exports[`shows stack trace 1`] = ` } /> `; + +exports[`shows warnings 1`] = ` +<AnalysisWarningsModal + onClose={[Function]} + taskId="123" +/> +`; diff --git a/server/sonar-web/src/main/js/components/common/AnalysisWarningsModal.tsx b/server/sonar-web/src/main/js/components/common/AnalysisWarningsModal.tsx new file mode 100644 index 00000000000..ec73d16e563 --- /dev/null +++ b/server/sonar-web/src/main/js/components/common/AnalysisWarningsModal.tsx @@ -0,0 +1,103 @@ +/* + * 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 DeferredSpinner from './DeferredSpinner'; +import Modal from '../controls/Modal'; +import { ResetButtonLink } from '../ui/buttons'; +import { translate } from '../../helpers/l10n'; +import WarningIcon from '../icons-components/WarningIcon'; +import { getTask } from '../../api/ce'; + +interface Props { + onClose: () => void; + taskId: string; +} + +interface State { + loading: boolean; + warnings: string[]; +} + +export default class AnalysisWarningsModal extends React.PureComponent<Props, State> { + mounted = false; + state: State = { + loading: true, + warnings: [] + }; + + componentDidMount() { + this.mounted = true; + this.loadWarnings(); + } + + componentDidUpdate(prevProps: Props) { + if (prevProps.taskId !== this.props.taskId) { + this.loadWarnings(); + } + } + + componentWillUnmount() { + this.mounted = false; + } + + loadWarnings() { + this.setState({ loading: true }); + getTask(this.props.taskId, ['warnings']).then( + ({ warnings = [] }) => { + if (this.mounted) { + this.setState({ loading: false, warnings }); + } + }, + () => { + if (this.mounted) { + this.setState({ loading: false }); + } + } + ); + } + + render() { + const header = translate('warnings'); + return ( + <Modal contentLabel={header} onRequestClose={this.props.onClose}> + <header className="modal-head"> + <h2>{header}</h2> + </header> + + <div className="modal-body js-analysis-warnings"> + <DeferredSpinner loading={this.state.loading}> + {this.state.warnings.map((warning, index) => ( + <div className="panel panel-vertical" key={index}> + <WarningIcon className="pull-left spacer-right" /> + <div className="overflow-hidden markdown">{warning}</div> + </div> + ))} + </DeferredSpinner> + </div> + + <footer className="modal-foot"> + <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}> + {translate('close')} + </ResetButtonLink> + </footer> + </Modal> + ); + } +} diff --git a/server/sonar-web/src/main/js/components/common/__tests__/AnalysisWarningsModal-test.tsx b/server/sonar-web/src/main/js/components/common/__tests__/AnalysisWarningsModal-test.tsx new file mode 100644 index 00000000000..4d833e396f7 --- /dev/null +++ b/server/sonar-web/src/main/js/components/common/__tests__/AnalysisWarningsModal-test.tsx @@ -0,0 +1,35 @@ +/* + * 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 AnalysisWarningsModal from '../AnalysisWarningsModal'; +import { waitAndUpdate } from '../../../helpers/testUtils'; +import { getTask } from '../../../api/ce'; + +jest.mock('../../../api/ce', () => ({ + getTask: jest.fn().mockResolvedValue({ warnings: ['message foo', 'message-bar'] }) +})); + +it('should fetch warnings and render', async () => { + const wrapper = shallow(<AnalysisWarningsModal onClose={jest.fn()} taskId="abcd1234" />); + await waitAndUpdate(wrapper); + expect(wrapper).toMatchSnapshot(); + expect(getTask).toBeCalledWith('abcd1234', ['warnings']); +}); diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/AnalysisWarningsModal-test.tsx.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/AnalysisWarningsModal-test.tsx.snap new file mode 100644 index 00000000000..a5823a94881 --- /dev/null +++ b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/AnalysisWarningsModal-test.tsx.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should fetch warnings and render 1`] = ` +<Modal + contentLabel="warnings" + onRequestClose={[MockFunction]} +> + <header + className="modal-head" + > + <h2> + warnings + </h2> + </header> + <div + className="modal-body js-analysis-warnings" + > + <DeferredSpinner + loading={false} + timeout={100} + > + <div + className="panel panel-vertical" + key="0" + > + <WarningIcon + className="pull-left spacer-right" + /> + <div + className="overflow-hidden markdown" + > + message foo + </div> + </div> + <div + className="panel panel-vertical" + key="1" + > + <WarningIcon + className="pull-left spacer-right" + /> + <div + className="overflow-hidden markdown" + > + message-bar + </div> + </div> + </DeferredSpinner> + </div> + <footer + className="modal-foot" + > + <ResetButtonLink + className="js-modal-close" + onClick={[MockFunction]} + > + close + </ResetButtonLink> + </footer> +</Modal> +`; diff --git a/server/sonar-web/src/main/js/components/icons-components/WarningIcon.tsx b/server/sonar-web/src/main/js/components/icons-components/WarningIcon.tsx new file mode 100644 index 00000000000..eb9616ab2b6 --- /dev/null +++ b/server/sonar-web/src/main/js/components/icons-components/WarningIcon.tsx @@ -0,0 +1,33 @@ +/* + * 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 Icon, { IconProps } from './Icon'; +import * as theme from '../../app/theme'; + +export default function WarningIcon({ className, fill = theme.warningIconColor, size }: IconProps) { + return ( + <Icon className={className} size={size}> + <path + d="M9 12.242v-1.484c0-.14-.11-.258-.25-.258h-1.5c-.14 0-.25.117-.25.258v1.484c0 .14.11.258.25.258h1.5c.14 0 .25-.117.25-.258zM8.984 9.32l.141-3.586a.189.189 0 0 0-.078-.148C9 5.546 8.93 5.5 8.859 5.5H7.141c-.07 0-.141.047-.188.086-.055.039-.078.117-.078.164l.133 3.57c0 .102.117.18.265.18H8.72c.14 0 .258-.078.265-.18zm-.109-7.297l6 11A1 1 0 0 1 14 14.5H2a1 1 0 0 1-.875-1.477l6-11a.994.994 0 0 1 1.75 0z" + style={{ fill }} + /> + </Icon> + ); +} |