Merge pull request #7956 from overleaf/ii-refactor-toolitp-usage

Tooltip usage refactoring

GitOrigin-RevId: f4b2d4d57722172141a081dd60e4394ff7fff332
This commit is contained in:
Timothée Alby 2022-05-18 15:46:10 +02:00 committed by Copybot
parent 835a11df1e
commit 5731463d32
20 changed files with 253 additions and 276 deletions

View file

@ -1,18 +1,15 @@
import Icon from '../../../shared/components/icon'
import { useTranslation } from 'react-i18next'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
function BackToProjectsButton() {
const { t } = useTranslation()
return (
<OverlayTrigger
placement="right"
overlay={
<Tooltip id="back-to-projects-tooltip">
{t('back_to_your_projects')}
</Tooltip>
}
<Tooltip
id="back-to-projects"
description={t('back_to_your_projects')}
overlayProps={{ placement: 'right' }}
>
<div className="toolbar-item">
<a className="btn btn-full-height" href="/project">
@ -23,7 +20,7 @@ function BackToProjectsButton() {
/>
</a>
</div>
</OverlayTrigger>
</Tooltip>
)
}

View file

@ -1,7 +1,8 @@
import { useCallback } from 'react'
import PropTypes from 'prop-types'
import { Dropdown, MenuItem, OverlayTrigger, Tooltip } from 'react-bootstrap'
import { Dropdown, MenuItem } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
import IconChecked from '../../../shared/components/icon-checked'
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
@ -50,21 +51,19 @@ function PdfDetachMenuItem({ handleDetach, children }) {
if (!('BroadcastChannel' in window)) {
return (
<OverlayTrigger
placement="left"
overlay={
<Tooltip id="detach-disabled-tooltip">
{t('your_browser_does_not_support_this_feature')}
</Tooltip>
}
<Tooltip
id="detach-disabled"
description={t('your_browser_does_not_support_this_feature')}
overlayProps={{ placement: 'left' }}
>
<MenuItem disabled>{children}</MenuItem>
</OverlayTrigger>
</Tooltip>
)
}
return <MenuItem onSelect={handleDetach}>{children}</MenuItem>
}
PdfDetachMenuItem.propTypes = {
handleDetach: PropTypes.func.isRequired,
children: PropTypes.arrayOf(PropTypes.node).isRequired,
@ -126,13 +125,13 @@ function LayoutDropdownButton() {
<Dropdown.Toggle className="btn-full-height" bsStyle="link">
{processing ? <IconRefresh /> : <IconLayout />}
<span className="toolbar-label">{t('layout')}</span>
<OverlayTrigger
placement="bottom"
overlay={<Tooltip id="pdf-detach-badge">Beta feature</Tooltip>}
delayHide={100}
<Tooltip
id="pdf-detach-badge"
description="Beta feature"
overlayProps={{ placement: 'bottom', delayHide: 100 }}
>
<span className="beta-badge" />
</OverlayTrigger>
</Tooltip>
</Dropdown.Toggle>
<Dropdown.Menu id="layout-dropdown-list">
<MenuItem onSelect={() => handleChangeLayout('sideBySide')}>

View file

@ -1,7 +1,8 @@
import React from 'react'
import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next'
import { Dropdown, MenuItem, OverlayTrigger, Tooltip } from 'react-bootstrap'
import { Dropdown, MenuItem } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
import { getHueForUserId } from '../../../shared/utils/colors'
import ControlledDropdown from '../../../shared/components/controlled-dropdown'
@ -37,17 +38,17 @@ function OnlineUsersWidget({ onlineUsers, goToUser }) {
return (
<div className="online-users">
{onlineUsers.map((user, index) => (
<OverlayTrigger
<Tooltip
key={`${user.user_id}_${index}`}
placement="bottom"
trigger={['hover', 'focus']}
overlay={<Tooltip id="tooltip-online-user">{user.name}</Tooltip>}
id="online-user"
description={user.name}
overlayProps={{ placement: 'bottom', trigger: ['hover', 'focus'] }}
>
<span>
{/* OverlayTrigger won't fire unless UserIcon is wrapped in a span */}
<UserIcon user={user} onClick={goToUser} />
</span>
</OverlayTrigger>
</Tooltip>
))}
</div>
)
@ -94,11 +95,10 @@ UserIcon.propTypes = {
const DropDownToggleButton = React.forwardRef((props, ref) => {
const { t } = useTranslation()
return (
<OverlayTrigger
placement="left"
overlay={
<Tooltip id="tooltip-connected-users">{t('connected_users')}</Tooltip>
}
<Tooltip
id="connected-users"
description={t('connected_users')}
overlayProps={{ placement: 'left' }}
>
<button
className="btn online-user online-user-multi"
@ -107,7 +107,7 @@ const DropDownToggleButton = React.forwardRef((props, ref) => {
<strong>{props.onlineUserCount}</strong>
<Icon type="users" fw />
</button>
</OverlayTrigger>
</Tooltip>
)
})

View file

@ -1,9 +1,13 @@
import PropTypes from 'prop-types'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import classNames from 'classnames'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
function PdfToggleButton({ onClick, pdfViewIsOpen }) {
type PdfToggleButtonProps = {
onClick: () => void
pdfViewIsOpen?: boolean
}
function PdfToggleButton({ onClick, pdfViewIsOpen }: PdfToggleButtonProps) {
const classes = classNames(
'btn',
'btn-full-height',
@ -14,22 +18,17 @@ function PdfToggleButton({ onClick, pdfViewIsOpen }) {
)
return (
<OverlayTrigger
placement="bottom"
trigger={['hover', 'focus']}
overlay={<Tooltip id="tooltip-online-user">PDF</Tooltip>}
<Tooltip
id="online-user"
description="PDF"
overlayProps={{ placement: 'bottom', trigger: ['hover', 'focus'] }}
>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid,jsx-a11y/click-events-have-key-events,jsx-a11y/interactive-supports-focus */}
<a role="button" className={classes} onClick={onClick}>
<Icon type="file-pdf-o" fw accessibilityLabel="PDF" />
</a>
</OverlayTrigger>
</Tooltip>
)
}
PdfToggleButton.propTypes = {
onClick: PropTypes.func.isRequired,
pdfViewIsOpen: PropTypes.bool,
}
export default PdfToggleButton

View file

@ -1,16 +1,22 @@
import { useEffect, useState, useRef } from 'react'
import PropTypes from 'prop-types'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import classNames from 'classnames'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
type ProjectNameEditableLabelProps = {
projectName: string
onChange: (value: string) => void
hasRenamePermissions?: boolean
className?: string
}
function ProjectNameEditableLabel({
projectName,
hasRenamePermissions,
onChange,
className,
}) {
}: ProjectNameEditableLabelProps) {
const { t } = useTranslation()
const [isRenaming, setIsRenaming] = useState(false)
@ -19,11 +25,11 @@ function ProjectNameEditableLabel({
const [inputContent, setInputContent] = useState(projectName)
const inputRef = useRef(null)
const inputRef = useRef<HTMLInputElement | null>(null)
useEffect(() => {
if (isRenaming) {
inputRef.current.select()
inputRef.current?.select()
}
}, [isRenaming])
@ -39,14 +45,14 @@ function ProjectNameEditableLabel({
onChange(inputContent)
}
function handleKeyDown(event) {
function handleKeyDown(event: React.KeyboardEvent) {
if (event.key === 'Enter') {
event.preventDefault()
finishRenaming()
}
}
function handleOnChange(event) {
function handleOnChange(event: React.ChangeEvent<HTMLInputElement>) {
setInputContent(event.target.value)
}
@ -75,26 +81,19 @@ function ProjectNameEditableLabel({
/>
)}
{canRename && (
<OverlayTrigger
placement="bottom"
trigger={['hover', 'focus']}
overlay={<Tooltip id="tooltip-online-user">{t('rename')}</Tooltip>}
<Tooltip
id="online-user"
description={t('rename')}
overlayProps={{ placement: 'bottom', trigger: ['hover', 'focus'] }}
>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid, jsx-a11y/click-events-have-key-events, jsx-a11y/interactive-supports-focus */}
<a className="rename" role="button" onClick={startRenaming}>
<Icon type="pencil" fw />
</a>
</OverlayTrigger>
</Tooltip>
)}
</div>
)
}
ProjectNameEditableLabel.propTypes = {
projectName: PropTypes.string.isRequired,
hasRenamePermissions: PropTypes.bool,
onChange: PropTypes.func.isRequired,
className: PropTypes.string,
}
export default ProjectNameEditableLabel

View file

@ -1,8 +1,9 @@
import PropTypes from 'prop-types'
import { useTranslation } from 'react-i18next'
import { Button } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
import TooltipButton from '../../../shared/components/tooltip-button'
import { useEditorContext } from '../../../shared/context/editor-context'
import { useFileTreeActionable } from '../contexts/file-tree-actionable'
@ -37,27 +38,33 @@ function FileTreeToolbarLeft() {
return (
<div className="toolbar-left">
<TooltipButton
id="new_file"
<Tooltip
id="new-file"
description={t('new_file')}
onClick={startCreatingDocOrFile}
overlayProps={{ placement: 'bottom' }}
>
<Icon type="file" fw accessibilityLabel={t('new_file')} />
</TooltipButton>
<TooltipButton
id="new_folder"
<Button onClick={startCreatingDocOrFile}>
<Icon type="file" fw accessibilityLabel={t('new_file')} />
</Button>
</Tooltip>
<Tooltip
id="new-folder"
description={t('new_folder')}
onClick={startCreatingFolder}
overlayProps={{ placement: 'bottom' }}
>
<Icon type="folder" fw accessibilityLabel={t('new_folder')} />
</TooltipButton>
<TooltipButton
<Button onClick={startCreatingFolder}>
<Icon type="folder" fw accessibilityLabel={t('new_folder')} />
</Button>
</Tooltip>
<Tooltip
id="upload"
description={t('upload')}
onClick={startUploadingDocOrFile}
overlayProps={{ placement: 'bottom' }}
>
<Icon type="upload" fw accessibilityLabel={t('upload')} />
</TooltipButton>
<Button onClick={startUploadingDocOrFile}>
<Icon type="upload" fw accessibilityLabel={t('upload')} />
</Button>
</Tooltip>
</div>
)
}
@ -74,22 +81,26 @@ function FileTreeToolbarRight() {
return (
<div className="toolbar-right">
{canRename ? (
<TooltipButton
<Tooltip
id="rename"
description={t('rename')}
onClick={startRenaming}
overlayProps={{ placement: 'bottom' }}
>
<Icon type="pencil" fw accessibilityLabel={t('rename')} />
</TooltipButton>
<Button onClick={startRenaming}>
<Icon type="pencil" fw accessibilityLabel={t('rename')} />
</Button>
</Tooltip>
) : null}
{canDelete ? (
<TooltipButton
<Tooltip
id="delete"
description={t('delete')}
onClick={startDeleting}
overlayProps={{ placement: 'bottom' }}
>
<Icon type="trash-o" fw accessibilityLabel={t('delete')} />
</TooltipButton>
<Button onClick={startDeleting}>
<Icon type="trash-o" fw accessibilityLabel={t('delete')} />
</Button>
</Tooltip>
) : null}
</div>
)

View file

@ -1,26 +1,35 @@
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap'
import PropTypes from 'prop-types'
import { Button } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
import { useTranslation } from 'react-i18next'
import { memo } from 'react'
const modifierKey = /Mac/i.test(navigator.platform) ? 'Cmd' : 'Ctrl'
function PdfCompileButtonInner({ startCompile, compiling }) {
type PdfCompileButtonInnerProps = {
startCompile: () => void
compiling: boolean
}
function PdfCompileButtonInner({
startCompile,
compiling,
}: PdfCompileButtonInnerProps) {
const { t } = useTranslation()
const compileButtonLabel = compiling ? t('compiling') + '…' : t('recompile')
return (
<OverlayTrigger
placement="bottom"
delayShow={500}
overlay={
<Tooltip id="tooltip-logs-toggle" className="keyboard-tooltip">
<Tooltip
id="logs-toggle"
description={
<>
{t('recompile_pdf')}{' '}
<span className="keyboard-shortcut">({modifierKey} + Enter)</span>
</Tooltip>
</>
}
tooltipProps={{ className: 'keyboard-tooltip' }}
overlayProps={{ delayShow: 500 }}
>
<Button
className="btn-recompile"
@ -34,13 +43,8 @@ function PdfCompileButtonInner({ startCompile, compiling }) {
{compileButtonLabel}
</span>
</Button>
</OverlayTrigger>
</Tooltip>
)
}
PdfCompileButtonInner.propTypes = {
compiling: PropTypes.bool.isRequired,
startCompile: PropTypes.func.isRequired,
}
export default memo(PdfCompileButtonInner)

View file

@ -1,7 +1,8 @@
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { Button } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
import { memo, useMemo } from 'react'
import { useMemo } from 'react'
import { useLayoutContext } from '../../../shared/context/layout-context'
function PdfExpandButton() {
@ -14,9 +15,10 @@ function PdfExpandButton() {
}, [pdfLayout, t])
return (
<OverlayTrigger
placement="left"
overlay={<Tooltip id="expand-pdf-btn">{text}</Tooltip>}
<Tooltip
id="expand-pdf-btn"
description="text"
overlayProps={{ placement: 'left' }}
>
<Button
onClick={switchLayout}
@ -25,8 +27,8 @@ function PdfExpandButton() {
>
<Icon type={pdfLayout === 'sideBySide' ? 'expand' : 'compress'} />
</Button>
</OverlayTrigger>
</Tooltip>
)
}
export default memo(PdfExpandButton)
export default PdfExpandButton

View file

@ -1,25 +1,19 @@
import { useTranslation } from 'react-i18next'
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap'
import { Button } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
import { memo } from 'react'
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
function PdfHybridDownloadButton() {
const { pdfDownloadUrl } = useCompileContext()
const { t } = useTranslation()
const description = pdfDownloadUrl
? t('download_pdf')
: t('please_compile_pdf_before_download')
return (
<OverlayTrigger
placement="bottom"
overlay={
<Tooltip id="tooltip-logs-toggle">
{pdfDownloadUrl
? t('download_pdf')
: t('please_compile_pdf_before_download')}
</Tooltip>
}
>
<Tooltip id="logs-toggle" description={description}>
<Button
bsStyle="link"
disabled={!pdfDownloadUrl}
@ -30,8 +24,8 @@ function PdfHybridDownloadButton() {
>
<Icon type="download" fw />
</Button>
</OverlayTrigger>
</Tooltip>
)
}
export default memo(PdfHybridDownloadButton)
export default PdfHybridDownloadButton

View file

@ -1,6 +1,7 @@
import { memo, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { Button, Label, OverlayTrigger, Tooltip } from 'react-bootstrap'
import { Button, Label } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
@ -18,11 +19,10 @@ function PdfHybridLogsButton() {
const totalCount = errorCount + warningCount
return (
<OverlayTrigger
placement="bottom"
overlay={
<Tooltip id="tooltip-logs-toggle">{t('logs_and_output_files')}</Tooltip>
}
<Tooltip
id="logs-toggle"
description={t('logs_and_output_files')}
overlayProps={{ placement: 'bottom' }}
>
<Button
bsStyle="link"
@ -41,7 +41,7 @@ function PdfHybridLogsButton() {
</Label>
)}
</Button>
</OverlayTrigger>
</Tooltip>
)
}

View file

@ -7,7 +7,8 @@ import { getJSON } from '../../../infrastructure/fetch-json'
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
import { useLayoutContext } from '../../../shared/context/layout-context'
import useScopeValue from '../../../shared/hooks/use-scope-value'
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap'
import { Button } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
import { useTranslation } from 'react-i18next'
import useIsMounted from '../../../shared/hooks/use-is-mounted'
@ -38,13 +39,10 @@ function GoToCodeButton({
}
return (
<OverlayTrigger
placement={tooltipPlacement}
overlay={
<Tooltip id="sync-to-code-tooltip">
{t('go_to_pdf_location_in_code')}
</Tooltip>
}
<Tooltip
id="sync-to-code"
description={t('go_to_pdf_location_in_code')}
overlayProps={{ placement: tooltipPlacement }}
>
<Button
bsStyle="default"
@ -57,7 +55,7 @@ function GoToCodeButton({
{buttonIcon}
{isDetachLayout ? <span>&nbsp;{t('show_in_code')}</span> : ''}
</Button>
</OverlayTrigger>
</Tooltip>
)
}
@ -82,13 +80,10 @@ function GoToPdfButton({
}
return (
<OverlayTrigger
placement={tooltipPlacement}
overlay={
<Tooltip id="sync-to-pdf-tooltip">
{t('go_to_code_location_in_pdf')}
</Tooltip>
}
<Tooltip
id="sync-to-pdf"
description={t('go_to_code_location_in_pdf')}
overlayProps={{ placement: tooltipPlacement }}
>
<Button
bsStyle="default"
@ -101,7 +96,7 @@ function GoToPdfButton({
{buttonIcon}
{isDetachLayout ? <span>&nbsp;{t('show_in_pdf')}</span> : ''}
</Button>
</OverlayTrigger>
</Tooltip>
)
}

View file

@ -2,8 +2,8 @@ import PropTypes from 'prop-types'
import classNames from 'classnames'
import { useState, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import useResizeObserver from '../hooks/use-resize-observer'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
function PreviewLogEntryHeader({
@ -84,13 +84,6 @@ function PreviewLogEntryHeader({
</button>
) : null
const locationTooltip =
locationSpanOverflown && locationLinkText ? (
<Tooltip id={locationLinkText} className="log-location-tooltip">
{locationLinkText}
</Tooltip>
) : null
const headerTitleText = logType ? `${logType} ${headerTitle}` : headerTitle
return (
@ -99,10 +92,15 @@ function PreviewLogEntryHeader({
<div className="log-entry-header-icon-container">{headerIcon}</div>
) : null}
<h3 className="log-entry-header-title">{headerTitleText}</h3>
{locationTooltip ? (
<OverlayTrigger placement="left" overlay={locationTooltip}>
{locationSpanOverflown && locationLinkText ? (
<Tooltip
id={locationLinkText}
description={locationLinkText}
overlayProps={{ placement: 'left' }}
tooltipProps={{ className: 'log-location-tooltip' }}
>
{locationLink}
</OverlayTrigger>
</Tooltip>
) : (
locationLink
)}

View file

@ -2,18 +2,11 @@ import { useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import { Trans, useTranslation } from 'react-i18next'
import { useShareProjectContext } from './share-project-modal'
import Icon from '../../../shared/components/icon'
import TransferOwnershipModal from './transfer-ownership-modal'
import {
Button,
Col,
Form,
FormControl,
FormGroup,
OverlayTrigger,
Tooltip,
} from 'react-bootstrap'
import { removeMemberFromProject, updateMember } from '../utils/api'
import { Button, Col, Form, FormControl, FormGroup } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
import { useProjectContext } from '../../../shared/context/project-context'
export default function EditMember({ member }) {
@ -110,6 +103,7 @@ function SelectPrivilege({ value, handleChange }) {
</FormControl>
)
}
SelectPrivilege.propTypes = {
value: PropTypes.string.isRequired,
handleChange: PropTypes.func.isRequired,
@ -134,13 +128,10 @@ function RemoveMemberAction({ member }) {
return (
<FormControl.Static className="text-center">
<OverlayTrigger
placement="bottom"
overlay={
<Tooltip id="tooltip-remove-collaborator">
<Trans i18nKey="remove_collaborator" />
</Tooltip>
}
<Tooltip
id="remove-collaborator"
description={<Trans i18nKey="remove_collaborator" />}
overlayProps={{ placement: 'bottom' }}
>
<Button
type="button"
@ -151,10 +142,11 @@ function RemoveMemberAction({ member }) {
>
<Icon type="times" />
</Button>
</OverlayTrigger>
</Tooltip>
</FormControl.Static>
)
}
RemoveMemberAction.propTypes = {
member: PropTypes.shape({
_id: PropTypes.string.isRequired,
@ -179,6 +171,7 @@ function ChangePrivilegesActions({ handleReset }) {
</div>
)
}
ChangePrivilegesActions.propTypes = {
handleReset: PropTypes.func.isRequired,
}

View file

@ -2,7 +2,8 @@ import { useCallback } from 'react'
import PropTypes from 'prop-types'
import { useShareProjectContext } from './share-project-modal'
import Icon from '../../../shared/components/icon'
import { Button, Col, Row, OverlayTrigger, Tooltip } from 'react-bootstrap'
import { Button, Col, Row } from 'react-bootstrap'
import Tooltip from '../../../shared/components/tooltip'
import { Trans, useTranslation } from 'react-i18next'
import MemberPrivileges from './member-privileges'
import { resendInvite, revokeInvite } from '../utils/api'
@ -68,6 +69,7 @@ function ResendInvite({ invite }) {
</Button>
)
}
ResendInvite.propTypes = {
invite: PropTypes.object.isRequired,
}
@ -88,13 +90,10 @@ function RevokeInvite({ invite }) {
}
return (
<OverlayTrigger
placement="bottom"
overlay={
<Tooltip id="tooltip-revoke-invite">
<Trans i18nKey="revoke_invite" />
</Tooltip>
}
<Tooltip
id="revoke-invite"
description={<Trans i18nKey="revoke_invite" />}
overlayProps={{ placement: 'bottom' }}
>
<Button
type="button"
@ -105,9 +104,10 @@ function RevokeInvite({ invite }) {
>
<Icon type="times" />
</Button>
</OverlayTrigger>
</Tooltip>
)
}
RevokeInvite.propTypes = {
invite: PropTypes.object.isRequired,
}

View file

@ -1,7 +1,8 @@
import { useCallback, useState } from 'react'
import PropTypes from 'prop-types'
import { Button, Col, OverlayTrigger, Row, Tooltip } from 'react-bootstrap'
import { Button, Col, Row } from 'react-bootstrap'
import { Trans } from 'react-i18next'
import Tooltip from '../../../shared/components/tooltip'
import Icon from '../../../shared/components/icon'
import { useShareProjectContext } from './share-project-modal'
import { setProjectAccessLevel } from '../utils/api'
@ -93,6 +94,7 @@ function PrivateSharing({ setAccessLevel, inflight }) {
</Row>
)
}
PrivateSharing.propTypes = {
setAccessLevel: PropTypes.func.isRequired,
inflight: PropTypes.bool,
@ -144,6 +146,7 @@ function TokenBasedSharing({ setAccessLevel, inflight }) {
</Row>
)
}
TokenBasedSharing.propTypes = {
setAccessLevel: PropTypes.func.isRequired,
inflight: PropTypes.bool,
@ -177,6 +180,7 @@ function LegacySharing({ accessLevel, setAccessLevel, inflight }) {
</Row>
)
}
LegacySharing.propTypes = {
accessLevel: PropTypes.string.isRequired,
setAccessLevel: PropTypes.func.isRequired,
@ -230,6 +234,7 @@ function AccessToken({ token, path, tooltipId }) {
</pre>
)
}
AccessToken.propTypes = {
token: PropTypes.string,
tooltipId: PropTypes.string.isRequired,
@ -238,13 +243,9 @@ AccessToken.propTypes = {
function LinkSharingInfo() {
return (
<OverlayTrigger
placement="top"
overlay={
<Tooltip id="tooltip-link-sharing-info">
<Trans i18nKey="learn_more_about_link_sharing" />
</Tooltip>
}
<Tooltip
id="link-sharing-info"
description={<Trans i18nKey="learn_more_about_link_sharing" />}
>
<a
href="/learn/how-to/What_is_Link_Sharing%3F"
@ -253,6 +254,6 @@ function LinkSharingInfo() {
>
<Icon type="question-circle" />
</a>
</OverlayTrigger>
</Tooltip>
)
}

View file

@ -1,35 +0,0 @@
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import PropTypes from 'prop-types'
export default function BetaBadge({ tooltip, url = '/beta/participate' }) {
return (
<OverlayTrigger
placement={tooltip.placement || 'bottom'}
overlay={
<Tooltip id={tooltip.id} className={tooltip.className}>
{tooltip.text}
</Tooltip>
}
delayHide={100}
>
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className="badge beta-badge"
>
<span className="sr-only">{tooltip.text}</span>
</a>
</OverlayTrigger>
)
}
BetaBadge.propTypes = {
tooltip: PropTypes.shape({
id: PropTypes.string.isRequired,
text: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
placement: PropTypes.string,
className: PropTypes.string,
}),
url: PropTypes.string,
}

View file

@ -0,0 +1,39 @@
import Tooltip from './tooltip'
import { OverlayTriggerProps } from 'react-bootstrap'
type TooltipProps = {
id: string
text: React.ReactNode
placement?: OverlayTriggerProps['placement']
className?: string
}
type BetaBadgeProps = {
tooltip: TooltipProps
url?: string
}
function BetaBadge({ tooltip, url = '/beta/participate' }: BetaBadgeProps) {
return (
<Tooltip
id={tooltip.id}
description={tooltip.text}
tooltipProps={{ className: tooltip.className }}
overlayProps={{
placement: tooltip.placement || 'bottom',
delayHide: 100,
}}
>
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className="badge beta-badge"
>
<span className="sr-only">{tooltip.text}</span>
</a>
</Tooltip>
)
}
export default BetaBadge

View file

@ -1,10 +1,15 @@
import { useCallback, useState } from 'react'
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap'
import PropTypes from 'prop-types'
import { Button } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
import Tooltip from './tooltip'
import Icon from './icon'
export default function CopyLink({ link, tooltipId }) {
type CopyLinkProps = {
link: string
tooltipId: string
}
function CopyLink({ link, tooltipId }: CopyLinkProps) {
const { t } = useTranslation()
const [copied, setCopied] = useState(false)
@ -23,15 +28,13 @@ export default function CopyLink({ link, tooltipId }) {
}
return (
<OverlayTrigger
placement="top"
delayHide={copied ? 1000 : 250}
shouldUpdatePosition
overlay={
<Tooltip id={tooltipId}>
{copied ? 'Copied!' : <Trans i18nKey="copy" />}
</Tooltip>
}
<Tooltip
id={tooltipId}
description={copied ? 'Copied!' : <Trans i18nKey="copy" />}
overlayProps={{
delayHide: copied ? 1000 : 250,
shouldUpdatePosition: true,
}}
>
<Button
onClick={handleClick}
@ -42,10 +45,8 @@ export default function CopyLink({ link, tooltipId }) {
>
{copied ? <Icon type="check" /> : <Icon type="clipboard" />}
</Button>
</OverlayTrigger>
</Tooltip>
)
}
CopyLink.propTypes = {
link: PropTypes.string.isRequired,
tooltipId: PropTypes.string.isRequired,
}
export default CopyLink

View file

@ -1,24 +0,0 @@
import PropTypes from 'prop-types'
import { Button, Tooltip, OverlayTrigger } from 'react-bootstrap'
function TooltipButton({ id, description, onClick, children }) {
const tooltip = <Tooltip id={`${id}_tooltip`}>{description}</Tooltip>
return (
<OverlayTrigger placement="bottom" overlay={tooltip}>
<Button onClick={onClick}>{children}</Button>
</OverlayTrigger>
)
}
TooltipButton.propTypes = {
id: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
onClick: PropTypes.func,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
}
export default TooltipButton

View file

@ -4,11 +4,15 @@ import {
Tooltip as BSTooltip,
} from 'react-bootstrap'
type OverlayTriggerCustomProps = {
shouldUpdatePosition?: boolean // Not officially documented https://stackoverflow.com/a/43138470
} & OverlayTriggerProps
type TooltipProps = {
children: React.ReactNode
description: string
description: React.ReactNode
id: string
overlayProps?: Omit<OverlayTriggerProps, 'overlay'>
overlayProps?: Omit<OverlayTriggerCustomProps, 'overlay'>
tooltipProps?: BSTooltip.TooltipProps
}