import { getSystemStatus, waitSystemUPStatus } from '../../api/system';
import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization';
import { translate } from '../../helpers/l10n';
+import { AppState } from '../../types/appstate';
import { PendingPluginResult } from '../../types/plugins';
-import { AppState, Extension, SysStatus } from '../../types/types';
+import { Extension, SysStatus } from '../../types/types';
import AdminContext, { defaultPendingPlugins, defaultSystemStatus } from './AdminContext';
import withAppStateContext from './app-state/withAppStateContext';
import SettingsNav from './nav/settings/SettingsNav';
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
import { lazyLoadComponent } from '../../components/lazyLoadComponent';
-import { getGlobalSettingValue, Store } from '../../store/rootReducer';
+import { AppState } from '../../types/appstate';
+import { GlobalSettingKeys } from '../../types/settings';
+import withAppStateContext from './app-state/withAppStateContext';
import KeyboardShortcutsModal from './KeyboardShortcutsModal';
const PageTracker = lazyLoadComponent(() => import('./PageTracker'));
interface Props {
- enableGravatar: boolean;
- gravatarServerUrl: string;
+ appState: AppState;
}
export class App extends React.PureComponent<Props> {
};
renderPreconnectLink = () => {
+ const {
+ appState: { settings }
+ } = this.props;
+
+ const enableGravatar = settings[GlobalSettingKeys.EnableGravatar] === 'true';
+ const gravatarServerUrl = settings[GlobalSettingKeys.GravatarServerUrl];
+
+ if (!enableGravatar || !gravatarServerUrl) {
+ return null;
+ }
+
const parser = document.createElement('a');
- parser.href = this.props.gravatarServerUrl;
+ parser.href = gravatarServerUrl;
if (parser.hostname !== window.location.hostname) {
return <link href={parser.origin} rel="preconnect" />;
}
render() {
return (
<>
- <PageTracker>{this.props.enableGravatar && this.renderPreconnectLink()}</PageTracker>
+ <PageTracker>{this.renderPreconnectLink()}</PageTracker>
{this.props.children}
<KeyboardShortcutsModal />
</>
}
}
-const mapStateToProps = (state: Store) => {
- const enableGravatar = getGlobalSettingValue(state, 'sonar.lf.enableGravatar');
- const gravatarServerUrl = getGlobalSettingValue(state, 'sonar.lf.gravatarServerUrl');
- return {
- enableGravatar: Boolean(enableGravatar && enableGravatar.value === 'true'),
- gravatarServerUrl: (gravatarServerUrl && gravatarServerUrl.value) || ''
- };
-};
-
-export default connect(mapStateToProps)(App);
+export default withAppStateContext(App);
ProjectAlmBindingConfigurationErrors,
ProjectAlmBindingResponse
} from '../../types/alm-settings';
+import { AppState } from '../../types/appstate';
import { BranchLike } from '../../types/branch-like';
import { ComponentQualifier, isPortfolioLike } from '../../types/component';
import { Task, TaskStatuses, TaskTypes, TaskWarning } from '../../types/tasks';
-import { AppState, Component, Status } from '../../types/types';
+import { Component, Status } from '../../types/types';
import handleRequiredAuthorization from '../utils/handleRequiredAuthorization';
import withAppStateContext from './app-state/withAppStateContext';
import ComponentContainerNotFound from './ComponentContainerNotFound';
import { Alert } from '../../components/ui/Alert';
import { getEdition } from '../../helpers/editions';
import { translate, translateWithParameters } from '../../helpers/l10n';
+import { AppState } from '../../types/appstate';
import { EditionKey } from '../../types/editions';
-import { AppState } from '../../types/types';
import withAppStateContext from './app-state/withAppStateContext';
import GlobalFooterBranding from './GlobalFooterBranding';
*/
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
-import { connect } from 'react-redux';
import { Location, withRouter } from '../../components/hoc/withRouter';
-import { gtm } from '../../helpers/analytics';
import { installScript } from '../../helpers/extensions';
import { getWebAnalyticsPageHandlerFromCache } from '../../helpers/extensionsHandler';
import { getInstance } from '../../helpers/system';
-import { getGlobalSettingValue, Store } from '../../store/rootReducer';
-import { AppState } from '../../types/types';
+import { AppState } from '../../types/appstate';
import withAppStateContext from './app-state/withAppStateContext';
interface Props {
location: Location;
- trackingIdGTM?: string;
appState: AppState;
}
state: State = {};
componentDidMount() {
- const { trackingIdGTM, appState } = this.props;
+ const { appState } = this.props;
if (appState.webAnalyticsJsPath && !getWebAnalyticsPageHandlerFromCache()) {
installScript(appState.webAnalyticsJsPath, 'head');
}
-
- if (trackingIdGTM) {
- gtm(trackingIdGTM);
- }
}
trackPage = () => {
- const { location, trackingIdGTM } = this.props;
+ const { location } = this.props;
const { lastLocation } = this.state;
- const { dataLayer } = window as any;
const locationChanged = location.pathname !== lastLocation;
const webAnalyticsPageChange = getWebAnalyticsPageHandlerFromCache();
if (webAnalyticsPageChange && locationChanged) {
this.setState({ lastLocation: location.pathname });
setTimeout(() => webAnalyticsPageChange(location.pathname), 500);
- } else if (dataLayer && dataLayer.push && trackingIdGTM && location.pathname !== '/') {
- this.setState({ lastLocation: location.pathname });
- setTimeout(() => dataLayer.push({ event: 'render-end' }), 500);
}
};
render() {
- const { trackingIdGTM, appState } = this.props;
+ const { appState } = this.props;
return (
<Helmet
defaultTitle={getInstance()}
defer={false}
- onChangeClientState={
- trackingIdGTM || appState.webAnalyticsJsPath ? this.trackPage : undefined
- }>
+ onChangeClientState={appState.webAnalyticsJsPath ? this.trackPage : undefined}>
{this.props.children}
</Helmet>
);
}
}
-const mapStateToProps = (state: Store) => {
- const trackingIdGTM = getGlobalSettingValue(state, 'sonar.analytics.gtm.trackingId');
- return {
- trackingIdGTM: trackingIdGTM && trackingIdGTM.value
- };
-};
-
-export default withRouter(connect(mapStateToProps)(withAppStateContext(PageTracker)));
+export default withRouter(withAppStateContext(PageTracker));
import { get, save } from '../../helpers/storage';
import { isLoggedIn } from '../../helpers/users';
import { getCurrentUser, Store } from '../../store/rootReducer';
+import { AppState } from '../../types/appstate';
import { EditionKey } from '../../types/editions';
-import { AppState, CurrentUser } from '../../types/types';
+import { CurrentUser } from '../../types/types';
import withAppStateContext from './app-state/withAppStateContext';
const LicensePromptModal = lazyLoadComponent(
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { connect } from 'react-redux';
+import { mockAppState } from '../../../helpers/testMocks';
import { App } from '../App';
-jest.mock('react-redux', () => ({
- connect: jest.fn(() => (a: any) => a)
-}));
-
-jest.mock('../../../store/rootReducer', () => ({
- getGlobalSettingValue: jest.fn((_, key: string) => ({
- value: key === 'sonar.lf.enableGravatar' ? 'true' : 'http://gravatar.com'
- }))
-}));
-
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
expect(
- shallowRender({ enableGravatar: true, gravatarServerUrl: 'http://example.com' })
+ shallowRender({
+ appState: mockAppState({
+ settings: {
+ 'sonar.lf.enableGravatar': 'true',
+ 'sonar.lf.gravatarServerUrl': 'http://example.com'
+ }
+ })
+ })
).toMatchSnapshot('with gravatar');
});
expect(document.body.style.getPropertyValue('--sbw')).toBe('0px');
});
-describe('redux', () => {
- it('should correctly map state props', () => {
- const [mapStateToProps] = (connect as jest.Mock).mock.calls[0];
-
- expect(mapStateToProps({})).toEqual({
- enableGravatar: true,
- gravatarServerUrl: 'http://gravatar.com'
- });
- });
-});
-
function shallowRender(props: Partial<App['props']> = {}) {
- return shallow<App>(<App enableGravatar={false} gravatarServerUrl="" {...props} />);
+ return shallow<App>(
+ <App
+ appState={mockAppState({
+ settings: {
+ 'sonar.lf.enableGravatar': 'false',
+ 'sonar.lf.gravatarServerUrl': ''
+ }
+ })}
+ {...props}
+ />
+ );
}
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { gtm } from '../../../helpers/analytics';
import { installScript } from '../../../helpers/extensions';
import { getWebAnalyticsPageHandlerFromCache } from '../../../helpers/extensionsHandler';
import { mockAppState, mockLocation } from '../../../helpers/testMocks';
getWebAnalyticsPageHandlerFromCache: jest.fn().mockReturnValue(undefined)
}));
-jest.mock('../../../helpers/analytics', () => ({ gtm: jest.fn() }));
beforeAll(() => {
jest.useFakeTimers();
});
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot();
expect(installScript).not.toHaveBeenCalled();
- expect(gtm).not.toHaveBeenCalled();
});
it('should work for WebAnalytics plugin', () => {
expect(pageChange).toHaveBeenCalledWith('/path');
});
-it('should work for Google Tag Manager', () => {
- (window as any).dataLayer = [];
- const { dataLayer } = window as any;
- const push = jest.spyOn(dataLayer, 'push');
- const wrapper = shallowRender({ trackingIdGTM: '123' });
-
- expect(wrapper.find('Helmet').prop('onChangeClientState')).toBe(wrapper.instance().trackPage);
- expect(gtm).toBeCalled();
- expect(dataLayer).toHaveLength(0);
-
- wrapper.instance().trackPage();
- jest.runAllTimers();
- expect(push).toBeCalledWith({ event: 'render-end' });
- expect(dataLayer).toHaveLength(1);
-});
-
function shallowRender(props: Partial<PageTracker['props']> = {}) {
return shallow<PageTracker>(
<PageTracker appState={mockAppState()} location={mockLocation()} {...props} />
*/
import * as React from 'react';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
const defaultAppState = {
authenticationError: false,
*/
import * as React from 'react';
-import { connect } from 'react-redux';
-import { setValues } from '../../../apps/settings/store/actions';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import { AppStateContext } from './AppStateContext';
export interface AppStateContextProviderProps {
- setValues: typeof setValues;
appState: AppState;
}
-export function AppStateContextProvider({
- setValues,
+export default function AppStateContextProvider({
appState,
children
}: React.PropsWithChildren<AppStateContextProviderProps>) {
- React.useEffect(() => {
- setValues(
- Object.keys(appState.settings),
- Object.entries(appState.settings).map(([key, value]) => ({ key, value }))
- );
- });
-
return <AppStateContext.Provider value={appState}>{children}</AppStateContext.Provider>;
}
-const mapDispatchToProps = { setValues };
-export default connect(null, mapDispatchToProps)(AppStateContextProvider);
import * as React from 'react';
import { mockAppState } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
-import { AppStateContextProvider, AppStateContextProviderProps } from '../AppStateContextProvider';
+import AppStateContextProvider, { AppStateContextProviderProps } from '../AppStateContextProvider';
it('should set value correctly', async () => {
- const setValues = jest.fn();
+ const appState = mockAppState({ settings: { 'sonar.lf.logoUrl': 'whatevs/' } });
const wrapper = render({
- appState: mockAppState({ settings: { foo: 'bar' } }),
- setValues
+ appState
});
await waitAndUpdate(wrapper);
- expect(setValues).toHaveBeenCalledWith(['foo'], [{ key: 'foo', value: 'bar' }]);
+
+ expect(wrapper).toMatchSnapshot();
});
function render(override?: Partial<AppStateContextProviderProps>) {
- return mount(
- <AppStateContextProvider appState={mockAppState()} setValues={jest.fn()} {...override} />
- );
+ return mount(<AppStateContextProvider appState={mockAppState()} {...override} />);
}
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should set value correctly 1`] = `
+<AppStateContextProvider
+ appState={
+ Object {
+ "edition": "community",
+ "productionDatabase": true,
+ "qualifiers": Array [
+ "TRK",
+ ],
+ "settings": Object {
+ "sonar.lf.logoUrl": "whatevs/",
+ },
+ "version": "1.0",
+ }
+ }
+/>
+`;
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockAppState } from '../../../../helpers/testMocks';
-import { AppState } from '../../../../types/types';
+import { AppState } from '../../../../types/appstate';
import withAppStateContext from '../withAppStateContext';
const appState = mockAppState();
import * as React from 'react';
import { getWrappedDisplayName } from '../../../components/hoc/utils';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import { AppStateContext } from './AppStateContext';
export interface WithAppStateContextProps {
import { getBaseUrl } from '../../../helpers/system';
import { addGlobalErrorMessage } from '../../../store/globalMessages';
import { getCurrentUser, Store } from '../../../store/rootReducer';
+import { AppState } from '../../../types/appstate';
import { ExtensionStartMethod } from '../../../types/extension';
-import { AppState, CurrentUser, Dict, Extension as TypeExtension } from '../../../types/types';
+import { CurrentUser, Dict, Extension as TypeExtension } from '../../../types/types';
import * as theme from '../../theme';
import getStore from '../../utils/getStore';
import withAppStateContext from '../app-state/withAppStateContext';
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import withAppStateContext from '../app-state/withAppStateContext';
import NotFound from '../NotFound';
import Extension from './Extension';
import DateFromNow from '../../../components/intl/DateFromNow';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import Measure from '../../../components/measure/Measure';
+import RatingTooltipContent from '../../../components/measure/RatingTooltipContent';
import { Alert } from '../../../components/ui/Alert';
import CoverageRating from '../../../components/ui/CoverageRating';
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
Radio,
RadioToggle,
Rating,
+ RatingTooltipContent,
ReloadButton,
ResetButtonLink,
SearchBox,
*/
/* eslint-disable react/no-unused-state */
import * as React from 'react';
+import { AppState } from '../../../types/appstate';
import { IndexationContextInterface, IndexationStatus } from '../../../types/indexation';
-import { AppState } from '../../../types/types';
import withAppStateContext from '../app-state/withAppStateContext';
import { IndexationContext } from './IndexationContext';
import IndexationNotificationHelper from './IndexationNotificationHelper';
import { isValidLicense } from '../../../../api/marketplace';
import { Alert } from '../../../../components/ui/Alert';
import { translate, translateWithParameters } from '../../../../helpers/l10n';
+import { AppState } from '../../../../types/appstate';
import { ComponentQualifier } from '../../../../types/component';
import { Task } from '../../../../types/tasks';
-import { AppState } from '../../../../types/types';
import withAppStateContext from '../../app-state/withAppStateContext';
interface Props {
import { getBranchLikeQuery, isPullRequest } from '../../../../helpers/branch-like';
import { hasMessage, translate, translateWithParameters } from '../../../../helpers/l10n';
import { getPortfolioUrl, getProjectQueryUrl } from '../../../../helpers/urls';
+import { AppState } from '../../../../types/appstate';
import { BranchLike, BranchParameters } from '../../../../types/branch-like';
import { ComponentQualifier, isPortfolioLike } from '../../../../types/component';
-import { AppState, Component, Extension } from '../../../../types/types';
+import { Component, Extension } from '../../../../types/types';
import withAppStateContext from '../../app-state/withAppStateContext';
import './Menu.css';
import * as React from 'react';
import Toggler from '../../../../../components/controls/Toggler';
import { ProjectAlmBindingResponse } from '../../../../../types/alm-settings';
+import { AppState } from '../../../../../types/appstate';
import { BranchLike } from '../../../../../types/branch-like';
-import { AppState, Component } from '../../../../../types/types';
+import { Component } from '../../../../../types/types';
import withAppStateContext from '../../../app-state/withAppStateContext';
import './BranchLikeNavigation.css';
import CurrentBranchLike from './CurrentBranchLike';
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
import { Link } from 'react-router';
import { translate } from '../../../../helpers/l10n';
import { getBaseUrl } from '../../../../helpers/system';
-import { getGlobalSettingValue, Store } from '../../../../store/rootReducer';
+import { AppState } from '../../../../types/appstate';
+import { GlobalSettingKeys } from '../../../../types/settings';
+import withAppStateContext from '../../app-state/withAppStateContext';
-interface StateProps {
- customLogoUrl?: string;
- customLogoWidth?: string | number;
+export interface GlobalNavBrandingProps {
+ appState: AppState;
}
-export function GlobalNavBranding({ customLogoUrl, customLogoWidth }: StateProps) {
+export function GlobalNavBranding({ appState: { settings } }: GlobalNavBrandingProps) {
+ const customLogoUrl = settings[GlobalSettingKeys.LogoUrl];
+ const customLogoWidth = settings[GlobalSettingKeys.LogoWidth];
+
const title = translate('layout.sonar.slogan');
const url = customLogoUrl || `${getBaseUrl()}/images/logo.svg?v=6.6`;
const width = customLogoUrl ? customLogoWidth || 100 : 83;
);
}
-const mapStateToProps = (state: Store): StateProps => {
- const customLogoUrl = getGlobalSettingValue(state, 'sonar.lf.logoUrl');
- const customLogoWidth = getGlobalSettingValue(state, 'sonar.lf.logoWidthPx');
- return {
- customLogoUrl: customLogoUrl && customLogoUrl.value,
- customLogoWidth: customLogoWidth && customLogoWidth.value
- };
-};
-
-export default connect(mapStateToProps)(GlobalNavBranding);
+export default withAppStateContext(GlobalNavBranding);
import DropdownIcon from '../../../../components/icons/DropdownIcon';
import { translate } from '../../../../helpers/l10n';
import { getQualityGatesUrl } from '../../../../helpers/urls';
+import { AppState } from '../../../../types/appstate';
import { ComponentQualifier } from '../../../../types/component';
-import { AppState, CurrentUser, Extension } from '../../../../types/types';
+import { CurrentUser, Extension } from '../../../../types/types';
import withAppStateContext from '../../app-state/withAppStateContext';
interface Props {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockAppState } from '../../../../../helpers/testMocks';
+import { GlobalNavBranding, GlobalNavBrandingProps } from '../GlobalNavBranding';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('default');
+ expect(
+ shallowRender({
+ appState: mockAppState({
+ settings: {
+ 'sonar.lf.logoUrl': 'http://sonarsource.com/custom-logo.svg'
+ }
+ })
+ })
+ ).toMatchSnapshot('with logo');
+ expect(
+ shallowRender({
+ appState: mockAppState({
+ settings: {
+ 'sonar.lf.logoUrl': 'http://sonarsource.com/custom-logo.svg',
+ 'sonar.lf.logoWidthPx': '200'
+ }
+ })
+ })
+ ).toMatchSnapshot('with logo and width');
+});
+
+function shallowRender(overrides: Partial<GlobalNavBrandingProps> = {}) {
+ return shallow(<GlobalNavBranding appState={mockAppState()} {...overrides} />);
+}
height={48}
id="global-navigation"
>
- <Connect(GlobalNavBranding) />
+ <withAppStateContext(GlobalNavBranding) />
<withAppStateContext(GlobalNavMenu)
currentUser={
Object {
height={48}
id="global-navigation"
>
- <Connect(GlobalNavBranding) />
+ <withAppStateContext(GlobalNavBranding) />
<withAppStateContext(GlobalNavMenu)
currentUser={
Object {
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: default 1`] = `
+<Link
+ className="navbar-brand"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/"
+>
+ <img
+ alt="layout.sonar.slogan"
+ height={30}
+ src="/images/logo.svg?v=6.6"
+ title="layout.sonar.slogan"
+ width={83}
+ />
+</Link>
+`;
+
+exports[`should render correctly: with logo 1`] = `
+<Link
+ className="navbar-brand"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/"
+>
+ <img
+ alt="layout.sonar.slogan"
+ height={30}
+ src="http://sonarsource.com/custom-logo.svg"
+ title="layout.sonar.slogan"
+ width={100}
+ />
+</Link>
+`;
+
+exports[`should render correctly: with logo and width 1`] = `
+<Link
+ className="navbar-brand"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/"
+>
+ <img
+ alt="layout.sonar.slogan"
+ height={30}
+ src="http://sonarsource.com/custom-logo.svg"
+ title="layout.sonar.slogan"
+ width="200"
+ />
+</Link>
+`;
href="#"
title="Skywalker"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
name="Skywalker"
size={32}
/>
import { sortUpgrades, UpdateUseCase } from '../../../components/upgrade/utils';
import { translate } from '../../../helpers/l10n';
import { hasGlobalPermission, isLoggedIn } from '../../../helpers/users';
+import { AppState } from '../../../types/appstate';
import { Permissions } from '../../../types/permissions';
import { SystemUpgrade } from '../../../types/system';
-import { AppState, CurrentUser, Dict } from '../../../types/types';
+import { CurrentUser, Dict } from '../../../types/types';
import withAppStateContext from '../app-state/withAppStateContext';
import './UpdateNotification.css';
import { loadL10nBundle } from '../helpers/l10n';
import { parseJSON, request } from '../helpers/request';
import { getBaseUrl, getSystemStatus } from '../helpers/system';
-import { AppState } from '../types/types';
+import { AppState } from '../types/appstate';
import './styles/sonar.ts';
installWebAnalyticsHandler();
import withIndexationGuard from '../../components/hoc/withIndexationGuard';
import { lazyLoadComponent } from '../../components/lazyLoadComponent';
import getHistory from '../../helpers/getHistory';
-import { AppState, CurrentUser } from '../../types/types';
+import { AppState } from '../../types/appstate';
+import { CurrentUser } from '../../types/types';
import App from '../components/App';
import AppStateContextProvider from '../components/app-state/AppStateContextProvider';
import GlobalContainer from '../components/GlobalContainer';
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
-import { getGlobalSettingValue, Store } from '../../../store/rootReducer';
+import { getValues } from '../../../api/settings';
import { AdminPageExtension } from '../../../types/extension';
+import { SettingsKey } from '../../../types/settings';
import { Extension } from '../../../types/types';
-import { fetchValues } from '../../settings/store/actions';
import '../style.css';
import { HousekeepingPolicy, RangeOption } from '../utils';
import AuditAppRenderer from './AuditAppRenderer';
interface Props {
- auditHousekeepingPolicy: HousekeepingPolicy;
- fetchValues: typeof fetchValues;
adminPages: Extension[];
}
interface State {
dateRange?: { from?: Date; to?: Date };
- hasGovernanceExtension?: boolean;
downloadStarted: boolean;
+ housekeepingPolicy: HousekeepingPolicy;
selection: RangeOption;
}
-export class AuditApp extends React.PureComponent<Props, State> {
+export default class AuditApp extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
- const hasGovernanceExtension = Boolean(
- props.adminPages?.find(e => e.key === AdminPageExtension.GovernanceConsole)
- );
+
this.state = {
downloadStarted: false,
- selection: RangeOption.Today,
- hasGovernanceExtension
+ housekeepingPolicy: HousekeepingPolicy.Monthly,
+ selection: RangeOption.Today
};
}
componentDidMount() {
- const { hasGovernanceExtension } = this.state;
-
- if (hasGovernanceExtension) {
- this.props.fetchValues(['sonar.dbcleaner.auditHousekeeping']);
+ if (this.hasGovernanceExtension()) {
+ this.fetchHouseKeepingPolicy();
}
}
componentDidUpdate(prevProps: Props) {
- if (prevProps.adminPages !== this.props.adminPages) {
- const hasGovernanceExtension = Boolean(
- this.props.adminPages?.find(e => e.key === AdminPageExtension.GovernanceConsole)
- );
- this.setState({
- hasGovernanceExtension
- });
+ if (prevProps.adminPages !== this.props.adminPages && this.hasGovernanceExtension()) {
+ this.fetchHouseKeepingPolicy();
}
}
+ fetchHouseKeepingPolicy = async () => {
+ const results = await getValues({ keys: SettingsKey.AuditHouseKeeping });
+
+ this.setState({
+ housekeepingPolicy:
+ (results[0]?.value as HousekeepingPolicy | undefined) ?? HousekeepingPolicy.Monthly
+ });
+ };
+
+ hasGovernanceExtension = () => {
+ return Boolean(
+ this.props.adminPages?.find(e => e.key === AdminPageExtension.GovernanceConsole)
+ );
+ };
+
handleDateSelection = (dateRange: { from?: Date; to?: Date }) =>
this.setState({ dateRange, downloadStarted: false, selection: RangeOption.Custom });
};
render() {
- const { hasGovernanceExtension, ...auditAppRendererProps } = this.state;
- const { auditHousekeepingPolicy } = this.props;
-
- if (!hasGovernanceExtension) {
+ if (!this.hasGovernanceExtension()) {
return null;
}
handleDateSelection={this.handleDateSelection}
handleOptionSelection={this.handleOptionSelection}
handleStartDownload={this.handleStartDownload}
- housekeepingPolicy={auditHousekeepingPolicy || HousekeepingPolicy.Monthly}
- {...auditAppRendererProps}
+ {...this.state}
/>
);
}
}
-
-const mapDispatchToProps = { fetchValues };
-
-const mapStateToProps = (state: Store) => {
- const settingValue = getGlobalSettingValue(state, 'sonar.dbcleaner.auditHousekeeping');
- return {
- auditHousekeepingPolicy: settingValue?.value as HousekeepingPolicy
- };
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(AuditApp);
import { subDays } from 'date-fns';
import { shallow } from 'enzyme';
import * as React from 'react';
+import { getValues } from '../../../../api/settings';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { AdminPageExtension } from '../../../../types/extension';
import { HousekeepingPolicy, RangeOption } from '../../utils';
-import { AuditApp } from '../AuditApp';
+import AuditApp from '../AuditApp';
import AuditAppRenderer from '../AuditAppRenderer';
+jest.mock('../../../../api/settings', () => ({
+ getValues: jest.fn().mockResolvedValue([])
+}));
+
+beforeEach(() => {
+ jest.clearAllMocks();
+});
+
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});
it('should do nothing if governance is not available', async () => {
- const fetchValues = jest.fn();
- const wrapper = shallowRender({ fetchValues, adminPages: [] });
+ const wrapper = shallowRender({ adminPages: [] });
await waitAndUpdate(wrapper);
expect(wrapper.type()).toBeNull();
- expect(fetchValues).not.toBeCalled();
+ expect(getValues).not.toBeCalled();
});
-it('should fetch houskeeping policy on mount', async () => {
- const fetchValues = jest.fn();
- const wrapper = shallowRender({ fetchValues });
+it('should handle housekeeping policy', async () => {
+ (getValues as jest.Mock).mockResolvedValueOnce([{ value: HousekeepingPolicy.Weekly }]);
+
+ const wrapper = shallowRender();
+
await waitAndUpdate(wrapper);
- expect(fetchValues).toBeCalled();
+
+ expect(wrapper.find(AuditAppRenderer).props().housekeepingPolicy).toBe(HousekeepingPolicy.Weekly);
});
it('should handle date selection', () => {
expect(wrapper.state().dateRange).toBeUndefined();
});
+it('should handle update to admin pages', async () => {
+ const wrapper = shallowRender({ adminPages: [] });
+ await waitAndUpdate(wrapper);
+
+ expect(wrapper.type()).toBeNull();
+ expect(getValues).not.toBeCalled();
+
+ wrapper.setProps({ adminPages: [{ key: AdminPageExtension.GovernanceConsole, name: 'name' }] });
+ await waitAndUpdate(wrapper);
+
+ expect(getValues).toBeCalled();
+});
+
function shallowRender(props: Partial<AuditApp['props']> = {}) {
return shallow<AuditApp>(
<AuditApp
- auditHousekeepingPolicy={HousekeepingPolicy.Monthly}
- fetchValues={jest.fn()}
adminPages={[{ key: AdminPageExtension.GovernanceConsole, name: 'name' }]}
{...props}
/>
import ConfirmButton from '../../../components/controls/ConfirmButton';
import Tooltip from '../../../components/controls/Tooltip';
import { translate } from '../../../helpers/l10n';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
export interface Props {
appState: AppState;
import { changePassword } from '../../api/users';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import { Location, withRouter } from '../../components/hoc/withRouter';
-import { AppState } from '../../types/types';
+import { AppState } from '../../types/appstate';
import ChangeAdminPasswordAppRenderer from './ChangeAdminPasswordAppRenderer';
import { DEFAULT_ADMIN_LOGIN, DEFAULT_ADMIN_PASSWORD } from './constants';
import { translate } from '../../../helpers/l10n';
import { formatMeasure } from '../../../helpers/measures';
import { getIssuesUrl } from '../../../helpers/urls';
-import { AppState, RuleDetails } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { RuleDetails } from '../../../types/types';
interface Props {
appState: AppState;
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { AlmKeys } from '../../../types/alm-settings';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import { CreateProjectModes } from './types';
export interface CreateProjectModeSelectionProps {
import { translate } from '../../../helpers/l10n';
import { getProjectUrl } from '../../../helpers/urls';
import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
-import { AppState, LoggedInUser } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { LoggedInUser } from '../../../types/types';
import AlmBindingDefinitionForm from '../../settings/components/almIntegration/AlmBindingDefinitionForm';
import AzureProjectCreate from './AzureProjectCreate';
import BitbucketCloudProjectCreate from './BitbucketCloudProjectCreate';
displayReset={true}
onReset={[Function]}
/>
- <Connect(Sidebar)
+ <withAppStateContext(Sidebar)
component={
Object {
"breadcrumbs": Array [],
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { connect } from 'react-redux';
+import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { isBranch, isPullRequest } from '../../../helpers/branch-like';
-import { getGlobalSettingValue, Store } from '../../../store/rootReducer';
+import { AppState } from '../../../types/appstate';
import { BranchLike } from '../../../types/branch-like';
import { ComponentQualifier, isApplication, isPortfolioLike } from '../../../types/component';
import {
ReferencedLanguage,
ReferencedRule
} from '../../../types/issues';
+import { GlobalSettingKeys } from '../../../types/settings';
import { Component, Dict, UserBase } from '../../../types/types';
import { Query } from '../utils';
import AssigneeFacet from './AssigneeFacet';
import TypeFacet from './TypeFacet';
export interface Props {
+ appState: AppState;
branchLike?: BranchLike;
component: Component | undefined;
createdAfterIncludesTime: boolean;
referencedLanguages: Dict<ReferencedLanguage>;
referencedRules: Dict<ReferencedRule>;
referencedUsers: Dict<UserBase>;
- disableDeveloperAggregatedInfo: boolean;
}
export class Sidebar extends React.PureComponent<Props> {
render() {
const {
+ appState: { settings },
component,
createdAfterIncludesTime,
facets,
branchLike
} = this.props;
+ const disableDeveloperAggregatedInfo =
+ settings[GlobalSettingKeys.DeveloperAggregatedInfoDisabled] === 'true';
+
const branch =
(isBranch(branchLike) && branchLike.name) ||
(isPullRequest(branchLike) && branchLike.branch) ||
/>
)}
{this.renderComponentFacets()}
- {!this.props.myIssues && !this.props.disableDeveloperAggregatedInfo && (
+ {!this.props.myIssues && !disableDeveloperAggregatedInfo && (
<AssigneeFacet
assigned={query.assigned}
assignees={query.assignees}
stats={facets.assignees}
/>
)}
- {displayAuthorFacet && !this.props.disableDeveloperAggregatedInfo && (
+ {displayAuthorFacet && !disableDeveloperAggregatedInfo && (
<AuthorFacet
author={query.author}
component={component}
}
}
-export const mapStateToProps = (state: Store) => {
- const disableDeveloperAggregatedInfo = getGlobalSettingValue(
- state,
- 'sonar.developerAggregatedInfo.disabled'
- );
- return {
- disableDeveloperAggregatedInfo: disableDeveloperAggregatedInfo?.value === true.toString()
- };
-};
-
-export default connect(mapStateToProps)(Sidebar);
+export default withAppStateContext(Sidebar);
import { flatten } from 'lodash';
import * as React from 'react';
import { mockComponent } from '../../../../helpers/mocks/component';
-import { getGlobalSettingValue } from '../../../../store/rootReducer';
+import { mockAppState } from '../../../../helpers/testMocks';
+import { GlobalSettingKeys } from '../../../../types/settings';
import { ComponentQualifier } from '../../../../types/component';
import { Query } from '../../utils';
-import { mapStateToProps, Sidebar } from '../Sidebar';
-
-jest.mock('../../../../store/rootReducer', () => ({ getGlobalSettingValue: jest.fn() }));
+import { Sidebar } from '../Sidebar';
it('should render facets for global page', () => {
expect(renderSidebar()).toMatchSnapshot();
});
it('should not render developer nominative facets when asked not to', () => {
- expect(renderSidebar({ disableDeveloperAggregatedInfo: true })).toMatchSnapshot();
-});
-
-it('should init the component with the proper store value', () => {
- mapStateToProps({} as any);
-
- expect(getGlobalSettingValue).toHaveBeenCalledWith(
- expect.any(Object),
- 'sonar.developerAggregatedInfo.disabled'
- );
+ expect(
+ renderSidebar({
+ appState: mockAppState({
+ settings: { [GlobalSettingKeys.DeveloperAggregatedInfoDisabled]: 'true' }
+ })
+ })
+ ).toMatchSnapshot();
});
const renderSidebar = (props?: Partial<Sidebar['props']>) => {
mapChildren(
shallow<Sidebar>(
<Sidebar
+ appState={mockAppState({
+ settings: { [GlobalSettingKeys.DeveloperAggregatedInfoDisabled]: 'false' }
+ })}
component={undefined}
createdAfterIncludesTime={false}
facets={{}}
referencedLanguages={{}}
referencedRules={{}}
referencedUsers={{}}
- disableDeveloperAggregatedInfo={false}
{...props}
/>
)
exports[`test behavior should correctly render facet item 1`] = `
<React.Fragment>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Name Baz"
size={16}
exports[`test behavior should correctly render facet item 2`] = `
<React.Fragment>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="foo"
size={16}
exports[`test behavior should correctly render search result correctly 1`] = `
<React.Fragment>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Name Bar"
size={16}
exports[`test behavior should correctly render search result correctly 2`] = `
<React.Fragment>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="foo"
size={16}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { connect } from 'react-redux';
-import AdminContext from '../../app/components/AdminContext';
-import withAppStateContext from '../../app/components/app-state/withAppStateContext';
-import { getGlobalSettingValue, Store } from '../../store/rootReducer';
-import { EditionKey } from '../../types/editions';
-import { AppState, RawQuery } from '../../types/types';
-import { fetchValues } from '../settings/store/actions';
-import App from './App';
-
-interface OwnProps {
- location: { pathname: string; query: RawQuery };
- appState: AppState;
-}
-
-interface StateToProps {
- fetchValues: typeof fetchValues;
- updateCenterActive: boolean;
-}
-
-function WithAdminContext(props: StateToProps & OwnProps) {
- React.useEffect(() => {
- props.fetchValues(['sonar.updatecenter.activate']);
- });
-
- const propsToPass = {
- location: props.location,
- updateCenterActive: props.updateCenterActive,
- currentEdition: props.appState.edition as EditionKey,
- standaloneMode: props.appState.standalone
- };
-
- return (
- <AdminContext.Consumer>
- {({ fetchPendingPlugins, pendingPlugins }) => (
- <App
- fetchPendingPlugins={fetchPendingPlugins}
- pendingPlugins={pendingPlugins}
- {...propsToPass}
- />
- )}
- </AdminContext.Consumer>
- );
-}
-
-const mapDispatchToProps = { fetchValues };
-
-const mapStateToProps = (state: Store) => {
- const updateCenterActive = getGlobalSettingValue(state, 'sonar.updatecenter.activate');
- return {
- updateCenterActive: Boolean(updateCenterActive && updateCenterActive.value === 'true')
- };
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(withAppStateContext(WithAdminContext));
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 AdminContext from '../../app/components/AdminContext';
+import withAppStateContext from '../../app/components/app-state/withAppStateContext';
+import { AppState } from '../../types/appstate';
+import { EditionKey } from '../../types/editions';
+import { GlobalSettingKeys } from '../../types/settings';
+import { RawQuery } from '../../types/types';
+import App from './App';
+
+export interface MarketplaceAppContainerProps {
+ location: { pathname: string; query: RawQuery };
+ appState: AppState;
+}
+
+export function MarketplaceAppContainer(props: MarketplaceAppContainerProps) {
+ const { appState, location } = props;
+
+ const propsToPass = {
+ location,
+ updateCenterActive: appState.settings[GlobalSettingKeys.UpdatecenterActivated] === 'true',
+ currentEdition: appState.edition as EditionKey,
+ standaloneMode: appState.standalone
+ };
+
+ return (
+ <AdminContext.Consumer>
+ {({ fetchPendingPlugins, pendingPlugins }) => (
+ <App
+ fetchPendingPlugins={fetchPendingPlugins}
+ pendingPlugins={pendingPlugins}
+ {...propsToPass}
+ />
+ )}
+ </AdminContext.Consumer>
+ );
+}
+
+export default withAppStateContext(MarketplaceAppContainer);
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { connect } from 'react-redux';
-import { mockStore } from '../../../helpers/testMocks';
-import { getGlobalSettingValue } from '../../../store/rootReducer';
-import '../AppContainer';
-
-jest.mock('react-redux', () => ({
- connect: jest.fn(() => (a: any) => a)
-}));
-
-jest.mock('../../../store/rootReducer', () => {
- return {
- getGlobalSettingValue: jest.fn()
- };
-});
-
-describe('redux', () => {
- it('should correctly map state and dispatch props', () => {
- const store = mockStore();
- const updateCenterActive = true;
- (getGlobalSettingValue as jest.Mock).mockReturnValueOnce({
- value: `${updateCenterActive}`
- });
-
- const [mapStateToProps] = (connect as jest.Mock).mock.calls[0];
-
- const props = mapStateToProps(store);
- expect(props).toEqual({
- updateCenterActive
- });
-
- expect(getGlobalSettingValue).toHaveBeenCalledWith(store, 'sonar.updatecenter.activate');
- });
-});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 { shallow } from 'enzyme';
+import React from 'react';
+import { mockAppState, mockLocation } from '../../../helpers/testMocks';
+import { GlobalSettingKeys } from '../../../types/settings';
+import { EditionKey } from '../../../types/editions';
+import { MarketplaceAppContainer, MarketplaceAppContainerProps } from '../MarketplaceAppContainer';
+
+it('should render correctly', () => {
+ expect(shallowRender().dive()).toMatchSnapshot('default');
+ expect(
+ shallowRender({
+ appState: mockAppState({
+ settings: {
+ [GlobalSettingKeys.UpdatecenterActivated]: 'true'
+ }
+ })
+ }).dive()
+ ).toMatchSnapshot('update center active');
+});
+
+function shallowRender(overrides: Partial<MarketplaceAppContainerProps> = {}) {
+ return shallow<MarketplaceAppContainerProps>(
+ <MarketplaceAppContainer
+ appState={mockAppState({ edition: EditionKey.community, standalone: true })}
+ location={mockLocation()}
+ {...overrides}
+ />
+ );
+}
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: default 1`] = `
+<withRouter(App)
+ currentEdition="community"
+ fetchPendingPlugins={[Function]}
+ location={
+ Object {
+ "action": "PUSH",
+ "hash": "",
+ "key": "key",
+ "pathname": "/path",
+ "query": Object {},
+ "search": "",
+ "state": Object {},
+ }
+ }
+ pendingPlugins={
+ Object {
+ "installing": Array [],
+ "removing": Array [],
+ "updating": Array [],
+ }
+ }
+ standaloneMode={true}
+ updateCenterActive={false}
+/>
+`;
+
+exports[`should render correctly: update center active 1`] = `
+<withRouter(App)
+ currentEdition="community"
+ fetchPendingPlugins={[Function]}
+ location={
+ Object {
+ "action": "PUSH",
+ "hash": "",
+ "key": "key",
+ "pathname": "/path",
+ "query": Object {},
+ "search": "",
+ "state": Object {},
+ }
+ }
+ pendingPlugins={
+ Object {
+ "installing": Array [],
+ "removing": Array [],
+ "updating": Array [],
+ }
+ }
+ updateCenterActive={true}
+/>
+`;
const routes = [
{
- indexRoute: { component: lazyLoadComponent(() => import('./AppContainer')) }
+ indexRoute: { component: lazyLoadComponent(() => import('./MarketplaceAppContainer')) }
}
];
import { lazyLoadComponent } from '../../../components/lazyLoadComponent';
import { isPullRequest } from '../../../helpers/branch-like';
import { ProjectAlmBindingResponse } from '../../../types/alm-settings';
+import { AppState } from '../../../types/appstate';
import { BranchLike } from '../../../types/branch-like';
import { isPortfolioLike } from '../../../types/component';
-import { AppState, Component } from '../../../types/types';
+import { Component } from '../../../types/types';
import BranchOverview from '../branches/BranchOverview';
const EmptyOverview = lazyLoadComponent(() => import('./EmptyOverview'));
*/
import * as React from 'react';
import Tooltip from '../../../components/controls/Tooltip';
-import { getLeakValue, getRatingTooltip } from '../../../components/measure/utils';
+import RatingTooltipContent from '../../../components/measure/RatingTooltipContent';
+import { getLeakValue } from '../../../components/measure/utils';
import DrilldownLink from '../../../components/shared/DrilldownLink';
import Rating from '../../../components/ui/Rating';
import { findMeasure } from '../../../helpers/measures';
}
const value = measure && (useDiffMetric ? getLeakValue(measure) : measure.value);
- const tooltip = value && getRatingTooltip(rating, Number(value));
return (
- <Tooltip overlay={tooltip}>
+ <Tooltip overlay={value && <RatingTooltipContent metricKey={rating} value={value} />}>
<span>
<DrilldownLink
branchLike={branchLike}
metric_domain.Reliability
</span>
<Tooltip
- overlay="metric.reliability_rating.tooltip.A"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="reliability_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
metric_domain.Reliability
</span>
<Tooltip
- overlay="metric.reliability_rating.tooltip.A"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="new_reliability_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
metric_domain.Maintainability
</span>
<Tooltip
- overlay="metric.sqale_rating.tooltip.A.0.0%"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="sqale_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
metric_domain.Maintainability
</span>
<Tooltip
- overlay="metric.sqale_rating.tooltip.A.0.0%"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="new_maintainability_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
metric_domain.Security
</span>
<Tooltip
- overlay="metric.security_rating.tooltip.A"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="security_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
metric_domain.Security
</span>
<Tooltip
- overlay="metric.security_rating.tooltip.A"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="new_security_rating"
+ value="1.0"
+ />
+ }
>
<span>
<DrilldownLink
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import { translate } from '../../../helpers/l10n';
-import { AppState, Permission, PermissionTemplate } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { Permission, PermissionTemplate } from '../../../types/types';
import '../../permissions/styles.css';
import { mergeDefaultsToTemplates, mergePermissionsToTemplates, sortPermissions } from '../utils';
import Home from './Home';
import * as React from 'react';
import withAppStateContext from '../../../../app/components/app-state/withAppStateContext';
import ListFooter from '../../../../components/controls/ListFooter';
+import { AppState } from '../../../../types/appstate';
import { ComponentQualifier } from '../../../../types/component';
-import { AppState, Paging, PermissionGroup, PermissionUser } from '../../../../types/types';
+import { Paging, PermissionGroup, PermissionUser } from '../../../../types/types';
import HoldersList from '../../shared/components/HoldersList';
import SearchForm from '../../shared/components/SearchForm';
import {
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="text-middle big-spacer-right flex-0"
name="John Doe"
size={36}
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
import { isBranch, sortBranches } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
+import { AppState } from '../../../types/appstate';
import { Branch, BranchLike } from '../../../types/branch-like';
import {
- AppState,
Component,
NewCodePeriod,
NewCodePeriodSettingType,
import * as React from 'react';
import { getValues } from '../../../api/settings';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
+import { AppState } from '../../../types/appstate';
import { SettingsKey } from '../../../types/settings';
-import { AppState } from '../../../types/types';
import LifetimeInformationRenderer from './LifetimeInformationRenderer';
interface Props {
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import throwGlobalError from '../../app/utils/throwGlobalError';
import { translate } from '../../helpers/l10n';
+import { AppState } from '../../types/appstate';
import { DumpStatus, DumpTask } from '../../types/project-dump';
import { TaskStatuses, TaskTypes } from '../../types/tasks';
-import { AppState, Component } from '../../types/types';
+import { Component } from '../../types/types';
import Export from './components/Export';
import Import from './components/Import';
import './styles.css';
import { addSideBarClass, removeSideBarClass } from '../../../helpers/pages';
import { get, save } from '../../../helpers/storage';
import { isLoggedIn } from '../../../helpers/users';
+import { AppState } from '../../../types/appstate';
import { ComponentQualifier } from '../../../types/component';
-import { AppState, CurrentUser, RawQuery } from '../../../types/types';
+import { CurrentUser, RawQuery } from '../../../types/types';
import { hasFilterParams, hasViewParams, parseUrlQuery, Query } from '../query';
import '../styles.css';
import { Facets, Project } from '../types';
import { translate } from '../../../helpers/l10n';
import { getComponentAdminUrl, getComponentOverviewUrl } from '../../../helpers/urls';
import { hasGlobalPermission } from '../../../helpers/users';
+import { AppState } from '../../../types/appstate';
import { ComponentQualifier } from '../../../types/component';
import { Permissions } from '../../../types/permissions';
-import { AppState, LoggedInUser } from '../../../types/types';
+import { LoggedInUser } from '../../../types/types';
export interface ApplicationCreationProps {
appState: AppState;
import SelectLegacy from '../../components/controls/SelectLegacy';
import QualifierIcon from '../../components/icons/QualifierIcon';
import { translate } from '../../helpers/l10n';
-import { AppState, Visibility } from '../../types/types';
+import { AppState } from '../../types/appstate';
+import { Visibility } from '../../types/types';
import BulkApplyTemplateModal from './BulkApplyTemplateModal';
import DeleteModal from './DeleteModal';
import { Alert } from '../../../components/ui/Alert';
import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
import { isDiffMetric } from '../../../helpers/measures';
+import { AppState } from '../../../types/appstate';
import { MetricKey } from '../../../types/metrics';
-import {
- AppState,
- Condition as ConditionType,
- Dict,
- Metric,
- QualityGate
-} from '../../../types/types';
+import { Condition as ConditionType, Dict, Metric, QualityGate } from '../../../types/types';
import Condition from './Condition';
import ConditionModal from './ConditionModal';
<div
className="display-flex-center permission-list-item padded"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="spacer-right"
name="John Doe"
size={32}
exports[`should render options correctly: user 1`] = `
<React.Fragment>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
hash="A"
name="name"
size={16}
className="pull-right spacer-top spacer-left spacer-right button-small"
onClick={[Function]}
/>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="pull-left spacer-right"
name="Luke Skywalker"
size={32}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Luke Skywalker"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Luke Skywalker"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Luke Skywalker"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="Luke Skywalker"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="john.doe"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
<div
className="display-flex-center"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="John Doe"
size={20}
key="john.doe"
onClick={[Function]}
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="spacer-right"
name="John Doe"
size={16}
key="highlighted"
onClick={[Function]}
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="spacer-right"
name="John Doe"
size={16}
import { IndexLink } from 'react-router';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { getGlobalSettingsUrl, getProjectSettingsUrl } from '../../../helpers/urls';
-import { AppState, Component } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { Component } from '../../../types/types';
import { getCategoryName } from '../utils';
import { ADDITIONAL_CATEGORIES } from './AdditionalCategories';
import CATEGORY_OVERRIDES from './CategoryOverrides';
AlmSettingsBindingStatus,
AlmSettingsBindingStatusType
} from '../../../../types/alm-settings';
+import { AppState } from '../../../../types/appstate';
import { ExtendedSettingDefinition } from '../../../../types/settings';
-import { AppState, Dict } from '../../../../types/types';
+import { Dict } from '../../../../types/types';
import AlmIntegrationRenderer from './AlmIntegrationRenderer';
interface Props extends Pick<WithRouterProps, 'location' | 'router'> {
import { getEdition, getEditionUrl } from '../../../../helpers/editions';
import { translate } from '../../../../helpers/l10n';
import { AlmKeys } from '../../../../types/alm-settings';
+import { AppState } from '../../../../types/appstate';
import { EditionKey } from '../../../../types/editions';
-import { AppState } from '../../../../types/types';
export interface CreationTooltipProps {
alm: AlmKeys;
AlmSettingsInstance,
ProjectAlmBindingResponse
} from '../../../../types/alm-settings';
+import { AppState } from '../../../../types/appstate';
import { EditionKey } from '../../../../types/editions';
-import { AppState, Dict } from '../../../../types/types';
+import { Dict } from '../../../../types/types';
export interface AlmSpecificFormProps {
alm: AlmKeys;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { fetchValues, setValues } from '../actions';
-
-jest.mock('../../../../api/settings', () => {
- const { mockSettingValue } = jest.requireActual('../../../../helpers/mocks/settings');
- return {
- getValues: jest.fn().mockResolvedValue([mockSettingValue()])
- };
-});
-
-it('should setValues correctly', () => {
- const dispatch = jest.fn();
- setValues(['test'], [{ key: 'test', value: 'foo' }])(dispatch);
- expect(dispatch).toHaveBeenCalledWith({
- component: undefined,
- settings: [
- {
- key: 'test',
- value: 'foo'
- }
- ],
- type: 'RECEIVE_VALUES',
- updateKeys: ['test']
- });
-});
-
-it('should fetchValue correclty', async () => {
- const dispatch = jest.fn();
- await fetchValues(['test'], 'foo')(dispatch);
- expect(dispatch).toHaveBeenCalledWith({
- component: 'foo',
- settings: [{ key: 'test' }],
- type: 'RECEIVE_VALUES',
- updateKeys: ['test']
- });
- expect(dispatch).toHaveBeenCalledWith({ type: 'CLOSE_ALL_GLOBAL_MESSAGES' });
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { Dispatch } from 'redux';
-import { getValues } from '../../../api/settings';
-import { closeAllGlobalMessages } from '../../../store/globalMessages';
-import { receiveValues } from './values';
-
-export function fetchValues(keys: string[], component?: string) {
- return (dispatch: Dispatch) =>
- getValues({ keys: keys.join(), component }).then(settings => {
- dispatch(receiveValues(keys, settings, component));
- dispatch(closeAllGlobalMessages());
- });
-}
-
-export function setValues(
- keys: string[],
- settings: Array<{ key: string; value?: string }>,
- component?: string
-) {
- return (dispatch: Dispatch) => dispatch(receiveValues(keys, settings, component));
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { combineReducers } from 'redux';
-import values, * as fromValues from './values';
-
-interface State {
- values: fromValues.State;
-}
-
-export default combineReducers({ values });
-
-export function getValue(state: State, key: string, component?: string) {
- return fromValues.getValue(state.values, key, component);
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { keyBy, omit } from 'lodash';
-import { combineReducers } from 'redux';
-import { ActionType } from '../../../store/utils/actions';
-import { SettingValue } from '../../../types/settings';
-import { Dict } from '../../../types/types';
-
-enum Actions {
- receiveValues = 'RECEIVE_VALUES'
-}
-
-type Action = ActionType<typeof receiveValues, Actions.receiveValues>;
-
-type SettingsState = Dict<SettingValue>;
-
-export interface State {
- components: Dict<SettingsState>;
- global: SettingsState;
-}
-
-export function receiveValues(
- updateKeys: string[],
- settings: Array<{ key: string; value?: string }>,
- component?: string
-) {
- return { type: Actions.receiveValues, updateKeys, settings, component };
-}
-
-function components(state: State['components'] = {}, action: Action) {
- const { component: key } = action;
- if (!key) {
- return state;
- }
- if (action.type === Actions.receiveValues) {
- const settingsByKey = keyBy(action.settings, 'key');
- return { ...state, [key]: { ...omit(state[key] || {}, action.updateKeys), ...settingsByKey } };
- }
- return state;
-}
-
-function global(state: State['components'] = {}, action: Action) {
- if (action.type === Actions.receiveValues) {
- if (action.component) {
- return state;
- }
- const settingsByKey = keyBy(action.settings, 'key');
- return { ...omit(state, action.updateKeys), ...settingsByKey };
- }
-
- return state;
-}
-
-export default combineReducers({ components, global });
-
-export function getValue(state: State, key: string, component?: string): SettingValue | undefined {
- if (component) {
- return state.components[component] && state.components[component][key];
- }
- return state.global[key];
-}
import { Alert } from '../../../components/ui/Alert';
import { toShortNotSoISOString } from '../../../helpers/dates';
import { translate } from '../../../helpers/l10n';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import PageActions from './PageActions';
export interface Props {
<td
className="thin nowrap text-middle"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
name="One"
size={36}
/>
<td
className="thin nowrap text-middle"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
name="One"
size={36}
/>
role="listitem"
title="Administrator"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
name="Administrator"
size={16}
/>
role="listitem"
title="Administrator"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
hash="7daf6c79d4802916d83f6266e24850af"
name="Administrator"
size={16}
<div
className="Select-value-label"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
hash="7daf6c79d4802916d83f6266e24850af"
name="Administrator"
size={16}
<div
className="Select-value-label"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
name="Administrator"
size={16}
/>
import addGlobalSuccessMessage from '../../app/utils/addGlobalSuccessMessage';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { isLoggedIn } from '../../helpers/users';
+import { AppState } from '../../types/appstate';
import { Branch } from '../../types/branch-like';
import { ComponentQualifier } from '../../types/component';
import { ComponentReportStatus } from '../../types/component-report';
-import { AppState, Component, CurrentUser } from '../../types/types';
+import { Component, CurrentUser } from '../../types/types';
import { withCurrentUser } from '../hoc/withCurrentUser';
import ComponentReportActionsRenderer from './ComponentReportActionsRenderer';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import DetachIcon from '../../components/icons/DetachIcon';
import { isSonarCloud } from '../../helpers/system';
-import { AppState } from '../../types/types';
+import { AppState } from '../../types/appstate';
interface OwnProps {
appState: AppState;
<span
className="text-top"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name=""
<span
className="text-top"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name=""
<span
className="text-top"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name=""
className="issue-comment-author"
title="John Doe"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name="John Doe"
className="issue-comment-author"
title="John Doe"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name="John Doe"
className="issue-comment-author"
title="John Doe"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name="John Doe"
className="issue-comment-author"
title="user.x_deleted.john.doe"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name="john.doe"
className="text-left text-top"
>
<p>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
hash="gravatarhash"
name="John Doe"
className="text-left text-top"
>
<p>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-right"
name="john.doe"
size={16}
item="luke"
key="luke"
>
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="spacer-right"
name="Skywalker"
size={16}
>
<span>
assigned_to
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-left little-spacer-right"
name="Luke Skywalker"
size={16}
>
<span>
assigned_to
- <Connect(Avatar)
+ <withAppStateContext(Avatar)
className="little-spacer-left little-spacer-right"
name="luke"
size={16}
import Level from '../../components/ui/Level';
import Rating from '../../components/ui/Rating';
import { formatMeasure } from '../../helpers/measures';
-import { getRatingTooltip } from './utils';
+import RatingTooltipContent from './RatingTooltipContent';
interface Props {
className?: string;
return <span className={className}>{formattedValue != null ? formattedValue : '–'}</span>;
}
- const tooltip = getRatingTooltip(metricKey, Number(value));
+ const tooltip = <RatingTooltipContent metricKey={metricKey} value={value} />;
const rating = <Rating small={small} value={value} />;
+
if (tooltip) {
return (
<Tooltip overlay={tooltip}>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 withAppStateContext from '../../app/components/app-state/withAppStateContext';
+import { translate, translateWithParameters } from '../../helpers/l10n';
+import { formatMeasure, isDiffMetric } from '../../helpers/measures';
+import { AppState } from '../../types/appstate';
+import { MetricKey } from '../../types/metrics';
+import { GlobalSettingKeys } from '../../types/settings';
+import { KNOWN_RATINGS } from './utils';
+
+const RATING_GRID_SIZE = 4;
+const DIFF_METRIC_PREFIX_LENGTH = 4;
+const PERCENT_MULTIPLIER = 100;
+const GRID_INDEX_OFFSET = 2; // Rating of 2 should get index 0 (threshold between 1 and 2)
+
+export interface RatingTooltipContentProps {
+ appState: AppState;
+ metricKey: MetricKey | string;
+ value: number | string;
+}
+
+function getMaintainabilityGrid(ratingGridSetting: string) {
+ const numbers = ratingGridSetting
+ .split(',')
+ .map(s => parseFloat(s))
+ .filter(n => !isNaN(n));
+
+ return numbers.length === RATING_GRID_SIZE ? numbers : [0, 0, 0, 0];
+}
+
+export function RatingTooltipContent(props: RatingTooltipContentProps) {
+ const {
+ appState: { settings },
+ metricKey,
+ value
+ } = props;
+
+ const finalMetricKey = isDiffMetric(metricKey)
+ ? metricKey.slice(DIFF_METRIC_PREFIX_LENGTH)
+ : metricKey;
+
+ if (!KNOWN_RATINGS.includes(finalMetricKey)) {
+ return null;
+ }
+
+ const rating = Number(value);
+ const ratingLetter = formatMeasure(value, 'RATING');
+
+ if (finalMetricKey !== 'sqale_rating' && finalMetricKey !== 'maintainability_rating') {
+ return <>{translate('metric', finalMetricKey, 'tooltip', ratingLetter)}</>;
+ }
+
+ const maintainabilityGrid = getMaintainabilityGrid(settings[GlobalSettingKeys.RatingGrid] ?? '');
+ const maintainabilityRatingThreshold =
+ maintainabilityGrid[Math.floor(rating) - GRID_INDEX_OFFSET];
+
+ return (
+ // Required to correctly satisfy the context typing
+ // eslint-disable-next-line react/jsx-no-useless-fragment
+ <>
+ {rating === 1
+ ? translateWithParameters(
+ 'metric.sqale_rating.tooltip.A',
+ formatMeasure(maintainabilityGrid[0] * PERCENT_MULTIPLIER, 'PERCENT')
+ )
+ : translateWithParameters(
+ 'metric.sqale_rating.tooltip',
+ ratingLetter,
+ formatMeasure(maintainabilityRatingThreshold * PERCENT_MULTIPLIER, 'PERCENT')
+ )}
+ </>
+ );
+}
+
+export default withAppStateContext(RatingTooltipContent);
import * as React from 'react';
import Measure from '../Measure';
-jest.mock('../../../helpers/measures', () => {
- const measures = jest.requireActual('../../../helpers/measures');
- measures.getRatingTooltip = jest.fn(() => 'tooltip');
- return measures;
-});
-
it('renders trivial measure', () => {
expect(
shallow(<Measure metricKey="coverage" metricType="PERCENT" value="73.0" />)
).toMatchSnapshot();
});
-it('renders known RATING', () => {
+it('renders RATING', () => {
expect(
shallow(<Measure metricKey="sqale_rating" metricType="RATING" value="3" />)
).toMatchSnapshot();
});
-it('renders unknown RATING', () => {
- expect(
- shallow(<Measure metricKey="foo_rating" metricType="RATING" value="4" />)
- ).toMatchSnapshot();
-});
-
it('renders undefined measure', () => {
expect(
shallow(<Measure metricKey="foo" metricType="PERCENT" value={undefined} />)
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 { shallow } from 'enzyme';
+import * as React from 'react';
+import { mockAppState } from '../../../helpers/testMocks';
+import { GlobalSettingKeys } from '../../../types/settings';
+import { MetricKey } from '../../../types/metrics';
+import { RatingTooltipContent, RatingTooltipContentProps } from '../RatingTooltipContent';
+
+it('should render maintainability correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('sqale rating');
+ expect(shallowRender({ value: 1 })).toMatchSnapshot('sqale rating A');
+ expect(shallowRender({ appState: mockAppState({ settings: {} }) })).toMatchSnapshot(
+ 'sqale rating default grid'
+ );
+ expect(
+ shallowRender({
+ appState: mockAppState({ settings: { [GlobalSettingKeys.RatingGrid]: '0,0.1' } })
+ })
+ ).toMatchSnapshot('sqale rating wrong grid');
+});
+
+it('should render other ratings correctly', () => {
+ expect(shallowRender({ metricKey: MetricKey.security_rating })).toMatchSnapshot(
+ 'security rating'
+ );
+ expect(shallowRender({ metricKey: MetricKey.new_security_rating })).toMatchSnapshot(
+ 'new security rating'
+ );
+});
+
+it('should ignore non-rating metrics', () => {
+ expect(shallowRender({ metricKey: MetricKey.code_smells }).type()).toBeNull();
+});
+
+function shallowRender(overrides: Partial<RatingTooltipContentProps> = {}) {
+ return shallow(
+ <RatingTooltipContent
+ appState={mockAppState({ settings: { [GlobalSettingKeys.RatingGrid]: '0.05,0.1,0.2,0.4' } })}
+ metricKey={MetricKey.sqale_rating}
+ value={2}
+ {...overrides}
+ />
+ );
+}
/>
`;
-exports[`renders known RATING 1`] = `
+exports[`renders RATING 1`] = `
<Tooltip
- overlay="tooltip"
+ overlay={
+ <withAppStateContext(RatingTooltipContent)
+ metricKey="sqale_rating"
+ value="3"
+ />
+ }
>
<span>
<Rating
–
</span>
`;
-
-exports[`renders unknown RATING 1`] = `
-<Rating
- value="4"
-/>
-`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render maintainability correctly: sqale rating 1`] = `
+<Fragment>
+ metric.sqale_rating.tooltip.B.5.0%
+</Fragment>
+`;
+
+exports[`should render maintainability correctly: sqale rating A 1`] = `
+<Fragment>
+ metric.sqale_rating.tooltip.A.5.0%
+</Fragment>
+`;
+
+exports[`should render maintainability correctly: sqale rating default grid 1`] = `
+<Fragment>
+ metric.sqale_rating.tooltip.B.0.0%
+</Fragment>
+`;
+
+exports[`should render maintainability correctly: sqale rating wrong grid 1`] = `
+<Fragment>
+ metric.sqale_rating.tooltip.B.0.0%
+</Fragment>
+`;
+
+exports[`should render other ratings correctly: new security rating 1`] = `
+<Fragment>
+ metric.security_rating.tooltip.B
+</Fragment>
+`;
+
+exports[`should render other ratings correctly: security rating 1`] = `
+<Fragment>
+ metric.security_rating.tooltip.B
+</Fragment>
+`;
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { getRatingTooltip as nextGetRatingTooltip, isDiffMetric } from '../../helpers/measures';
import { Dict, Measure, MeasureEnhanced, MeasureIntern, Metric } from '../../types/types';
-const KNOWN_RATINGS = [
+export const KNOWN_RATINGS = [
'sqale_rating',
'maintainability_rating', // Needed to provide the label for "new_maintainability_rating"
'reliability_rating',
export function getLeakValue(measure: MeasureIntern | undefined): string | undefined {
return measure?.period?.value;
}
-
-export function getRatingTooltip(metricKey: string, value: number): string | undefined {
- const finalMetricKey = isDiffMetric(metricKey) ? metricKey.substr(4) : metricKey;
- if (KNOWN_RATINGS.includes(finalMetricKey)) {
- return nextGetRatingTooltip(finalMetricKey, value);
- }
- return undefined;
-}
import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants';
import { translate } from '../../../../helpers/l10n';
import { AlmKeys } from '../../../../types/alm-settings';
-import { AppState } from '../../../../types/types';
+import { AppState } from '../../../../types/appstate';
import SentenceWithHighlights from '../../components/SentenceWithHighlights';
export interface PublishStepsProps {
import { Dictionary } from 'lodash';
import * as React from 'react';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
-import { AppState, Component } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { Component } from '../../../types/types';
import { CompilationInfo } from '../components/CompilationInfo';
import CreateYmlFile from '../components/CreateYmlFile';
import { BuildTools } from '../types';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { AlmKeys } from '../../../types/alm-settings';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import SentenceWithHighlights from './SentenceWithHighlights';
export interface AllSetProps {
*/
import * as React from 'react';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
-import { AppState, Component } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { Component } from '../../../types/types';
import { BuildTools } from '../types';
import CFamily from './commands/CFamily';
import DotNet from './commands/DotNet';
import withAppStateContext from '../../../app/components/app-state/withAppStateContext';
import { ClipboardIconButton } from '../../../components/controls/clipboard';
import { translate } from '../../../helpers/l10n';
-import { AppState } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
import FinishButton from '../components/FinishButton';
import GithubCFamilyExampleRepositories from '../components/GithubCFamilyExampleRepositories';
import Step from '../components/Step';
AlmSettingsInstance,
ProjectAlmBindingResponse
} from '../../../types/alm-settings';
-import { AppState, Component, CurrentUserSetting } from '../../../types/types';
+import { AppState } from '../../../types/appstate';
+import { Component, CurrentUserSetting } from '../../../types/types';
import AllSetStep from '../components/AllSetStep';
import JenkinsfileStep from './JenkinsfileStep';
import MultiBranchPipelineStep from './MultiBranchPipelineStep';
*/
import classNames from 'classnames';
import * as React from 'react';
-import { connect } from 'react-redux';
+import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import GenericAvatar from '../../components/ui/GenericAvatar';
-import { getGlobalSettingValue, Store } from '../../store/rootReducer';
+import { AppState } from '../../types/appstate';
+import { GlobalSettingKeys } from '../../types/settings';
+
+const GRAVATAR_SIZE_MULTIPLIER = 2;
interface Props {
+ appState: AppState;
className?: string;
- enableGravatar: boolean;
- gravatarServerUrl: string;
hash?: string;
name?: string;
size: number;
}
-function Avatar(props: Props) {
- if (!props.enableGravatar || !props.hash) {
- if (!props.name) {
+export function Avatar(props: Props) {
+ const {
+ appState: { settings },
+ className,
+ hash,
+ name,
+ size
+ } = props;
+
+ const enableGravatar = settings[GlobalSettingKeys.EnableGravatar] === 'true';
+
+ if (!enableGravatar || !hash) {
+ if (!name) {
return null;
}
- return <GenericAvatar className={props.className} name={props.name} size={props.size} />;
+ return <GenericAvatar className={className} name={name} size={size} />;
}
- const url = props.gravatarServerUrl
- .replace('{EMAIL_MD5}', props.hash)
- .replace('{SIZE}', String(props.size * 2));
+ const gravatarServerUrl = settings[GlobalSettingKeys.GravatarServerUrl] ?? '';
+ const url = gravatarServerUrl
+ .replace('{EMAIL_MD5}', hash)
+ .replace('{SIZE}', String(size * GRAVATAR_SIZE_MULTIPLIER));
return (
<img
- alt={props.name}
- className={classNames(props.className, 'rounded')}
- height={props.size}
+ alt={name}
+ className={classNames(className, 'rounded')}
+ height={size}
src={url}
- width={props.size}
+ width={size}
/>
);
}
-const mapStateToProps = (state: Store) => {
- const enableGravatar = getGlobalSettingValue(state, 'sonar.lf.enableGravatar');
- const gravatarServerUrl = getGlobalSettingValue(state, 'sonar.lf.gravatarServerUrl');
- return {
- enableGravatar: Boolean(enableGravatar && enableGravatar.value === 'true'),
- gravatarServerUrl: (gravatarServerUrl && gravatarServerUrl.value) || ''
- };
-};
-
-export default connect(mapStateToProps)(Avatar);
-
-export const unconnectedAvatar = Avatar;
+export default withAppStateContext(Avatar);
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { unconnectedAvatar as Avatar } from '../Avatar';
+import { mockAppState } from '../../../helpers/testMocks';
+import { GlobalSettingKeys } from '../../../types/settings';
+import { Avatar } from '../Avatar';
const gravatarServerUrl = 'http://example.com/{EMAIL_MD5}.jpg?s={SIZE}';
it('should be able to render with hash only', () => {
const avatar = shallow(
<Avatar
- enableGravatar={true}
- gravatarServerUrl={gravatarServerUrl}
+ appState={mockAppState({
+ settings: {
+ [GlobalSettingKeys.EnableGravatar]: 'true',
+ [GlobalSettingKeys.GravatarServerUrl]: gravatarServerUrl
+ }
+ })}
hash="7daf6c79d4802916d83f6266e24850af"
name="Foo"
size={30}
it('falls back to dummy avatar', () => {
const avatar = shallow(
- <Avatar enableGravatar={false} gravatarServerUrl="" name="Foo Bar" size={30} />
+ <Avatar appState={mockAppState({ settings: {} })} name="Foo Bar" size={30} />
);
expect(avatar).toMatchSnapshot();
});
it('do not fail when name is missing', () => {
const avatar = shallow(
- <Avatar enableGravatar={false} gravatarServerUrl="" name={undefined} size={30} />
+ <Avatar appState={mockAppState({ settings: {} })} name={undefined} size={30} />
);
expect(avatar.getElement()).toBeNull();
});
import * as React from 'react';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import { translate } from '../../helpers/l10n';
+import { AppState } from '../../types/appstate';
import { EditionKey } from '../../types/editions';
import { SystemUpgrade } from '../../types/system';
-import { AppState } from '../../types/types';
import { ResetButtonLink } from '../controls/buttons';
import Modal from '../controls/Modal';
import { Alert, AlertVariant } from '../ui/Alert';
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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.
- */
-// The body of the `gtm` function comes from Google Tag Manager docs; let's keep it like it was written.
-// @ts-ignore
-// prettier-ignore
-// eslint-disable-next-line
-const gtm = id => (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});const f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);}(window,document,'script','dataLayer',id));
-
-module.exports = { gtm };
return metricKey.indexOf('new_') === 0;
}
-function getRatingGrid(): string {
- // workaround cyclic dependencies
- const getStore = require('../app/utils/getStore').default;
- const { getGlobalSettingValue } = require('../store/rootReducer');
-
- const store = getStore();
- const settingValue = getGlobalSettingValue(store.getState(), 'sonar.technicalDebt.ratingGrid');
- return settingValue ? settingValue.value : '';
-}
-
-let maintainabilityRatingGrid: number[];
-
-function getMaintainabilityRatingGrid(): number[] {
- if (maintainabilityRatingGrid) {
- return maintainabilityRatingGrid;
- }
-
- const str = getRatingGrid();
- const numbers = str
- .split(',')
- .map(s => parseFloat(s))
- .filter(n => !isNaN(n));
-
- if (numbers.length === 4) {
- maintainabilityRatingGrid = numbers;
- } else {
- maintainabilityRatingGrid = [0, 0, 0, 0];
- }
-
- return maintainabilityRatingGrid;
-}
-
-function getMaintainabilityRatingTooltip(rating: number): string {
- const maintainabilityGrid = getMaintainabilityRatingGrid();
- const maintainabilityRatingThreshold = maintainabilityGrid[Math.floor(rating) - 2];
-
- if (rating < 2) {
- return translateWithParameters(
- 'metric.sqale_rating.tooltip.A',
- formatMeasure(maintainabilityGrid[0] * 100, 'PERCENT')
- );
- }
-
- const ratingLetter = formatMeasure(rating, 'RATING');
-
- return translateWithParameters(
- 'metric.sqale_rating.tooltip',
- ratingLetter,
- formatMeasure(maintainabilityRatingThreshold * 100, 'PERCENT')
- );
-}
-
-export function getRatingTooltip(metricKey: MetricKey | string, value: number | string): string {
- const ratingLetter = formatMeasure(value, 'RATING');
-
- const finalMetricKey = isDiffMetric(metricKey) ? metricKey.substr(4) : metricKey;
-
- return finalMetricKey === 'sqale_rating' || finalMetricKey === 'maintainability_rating'
- ? getMaintainabilityRatingTooltip(Number(value))
- : translate('metric', finalMetricKey, 'tooltip', ratingLetter);
-}
-
export function getDisplayMetrics(metrics: Metric[]) {
return metrics.filter(metric => !metric.hidden && !['DATA', 'DISTRIB'].includes(metric.type));
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Location } from 'history';
+import { Location, LocationDescriptor } from 'history';
import { InjectedRouter } from 'react-router';
import { createStore, Store } from 'redux';
import { DocumentationEntry } from '../apps/documentation/utils';
import { Exporter, Profile } from '../apps/quality-profiles/types';
+import { AppState } from '../types/appstate';
+import { EditionKey } from '../types/editions';
import { Language } from '../types/languages';
import { DumpStatus, DumpTask } from '../types/project-dump';
import { TaskStatuses } from '../types/tasks';
AlmApplication,
Analysis,
AnalysisEvent,
- AppState,
Condition,
CurrentUser,
FlowLocation,
export function mockAppState(overrides: Partial<AppState> = {}): AppState {
return {
- edition: 'community',
+ edition: EditionKey.community,
productionDatabase: true,
qualifiers: ['TRK'],
settings: {},
};
}
-export function mockRouter(overrides: { push?: Function; replace?: Function } = {}) {
+export function mockRouter(
+ overrides: {
+ push?: (loc: LocationDescriptor) => void;
+ replace?: (loc: LocationDescriptor) => void;
+ } = {}
+) {
return {
createHref: jest.fn(),
createPath: jest.fn(),
import getStore from '../app/utils/getStore';
import { RouteWithChildRoutes } from '../app/utils/startReactApp';
import { Store as State } from '../store/rootReducer';
-import { AppState, Dict, Metric } from '../types/types';
+import { AppState } from '../types/appstate';
+import { Dict, Metric } from '../types/types';
import { DEFAULT_METRICS } from './mocks/metrics';
import { mockAppState } from './testMocks';
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { combineReducers } from 'redux';
-import settingsApp, * as fromSettingsApp from '../apps/settings/store/rootReducer';
import { BranchLike } from '../types/branch-like';
import { CurrentUserSettingNames } from '../types/types';
import branches, * as fromBranches from './branches';
branches: fromBranches.State;
globalMessages: fromGlobalMessages.State;
users: fromUsers.State;
-
- // apps
- settingsApp: any;
};
export default combineReducers<Store>({
branches,
globalMessages,
- users,
-
- // apps
- settingsApp
+ users
});
export function getGlobalMessages(state: Store) {
return fromUsers.getCurrentUser(state.users);
}
-export function getGlobalSettingValue(state: Store, key: string) {
- return fromSettingsApp.getValue(state.settingsApp, key);
-}
-
export function getBranchStatusByBranchLike(
state: Store,
component: string,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 { EditionKey } from './editions';
+import { GlobalSettingKeys } from './settings';
+import { Extension } from './types';
+
+export interface AppState {
+ authenticationError?: boolean;
+ authorizationError?: boolean;
+ branchesEnabled?: boolean;
+ canAdmin?: boolean;
+ edition?: EditionKey;
+ globalPages?: Extension[];
+ projectImportFeatureEnabled?: boolean;
+ instanceUsesDefaultAdminCredentials?: boolean;
+ multipleAlmEnabled?: boolean;
+ needIssueSync?: boolean;
+ productionDatabase: boolean;
+ qualifiers: string[];
+ settings: { [key in GlobalSettingKeys]?: string };
+ standalone?: boolean;
+ version: string;
+ webAnalyticsJsPath?: string;
+}
import { Store as ReduxStore } from 'redux';
import { Location, Router } from '../components/hoc/withRouter';
import { Store } from '../store/rootReducer';
+import { AppState } from './appstate';
import { L10nBundle } from './l10n';
-import { AppState, CurrentUser, Dict } from './types';
+import { CurrentUser, Dict } from './types';
export enum AdminPageExtension {
GovernanceConsole = 'governance/views_console'
import { Dict } from './types';
export const enum SettingsKey {
+ AuditHouseKeeping = 'sonar.dbcleaner.auditHousekeeping',
DaysBeforeDeletingInactiveBranchesAndPRs = 'sonar.dbcleaner.daysBeforeDeletingInactiveBranchesAndPRs',
DefaultProjectVisibility = 'projects.default.visibility',
ServerBaseUrl = 'sonar.core.serverBaseURL',
PluginRiskConsent = 'sonar.plugins.risk.consent'
}
+export enum GlobalSettingKeys {
+ LogoUrl = 'sonar.lf.logoUrl',
+ LogoWidth = 'sonar.lf.logoWidthPx',
+ EnableGravatar = 'sonar.lf.enableGravatar',
+ GravatarServerUrl = 'sonar.lf.gravatarServerUrl',
+ RatingGrid = 'sonar.technicalDebt.ratingGrid',
+ DeveloperAggregatedInfoDisabled = 'sonar.developerAggregatedInfo.disabled',
+ UpdatecenterActivated = 'sonar.updatecenter.activate'
+}
+
export type SettingDefinitionAndValue = {
definition: ExtendedSettingDefinition;
settingValue?: SettingValue;
};
}
-export interface AppState {
- authenticationError?: boolean;
- authorizationError?: boolean;
- branchesEnabled?: boolean;
- canAdmin?: boolean;
- edition: 'community' | 'developer' | 'enterprise' | 'datacenter' | undefined;
- globalPages?: Extension[];
- projectImportFeatureEnabled?: boolean;
- instanceUsesDefaultAdminCredentials?: boolean;
- multipleAlmEnabled?: boolean;
- needIssueSync?: boolean;
- productionDatabase: boolean;
- qualifiers: string[];
- settings: Dict<string>;
- standalone?: boolean;
- version: string;
- webAnalyticsJsPath?: string;
-}
-
export interface Breadcrumb {
key: string;
name: string;