diff options
author | Jeremy Davis <jeremy.davis@sonarsource.com> | 2022-03-08 15:21:03 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-03-11 20:02:49 +0000 |
commit | accf42f839ee635c43c683de657de95ff249603e (patch) | |
tree | a168628c7c3e373c3b7bf43788532f799fce7368 /server/sonar-web/src/main | |
parent | d05858841336b062e4bef229998fe721f0f43a85 (diff) | |
download | sonarqube-accf42f839ee635c43c683de657de95ff249603e.tar.gz sonarqube-accf42f839ee635c43c683de657de95ff249603e.zip |
SONAR-15992 Remove Global Settings from redux
Diffstat (limited to 'server/sonar-web/src/main')
106 files changed, 830 insertions, 764 deletions
diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx index 8a690681ac7..0f05906ec49 100644 --- a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx @@ -24,8 +24,9 @@ import { getPendingPlugins } from '../../api/plugins'; 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'; diff --git a/server/sonar-web/src/main/js/app/components/App.tsx b/server/sonar-web/src/main/js/app/components/App.tsx index b7e22a74638..e7dcd7d0654 100644 --- a/server/sonar-web/src/main/js/app/components/App.tsx +++ b/server/sonar-web/src/main/js/app/components/App.tsx @@ -18,16 +18,16 @@ * 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> { @@ -68,8 +68,19 @@ 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" />; } @@ -79,7 +90,7 @@ export class App extends React.PureComponent<Props> { render() { return ( <> - <PageTracker>{this.props.enableGravatar && this.renderPreconnectLink()}</PageTracker> + <PageTracker>{this.renderPreconnectLink()}</PageTracker> {this.props.children} <KeyboardShortcutsModal /> </> @@ -87,13 +98,4 @@ export class App extends React.PureComponent<Props> { } } -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); diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx index 61aafd19ee9..e663080820a 100644 --- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx @@ -39,10 +39,11 @@ import { 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'; diff --git a/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx b/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx index da00469d33c..fae270252e2 100644 --- a/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx +++ b/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx @@ -23,8 +23,8 @@ import InstanceMessage from '../../components/common/InstanceMessage'; 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'; diff --git a/server/sonar-web/src/main/js/app/components/PageTracker.tsx b/server/sonar-web/src/main/js/app/components/PageTracker.tsx index 02c47e467fb..7ae1adbf0a9 100644 --- a/server/sonar-web/src/main/js/app/components/PageTracker.tsx +++ b/server/sonar-web/src/main/js/app/components/PageTracker.tsx @@ -19,19 +19,15 @@ */ 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; } @@ -43,54 +39,37 @@ export class PageTracker extends React.Component<Props, State> { 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)); diff --git a/server/sonar-web/src/main/js/app/components/StartupModal.tsx b/server/sonar-web/src/main/js/app/components/StartupModal.tsx index ff8216a49d1..5b7c3915672 100644 --- a/server/sonar-web/src/main/js/app/components/StartupModal.tsx +++ b/server/sonar-web/src/main/js/app/components/StartupModal.tsx @@ -28,8 +28,9 @@ import { hasMessage } from '../../helpers/l10n'; 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( diff --git a/server/sonar-web/src/main/js/app/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/App-test.tsx index 75b9d805b88..842155e2977 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/App-test.tsx @@ -19,23 +19,20 @@ */ 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'); }); @@ -44,17 +41,16 @@ it('should correctly set the scrollbar width as a custom property', () => { 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} + /> + ); } diff --git a/server/sonar-web/src/main/js/app/components/__tests__/PageTracker-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/PageTracker-test.tsx index 5da29b31463..5e7d3dfde6c 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/PageTracker-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/PageTracker-test.tsx @@ -19,7 +19,6 @@ */ 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'; @@ -33,7 +32,6 @@ jest.mock('../../../helpers/extensionsHandler', () => ({ getWebAnalyticsPageHandlerFromCache: jest.fn().mockReturnValue(undefined) })); -jest.mock('../../../helpers/analytics', () => ({ gtm: jest.fn() })); beforeAll(() => { jest.useFakeTimers(); }); @@ -52,7 +50,6 @@ it('should not trigger if no analytics system is given', () => { const wrapper = shallowRender(); expect(wrapper).toMatchSnapshot(); expect(installScript).not.toHaveBeenCalled(); - expect(gtm).not.toHaveBeenCalled(); }); it('should work for WebAnalytics plugin', () => { @@ -70,22 +67,6 @@ 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} /> diff --git a/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx b/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx index 1e2dcf47d4f..a8e130c84d2 100644 --- a/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx +++ b/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx @@ -19,7 +19,7 @@ */ import * as React from 'react'; -import { AppState } from '../../../types/types'; +import { AppState } from '../../../types/appstate'; const defaultAppState = { authenticationError: false, diff --git a/server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx b/server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx index 1d48066c47a..43c937b651f 100644 --- a/server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx +++ b/server/sonar-web/src/main/js/app/components/app-state/AppStateContextProvider.tsx @@ -20,29 +20,16 @@ */ 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); diff --git a/server/sonar-web/src/main/js/app/components/app-state/__tests__/AppStateContextProvider-test.tsx b/server/sonar-web/src/main/js/app/components/app-state/__tests__/AppStateContextProvider-test.tsx index 05d5fe55e21..57e3f0a1e0a 100644 --- a/server/sonar-web/src/main/js/app/components/app-state/__tests__/AppStateContextProvider-test.tsx +++ b/server/sonar-web/src/main/js/app/components/app-state/__tests__/AppStateContextProvider-test.tsx @@ -21,20 +21,18 @@ import { mount } from 'enzyme'; 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} />); } diff --git a/server/sonar-web/src/main/js/app/components/app-state/__tests__/__snapshots__/AppStateContextProvider-test.tsx.snap b/server/sonar-web/src/main/js/app/components/app-state/__tests__/__snapshots__/AppStateContextProvider-test.tsx.snap new file mode 100644 index 00000000000..1e81e16bd33 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/app-state/__tests__/__snapshots__/AppStateContextProvider-test.tsx.snap @@ -0,0 +1,19 @@ +// 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", + } + } +/> +`; diff --git a/server/sonar-web/src/main/js/app/components/app-state/__tests__/withAppStateContext-test.tsx b/server/sonar-web/src/main/js/app/components/app-state/__tests__/withAppStateContext-test.tsx index d6323fb0f81..78e009131c2 100644 --- a/server/sonar-web/src/main/js/app/components/app-state/__tests__/withAppStateContext-test.tsx +++ b/server/sonar-web/src/main/js/app/components/app-state/__tests__/withAppStateContext-test.tsx @@ -20,7 +20,7 @@ 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(); diff --git a/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx b/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx index 64107510cf1..fa432377d1e 100644 --- a/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx +++ b/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx @@ -20,7 +20,7 @@ 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 { diff --git a/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx b/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx index aeb29e3d72a..db90d3e0342 100644 --- a/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx +++ b/server/sonar-web/src/main/js/app/components/extensions/Extension.tsx @@ -27,8 +27,9 @@ import { getCurrentL10nBundle, translate } from '../../../helpers/l10n'; 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'; diff --git a/server/sonar-web/src/main/js/app/components/extensions/GlobalPageExtension.tsx b/server/sonar-web/src/main/js/app/components/extensions/GlobalPageExtension.tsx index d3b0559d35c..0c4206497c2 100644 --- a/server/sonar-web/src/main/js/app/components/extensions/GlobalPageExtension.tsx +++ b/server/sonar-web/src/main/js/app/components/extensions/GlobalPageExtension.tsx @@ -18,7 +18,7 @@ * 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'; diff --git a/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts b/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts index e86c3868d16..68a380c179a 100644 --- a/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts +++ b/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts @@ -70,6 +70,7 @@ import DateFormatter from '../../../components/intl/DateFormatter'; 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'; @@ -237,6 +238,7 @@ const exposeLibraries = () => { Radio, RadioToggle, Rating, + RatingTooltipContent, ReloadButton, ResetButtonLink, SearchBox, diff --git a/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx b/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx index 091fc03024d..e2b4e58a538 100644 --- a/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx +++ b/server/sonar-web/src/main/js/app/components/indexation/IndexationContextProvider.tsx @@ -19,8 +19,8 @@ */ /* 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'; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavLicenseNotif.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavLicenseNotif.tsx index 37770276d4f..7c9e217e1ab 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavLicenseNotif.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavLicenseNotif.tsx @@ -22,9 +22,9 @@ import { Link } from 'react-router'; 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 { diff --git a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx index 7f7d3c32843..7b33626b62a 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx @@ -30,9 +30,10 @@ import NavBarTabs from '../../../../components/ui/NavBarTabs'; 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'; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx index 2e619a4e049..3688efac0b2 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchLikeNavigation.tsx @@ -21,8 +21,9 @@ import classNames from 'classnames'; 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'; diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.tsx index 2706aa5cffb..6a952f3cc4b 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.tsx @@ -18,18 +18,21 @@ * 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; @@ -41,13 +44,4 @@ export function GlobalNavBranding({ customLogoUrl, customLogoWidth }: StateProps ); } -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); diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx index daf8af77711..230bdffe274 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx @@ -25,8 +25,9 @@ import Dropdown from '../../../../components/controls/Dropdown'; 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 { diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavBranding-test.tsx b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavBranding-test.tsx new file mode 100644 index 00000000000..f182f4d43e7 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavBranding-test.tsx @@ -0,0 +1,50 @@ +/* + * 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} />); +} diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNav-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNav-test.tsx.snap index cc530594fb3..964e19a9cdb 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNav-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNav-test.tsx.snap @@ -6,7 +6,7 @@ exports[`should render correctly: anonymous users 1`] = ` height={48} id="global-navigation" > - <Connect(GlobalNavBranding) /> + <withAppStateContext(GlobalNavBranding) /> <withAppStateContext(GlobalNavMenu) currentUser={ Object { @@ -47,7 +47,7 @@ exports[`should render correctly: logged in users 1`] = ` height={48} id="global-navigation" > - <Connect(GlobalNavBranding) /> + <withAppStateContext(GlobalNavBranding) /> <withAppStateContext(GlobalNavMenu) currentUser={ Object { diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavBranding-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavBranding-test.tsx.snap new file mode 100644 index 00000000000..5419fd45cbb --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavBranding-test.tsx.snap @@ -0,0 +1,52 @@ +// 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> +`; diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap index b509fb25cd2..f131dfc9555 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap @@ -60,7 +60,7 @@ exports[`should render the right interface for logged in user 1`] = ` href="#" title="Skywalker" > - <Connect(Avatar) + <withAppStateContext(Avatar) name="Skywalker" size={32} /> diff --git a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx index 8958a1c02d6..b399276ddf0 100644 --- a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx +++ b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx @@ -27,9 +27,10 @@ import SystemUpgradeButton from '../../../components/upgrade/SystemUpgradeButton 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'; diff --git a/server/sonar-web/src/main/js/app/index.ts b/server/sonar-web/src/main/js/app/index.ts index 88081787e94..f63328930f4 100644 --- a/server/sonar-web/src/main/js/app/index.ts +++ b/server/sonar-web/src/main/js/app/index.ts @@ -21,7 +21,7 @@ import { installExtensionsHandler, installWebAnalyticsHandler } from '../helpers 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(); diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx index 0fa4217844b..6854fa5fbb7 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx @@ -60,7 +60,8 @@ import webhooksRoutes from '../../apps/webhooks/routes'; 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'; diff --git a/server/sonar-web/src/main/js/apps/audit-logs/components/AuditApp.tsx b/server/sonar-web/src/main/js/apps/audit-logs/components/AuditApp.tsx index df64cb4c24b..da2e3196a93 100644 --- a/server/sonar-web/src/main/js/apps/audit-logs/components/AuditApp.tsx +++ b/server/sonar-web/src/main/js/apps/audit-logs/components/AuditApp.tsx @@ -18,60 +18,63 @@ * 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 }); @@ -85,10 +88,7 @@ export class AuditApp extends React.PureComponent<Props, State> { }; render() { - const { hasGovernanceExtension, ...auditAppRendererProps } = this.state; - const { auditHousekeepingPolicy } = this.props; - - if (!hasGovernanceExtension) { + if (!this.hasGovernanceExtension()) { return null; } @@ -97,20 +97,8 @@ export class AuditApp extends React.PureComponent<Props, State> { 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); diff --git a/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-test.tsx b/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-test.tsx index 1b9740af760..baac6e88cda 100644 --- a/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-test.tsx +++ b/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-test.tsx @@ -20,30 +20,41 @@ 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', () => { @@ -76,11 +87,22 @@ it('should handle predefined 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} /> diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx index d1cc1e097c7..de54ad439fb 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx @@ -24,7 +24,7 @@ import { ClearButton } from '../../../components/controls/buttons'; 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; diff --git a/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordApp.tsx b/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordApp.tsx index 5bb5d8b601b..3582400a715 100644 --- a/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordApp.tsx +++ b/server/sonar-web/src/main/js/apps/change-admin-password/ChangeAdminPasswordApp.tsx @@ -21,7 +21,7 @@ import * as React from 'react'; 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'; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx index fa64d3cee56..5c282e3ee5f 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx @@ -26,7 +26,8 @@ import DeferredSpinner from '../../../components/ui/DeferredSpinner'; 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; diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx index 3cd2701916e..4471a7923b0 100644 --- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectModeSelection.tsx @@ -24,7 +24,7 @@ import ChevronsIcon from '../../../components/icons/ChevronsIcon'; 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 { diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx index 6f250980373..4c4a3d7bbb6 100644 --- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPage.tsx @@ -27,7 +27,8 @@ import { whenLoggedIn } from '../../../components/hoc/whenLoggedIn'; 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'; diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap index f5a71c751a2..073c704f38a 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesApp-test.tsx.snap @@ -65,7 +65,7 @@ exports[`should show warnning when not all projects are accessible 1`] = ` displayReset={true} onReset={[Function]} /> - <Connect(Sidebar) + <withAppStateContext(Sidebar) component={ Object { "breadcrumbs": Array [], diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx index fd455980dc0..4a80bd6dd96 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx @@ -18,9 +18,9 @@ * 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 { @@ -29,6 +29,7 @@ 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'; @@ -48,6 +49,7 @@ import TagFacet from './TagFacet'; import TypeFacet from './TypeFacet'; export interface Props { + appState: AppState; branchLike?: BranchLike; component: Component | undefined; createdAfterIncludesTime: boolean; @@ -64,7 +66,6 @@ export interface Props { referencedLanguages: Dict<ReferencedLanguage>; referencedRules: Dict<ReferencedRule>; referencedUsers: Dict<UserBase>; - disableDeveloperAggregatedInfo: boolean; } export class Sidebar extends React.PureComponent<Props> { @@ -108,6 +109,7 @@ export class Sidebar extends React.PureComponent<Props> { render() { const { + appState: { settings }, component, createdAfterIncludesTime, facets, @@ -116,6 +118,9 @@ export class Sidebar extends React.PureComponent<Props> { branchLike } = this.props; + const disableDeveloperAggregatedInfo = + settings[GlobalSettingKeys.DeveloperAggregatedInfoDisabled] === 'true'; + const branch = (isBranch(branchLike) && branchLike.name) || (isPullRequest(branchLike) && branchLike.branch) || @@ -255,7 +260,7 @@ export class Sidebar extends React.PureComponent<Props> { /> )} {this.renderComponentFacets()} - {!this.props.myIssues && !this.props.disableDeveloperAggregatedInfo && ( + {!this.props.myIssues && !disableDeveloperAggregatedInfo && ( <AssigneeFacet assigned={query.assigned} assignees={query.assignees} @@ -269,7 +274,7 @@ export class Sidebar extends React.PureComponent<Props> { stats={facets.assignees} /> )} - {displayAuthorFacet && !this.props.disableDeveloperAggregatedInfo && ( + {displayAuthorFacet && !disableDeveloperAggregatedInfo && ( <AuthorFacet author={query.author} component={component} @@ -287,14 +292,4 @@ export class Sidebar extends React.PureComponent<Props> { } } -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); diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx index 8e7b29ca2af..916e17efba1 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-test.tsx @@ -21,12 +21,11 @@ import { shallow, ShallowWrapper } from 'enzyme'; 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(); @@ -52,16 +51,13 @@ it('should render facets when my issues are selected', () => { }); 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']>) => { @@ -69,6 +65,9 @@ const renderSidebar = (props?: Partial<Sidebar['props']>) => { mapChildren( shallow<Sidebar>( <Sidebar + appState={mockAppState({ + settings: { [GlobalSettingKeys.DeveloperAggregatedInfoDisabled]: 'false' } + })} component={undefined} createdAfterIncludesTime={false} facets={{}} @@ -84,7 +83,6 @@ const renderSidebar = (props?: Partial<Sidebar['props']>) => { referencedLanguages={{}} referencedRules={{}} referencedUsers={{}} - disableDeveloperAggregatedInfo={false} {...props} /> ) diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap index 6e5f27e4e47..ee6aa25506d 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.tsx.snap @@ -41,7 +41,7 @@ exports[`should render 1`] = ` exports[`test behavior should correctly render facet item 1`] = ` <React.Fragment> - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="Name Baz" size={16} @@ -52,7 +52,7 @@ exports[`test behavior should correctly render facet item 1`] = ` exports[`test behavior should correctly render facet item 2`] = ` <React.Fragment> - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="foo" size={16} @@ -63,7 +63,7 @@ exports[`test behavior should correctly render facet item 2`] = ` 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} @@ -80,7 +80,7 @@ exports[`test behavior should correctly render search result correctly 1`] = ` exports[`test behavior should correctly render search result correctly 2`] = ` <React.Fragment> - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="foo" size={16} diff --git a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx b/server/sonar-web/src/main/js/apps/marketplace/MarketplaceAppContainer.tsx index 501b4f90072..77050b3ac91 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/MarketplaceAppContainer.tsx @@ -18,35 +18,27 @@ * 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 { AppState } from '../../types/appstate'; import { EditionKey } from '../../types/editions'; -import { AppState, RawQuery } from '../../types/types'; -import { fetchValues } from '../settings/store/actions'; +import { GlobalSettingKeys } from '../../types/settings'; +import { RawQuery } from '../../types/types'; import App from './App'; -interface OwnProps { +export interface MarketplaceAppContainerProps { 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']); - }); +export function MarketplaceAppContainer(props: MarketplaceAppContainerProps) { + const { appState, location } = props; const propsToPass = { - location: props.location, - updateCenterActive: props.updateCenterActive, - currentEdition: props.appState.edition as EditionKey, - standaloneMode: props.appState.standalone + location, + updateCenterActive: appState.settings[GlobalSettingKeys.UpdatecenterActivated] === 'true', + currentEdition: appState.edition as EditionKey, + standaloneMode: appState.standalone }; return ( @@ -62,13 +54,4 @@ function WithAdminContext(props: StateToProps & OwnProps) { ); } -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)); +export default withAppStateContext(MarketplaceAppContainer); diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/AppContainer-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/AppContainer-test.tsx deleted file mode 100644 index d306400526f..00000000000 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/AppContainer-test.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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'); - }); -}); diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceAppContainer-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceAppContainer-test.tsx new file mode 100644 index 00000000000..476b4035805 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/MarketplaceAppContainer-test.tsx @@ -0,0 +1,48 @@ +/* + * 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} + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/MarketplaceAppContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/MarketplaceAppContainer-test.tsx.snap new file mode 100644 index 00000000000..58d697aede5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/MarketplaceAppContainer-test.tsx.snap @@ -0,0 +1,54 @@ +// 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} +/> +`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/routes.ts b/server/sonar-web/src/main/js/apps/marketplace/routes.ts index c4d59b1b922..211f08c5a06 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/routes.ts +++ b/server/sonar-web/src/main/js/apps/marketplace/routes.ts @@ -21,7 +21,7 @@ import { lazyLoadComponent } from '../../components/lazyLoadComponent'; const routes = [ { - indexRoute: { component: lazyLoadComponent(() => import('./AppContainer')) } + indexRoute: { component: lazyLoadComponent(() => import('./MarketplaceAppContainer')) } } ]; diff --git a/server/sonar-web/src/main/js/apps/overview/components/App.tsx b/server/sonar-web/src/main/js/apps/overview/components/App.tsx index 665b63e6ddc..0477fff99e9 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/App.tsx @@ -24,9 +24,10 @@ import { Router, withRouter } from '../../../components/hoc/withRouter'; 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')); diff --git a/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx b/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx index ec83f1ab978..2eee2295c08 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/IssueRating.tsx @@ -19,7 +19,8 @@ */ 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'; @@ -50,10 +51,9 @@ function renderRatingLink(props: IssueRatingProps) { } 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} diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap index f8f24334651..6bc60fbd24e 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/IssueRating-test.tsx.snap @@ -8,7 +8,12 @@ exports[`should render correctly for bugs 1`] = ` metric_domain.Reliability </span> <Tooltip - overlay="metric.reliability_rating.tooltip.A" + overlay={ + <withAppStateContext(RatingTooltipContent) + metricKey="reliability_rating" + value="1.0" + /> + } > <span> <DrilldownLink @@ -43,7 +48,12 @@ exports[`should render correctly for bugs 2`] = ` metric_domain.Reliability </span> <Tooltip - overlay="metric.reliability_rating.tooltip.A" + overlay={ + <withAppStateContext(RatingTooltipContent) + metricKey="new_reliability_rating" + value="1.0" + /> + } > <span> <DrilldownLink @@ -78,7 +88,12 @@ exports[`should render correctly for code smells 1`] = ` metric_domain.Maintainability </span> <Tooltip - overlay="metric.sqale_rating.tooltip.A.0.0%" + overlay={ + <withAppStateContext(RatingTooltipContent) + metricKey="sqale_rating" + value="1.0" + /> + } > <span> <DrilldownLink @@ -113,7 +128,12 @@ exports[`should render correctly for code smells 2`] = ` 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 @@ -148,7 +168,12 @@ exports[`should render correctly for vulnerabilities 1`] = ` metric_domain.Security </span> <Tooltip - overlay="metric.security_rating.tooltip.A" + overlay={ + <withAppStateContext(RatingTooltipContent) + metricKey="security_rating" + value="1.0" + /> + } > <span> <DrilldownLink @@ -183,7 +208,12 @@ exports[`should render correctly for vulnerabilities 2`] = ` metric_domain.Security </span> <Tooltip - overlay="metric.security_rating.tooltip.A" + overlay={ + <withAppStateContext(RatingTooltipContent) + metricKey="new_security_rating" + value="1.0" + /> + } > <span> <DrilldownLink diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx index f5fea8b899a..178579337eb 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx @@ -24,7 +24,8 @@ import { getPermissionTemplates } from '../../../api/permissions'; 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'; diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx b/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx index 0f2d5f98748..8c4a4eb19f0 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersList.tsx @@ -20,8 +20,9 @@ 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 { diff --git a/server/sonar-web/src/main/js/apps/permissions/shared/components/__tests__/__snapshots__/UserHolder-test.tsx.snap b/server/sonar-web/src/main/js/apps/permissions/shared/components/__tests__/__snapshots__/UserHolder-test.tsx.snap index a132c867d35..10307a9b053 100644 --- a/server/sonar-web/src/main/js/apps/permissions/shared/components/__tests__/__snapshots__/UserHolder-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/permissions/shared/components/__tests__/__snapshots__/UserHolder-test.tsx.snap @@ -90,7 +90,7 @@ exports[`should render correctly: default 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="text-middle big-spacer-right flex-0" name="John Doe" size={36} diff --git a/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx b/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx index d515f2294e7..4c123c72f03 100644 --- a/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/projectBaseline/components/App.tsx @@ -27,9 +27,9 @@ import AlertSuccessIcon from '../../../components/icons/AlertSuccessIcon'; 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, diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformation.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformation.tsx index 3e855acdfa6..aabaf35c730 100644 --- a/server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformation.tsx +++ b/server/sonar-web/src/main/js/apps/projectBranches/components/LifetimeInformation.tsx @@ -20,8 +20,8 @@ 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 { diff --git a/server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx b/server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx index cd067423adf..29043ef9a85 100644 --- a/server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx +++ b/server/sonar-web/src/main/js/apps/projectDump/ProjectDumpApp.tsx @@ -23,9 +23,10 @@ import { getStatus } from '../../api/project-dump'; 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'; diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx index 38fa2a56eaf..ed14cc84902 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx @@ -33,8 +33,9 @@ import { translate } from '../../../helpers/l10n'; 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'; diff --git a/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx b/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx index 4fe00dc2785..dac6645952c 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx @@ -27,9 +27,10 @@ import { Router, withRouter } from '../../../components/hoc/withRouter'; 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; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx index 4d19a37e27a..81640c8c164 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/Search.tsx @@ -29,7 +29,8 @@ import SearchBox from '../../components/controls/SearchBox'; 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'; diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx index ef92de4334b..14f98297bf7 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx @@ -27,14 +27,9 @@ import ModalButton from '../../../components/controls/ModalButton'; 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'; diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap index 5ddfe0ab0bc..096ddf281a6 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap @@ -25,7 +25,7 @@ exports[`should render correctly: user 1`] = ` <div className="display-flex-center permission-list-item padded" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="spacer-right" name="John Doe" size={32} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsAddModalRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsAddModalRenderer-test.tsx.snap index d479967b848..5994dbc36f5 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsAddModalRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsAddModalRenderer-test.tsx.snap @@ -334,7 +334,7 @@ exports[`should render options correctly: group 1`] = ` exports[`should render options correctly: user 1`] = ` <React.Fragment> - <Connect(Avatar) + <withAppStateContext(Avatar) hash="A" name="name" size={16} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap index 305d0624f07..fc1970f5c72 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap @@ -8,7 +8,7 @@ exports[`renders 1`] = ` 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} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap index 2475a3723b9..125a1fba9b4 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap @@ -10,7 +10,7 @@ exports[`should render correctly: default 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="Luke Skywalker" size={20} @@ -49,7 +49,7 @@ exports[`should render correctly: default 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="Luke Skywalker" size={20} @@ -88,7 +88,7 @@ exports[`should render correctly: default 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="John Doe" size={20} @@ -130,7 +130,7 @@ exports[`should render correctly: default 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="John Doe" size={20} @@ -172,7 +172,7 @@ exports[`should render correctly: default 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="John Doe" size={20} @@ -255,7 +255,7 @@ exports[`should render correctly: show full list 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="Luke Skywalker" size={20} @@ -294,7 +294,7 @@ exports[`should render correctly: show full list 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="Luke Skywalker" size={20} @@ -333,7 +333,7 @@ exports[`should render correctly: show full list 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="John Doe" size={20} @@ -375,7 +375,7 @@ exports[`should render correctly: show full list 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="John Doe" size={20} @@ -417,7 +417,7 @@ exports[`should render correctly: show full list 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="John Doe" size={20} @@ -459,7 +459,7 @@ exports[`should render correctly: show full list 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="john.doe" size={20} @@ -501,7 +501,7 @@ exports[`should render correctly: show full list 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="John Doe" size={20} @@ -592,7 +592,7 @@ exports[`should render correctly: show full list 1`] = ` <div className="display-flex-center" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="John Doe" size={20} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/__snapshots__/AssigneeSelectionRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/__snapshots__/AssigneeSelectionRenderer-test.tsx.snap index 087a963a423..7d05daab361 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/__snapshots__/AssigneeSelectionRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/assignee/__tests__/__snapshots__/AssigneeSelectionRenderer-test.tsx.snap @@ -71,7 +71,7 @@ exports[`should render correctly: open with results 1`] = ` key="john.doe" onClick={[Function]} > - <Connect(Avatar) + <withAppStateContext(Avatar) className="spacer-right" name="John Doe" size={16} @@ -83,7 +83,7 @@ exports[`should render correctly: open with results 1`] = ` key="highlighted" onClick={[Function]} > - <Connect(Avatar) + <withAppStateContext(Avatar) className="spacer-right" name="John Doe" size={16} diff --git a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx index 69722c98a05..bfc388ce5b2 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx @@ -23,7 +23,8 @@ import * as React from 'react'; 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'; diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx index 7a54c0bc12b..dd57a2c7464 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx @@ -34,8 +34,9 @@ import { 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'> { diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/CreationTooltip.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/CreationTooltip.tsx index 9f53a41396f..eaca7a893d2 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/CreationTooltip.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/CreationTooltip.tsx @@ -24,8 +24,8 @@ import Tooltip from '../../../../components/controls/Tooltip'; 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; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx index cd60e263b9d..2da3d4765a7 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx @@ -32,8 +32,9 @@ import { 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; diff --git a/server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts b/server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts deleted file mode 100644 index 5fc7dd885cf..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/store/__tests__/actions-test.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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' }); -}); diff --git a/server/sonar-web/src/main/js/apps/settings/store/actions.ts b/server/sonar-web/src/main/js/apps/settings/store/actions.ts deleted file mode 100644 index 2c5430e48e7..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/store/actions.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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)); -} diff --git a/server/sonar-web/src/main/js/apps/settings/store/values.ts b/server/sonar-web/src/main/js/apps/settings/store/values.ts deleted file mode 100644 index 478a873c02d..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/store/values.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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]; -} diff --git a/server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx b/server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx index 486c80de4c3..b36b03b3c9f 100644 --- a/server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx +++ b/server/sonar-web/src/main/js/apps/system/components/PageHeader.tsx @@ -23,7 +23,7 @@ import { ClipboardButton } from '../../../components/controls/clipboard'; 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 { diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap index 5e34a0c7b61..9d62cbde539 100644 --- a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UserListItem-test.tsx.snap @@ -5,7 +5,7 @@ exports[`should render correctly 1`] = ` <td className="thin nowrap text-middle" > - <Connect(Avatar) + <withAppStateContext(Avatar) name="One" size={36} /> @@ -92,7 +92,7 @@ exports[`should render correctly without last connection date 1`] = ` <td className="thin nowrap text-middle" > - <Connect(Avatar) + <withAppStateContext(Avatar) name="One" size={36} /> diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap index c3e1c99564e..0d6493812cf 100644 --- a/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/__snapshots__/UsersSelectSearch-test.tsx.snap @@ -88,7 +88,7 @@ exports[`UsersSelectSearchOption should render correctly with email instead of h role="listitem" title="Administrator" > - <Connect(Avatar) + <withAppStateContext(Avatar) name="Administrator" size={16} /> @@ -113,7 +113,7 @@ exports[`UsersSelectSearchOption should render correctly without all parameters role="listitem" title="Administrator" > - <Connect(Avatar) + <withAppStateContext(Avatar) hash="7daf6c79d4802916d83f6266e24850af" name="Administrator" size={16} @@ -139,7 +139,7 @@ exports[`UsersSelectSearchValue should render correctly with a user 1`] = ` <div className="Select-value-label" > - <Connect(Avatar) + <withAppStateContext(Avatar) hash="7daf6c79d4802916d83f6266e24850af" name="Administrator" size={16} @@ -166,7 +166,7 @@ exports[`UsersSelectSearchValue should render correctly with email instead of ha <div className="Select-value-label" > - <Connect(Avatar) + <withAppStateContext(Avatar) name="Administrator" size={16} /> diff --git a/server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx b/server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx index d4403291c37..88c25f5d75d 100644 --- a/server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx +++ b/server/sonar-web/src/main/js/components/controls/ComponentReportActions.tsx @@ -27,10 +27,11 @@ import withAppStateContext from '../../app/components/app-state/withAppStateCont 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'; diff --git a/server/sonar-web/src/main/js/components/docs/DocLink.tsx b/server/sonar-web/src/main/js/components/docs/DocLink.tsx index 1263177bf8f..77e6cbd833c 100644 --- a/server/sonar-web/src/main/js/components/docs/DocLink.tsx +++ b/server/sonar-web/src/main/js/components/docs/DocLink.tsx @@ -22,7 +22,7 @@ import { Link } from 'react-router'; 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; diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap index 50e8aacb158..69b5f81e7f5 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap @@ -32,7 +32,7 @@ exports[`should open the popup when the button is clicked 2`] = ` <span className="text-top" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" hash="gravatarhash" name="" @@ -108,7 +108,7 @@ exports[`should render with the action 1`] = ` <span className="text-top" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" hash="gravatarhash" name="" @@ -133,7 +133,7 @@ exports[`should render without the action when the correct rights are missing 1` <span className="text-top" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" hash="gravatarhash" name="" diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.tsx.snap index 1a6849c827e..a6864ce05b5 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.tsx.snap @@ -20,7 +20,7 @@ exports[`should open the right popups when the buttons are clicked 3`] = ` className="issue-comment-author" title="John Doe" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" hash="gravatarhash" name="John Doe" @@ -118,7 +118,7 @@ exports[`should render correctly a comment that is not updatable 1`] = ` className="issue-comment-author" title="John Doe" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" hash="gravatarhash" name="John Doe" @@ -160,7 +160,7 @@ exports[`should render correctly a comment that is updatable 1`] = ` className="issue-comment-author" title="John Doe" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" hash="gravatarhash" name="John Doe" @@ -255,7 +255,7 @@ exports[`should render correctly a comment with a deleted author 1`] = ` className="issue-comment-author" title="user.x_deleted.john.doe" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" hash="gravatarhash" name="john.doe" diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.tsx.snap index 2bf516e280b..1de31a413cc 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.tsx.snap @@ -39,7 +39,7 @@ exports[`should render the changelog popup correctly 1`] = ` className="text-left text-top" > <p> - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" hash="gravatarhash" name="John Doe" @@ -104,7 +104,7 @@ exports[`should render the changelog popup when we have a deleted user 1`] = ` className="text-left text-top" > <p> - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-right" name="john.doe" size={16} diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetAssigneePopup-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetAssigneePopup-test.tsx.snap index b539ed1acf0..88c0d0ba1cf 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetAssigneePopup-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetAssigneePopup-test.tsx.snap @@ -33,7 +33,7 @@ exports[`should render correctly 1`] = ` item="luke" key="luke" > - <Connect(Avatar) + <withAppStateContext(Avatar) className="spacer-right" name="Skywalker" size={16} diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SimilarIssuesPopup-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SimilarIssuesPopup-test.tsx.snap index 595727bceb6..aabffd0885e 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SimilarIssuesPopup-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SimilarIssuesPopup-test.tsx.snap @@ -127,7 +127,7 @@ exports[`should render correctly when assigned 1`] = ` > <span> assigned_to - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-left little-spacer-right" name="Luke Skywalker" size={16} @@ -143,7 +143,7 @@ exports[`should render correctly when assigned 2`] = ` > <span> assigned_to - <Connect(Avatar) + <withAppStateContext(Avatar) className="little-spacer-left little-spacer-right" name="luke" size={16} diff --git a/server/sonar-web/src/main/js/components/measure/Measure.tsx b/server/sonar-web/src/main/js/components/measure/Measure.tsx index 9ed89f92189..8abc4e62781 100644 --- a/server/sonar-web/src/main/js/components/measure/Measure.tsx +++ b/server/sonar-web/src/main/js/components/measure/Measure.tsx @@ -22,7 +22,7 @@ import Tooltip from '../../components/controls/Tooltip'; 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; @@ -57,8 +57,9 @@ export default function Measure({ 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}> diff --git a/server/sonar-web/src/main/js/components/measure/RatingTooltipContent.tsx b/server/sonar-web/src/main/js/components/measure/RatingTooltipContent.tsx new file mode 100644 index 00000000000..855449adc3a --- /dev/null +++ b/server/sonar-web/src/main/js/components/measure/RatingTooltipContent.tsx @@ -0,0 +1,94 @@ +/* + * 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); diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx b/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx index 5bbf237365c..5fbc431a0b7 100644 --- a/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx +++ b/server/sonar-web/src/main/js/components/measure/__tests__/Measure-test.tsx @@ -21,12 +21,6 @@ import { shallow } from 'enzyme'; 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" />) @@ -45,18 +39,12 @@ it('renders LEVEL', () => { ).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} />) diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/RatingTooltipContent-test.tsx b/server/sonar-web/src/main/js/components/measure/__tests__/RatingTooltipContent-test.tsx new file mode 100644 index 00000000000..4a490d6481f --- /dev/null +++ b/server/sonar-web/src/main/js/components/measure/__tests__/RatingTooltipContent-test.tsx @@ -0,0 +1,62 @@ +/* + * 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} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap index 437ca28a87b..29e685efb6b 100644 --- a/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap @@ -6,9 +6,14 @@ exports[`renders LEVEL 1`] = ` /> `; -exports[`renders known RATING 1`] = ` +exports[`renders RATING 1`] = ` <Tooltip - overlay="tooltip" + overlay={ + <withAppStateContext(RatingTooltipContent) + metricKey="sqale_rating" + value="3" + /> + } > <span> <Rating @@ -35,9 +40,3 @@ exports[`renders undefined measure 1`] = ` – </span> `; - -exports[`renders unknown RATING 1`] = ` -<Rating - value="4" -/> -`; diff --git a/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/RatingTooltipContent-test.tsx.snap b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/RatingTooltipContent-test.tsx.snap new file mode 100644 index 00000000000..2ce6e0cef37 --- /dev/null +++ b/server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/RatingTooltipContent-test.tsx.snap @@ -0,0 +1,37 @@ +// 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> +`; diff --git a/server/sonar-web/src/main/js/components/measure/utils.ts b/server/sonar-web/src/main/js/components/measure/utils.ts index aea8911ff2e..8fb375bc9a0 100644 --- a/server/sonar-web/src/main/js/components/measure/utils.ts +++ b/server/sonar-web/src/main/js/components/measure/utils.ts @@ -17,10 +17,9 @@ * 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', @@ -39,11 +38,3 @@ export function enhanceMeasure(measure: Measure, metrics: Dict<Metric>): Measure 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; -} diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PublishSteps.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PublishSteps.tsx index 3d8c44c888b..13d10fb06ec 100644 --- a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PublishSteps.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/PublishSteps.tsx @@ -25,7 +25,7 @@ import { Alert } from '../../../../components/ui/Alert'; 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 { diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/AnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/AnalysisCommand.tsx index 31c332fa14f..b0e372c194f 100644 --- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/AnalysisCommand.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/AnalysisCommand.tsx @@ -20,7 +20,8 @@ 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'; diff --git a/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx b/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx index 20b11cbb19d..1a8b2bcf243 100644 --- a/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx @@ -22,7 +22,7 @@ import withAppStateContext from '../../../app/components/app-state/withAppStateC 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 { diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx index 4038f37237e..d0bd6b5c0b2 100644 --- a/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx @@ -19,7 +19,8 @@ */ 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'; diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx b/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx index bc6ec8c0039..7b83e9de998 100644 --- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/YmlFileStep.tsx @@ -22,7 +22,7 @@ import { FormattedMessage } from 'react-intl'; 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'; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx index 15ddfb7fa86..bb65db801d6 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx @@ -28,7 +28,8 @@ import { 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'; diff --git a/server/sonar-web/src/main/js/components/ui/Avatar.tsx b/server/sonar-web/src/main/js/components/ui/Avatar.tsx index b9c60bcceef..82eb5fe5b56 100644 --- a/server/sonar-web/src/main/js/components/ui/Avatar.tsx +++ b/server/sonar-web/src/main/js/components/ui/Avatar.tsx @@ -19,51 +19,53 @@ */ 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); diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/Avatar-test.tsx b/server/sonar-web/src/main/js/components/ui/__tests__/Avatar-test.tsx index b5d138f2e25..b9bb9fec681 100644 --- a/server/sonar-web/src/main/js/components/ui/__tests__/Avatar-test.tsx +++ b/server/sonar-web/src/main/js/components/ui/__tests__/Avatar-test.tsx @@ -19,15 +19,21 @@ */ 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} @@ -38,14 +44,14 @@ it('should be able to render with hash only', () => { 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(); }); diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx index 6826d56c336..d78e057f2f6 100644 --- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx +++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx @@ -21,9 +21,9 @@ import { filter, flatMap, isEmpty, negate } from 'lodash'; 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'; diff --git a/server/sonar-web/src/main/js/helpers/analytics.js b/server/sonar-web/src/main/js/helpers/analytics.js deleted file mode 100644 index 317c675157a..00000000000 --- a/server/sonar-web/src/main/js/helpers/analytics.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 }; diff --git a/server/sonar-web/src/main/js/helpers/measures.ts b/server/sonar-web/src/main/js/helpers/measures.ts index 3a6f15040a8..cedf304914a 100644 --- a/server/sonar-web/src/main/js/helpers/measures.ts +++ b/server/sonar-web/src/main/js/helpers/measures.ts @@ -63,68 +63,6 @@ export function isDiffMetric(metricKey: MetricKey | string): boolean { 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)); } diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts index fde809bb117..d94d804787a 100644 --- a/server/sonar-web/src/main/js/helpers/testMocks.ts +++ b/server/sonar-web/src/main/js/helpers/testMocks.ts @@ -17,11 +17,13 @@ * 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'; @@ -29,7 +31,6 @@ import { AlmApplication, Analysis, AnalysisEvent, - AppState, Condition, CurrentUser, FlowLocation, @@ -117,7 +118,7 @@ export function mockAnalysisEvent(overrides: Partial<AnalysisEvent> = {}): Analy export function mockAppState(overrides: Partial<AppState> = {}): AppState { return { - edition: 'community', + edition: EditionKey.community, productionDatabase: true, qualifiers: ['TRK'], settings: {}, @@ -526,7 +527,12 @@ export function mockQualityProfileExporter(override?: Partial<Exporter>): Export }; } -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(), diff --git a/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx b/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx index fbc4f8ace63..c1a3ec926e3 100644 --- a/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx +++ b/server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx @@ -30,7 +30,8 @@ import { MetricsContext } from '../app/components/metrics/MetricsContext'; 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'; diff --git a/server/sonar-web/src/main/js/store/rootReducer.ts b/server/sonar-web/src/main/js/store/rootReducer.ts index b7ba18c3020..074d20e2a59 100644 --- a/server/sonar-web/src/main/js/store/rootReducer.ts +++ b/server/sonar-web/src/main/js/store/rootReducer.ts @@ -18,7 +18,6 @@ * 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'; @@ -29,18 +28,12 @@ export type Store = { 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) { @@ -55,10 +48,6 @@ export function getCurrentUser(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, diff --git a/server/sonar-web/src/main/js/apps/settings/store/rootReducer.ts b/server/sonar-web/src/main/js/types/appstate.ts index 4bc466337b5..62555148764 100644 --- a/server/sonar-web/src/main/js/apps/settings/store/rootReducer.ts +++ b/server/sonar-web/src/main/js/types/appstate.ts @@ -17,15 +17,26 @@ * 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 }); +import { EditionKey } from './editions'; +import { GlobalSettingKeys } from './settings'; +import { Extension } from './types'; -export function getValue(state: State, key: string, component?: string) { - return fromValues.getValue(state.values, key, component); +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; } diff --git a/server/sonar-web/src/main/js/types/extension.ts b/server/sonar-web/src/main/js/types/extension.ts index a142cf56861..b444b100e69 100644 --- a/server/sonar-web/src/main/js/types/extension.ts +++ b/server/sonar-web/src/main/js/types/extension.ts @@ -21,8 +21,9 @@ import { IntlShape } from 'react-intl'; 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' diff --git a/server/sonar-web/src/main/js/types/settings.ts b/server/sonar-web/src/main/js/types/settings.ts index 909e801bfa6..bf95ca0336b 100644 --- a/server/sonar-web/src/main/js/types/settings.ts +++ b/server/sonar-web/src/main/js/types/settings.ts @@ -20,12 +20,23 @@ 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; diff --git a/server/sonar-web/src/main/js/types/types.ts b/server/sonar-web/src/main/js/types/types.ts index cbff79bdbc7..2c6342be3dd 100644 --- a/server/sonar-web/src/main/js/types/types.ts +++ b/server/sonar-web/src/main/js/types/types.ts @@ -83,25 +83,6 @@ export interface AnalysisEvent { }; } -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; |