From 3c3832f4bba46afe1a864321405cc7ac3a4c8f43 Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Mon, 4 Mar 2024 10:36:33 +0100 Subject: [PATCH] SONAR-21656 Migrate SonarLint promotion Notification to the new UI --- .../design-system/src/theme/light.ts | 5 ++ .../PromotionNotification.css | 38 ----------- .../PromotionNotification.tsx | 63 +++++++++++------- .../__tests__/PromotionNotification-test.tsx | 65 +++++++++++++++++++ 4 files changed, 108 insertions(+), 63 deletions(-) delete mode 100644 server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.css create mode 100644 server/sonar-web/src/main/js/app/components/promotion-notification/__tests__/PromotionNotification-test.tsx diff --git a/server/sonar-web/design-system/src/theme/light.ts b/server/sonar-web/design-system/src/theme/light.ts index 7687214258a..24c872e99e9 100644 --- a/server/sonar-web/design-system/src/theme/light.ts +++ b/server/sonar-web/design-system/src/theme/light.ts @@ -646,6 +646,11 @@ export const lightTheme = { //education principles educationPrincipleBackground: COLORS.indigo[25], educationPrincipleBorder: COLORS.indigo[300], + + // SonarLint PromotionNotification + promotionNotification: COLORS.blueGrey[35], + promotionNotificationBackground: COLORS.blueGrey[700], + promotionNotificationSeparator: COLORS.blueGrey[500], }, // contrast colors to be used for text when using a color background with the same name diff --git a/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.css b/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.css deleted file mode 100644 index 9d6bf186a4a..00000000000 --- a/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.css +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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. - */ -.toaster { - position: fixed; - right: 10px; - bottom: 10px; - max-width: 550px; - background-color: var(--darkBackground); - z-index: var(--popupZIndex); - box-shadow: 1px 1px 5px 0px black; - color: var(--darkBackgroundFontColor); - border-radius: 4px; -} - -.toaster-content { - border-right: 1px solid var(--darkBackgroundSeparator); -} - -.toaster-link { - color: var(--darkBackgroundFontColor); -} diff --git a/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx b/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx index cd423d7a912..b47b0ebf588 100644 --- a/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx +++ b/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx @@ -17,58 +17,71 @@ * 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 { ButtonPrimary, ButtonSecondary, themeBorder, themeColor } from 'design-system'; import * as React from 'react'; import { dismissNotice } from '../../../api/users'; -import { ButtonLink } from '../../../components/controls/buttons'; import { translate } from '../../../helpers/l10n'; import { getBaseUrl } from '../../../helpers/system'; -import { isLoggedIn, NoticeType } from '../../../types/users'; +import { NoticeType, isLoggedIn } from '../../../types/users'; import { CurrentUserContextInterface } from '../current-user/CurrentUserContext'; import withCurrentUserContext from '../current-user/withCurrentUserContext'; -import './PromotionNotification.css'; export function PromotionNotification(props: CurrentUserContextInterface) { - const { currentUser } = props; + const { currentUser, updateDismissedNotices } = props; - if (!isLoggedIn(currentUser) || currentUser.dismissedNotices[NoticeType.SONARLINT_AD]) { - return null; - } - - const onClick = () => { - dismissNotice(NoticeType.SONARLINT_AD) + const onClick = React.useCallback(() => { + return dismissNotice(NoticeType.SONARLINT_AD) .then(() => { - props.updateDismissedNotices(NoticeType.SONARLINT_AD, true); + updateDismissedNotices(NoticeType.SONARLINT_AD, true); }) .catch(() => { /* noop */ }); - }; + }, [updateDismissedNotices]); + + if (!isLoggedIn(currentUser) || currentUser.dismissedNotices[NoticeType.SONARLINT_AD]) { + return null; + } return ( -
+
SonarQube + SonarLint
-
+ {translate('promotion.sonarlint.title')}

{translate('promotion.sonarlint.content')}

-
-
- +
+ {translate('learn_more')} - - + + {translate('dismiss')} - +
-
+
); } export default withCurrentUserContext(PromotionNotification); + +const PromotionNotificationWrapper = styled.div` + position: fixed; + right: 10px; + bottom: 10px; + max-width: 600px; + box-shadow: 1px 1px 5px 0px black; + + background: ${themeColor('promotionNotificationBackground')}; + color: ${themeColor('promotionNotification')}; +`; + +const PromotionNotificationContent = styled.div` + border-right: ${themeBorder('default', 'promotionNotificationSeparator')}; +`; diff --git a/server/sonar-web/src/main/js/app/components/promotion-notification/__tests__/PromotionNotification-test.tsx b/server/sonar-web/src/main/js/app/components/promotion-notification/__tests__/PromotionNotification-test.tsx new file mode 100644 index 00000000000..723c55e6659 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/promotion-notification/__tests__/PromotionNotification-test.tsx @@ -0,0 +1,65 @@ +/* + * 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 userEvent from '@testing-library/user-event'; +import * as React from 'react'; +import { dismissNotice } from '../../../../api/users'; +import { mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks'; +import { renderComponent } from '../../../../helpers/testReactTestingUtils'; +import { byRole, byText } from '../../../../helpers/testSelector'; +import { CurrentUser, NoticeType } from '../../../../types/users'; +import PromotionNotification from '../PromotionNotification'; + +jest.mock('../../../../api/users', () => ({ + dismissNotice: jest.fn().mockResolvedValue(undefined), +})); + +it('should not render when anonymous', () => { + renderPromotionNotification(mockCurrentUser({ isLoggedIn: false })); + + expect(byText('promotion.sonarlint.title').query()).not.toBeInTheDocument(); +}); + +it('should not render if previously dismissed', () => { + renderPromotionNotification( + mockLoggedInUser({ dismissedNotices: { [NoticeType.SONARLINT_AD]: true } }), + ); + + expect(byText('promotion.sonarlint.title').query()).not.toBeInTheDocument(); +}); + +it('should be dismissable', async () => { + const user = userEvent.setup(); + + renderPromotionNotification(); + + expect(byText('promotion.sonarlint.title').get()).toBeInTheDocument(); + const dismissButton = byRole('button', { name: 'dismiss' }).get(); + + expect(dismissButton).toBeInTheDocument(); + await user.click(dismissButton); + + expect(dismissNotice).toHaveBeenCalledWith(NoticeType.SONARLINT_AD); +}); + +function renderPromotionNotification(currentUser: CurrentUser = mockLoggedInUser()) { + return renderComponent(, '', { + currentUser, + }); +} -- 2.39.5