* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
export * from './Accordion';
export { Badge } from './Badge';
export * from './Banner';
export * from './Text';
export * from './TextAccordion';
export * from './Title';
-export { ToggleButton } from './ToggleButton';
+export * from './ToggleButton';
export { Tooltip } from './Tooltip';
export { TopBar } from './TopBar';
export * from './TreeMap';
addGlobalErrorMessage(<span>error</span>, { position: 'top-left' });
expect(toast).toHaveBeenCalledWith(
- <div className="fs-mask sw-body-sm sw-p-3 sw-pb-4" data-test="global-message__ERROR">
+ <div className="fs-mask sw-body-sm sw-p-3 sw-pb-4" data-testid="global-message__ERROR">
<span>error</span>
</div>,
{ icon: <FlagErrorIcon />, type: 'error', position: 'top-left' },
addGlobalSuccessMessage('it worked');
expect(toast).toHaveBeenCalledWith(
- <div className="fs-mask sw-body-sm sw-p-3 sw-pb-4" data-test="global-message__SUCCESS">
+ <div className="fs-mask sw-body-sm sw-p-3 sw-pb-4" data-testid="global-message__SUCCESS">
it worked
</div>,
{ icon: <FlagSuccessIcon />, type: 'success' },
function createToast(message: ReactNode, level: MessageLevel, overrides?: ToastOptions) {
return toast(
- <div className="fs-mask sw-body-sm sw-p-3 sw-pb-4" data-test={`global-message__${level}`}>
+ <div className="fs-mask sw-body-sm sw-p-3 sw-pb-4" data-testid={`global-message__${level}`}>
{message}
</div>,
{
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Card, CenteredLayout, Link, SubHeading } from 'design-system/lib';
+
+import { Link } from '@sonarsource/echoes-react';
+import { Card, CenteredLayout, SubHeading } from 'design-system';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import { translate } from '../../helpers/l10n';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { ThemeProvider } from '@emotion/react';
import styled from '@emotion/styled';
-import { lightTheme, themeColor, ToastMessageContainer } from 'design-system';
+import { lightTheme, themeColor } from 'design-system';
import * as React from 'react';
import { Outlet, useLocation } from 'react-router-dom';
import A11yProvider from '../../components/a11y/A11yProvider';
import NCDAutoUpdateMessage from '../../components/new-code-definition/NCDAutoUpdateMessage';
import Workspace from '../../components/workspace/Workspace';
import GlobalFooter from './GlobalFooter';
+import StartupModal from './StartupModal';
+import SystemAnnouncement from './SystemAnnouncement';
import IndexationContextProvider from './indexation/IndexationContextProvider';
import IndexationNotification from './indexation/IndexationNotification';
import LanguagesContextProvider from './languages/LanguagesContextProvider';
import MetricsContextProvider from './metrics/MetricsContextProvider';
import GlobalNav from './nav/global/GlobalNav';
import PromotionNotification from './promotion-notification/PromotionNotification';
-import StartupModal from './StartupModal';
-import SystemAnnouncement from './SystemAnnouncement';
import UpdateNotification from './update-notification/UpdateNotification';
/*
>
<div className="page-container">
<Workspace>
- <ToastMessageContainer />
<IndexationContextProvider>
<LanguagesContextProvider>
<MetricsContextProvider>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { keyframes } from '@emotion/react';
-import styled from '@emotion/styled';
-import * as React from 'react';
-import { ClearButton } from '../../components/controls/buttons';
-import { cutLongWords } from '../../helpers/path';
-import { Message } from '../../types/globalMessages';
-import { colors, sizes } from '../theme';
-
-export interface GlobalMessageProps {
- closeGlobalMessage: (id: string) => void;
- message: Message;
-}
-
-export default function GlobalMessage(props: GlobalMessageProps) {
- const { message } = props;
- return (
- <MessageBox data-testid={`global-message__${message.level}`} level={message.level}>
- {cutLongWords(message.text)}
- <CloseButton
- className="button-small"
- color="#fff"
- level={message.level}
- onClick={() => props.closeGlobalMessage(message.id)}
- />
- </MessageBox>
- );
-}
-
-const appearAnim = keyframes`
- from {
- opacity: 0;
- }
- to {
- opacity: 1;
- }
-`;
-
-const MessageBox = styled.div<Pick<Message, 'level'>>`
- position: relative;
- padding: 0 30px 0 10px;
- line-height: ${sizes.controlHeight};
- border-radius: 0 0 3px 3px;
- box-sizing: border-box;
- color: #ffffff;
- background-color: ${({ level }) => (level === 'SUCCESS' ? colors.green : colors.red)};
- text-align: center;
- opacity: 0;
- animation: ${appearAnim} 0.2s ease forwards;
-
- margin-top: calc(${sizes.gridSize} / 2);
- border-radius: 3px;
-`;
-
-const CloseButton = styled(ClearButton)<Pick<Message, 'level'>>`
- position: absolute;
- top: calc(${sizes.gridSize} / 4);
- right: calc(${sizes.gridSize} / 4);
-
- &.button-icon:hover svg,
- &.button-icon:focus svg {
- color: ${({ level }) => (level === 'SUCCESS' ? colors.green : colors.red)};
- }
-`;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 styled from '@emotion/styled';
-import React from 'react';
-import { registerListener, unregisterListener } from '../../helpers/globalMessages';
-import { Message, MessageLevel } from '../../types/globalMessages';
-import { zIndexes } from '../theme';
-import GlobalMessage from './GlobalMessage';
-
-const MESSAGE_DISPLAY_TIME = 10000;
-const MAX_MESSAGES = 3;
-
-interface State {
- messages: Message[];
-}
-
-export default class GlobalMessagesContainer extends React.Component<{}, State> {
- mounted = false;
-
- constructor(props: {}) {
- super(props);
-
- this.state = {
- messages: [],
- };
- }
-
- componentDidMount() {
- this.mounted = true;
- registerListener(this.handleAddMessage);
- }
-
- componentWillUnmount() {
- this.mounted = false;
- unregisterListener(this.handleAddMessage);
- }
-
- handleAddMessage = (message: Message) => {
- if (
- this.mounted &&
- !this.state.messages.some((m) => m.level === MessageLevel.Error && m.text === message.text)
- ) {
- this.setState(({ messages }) => ({
- messages: [...messages, message].slice(-MAX_MESSAGES),
- }));
-
- setTimeout(() => {
- this.closeMessage(message.id);
- }, MESSAGE_DISPLAY_TIME);
- }
- };
-
- closeMessage = (messageId: string) => {
- if (this.mounted) {
- this.setState(({ messages }) => {
- return { messages: messages.filter((m) => m.id !== messageId) };
- });
- }
- };
-
- render() {
- const { messages } = this.state;
-
- if (messages.length === 0) {
- return null;
- }
-
- return (
- <MessagesContainer>
- <div role="alert">
- {messages
- .filter((m) => m.level === MessageLevel.Error)
- .map((message) => (
- <GlobalMessage
- closeGlobalMessage={this.closeMessage}
- key={message.id}
- message={message}
- />
- ))}
- </div>
- <output>
- {messages
- .filter((m) => m.level === MessageLevel.Success)
- .map((message) => (
- <GlobalMessage
- closeGlobalMessage={this.closeMessage}
- key={message.id}
- message={message}
- />
- ))}
- </output>
- </MessagesContainer>
- );
- }
-}
-
-const MessagesContainer = styled.div`
- position: fixed;
- z-index: ${zIndexes.processContainerZIndex};
- top: 0;
- left: 50%;
- width: 350px;
- margin-left: -175px;
- z-index: 8600;
-`;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { act, screen } from '@testing-library/react';
-import React from 'react';
-import { addGlobalErrorMessage, addGlobalSuccessMessage } from '../../../helpers/globalMessages';
-import { renderApp } from '../../../helpers/testReactTestingUtils';
-
-function NullComponent() {
- return null;
-}
-
-it('should display messages', async () => {
- jest.useFakeTimers();
-
- // we render anything, the GlobalMessageContainer is rendered independently from routing
- renderApp('sonarqube', <NullComponent />);
-
- act(() => {
- addGlobalErrorMessage('This is an error');
- addGlobalSuccessMessage('This was a triumph!');
- });
-
- expect(screen.getByRole('alert')).toHaveTextContent('This is an error');
- expect(screen.getByRole('status')).toHaveTextContent('This was a triumph!');
-
- // No duplicate message
- act(() => {
- addGlobalErrorMessage('This is an error');
- });
-
- expect(screen.getByRole('alert')).toHaveTextContent(/^This is an error$/);
- act(() => {
- addGlobalSuccessMessage('This was a triumph!');
- });
- expect(await screen.findByRole('status')).toHaveTextContent(
- /^This was a triumph!This was a triumph!$/,
- );
-
- act(() => {
- jest.runAllTimers();
- });
- expect(screen.queryByRole('alert')).not.toBeInTheDocument();
- expect(screen.queryByRole('status')).not.toBeInTheDocument();
-
- jest.useRealTimers();
-});
import { withTheme } from '@emotion/react';
import { QueryClient } from '@tanstack/react-query';
-import { Theme } from 'design-system';
+import { addGlobalErrorMessage, Theme } from 'design-system';
import { isEqual } from 'lodash';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import { injectIntl, WrappedComponentProps } from 'react-intl';
import { Location, Router, withRouter } from '../../../components/hoc/withRouter';
import { getExtensionStart } from '../../../helpers/extensions';
-import { addGlobalErrorMessage } from '../../../helpers/globalMessages';
import { translate } from '../../../helpers/l10n';
import { getCurrentL10nBundle } from '../../../helpers/l10nBundle';
import { getBaseUrl } from '../../../helpers/system';
return (
<div>
<Helmet title={this.props.extension.name} />
+
{this.state.extensionElement ? (
this.state.extensionElement
) : (
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import { lightTheme } from 'design-system';
+import { addGlobalErrorMessage, lightTheme } from 'design-system';
import * as React from 'react';
import { IntlShape } from 'react-intl';
import { getEnhancedWindow } from '../../../../helpers/browser';
import { installExtensionsHandler } from '../../../../helpers/extensionsHandler';
-import { addGlobalErrorMessage } from '../../../../helpers/globalMessages';
import {
mockAppState,
mockCurrentUser,
import { ExtensionStartMethodParameter } from '../../../../types/extension';
import Extension, { ExtensionProps } from '../Extension';
-jest.mock('../../../../helpers/globalMessages');
+jest.mock('design-system', () => ({
+ ...jest.requireActual('design-system'),
+ addGlobalErrorMessage: jest.fn(),
+}));
beforeAll(() => {
installExtensionsHandler();
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import { addGlobalSuccessMessage } from 'design-system';
import { throwGlobalError } from '../../../helpers/error';
-import { addGlobalSuccessMessage } from '../../../helpers/globalMessages';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import {
get,
throwGlobalError,
addGlobalSuccessMessage,
};
+
global.t = translate;
global.tp = translateWithParameters;
};
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { ButtonSecondary, FlagMessage } from 'design-system/lib';
+
+import { ButtonSecondary, FlagMessage } from 'design-system';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { cancelPendingPlugins } from '../../../../api/plugins';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
/* NOTE: esbuild will transpile the _syntax_ down to what the TARGET_BROWSERS (in config/utils) */
/* understand. It will _not_, however, polyfill missing API methods, such as */
/* String.prototype.replaceAll. This is why we also import core-js. */
import 'core-js/stable';
/* */
import axios from 'axios';
+import { addGlobalErrorMessage } from 'design-system';
import 'react-day-picker/dist/style.css';
import { getAvailableFeatures } from '../api/features';
import { getGlobalNavigation } from '../api/navigation';
import { getCurrentUser } from '../api/users';
import { installExtensionsHandler, installWebAnalyticsHandler } from '../helpers/extensionsHandler';
-import { addGlobalErrorMessage } from '../helpers/globalMessages';
import { loadL10nBundle } from '../helpers/l10nBundle';
import { axiosToCatch, parseErrorResponse } from '../helpers/request';
import { getBaseUrl, getSystemStatus, initAppVariables } from '../helpers/system';
axiosToCatch.defaults.headers.patch['Content-Type'] = 'application/merge-patch+json';
axios.defaults.headers.patch['Content-Type'] = 'application/merge-patch+json';
axios.defaults.baseURL = getBaseUrl();
+
axios.interceptors.response.use(
(response) => response.data,
(error) => {
const { response } = error;
addGlobalErrorMessage(parseErrorResponse(response));
+
return Promise.reject(response);
},
);
+
const [l10nBundle, currentUser, appState, availableFeatures] = await Promise.all([
loadL10nBundle(),
isMainApp() ? getCurrentUser() : undefined,
function isMainApp() {
const { pathname } = window.location;
+
return (
getSystemStatus() === 'UP' &&
!pathname.startsWith(`${getBaseUrl()}/sessions`) &&
*/
import { Global, css, useTheme } from '@emotion/react';
-import { themeColor } from 'design-system/lib';
+import { themeColor } from 'design-system';
import React from 'react';
import twDefaultTheme from 'tailwindcss/defaultTheme';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { ThemeProvider } from '@emotion/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { lightTheme } from 'design-system';
+import { ToastMessageContainer, lightTheme } from 'design-system';
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import { Helmet, HelmetProvider } from 'react-helmet-async';
import DocumentationRedirect from '../components/DocumentationRedirect';
import FormattingHelp from '../components/FormattingHelp';
import GlobalContainer from '../components/GlobalContainer';
-import GlobalMessagesContainer from '../components/GlobalMessagesContainer';
import Landing from '../components/Landing';
import MigrationContainer from '../components/MigrationContainer';
import NonAdminPagesContainer from '../components/NonAdminPagesContainer';
<ThemeProvider theme={lightTheme}>
<QueryClientProvider client={queryClient}>
<GlobalStyles />
- <GlobalMessagesContainer />
+ <ToastMessageContainer />
<Helmet titleTemplate={translate('page_title.template.default')} />
<RouterProvider router={router} />
</QueryClientProvider>
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { endOfDay, startOfDay, subDays } from 'date-fns';
-import { ButtonPrimary } from 'design-system/lib';
+import { ButtonPrimary } from 'design-system';
import * as React from 'react';
import { now } from '../../../helpers/dates';
import { translate } from '../../../helpers/l10n';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { NumericalCell } from 'design-system/lib';
+
+import { NumericalCell } from 'design-system';
import * as React from 'react';
import { formatDuration } from '../utils';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { FlagMessage } from 'design-system/lib';
+
+import { FlagMessage } from 'design-system';
import * as React from 'react';
import ConfirmModal from '../../../../components/controls/ConfirmModal';
import { translate, translateWithParameters } from '../../../../helpers/l10n';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { CenteredLayout, PageContentFontWrapper } from 'design-system/lib';
+
+import { CenteredLayout, PageContentFontWrapper } from 'design-system';
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
import { ComponentContext } from '../../app/components/componentContext/ComponentContext';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { DangerButtonPrimary } from 'design-system/lib';
+
+import { DangerButtonPrimary, addGlobalSuccessMessage } from 'design-system';
import * as React from 'react';
import { deleteApplication } from '../../api/application';
import { deletePortfolio, deleteProject } from '../../api/project-management';
import ConfirmButton from '../../components/controls/ConfirmButton';
import { Router, withRouter } from '../../components/hoc/withRouter';
-import { addGlobalSuccessMessage } from '../../helpers/globalMessages';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { isApplication, isPortfolioLike } from '../../types/component';
import { Component } from '../../types/types';
const { component } = this.props;
let deleteMethod = deleteProject;
let redirectTo = '/';
+
if (isPortfolioLike(component.qualifier)) {
deleteMethod = deletePortfolio;
redirectTo = '/portfolios';
addGlobalSuccessMessage(
translateWithParameters('project_deletion.resource_deleted', component.name),
);
+
this.props.router.replace(redirectTo);
};
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { SubHeading } from 'design-system/lib';
+
+import { SubHeading } from 'design-system';
import React from 'react';
import MetaLink from '../../../../components/common/MetaLink';
import { translate } from '../../../../helpers/l10n';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import { waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import { addGlobalSuccessMessage } from 'design-system';
+import { addGlobalErrorMessage, addGlobalSuccessMessage } from 'design-system';
import selectEvent from 'react-select-event';
import { QualityGatesServiceMock } from '../../../api/mocks/QualityGatesServiceMock';
import handleRequiredAuthorization from '../../../app/utils/handleRequiredAuthorization';
saveButton: byRole('button', { name: 'save' }),
noConditionsNewCodeWarning: byText('project_quality_gate.no_condition_on_new_code'),
- alertMessage: byText('unknown'),
};
beforeAll(() => {
handler.setThrowOnGetGateForProject(true);
renderProjectQualityGateApp();
- expect(await ui.alertMessage.find()).toBeInTheDocument();
+ await waitFor(() => {
+ expect(addGlobalErrorMessage).toHaveBeenCalledWith('unknown');
+ });
+
expect(ui.qualityGateHeading.query()).not.toBeInTheDocument();
});
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import {
ButtonPrimary,
FlagMessage,
loadPermissionTemplates() {
this.setState({ loading: true });
+
getPermissionTemplates().then(
({ permissionTemplates }) => {
if (this.mounted) {
event.preventDefault();
const { analyzedBefore } = this.props;
const { permissionTemplate } = this.state;
+
if (permissionTemplate) {
this.setState({ submitting: true });
const selection = this.props.selection.filter((s) => !s.managed);
+
const parameters = selection.length
? {
projects: selection.map((s) => s.key).join(),
q: this.props.query || undefined,
templateId: permissionTemplate,
};
+
bulkApplyTemplate(parameters).then(
() => {
if (this.mounted) {
</FlagMessage>
);
}
+
return (
<FlagMessage variant="warning" className="sw-my-2">
{translateWithParameters(
this.state.permissionTemplates !== undefined
? this.state.permissionTemplates.map((t) => ({ label: t.name, value: t.id }))
: [];
+
return (
<FormField htmlFor="bulk-apply-template-input" label={translate('template')} required>
<InputSelect
)}
</form>
);
+
return (
<Modal
isScrollable={false}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { ButtonPrimary, FormField, InputField, Modal } from 'design-system/lib';
+
+import { ButtonPrimary, FormField, InputField, Modal } from 'design-system';
import * as React from 'react';
import { useRouter } from '../../../components/hoc/withRouter';
import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
await user.click(ui.activeAssignee.get());
await user.click(ui.currentUserSelectionItem.get());
- expect(ui.successGlobalMessage.get()).toHaveTextContent(`hotspots.assign.success.foo`);
+ expect(await ui.successGlobalMessage.find()).toHaveTextContent(`hotspots.assign.success.foo`);
expect(ui.activeAssignee.get()).toHaveTextContent('foo');
});
expect(getUsers).toHaveBeenLastCalledWith({ q: 'User' });
await user.keyboard('{Enter}');
- expect(ui.successGlobalMessage.get()).toHaveTextContent(`hotspots.assign.success.User John`);
+ expect(await ui.successGlobalMessage.find()).toHaveTextContent(
+ `hotspots.assign.success.User John`,
+ );
});
it('should be able to change the status of a hotspot', async () => {
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { LabelValueSelectOption, SearchSelectDropdown } from 'design-system';
+
+import {
+ LabelValueSelectOption,
+ SearchSelectDropdown,
+ addGlobalSuccessMessage,
+} from 'design-system';
import { noop } from 'lodash';
import * as React from 'react';
import { Options, SingleValue } from 'react-select';
import { getUsers } from '../../../api/users';
import { CurrentUserContext } from '../../../app/components/current-user/CurrentUserContext';
import Avatar from '../../../components/ui/Avatar';
-import { addGlobalSuccessMessage } from '../../../helpers/globalMessages';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { Hotspot, HotspotResolution, HotspotStatus } from '../../../types/security-hotspots';
import { RestUser, isLoggedIn, isUserActive } from '../../../types/users';
const {
hotspot: { assigneeUser, status, resolution, key },
} = props;
+
const { currentUser } = React.useContext(CurrentUserContext);
const allowCurrentUserSelection =
value: u.login,
Icon: renderAvatar(u.name, u.avatar),
}));
+
cb(options);
})
.catch(() => {
})
.then(() => {
props.onAssigneeChange();
+
addGlobalSuccessMessage(
userOption.value
? translateWithParameters('hotspots.assign.success', userOption.label)
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import {
ButtonSecondary,
DropdownMenu,
PopupPlacement,
PopupZLevel,
Spinner,
+ addGlobalErrorMessage,
+ addGlobalSuccessMessage,
} from 'design-system';
import * as React from 'react';
-import { addGlobalErrorMessage, addGlobalSuccessMessage } from '../../../helpers/globalMessages';
import { translate } from '../../../helpers/l10n';
import { openHotspot, probeSonarLintServers } from '../../../helpers/sonarlint';
import { Ide } from '../../../types/sonarlint';
handleOnClick = async () => {
this.setState({ loading: true, ides: [] });
const ides = await probeSonarLintServers();
+
if (ides.length === 0) {
if (this.mounted) {
this.setState({ loading: false });
openHotspot = (ide: Ide) => {
this.setState({ loading: true, ides: [] as Ide[] });
const { projectKey, hotspotKey } = this.props;
+
return openHotspot(ide.port, projectKey, hotspotKey)
.then(this.showSuccess)
.catch(this.showError)
render() {
const { ides, loading } = this.state;
+
return (
<div>
<DropdownToggler
{ides.map((ide) => {
const { ideName, description } = ide;
const label = ideName + (description ? ` - ${description}` : '');
+
return (
<ItemButton
key={ide.port}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import { addGlobalErrorMessage } from 'design-system';
import * as React from 'react';
import { logIn } from '../../../api/auth';
import { getLoginMessage } from '../../../api/settings';
import { getIdentityProviders } from '../../../api/users';
import { Location, withRouter } from '../../../components/hoc/withRouter';
-import { addGlobalErrorMessage } from '../../../helpers/globalMessages';
import { translate } from '../../../helpers/l10n';
import { getReturnUrl } from '../../../helpers/urls';
import { IdentityProvider } from '../../../types/types';
async loadLoginMessage() {
try {
const { message } = await getLoginMessage();
+
if (this.mounted) {
this.setState({ message });
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { CenteredLayout, PageContentFontWrapper } from 'design-system';
+
+import { CenteredLayout, PageContentFontWrapper, addGlobalErrorMessage } from 'design-system';
import * as React from 'react';
import { logOut } from '../../../api/auth';
import RecentHistory from '../../../app/components/RecentHistory';
-import { addGlobalErrorMessage } from '../../../helpers/globalMessages';
import { translate } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+import { addGlobalErrorMessage } from 'design-system';
import * as React from 'react';
import { getLoginMessage } from '../../../../api/settings';
import { getIdentityProviders } from '../../../../api/users';
-import { addGlobalErrorMessage } from '../../../../helpers/globalMessages';
import { mockLocation } from '../../../../helpers/testMocks';
import { renderComponent } from '../../../../helpers/testReactTestingUtils';
import { byLabelText, byRole } from '../../../../helpers/testSelector';
getLoginMessage: jest.fn().mockResolvedValue({ message: '' }),
}));
-jest.mock('../../../../helpers/globalMessages', () => ({
+jest.mock('design-system', () => ({
+ ...jest.requireActual('design-system'),
addGlobalErrorMessage: jest.fn(),
}));
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { screen, waitFor } from '@testing-library/react';
+import { addGlobalErrorMessage } from 'design-system';
import * as React from 'react';
import { logOut } from '../../../../api/auth';
import RecentHistory from '../../../../app/components/RecentHistory';
-import { addGlobalErrorMessage } from '../../../../helpers/globalMessages';
import { renderComponent } from '../../../../helpers/testReactTestingUtils';
import Logout from '../Logout';
logOut: jest.fn().mockResolvedValue(true),
}));
-jest.mock('../../../../helpers/globalMessages', () => ({
+jest.mock('design-system', () => ({
+ ...jest.requireActual('design-system'),
addGlobalErrorMessage: jest.fn(),
}));
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import styled from '@emotion/styled';
-import { LightLabel } from 'design-system/lib';
+import { LightLabel } from 'design-system';
import * as React from 'react';
import DocumentationLink from '../../../components/common/DocumentationLink';
import { translate } from '../../../helpers/l10n';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import styled from '@emotion/styled';
-import { themeBorder } from 'design-system/lib';
+import { themeBorder } from 'design-system';
import * as React from 'react';
import { SettingDefinitionAndValue } from '../../../types/settings';
import { Component } from '../../../types/types';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { InputTextArea } from 'design-system/lib';
+
+import { InputTextArea } from 'design-system';
import * as React from 'react';
import FormattingTipsWithLink from '../../../../components/common/FormattingTipsWithLink';
import { Button } from '../../../../components/controls/buttons';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { ButtonPrimary, FlagMessage, InputTextArea } from 'design-system/lib';
+
+import { ButtonPrimary, FlagMessage, InputTextArea } from 'design-system';
import * as React from 'react';
import { translate } from '../../../../helpers/l10n';
import { DefaultSpecializedInputProps, getPropertyName } from '../../utils';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { ButtonPrimary, FlagMessage, FormField, InputField, Modal } from 'design-system';
+
+import {
+ ButtonPrimary,
+ FlagMessage,
+ FormField,
+ InputField,
+ Modal,
+ addGlobalSuccessMessage,
+} from 'design-system';
import * as React from 'react';
import { changePassword } from '../../../api/users';
import { CurrentUserContext } from '../../../app/components/current-user/CurrentUserContext';
-import { addGlobalSuccessMessage } from '../../../helpers/globalMessages';
import { translate } from '../../../helpers/l10n';
import { ChangePasswordResults, RestUserDetailed, isLoggedIn } from '../../../types/users';
export default function PasswordForm(props: Props) {
const { user } = props;
const [confirmPassword, setConfirmPassword] = React.useState('');
+
const [errorTranslationKey, setErrorTranslationKey] = React.useState<string | undefined>(
undefined,
);
+
const [newPassword, setNewPassword] = React.useState('');
const [oldPassword, setOldPassword] = React.useState('');
const [submitting, setSubmitting] = React.useState(false);
const handleChangePassword = (event: React.SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
+
if (newPassword.length > 0 && newPassword === confirmPassword) {
setSubmitting(true);
+
changePassword({
login: user.login,
password: newPassword,
<input className="sw-hidden" aria-hidden name="old-password-fake" type="password" />
</FormField>
)}
+
<FormField htmlFor="user-password" label={translate('my_profile.password.new')} required>
<InputField
autoFocus
/>
<input className="sw-hidden" aria-hidden name="password-fake" type="password" />
</FormField>
+
<FormField
htmlFor="confirm-user-password"
label={translate('my_profile.password.confirm')}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { AxiosError, AxiosResponse } from 'axios';
import {
ButtonPrimary,
InputField,
Modal,
Spinner,
+ addGlobalErrorMessage,
} from 'design-system';
import * as React from 'react';
import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
-import { addGlobalErrorMessage } from '../../../helpers/globalMessages';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { parseErrorResponse } from '../../../helpers/request';
import { usePostUserMutation, useUpdateUserMutation } from '../../../queries/users';
const handleCreateUser = (e: React.SyntheticEvent<HTMLFormElement>) => {
e.preventDefault();
+
createUser(
{
email: email || undefined,
const handleUpdateUser = (e: React.SyntheticEvent<HTMLFormElement>) => {
e.preventDefault();
const { user } = props;
+
updateUser(
{
id: user?.id!,
{translate('users.cannot_update_delegated_user')}
</FlagMessage>
)}
+
<div className="sw-mb-4">
<MandatoryFieldsExplanation />
</div>
/>
</FormField>
)}
+
<FormField
description={translate('user.login_or_email_used_as_scm_account')}
label={translate('my_profile.scm_accounts')}
scmAccount={scm}
/>
))}
+
<div>
<ButtonSecondary className="it__scm-account-add" onClick={handleAddScmAccount}>
{translate('add_verb')}
primaryButton={
<>
<Spinner loading={isLoadingCreate || isLoadingUserUpdate} />
+
<ButtonPrimary
disabled={isLoadingCreate || isLoadingUserUpdate}
type="submit"
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { ButtonPrimary } from 'design-system/lib';
+
+import { ButtonPrimary } from 'design-system';
import * as React from 'react';
import { useState } from 'react';
import Tooltip from '../../../components/controls/Tooltip';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { FlagMessage } from 'design-system/lib';
+
+import { FlagMessage } from 'design-system';
import { intersection } from 'lodash';
import * as React from 'react';
import {
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { DangerButtonSecondary } from 'design-system/lib';
+
+import { DangerButtonSecondary } from 'design-system';
import * as React from 'react';
import { restart } from '../../api/system';
import ConfirmButton from '../../components/controls/ConfirmButton';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import { addGlobalSuccessMessage } from 'design-system';
import * as React from 'react';
import {
getReportStatus,
} from '../../api/component-report';
import withAppStateContext from '../../app/components/app-state/withAppStateContext';
import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext';
-import { addGlobalSuccessMessage } from '../../helpers/globalMessages';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { AppState } from '../../types/appstate';
import { Branch } from '../../types/branch-like';
componentDidMount() {
this.mounted = true;
const governanceEnabled = this.props.appState.qualifiers.includes(ComponentQualifier.Portfolio);
+
if (governanceEnabled) {
this.loadReportStatus();
}
const translationKey = subscribed
? 'component_report.subscribe_x_success'
: 'component_report.unsubscribe_x_success';
+
const frequencyTranslation = translate(
'report.frequency',
status?.componentFrequency ?? status?.globalFrequency ?? '',
).toLowerCase();
+
const qualifierTranslation = translate('qualifier', component.qualifier).toLowerCase();
addGlobalSuccessMessage(
*/
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+import { addGlobalSuccessMessage } from 'design-system';
import * as React from 'react';
import {
getReportStatus,
import { ComponentQualifier } from '../../../types/component';
import { ComponentReportActions } from '../ComponentReportActions';
+jest.mock('design-system', () => ({
+ ...jest.requireActual('design-system'),
+ addGlobalSuccessMessage: jest.fn(),
+}));
+
jest.mock('../../../api/component-report', () => ({
...jest.requireActual('../../../api/component-report'),
getReportStatus: jest
await user.click(subscribeButton);
expect(subscribeToEmailReport).toHaveBeenCalledWith(component.key, branch.name);
- expect(await screen.findByRole('status')).toBeInTheDocument();
+ expect(addGlobalSuccessMessage).toHaveBeenLastCalledWith(
+ 'component_report.subscribe_x_success.report.frequency.monthly.qualifier.trk',
+ );
// And unsubscribe!
await user.click(button);
await user.click(unsubscribeButton);
expect(unsubscribeFromEmailReport).toHaveBeenCalledWith(component.key, branch.name);
- expect(screen.getByRole('status')).toHaveTextContent(
- 'component_report.subscribe_x_s...component_report.unsubscribe_x...',
+ expect(addGlobalSuccessMessage).toHaveBeenLastCalledWith(
+ 'component_report.unsubscribe_x_success.report.frequency.monthly.qualifier.trk',
);
});
it('renders no global banner if user is not global admin', () => {
const { container } = renderGlobalMessage(mockLoggedInUser());
- expect(container).toBeEmptyDOMElement();
+ expect(container).toContainHTML('<div><div class="Toastify" /></div>');
});
it('renders global banner if user is global admin', async () => {
const { container } = renderProjectMessage(
mockComponent({ configuration: { showSettings: false } }),
);
- expect(container).toBeEmptyDOMElement();
+ expect(container).toContainHTML('<div><div class="Toastify" /></div>');
});
it('renders project banner if user is project admin', async () => {
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { ClipboardIconButton, CodeSnippet, NumberedListItem } from 'design-system/lib';
+
+import { ClipboardIconButton, CodeSnippet, NumberedListItem } from 'design-system';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { translate } from '../../../helpers/l10n';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { NumberedListItem } from 'design-system/lib';
+
+import { NumberedListItem } from 'design-system';
import * as React from 'react';
import { translate } from '../../../../helpers/l10n';
import { CompilationInfo } from '../../components/CompilationInfo';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { NumberedListItem } from 'design-system/lib';
+
+import { NumberedListItem } from 'design-system';
import * as React from 'react';
import { translate } from '../../../../helpers/l10n';
import { Component } from '../../../../types/types';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { keyframes } from '@emotion/react';
import styled from '@emotion/styled';
import {
Note,
Spinner,
ToggleButton,
+ ToggleButtonsOption,
TrashIcon,
} from 'design-system';
-import { ToggleButtonsOption } from 'design-system/lib/components/ToggleButton';
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { SingleValue } from 'react-select';
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
+import { addGlobalErrorMessage } from 'design-system';
import { throwGlobalError } from '../error';
-import { addGlobalErrorMessage } from '../globalMessages';
-jest.mock('../../helpers/globalMessages', () => ({
+jest.mock('design-system', () => ({
addGlobalErrorMessage: jest.fn(),
}));
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { MessageLevel } from '../../types/globalMessages';
-import {
- addGlobalErrorMessage,
- addGlobalSuccessMessage,
- registerListener,
-} from '../globalMessages';
-
-it('should work as expected', () => {
- const listener1 = jest.fn();
- registerListener(listener1);
-
- addGlobalErrorMessage('test');
-
- expect(listener1).toHaveBeenCalledWith(
- expect.objectContaining({ text: 'test', level: MessageLevel.Error }),
- );
-
- listener1.mockClear();
- const listener2 = jest.fn();
- registerListener(listener2);
-
- addGlobalSuccessMessage('test');
-
- expect(listener1).toHaveBeenCalledWith(
- expect.objectContaining({ text: 'test', level: MessageLevel.Success }),
- );
- expect(listener2).toHaveBeenCalledWith(
- expect.objectContaining({ text: 'test', level: MessageLevel.Success }),
- );
-});
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { addGlobalErrorMessage } from './globalMessages';
+
+import { addGlobalErrorMessage } from 'design-system';
import { parseError } from './request';
export function throwGlobalError(param: Response | any): Promise<Response | any> {
// Axios response object
if (param.data?.message) {
addGlobalErrorMessage(param.data?.message);
+
return Promise.reject(param);
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { uniqueId } from 'lodash';
-import { Message, MessageLevel } from '../types/globalMessages';
-import { parseError } from './request';
-
-const listeners: Array<(message: Message) => void> = [];
-
-export function registerListener(callback: (message: Message) => void) {
- listeners.push(callback);
-}
-
-export function unregisterListener(callback: (message: Message) => void) {
- const index = listeners.indexOf(callback);
-
- if (index > -1) {
- listeners.splice(index, 1);
- }
-}
-function addMessage(text: string, level: MessageLevel) {
- listeners.forEach((listener) =>
- listener({
- id: uniqueId('global-message-'),
- level,
- text,
- }),
- );
-}
-
-export function addGlobalErrorMessage(text: string) {
- addMessage(text, MessageLevel.Error);
-}
+import { addGlobalErrorMessage } from 'design-system';
+import { parseError } from './request';
export function addGlobalErrorMessageFromAPI(param: Response | string) {
if (param instanceof Response) {
/* ignore parsing errors */
});
}
+
if (typeof param === 'string') {
return Promise.resolve(param).then(addGlobalErrorMessage);
}
return Promise.resolve();
}
-
-export function addGlobalSuccessMessage(text: string) {
- addMessage(text, MessageLevel.Success);
-}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Matcher, RenderResult, render, screen, within } from '@testing-library/react';
import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup';
+import { ToastMessageContainer } from 'design-system';
import { omit } from 'lodash';
import * as React from 'react';
import { HelmetProvider } from 'react-helmet-async';
parsePath,
} from 'react-router-dom';
import AdminContext from '../app/components/AdminContext';
-import GlobalMessagesContainer from '../app/components/GlobalMessagesContainer';
import AppStateContextProvider from '../app/components/app-state/AppStateContextProvider';
import { AvailableFeaturesContext } from '../app/components/available-features/AvailableFeaturesContext';
import { ComponentContext } from '../app/components/componentContext/ComponentContext';
<AppStateContextProvider appState={appState}>
<IndexationContextProvider>
<QueryClientProvider client={queryClient}>
- <GlobalMessagesContainer />
+ <ToastMessageContainer />
+
<RouterProvider router={router} />
</QueryClientProvider>
</IndexationContextProvider>
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import { addGlobalSuccessMessage } from 'design-system';
import {
copyQualityGate,
createCondition,
updateCondition,
} from '../api/quality-gates';
import { getCorrectCaycCondition } from '../apps/quality-gates/utils';
-import { addGlobalSuccessMessage } from '../helpers/globalMessages';
import { translate } from '../helpers/l10n';
import { Condition, QualityGate } from '../types/types';
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.
- */
-export enum MessageLevel {
- Error = 'ERROR',
- Success = 'SUCCESS',
-}
-
-export interface Message {
- id: string;
- level: MessageLevel;
- text: string;
-}