mirror of
https://github.com/mattermost/focalboard.git
synced 2025-01-26 18:48:15 +02:00
* Adding the "contact admin" link (#3229) * Allowing to contact admin to upgrade from the card limit notification box * Adding the styles * Improving the interface * i18n changes * Fixing linter errors * Using the right notificationBox * Fixing tests * Fixing tests Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
parent
b5d8f2b9e1
commit
3f451d568b
@ -323,10 +323,11 @@
|
||||
"login.log-in-title": "Log in",
|
||||
"login.register-button": "or create an account if you don't have one",
|
||||
"notification-box-card-limit-reached.close-tooltip": "Snooze for 10 days",
|
||||
"notification-box-card-limit-reached.link": "upgrade to a paid plan",
|
||||
"notification-box-card-limit-reached.contact-link": "notify your admin",
|
||||
"notification-box-card-limit-reached.link": "Upgrade to a paid plan",
|
||||
"notification-box-card-limit-reached.title": "{cards} cards hidden from board",
|
||||
"notification-box-cards-hidden.title": "Your action hidden another card",
|
||||
"notification-box.card-limit-reached.not-admin.text": "To access archived cards, contact your admin to upgrade to a paid plan.",
|
||||
"notification-box-cards-hidden.title": "This action has hidden another card",
|
||||
"notification-box.card-limit-reached.not-admin.text": "To access archived cards, you can {contactLink} to upgrade to a paid plan.",
|
||||
"notification-box.card-limit-reached.text": "Card limit reached, to view older cards, {link}",
|
||||
"register.login-button": "or log in if you already have an account",
|
||||
"register.signup-title": "Sign up for your account",
|
||||
|
File diff suppressed because it is too large
Load Diff
13
webapp/src/components/cardLimitNotification.scss
Normal file
13
webapp/src/components/cardLimitNotification.scss
Normal file
@ -0,0 +1,13 @@
|
||||
.NotifyAdminSuccessNotify {
|
||||
position: fixed;
|
||||
bottom: 173px;
|
||||
height: unset;
|
||||
|
||||
.NotificationBox__icon {
|
||||
font-size: 24px;
|
||||
|
||||
svg {
|
||||
stroke: var(--online-indicator);
|
||||
}
|
||||
}
|
||||
}
|
@ -10,9 +10,11 @@ import {IUser, UserConfigPatch} from '../user'
|
||||
import {getMe, patchProps, getCardLimitSnoozeUntil, getCardHiddenWarningSnoozeUntil} from '../store/users'
|
||||
import {getCurrentBoardHiddenCardsCount, getCardHiddenWarning} from '../store/cards'
|
||||
import TelemetryClient, {TelemetryActions, TelemetryCategory} from '../telemetry/telemetryClient'
|
||||
import CheckIcon from '../widgets/icons/check'
|
||||
import NotificationBox from '../widgets/notificationBox/notificationBox'
|
||||
import octoClient from '../octoClient'
|
||||
|
||||
import NotificationBox from '../widgets/notification-box'
|
||||
import './cardLimitNotification.scss'
|
||||
|
||||
type Props = {
|
||||
showHiddenCardNotification: boolean
|
||||
@ -25,6 +27,7 @@ const checkSnoozeInterval = 1000 * 60 * 5
|
||||
const CardLimitNotification = (props: Props) => {
|
||||
const intl = useIntl()
|
||||
const [time, setTime] = useState(Date.now())
|
||||
const [showNotifyAdminSuccess, setShowNotifyAdminSuccess] = useState<boolean>(false)
|
||||
|
||||
const hiddenCards = useAppSelector<number>(getCurrentBoardHiddenCardsCount)
|
||||
const cardHiddenWarning = useAppSelector<boolean>(getCardHiddenWarning)
|
||||
@ -108,6 +111,13 @@ const CardLimitNotification = (props: Props) => {
|
||||
}
|
||||
}, [show])
|
||||
|
||||
const handleContactAdminClicked = useCallback(async () => {
|
||||
TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.LimitCardCTAPerformed)
|
||||
|
||||
await octoClient.notifyAdminUpgrade()
|
||||
setShowNotifyAdminSuccess(true)
|
||||
}, [me?.id])
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
(window as any).openPricingModal()()
|
||||
TelemetryClient.trackEvent(TelemetryCategory, TelemetryActions.LimitCardLimitLinkOpen, {})
|
||||
@ -153,7 +163,26 @@ const CardLimitNotification = (props: Props) => {
|
||||
{!hasPermissionToUpgrade &&
|
||||
<FormattedMessage
|
||||
id='notification-box.card-limit-reached.not-admin.text'
|
||||
defaultMessage='To access archived cards, contact your admin to upgrade to a paid plan.'
|
||||
defaultMessage='To access archived cards, you can {contactLink} to upgrade to a paid plan.'
|
||||
values={{
|
||||
contactLink: (
|
||||
<a
|
||||
onClick={handleContactAdminClicked}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='notification-box-card-limit-reached.contact-link'
|
||||
defaultMessage='notify your admin'
|
||||
/>
|
||||
</a>),
|
||||
}}
|
||||
/>}
|
||||
|
||||
{showNotifyAdminSuccess &&
|
||||
<NotificationBox
|
||||
className='NotifyAdminSuccessNotify'
|
||||
icon={<CheckIcon/>}
|
||||
title={intl.formatMessage({id: 'ViewLimitDialog.notifyAdmin.Success', defaultMessage: 'Your admin has been notified'})}
|
||||
onClose={() => setShowNotifyAdminSuccess(false)}
|
||||
/>}
|
||||
</NotificationBox>
|
||||
)
|
||||
|
@ -32,6 +32,7 @@ jest.mock('draft-js/lib/generateRandomKey', () => () => '123')
|
||||
const mockedUtils = mocked(Utils, true)
|
||||
const mockedMutator = mocked(Mutator, true)
|
||||
mockedUtils.createGuid.mockReturnValue('test-id')
|
||||
mockedUtils.generateClassName = jest.requireActual('../utils').Utils.generateClassName
|
||||
describe('components/centerPanel', () => {
|
||||
const board = TestBlockFactory.createBoard()
|
||||
board.id = '1'
|
||||
|
@ -46,6 +46,7 @@ export const TelemetryActions = {
|
||||
CloudMoreInfo: 'cloud_more_info',
|
||||
ViewLimitReached: 'limit_ViewLimitReached',
|
||||
ViewLimitCTAPerformed: 'limit_ViewLimitLinkOpen',
|
||||
LimitCardCTAPerformed: 'limit_CardLimitCTAPerformed',
|
||||
LimitCardLimitReached: 'limit_cardLimitReached',
|
||||
LimitCardLimitLinkOpen: 'limit_cardLimitLinkOpen',
|
||||
}
|
||||
|
@ -1,140 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`widgets/NotificationBox should match snapshot with close with tooltip 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="NotificationBox"
|
||||
>
|
||||
<div
|
||||
class="content"
|
||||
>
|
||||
<p
|
||||
class="title"
|
||||
>
|
||||
title
|
||||
</p>
|
||||
CONTENT
|
||||
</div>
|
||||
<div
|
||||
class="octo-tooltip tooltip-top"
|
||||
data-tooltip="tooltip"
|
||||
>
|
||||
<button
|
||||
class="IconButton"
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
class="CompassIcon icon-close CloseIcon"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`widgets/NotificationBox should match snapshot with close without tooltip 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="NotificationBox"
|
||||
>
|
||||
<div
|
||||
class="content"
|
||||
>
|
||||
<p
|
||||
class="title"
|
||||
>
|
||||
title
|
||||
</p>
|
||||
CONTENT
|
||||
</div>
|
||||
<button
|
||||
class="IconButton"
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
class="CompassIcon icon-close CloseIcon"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`widgets/NotificationBox should match snapshot with icon 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="NotificationBox"
|
||||
>
|
||||
<div
|
||||
class="NotificationBox__icon"
|
||||
>
|
||||
ICON
|
||||
</div>
|
||||
<div
|
||||
class="content"
|
||||
>
|
||||
<p
|
||||
class="title"
|
||||
>
|
||||
title
|
||||
</p>
|
||||
CONTENT
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`widgets/NotificationBox should match snapshot with icon and close with tooltip 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="NotificationBox"
|
||||
>
|
||||
<div
|
||||
class="NotificationBox__icon"
|
||||
>
|
||||
ICON
|
||||
</div>
|
||||
<div
|
||||
class="content"
|
||||
>
|
||||
<p
|
||||
class="title"
|
||||
>
|
||||
title
|
||||
</p>
|
||||
CONTENT
|
||||
</div>
|
||||
<div
|
||||
class="octo-tooltip tooltip-top"
|
||||
data-tooltip="tooltip"
|
||||
>
|
||||
<button
|
||||
class="IconButton"
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
class="CompassIcon icon-close CloseIcon"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`widgets/NotificationBox should match snapshot without icon and close 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="NotificationBox"
|
||||
>
|
||||
<div
|
||||
class="content"
|
||||
>
|
||||
<p
|
||||
class="title"
|
||||
>
|
||||
title
|
||||
</p>
|
||||
CONTENT
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1,44 +0,0 @@
|
||||
.NotificationBox {
|
||||
position: fixed;
|
||||
bottom: 52px;
|
||||
right: 32px;
|
||||
border-radius: 4px;
|
||||
background: rgb(var(--center-channel-bg-rgb));
|
||||
box-shadow: rgba(var(--center-channel-color-rgb), 0.1) 0 0 0 1px,
|
||||
rgba(var(--center-channel-color-rgb), 0.1) 0 2px 4px;
|
||||
display: flex;
|
||||
padding: 22px;
|
||||
width: 400px;
|
||||
height: 116px;
|
||||
z-index: 1000;
|
||||
|
||||
.NotificationBox__icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0;
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.IconButton {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.octo-tooltip {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
|
||||
.IconButton {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react'
|
||||
import {render} from '@testing-library/react'
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
import {wrapIntl} from '../testUtils'
|
||||
|
||||
import NotificationBox from './notification-box'
|
||||
|
||||
describe('widgets/NotificationBox', () => {
|
||||
beforeEach(() => {
|
||||
// Quick fix to disregard console error when unmounting a component
|
||||
console.error = jest.fn()
|
||||
document.execCommand = jest.fn()
|
||||
})
|
||||
|
||||
test('should match snapshot without icon and close', () => {
|
||||
const component = wrapIntl(
|
||||
<NotificationBox
|
||||
title='title'
|
||||
>
|
||||
{'CONTENT'}
|
||||
</NotificationBox>,
|
||||
)
|
||||
const {container} = render(component)
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('should match snapshot with icon', () => {
|
||||
const component = wrapIntl(
|
||||
<NotificationBox
|
||||
title='title'
|
||||
icon='ICON'
|
||||
>
|
||||
{'CONTENT'}
|
||||
</NotificationBox>,
|
||||
)
|
||||
const {container} = render(component)
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('should match snapshot with close without tooltip', () => {
|
||||
const component = wrapIntl(
|
||||
<NotificationBox
|
||||
title='title'
|
||||
onClose={() => null}
|
||||
>
|
||||
{'CONTENT'}
|
||||
</NotificationBox>,
|
||||
)
|
||||
const {container} = render(component)
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('should match snapshot with close with tooltip', () => {
|
||||
const component = wrapIntl(
|
||||
<NotificationBox
|
||||
title='title'
|
||||
onClose={() => null}
|
||||
closeTooltip='tooltip'
|
||||
>
|
||||
{'CONTENT'}
|
||||
</NotificationBox>,
|
||||
)
|
||||
const {container} = render(component)
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('should match snapshot with icon and close with tooltip', () => {
|
||||
const component = wrapIntl(
|
||||
<NotificationBox
|
||||
title='title'
|
||||
icon='ICON'
|
||||
onClose={() => null}
|
||||
closeTooltip='tooltip'
|
||||
>
|
||||
{'CONTENT'}
|
||||
</NotificationBox>,
|
||||
)
|
||||
const {container} = render(component)
|
||||
expect(container).toMatchSnapshot()
|
||||
})
|
||||
})
|
@ -1,57 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React from 'react'
|
||||
|
||||
import IconButton from './buttons/iconButton'
|
||||
import CloseIcon from './icons/close'
|
||||
import Tooltip from './tooltip'
|
||||
|
||||
import './notification-box.scss'
|
||||
|
||||
type Props = {
|
||||
title: string
|
||||
icon?: React.ReactNode
|
||||
children: React.ReactNode
|
||||
onClose?: () => void
|
||||
closeTooltip?: string
|
||||
}
|
||||
|
||||
function renderClose(onClose?: () => void, closeTooltip?: string) {
|
||||
if (!onClose) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (closeTooltip) {
|
||||
return (
|
||||
<Tooltip title={closeTooltip}>
|
||||
<IconButton
|
||||
icon={<CloseIcon/>}
|
||||
onClick={onClose}
|
||||
/>
|
||||
</Tooltip>)
|
||||
}
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
icon={<CloseIcon/>}
|
||||
onClick={onClose}
|
||||
/>)
|
||||
}
|
||||
|
||||
function NotificationBox(props: Props): JSX.Element {
|
||||
return (
|
||||
<div className='NotificationBox'>
|
||||
{props.icon &&
|
||||
<div className='NotificationBox__icon'>
|
||||
{props.icon}
|
||||
</div>}
|
||||
<div className='content'>
|
||||
<p className='title'>{props.title}</p>
|
||||
{props.children}
|
||||
</div>
|
||||
{renderClose(props.onClose, props.closeTooltip)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(NotificationBox)
|
Loading…
x
Reference in New Issue
Block a user