diff --git a/webapp/channels/src/components/admin_console/team_channel_settings/team/details/__snapshots__/team_profile.test.tsx.snap b/webapp/channels/src/components/admin_console/team_channel_settings/team/details/__snapshots__/team_profile.test.tsx.snap index b2dd960faa..ad6a0cc938 100644 --- a/webapp/channels/src/components/admin_console/team_channel_settings/team/details/__snapshots__/team_profile.test.tsx.snap +++ b/webapp/channels/src/components/admin_console/team_channel_settings/team/details/__snapshots__/team_profile.test.tsx.snap @@ -565,104 +565,26 @@ exports[`admin_console/team_channel_settings/team/TeamProfile__Cloud should matc
- -
- -
-
- -
- + -
- -
-
- -
- - } + overlay={} placement="bottom" trigger={ Array [ @@ -671,40 +593,95 @@ exports[`admin_console/team_channel_settings/team/TeamProfile__Cloud should matc ] } > -
- -
+ + + + Unarchive Team + + + +
+ - + - - + ); } return ( diff --git a/webapp/channels/src/components/analytics/activated_users_card/index.tsx b/webapp/channels/src/components/analytics/activated_users_card/index.tsx index 8e2f05a78d..e93a57d632 100644 --- a/webapp/channels/src/components/analytics/activated_users_card/index.tsx +++ b/webapp/channels/src/components/analytics/activated_users_card/index.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import React from 'react'; -import {FormattedMessage, defineMessages} from 'react-intl'; +import {FormattedMessage} from 'react-intl'; import {AlertOutlineIcon} from '@mattermost/compass-icons/components'; @@ -11,17 +11,15 @@ import StatisticCount from 'components/analytics/statistic_count'; import {calculateOverageUserActivated} from 'utils/overage_team'; +import Title from './title'; + type ActivatedUserCardProps = { seatsPurchased: number; activatedUsers: number | undefined; isCloud: boolean; } -export const messages = defineMessages({ - totalUsers: {id: 'analytics.team.totalUsers', defaultMessage: 'Total Active Users'}, -}); - -export const ActivatedUserCard = ({activatedUsers, seatsPurchased, isCloud}: ActivatedUserCardProps) => { +const ActivatedUserCard = ({activatedUsers, seatsPurchased, isCloud}: ActivatedUserCardProps) => { const {isBetween5PercerntAnd10PercentPurchasedSeats, isOver10PercerntPurchasedSeats} = calculateOverageUserActivated({seatsPurchased, activeUsers: activatedUsers || 0}); const showOverageWarning = !isCloud && (isBetween5PercerntAnd10PercentPurchasedSeats || isOver10PercerntPurchasedSeats); @@ -36,11 +34,7 @@ export const ActivatedUserCard = ({activatedUsers, seatsPurchased, isCloud}: Act return ( - } + title={} icon='fa-users' status={activeUserStatus} count={activatedUsers} @@ -67,3 +61,5 @@ export const ActivatedUserCard = ({activatedUsers, seatsPurchased, isCloud}: Act </StatisticCount> ); }; + +export default ActivatedUserCard; diff --git a/webapp/channels/src/components/analytics/activated_users_card/title.tsx b/webapp/channels/src/components/analytics/activated_users_card/title.tsx new file mode 100644 index 0000000000..76094d4308 --- /dev/null +++ b/webapp/channels/src/components/analytics/activated_users_card/title.tsx @@ -0,0 +1,35 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {defineMessage, defineMessages, useIntl} from 'react-intl'; + +import {InformationOutlineIcon} from '@mattermost/compass-icons/components'; + +import ExternalLink from 'components/external_link'; +import WithTooltip from 'components/with_tooltip'; + +export const messages = defineMessages({ + totalUsers: {id: 'analytics.team.totalUsers', defaultMessage: 'Total Activated Users'}, +}); + +const Title = () => { + const intl = useIntl(); + return ( + <WithTooltip + id='activated_user_title_tooltip' + title={defineMessage({id: 'analytics.team.totalUsers.title.tooltip.title', defaultMessage: 'Activated users on this server'})} + hint={defineMessage({id: 'analytics.team.totalUsers.title.tooltip.hint', defaultMessage: 'Also called Registered Users'})} + placement='top' + > + <span> + <ExternalLink href='https://mattermost.com/pl/site-statistics-definitions'> + {intl.formatMessage(messages.totalUsers)} + <InformationOutlineIcon size='16'/> + </ExternalLink> + </span> + </WithTooltip> + ); +}; + +export default Title; diff --git a/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx b/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx index 40305c403f..de6862e13d 100644 --- a/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx +++ b/webapp/channels/src/components/analytics/system_analytics/system_analytics.tsx @@ -9,7 +9,7 @@ import type {ClientLicense} from '@mattermost/types/config'; import * as AdminActions from 'actions/admin_actions.jsx'; -import {ActivatedUserCard} from 'components/analytics/activated_users_card'; +import ActivatedUserCard from 'components/analytics/activated_users_card'; import TrueUpReview from 'components/analytics/true_up_review'; import ExternalLink from 'components/external_link'; import AdminHeader from 'components/widgets/admin_console/admin_header'; diff --git a/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx b/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx index 967e1a127d..634b44e8c5 100644 --- a/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx +++ b/webapp/channels/src/components/analytics/team_analytics/team_analytics.tsx @@ -16,7 +16,8 @@ import {General} from 'mattermost-redux/constants'; import * as AdminActions from 'actions/admin_actions'; import Banner from 'components/admin_console/banner'; -import {ActivatedUserCard, messages as activatedUsersCardsMessages} from 'components/analytics/activated_users_card'; +import ActivatedUserCard from 'components/analytics/activated_users_card'; +import {messages as activatedUsersCardsMessages} from 'components/analytics/activated_users_card/title'; import LineChart from 'components/analytics/line_chart'; import StatisticCount from 'components/analytics/statistic_count'; import TableChart from 'components/analytics/table_chart'; diff --git a/webapp/channels/src/components/overlay_trigger.tsx b/webapp/channels/src/components/overlay_trigger.tsx index 0c15a9fba7..b6a743536c 100644 --- a/webapp/channels/src/components/overlay_trigger.tsx +++ b/webapp/channels/src/components/overlay_trigger.tsx @@ -19,6 +19,9 @@ type Props = OverlayTriggerProps & { className?: string; }; +/** + * @deprecated Use (and expand when extrictly needed) WithTooltip instead + */ const OverlayTrigger = React.forwardRef((props: Props, ref?: React.Ref<OriginalOverlayTrigger>) => { const {overlay, disabled, ...otherProps} = props; diff --git a/webapp/channels/src/components/shortcut_key/shortcut_key.scss b/webapp/channels/src/components/shortcut_key/shortcut_key.scss index 37ff6a4f20..ae327291b4 100644 --- a/webapp/channels/src/components/shortcut_key/shortcut_key.scss +++ b/webapp/channels/src/components/shortcut_key/shortcut_key.scss @@ -42,3 +42,20 @@ } } } + +.console__body { + .shortcut-key { + display: inline-block; + border-radius: 4px; + + &.shortcut-key--tooltip { + padding: 2px 5px; + background-color: rgba(255, 255, 255, 0.08); + color: rgba(255, 255, 255, 0.72); + font-family: inherit; + font-size: 12px; + font-weight: 600; + line-height: 16px; + } + } +} diff --git a/webapp/channels/src/components/tooltip.tsx b/webapp/channels/src/components/tooltip.tsx index 76d76d1e4e..a013193988 100644 --- a/webapp/channels/src/components/tooltip.tsx +++ b/webapp/channels/src/components/tooltip.tsx @@ -14,6 +14,9 @@ type Props = { placement?: string; }; +/** + * @deprecated Use (and expand when extrictly needed) WithTooltip instead + */ export default function Tooltip(props: Props) { return ( <RBTooltip diff --git a/webapp/channels/src/components/with_tooltip/create_tooltip.tsx b/webapp/channels/src/components/with_tooltip/create_tooltip.tsx new file mode 100644 index 0000000000..2d1ccfa4af --- /dev/null +++ b/webapp/channels/src/components/with_tooltip/create_tooltip.tsx @@ -0,0 +1,58 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import type {ComponentProps} from 'react'; +import type {MessageDescriptor} from 'react-intl'; + +import RenderEmoji from 'components/emoji/render_emoji'; +import {ShortcutKey, ShortcutKeyVariant} from 'components/shortcut_key'; +import Tooltip from 'components/tooltip'; + +import {getStringOrDescriptorComponent} from './utils'; + +export type CommonTooltipProps = { + id: string; + title: string | MessageDescriptor; + hint?: string | MessageDescriptor; + shortcut?: string[]; + emoji?: string; +} + +export function createTooltip(commonTooltipProps: CommonTooltipProps) { + return (props: Omit<ComponentProps<typeof Tooltip>, 'children' | 'id'>) => { + const title = getStringOrDescriptorComponent(commonTooltipProps.title); + const hint = getStringOrDescriptorComponent(commonTooltipProps.hint); + + const emoji = commonTooltipProps.emoji && ( + <RenderEmoji + emojiName={commonTooltipProps.emoji} + size={12} + /> + ); + return ( + <Tooltip + {...props} + id={commonTooltipProps.id} + > + <div className={'tooltip-title'}> + {emoji} + {title} + </div> + {commonTooltipProps.shortcut && ( + <div className={'tooltip-shortcuts-container'}> + {commonTooltipProps.shortcut.map((v) => ( + <ShortcutKey + key={v} + variant={ShortcutKeyVariant.Tooltip} + > + {v} + </ShortcutKey> + ))} + </div> + )} + {commonTooltipProps.hint && (<div className={'tooltip-hint'}>{hint}</div>)} + </Tooltip> + ); + }; +} diff --git a/webapp/channels/src/components/with_tooltip/index.tsx b/webapp/channels/src/components/with_tooltip/index.tsx new file mode 100644 index 0000000000..3728c547e9 --- /dev/null +++ b/webapp/channels/src/components/with_tooltip/index.tsx @@ -0,0 +1,48 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {useMemo} from 'react'; +import type {ComponentProps} from 'react'; + +import OverlayTrigger from 'components/overlay_trigger'; + +import Constants from 'utils/constants'; + +import type {CommonTooltipProps} from './create_tooltip'; +import {createTooltip} from './create_tooltip'; + +type OverlayTriggerProps = ComponentProps<typeof OverlayTrigger>; + +type WithTooltipProps = { + children: OverlayTriggerProps['children']; + placement: OverlayTriggerProps['placement']; +} & CommonTooltipProps; +const WithTooltip = ({ + id, + title, + emoji, + hint, + shortcut, + placement, + children, +}: WithTooltipProps) => { + const ThisTooltip = useMemo(() => createTooltip({ + id, + title, + emoji, + hint, + shortcut, + }), [id, title, emoji, hint, shortcut]); + + return ( + <OverlayTrigger + delay={Constants.OVERLAY_TIME_DELAY} + overlay={<ThisTooltip/>} + placement={placement} + > + {children} + </OverlayTrigger> + ); +}; + +export default WithTooltip; diff --git a/webapp/channels/src/components/with_tooltip/utils.tsx b/webapp/channels/src/components/with_tooltip/utils.tsx new file mode 100644 index 0000000000..bc90654e8d --- /dev/null +++ b/webapp/channels/src/components/with_tooltip/utils.tsx @@ -0,0 +1,24 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import type {ComponentProps} from 'react'; +import type {MessageDescriptor} from 'react-intl'; +import {FormattedMessage} from 'react-intl'; + +export function getStringOrDescriptorComponent(v: string | MessageDescriptor | undefined, values?: ComponentProps<typeof FormattedMessage>['values']) { + if (!v) { + return undefined; + } + + if (typeof v === 'string') { + return v; + } + + return ( + <FormattedMessage + {...v} + values={values} + /> + ); +} diff --git a/webapp/channels/src/i18n/en.json b/webapp/channels/src/i18n/en.json index a19b620aa4..3677509143 100644 --- a/webapp/channels/src/i18n/en.json +++ b/webapp/channels/src/i18n/en.json @@ -2713,7 +2713,9 @@ "analytics.team.recentUsers": "Recent Active Users", "analytics.team.title": "Team Statistics for {team}", "analytics.team.totalPosts": "Total Posts", - "analytics.team.totalUsers": "Total Active Users", + "analytics.team.totalUsers": "Total Activated Users", + "analytics.team.totalUsers.title.tooltip.hint": "Also called Registered Users", + "analytics.team.totalUsers.title.tooltip.title": "Activated users on this server", "announcement_bar.error.email_verification_required": "Check your email inbox to verify the address.", "announcement_bar.error.license_expired": "{licenseSku} license is expired and some features may be disabled.", "announcement_bar.error.license_expiring": "{licenseSku} license expires on {date, date, long}.", diff --git a/webapp/channels/src/sass/components/_tooltip.scss b/webapp/channels/src/sass/components/_tooltip.scss index 8c16b4d30e..a2e373d523 100644 --- a/webapp/channels/src/sass/components/_tooltip.scss +++ b/webapp/channels/src/sass/components/_tooltip.scss @@ -49,6 +49,29 @@ .tooltip-help { color: rgba(255, 255, 255, 0.64); } + + .tooltip-title { + font-weight: 600; + line-height: 15px; + } + + .tooltip-hint { + font-size: 11px; + line-height: 16px; + opacity: 56%; + } + + .tooltip-shortcuts-container { + display: flex; + justify-content: center; + padding: 4px 0; + gap: 2px; + } + + .emoticon { + margin-right: 6px; + vertical-align: center; + } } .floating-ui-tooltip { diff --git a/webapp/channels/src/sass/routes/_statistics.scss b/webapp/channels/src/sass/routes/_statistics.scss index fd11f63134..7885bf0bdb 100644 --- a/webapp/channels/src/sass/routes/_statistics.scss +++ b/webapp/channels/src/sass/routes/_statistics.scss @@ -10,6 +10,10 @@ } } +.analytics_tooltip_body { + font-weight: 400; +} + .team_statistics { &--warning { // Adding the !important because this class has less preference in some html elements @@ -71,6 +75,11 @@ width: calc(100% - 20px); text-overflow: ellipsis; white-space: nowrap; + + svg { + margin-left: 5px; + vertical-align: text-top; + } } .fa {