Browse Source

feat(theme): Add tooltipUnderline (dotted underline) utility (#33746)

* feat(theme): Add underline utilities to theme object

* ref(ui): Use tooltip indicators (dotted underlines))

* ref(theme): Remove linkUnderline util

This should be added in a separate PR.

* fix(global-styles): Use tooltipUnderline, not tooltipIndicator

* fix(tooltip): Forward trigger element's class name

* ref(theme): Rename tooltipIndicator to tooltipUnderline

* ref(theme): Don't use css function from emotion for tooltipUnderline

* ref(tooltip): Use shorter syntax for style prop

Co-authored-by: Evan Purkhiser <evanpurkhiser@gmail.com>

Co-authored-by: Evan Purkhiser <evanpurkhiser@gmail.com>
Vu Luong 2 years ago
parent
commit
2cf6a827cc

+ 0 - 1
static/app/components/events/eventDataSection.tsx

@@ -167,7 +167,6 @@ const SectionHeader = styled('div')<{isCentered?: boolean}>`
   }
   }
   & small > span {
   & small > span {
     color: ${p => p.theme.textColor};
     color: ${p => p.theme.textColor};
-    border-bottom: 1px dotted ${p => p.theme.border};
     font-weight: normal;
     font-weight: normal;
   }
   }
 
 

+ 1 - 1
static/app/components/events/interfaces/crashHeader/crashTitle.tsx

@@ -34,7 +34,7 @@ const CrashTitle = ({
           {title}
           {title}
         </GuideAnchor>
         </GuideAnchor>
         {onChange && (
         {onChange && (
-          <Tooltip title={t('Toggle stack trace order')}>
+          <Tooltip showUnderline title={t('Toggle stack trace order')}>
             <small>
             <small>
               (
               (
               <span onClick={handleToggleOrder}>
               <span onClick={handleToggleOrder}>

+ 1 - 0
static/app/components/group/seenInfo.tsx

@@ -48,6 +48,7 @@ class SeenInfo extends React.Component<Props> {
     return (
     return (
       <HovercardWrapper>
       <HovercardWrapper>
         <StyledHovercard
         <StyledHovercard
+          showUnderline
           header={
           header={
             <div>
             <div>
               <TimeSinceWrapper>
               <TimeSinceWrapper>

+ 12 - 2
static/app/components/hovercard.tsx

@@ -66,6 +66,11 @@ interface HovercardProps {
    * If set, is used INSTEAD OF the hover action to determine whether the hovercard is shown
    * If set, is used INSTEAD OF the hover action to determine whether the hovercard is shown
    */
    */
   show?: boolean;
   show?: boolean;
+  /**
+   * Whether to add a dotted underline to the trigger element, to indicate the
+   * presence of a tooltip.
+   */
+  showUnderline?: boolean;
   /**
   /**
    * Color of the arrow tip border
    * Color of the arrow tip border
    */
    */
@@ -149,14 +154,15 @@ function Hovercard(props: HovercardProps): React.ReactElement {
     <Manager>
     <Manager>
       <Reference>
       <Reference>
         {({ref}) => (
         {({ref}) => (
-          <span
+          <Trigger
             ref={ref}
             ref={ref}
             aria-describedby={tooltipId}
             aria-describedby={tooltipId}
             className={props.containerClassName}
             className={props.containerClassName}
+            showUnderline={props.showUnderline}
             {...hoverProps}
             {...hoverProps}
           >
           >
             {props.children}
             {props.children}
-          </span>
+          </Trigger>
         )}
         )}
       </Reference>
       </Reference>
       {createPortal(
       {createPortal(
@@ -273,6 +279,10 @@ function getTipDirection(
   return (prefix || 'top') as 'top' | 'bottom' | 'left' | 'right';
   return (prefix || 'top') as 'top' | 'bottom' | 'left' | 'right';
 }
 }
 
 
+const Trigger = styled('span')<{showUnderline?: boolean}>`
+  ${p => p.showUnderline && p.theme.tooltipUnderline};
+`;
+
 const HovercardContainer = styled('div')`
 const HovercardContainer = styled('div')`
   /* Some hovercards overlap the toplevel header and sidebar, and we need to appear on top */
   /* Some hovercards overlap the toplevel header and sidebar, and we need to appear on top */
   z-index: ${p => p.theme.zIndex.hovercard};
   z-index: ${p => p.theme.zIndex.hovercard};

+ 23 - 3
static/app/components/tooltip.tsx

@@ -9,7 +9,7 @@ import {
 } from 'react';
 } from 'react';
 import {createPortal} from 'react-dom';
 import {createPortal} from 'react-dom';
 import {Manager, Popper, PopperArrowProps, PopperProps, Reference} from 'react-popper';
 import {Manager, Popper, PopperArrowProps, PopperProps, Reference} from 'react-popper';
-import {SerializedStyles} from '@emotion/react';
+import {SerializedStyles, useTheme} from '@emotion/react';
 import styled from '@emotion/styled';
 import styled from '@emotion/styled';
 import {AnimatePresence, motion, MotionProps, MotionStyle} from 'framer-motion';
 import {AnimatePresence, motion, MotionProps, MotionStyle} from 'framer-motion';
 import * as PopperJS from 'popper.js';
 import * as PopperJS from 'popper.js';
@@ -96,6 +96,12 @@ export interface InternalTooltipProps {
    */
    */
   showOnlyOnOverflow?: boolean;
   showOnlyOnOverflow?: boolean;
 
 
+  /**
+   * Whether to add a dotted underline to the trigger element, to indicate the
+   * presence of a tooltip.
+   */
+  showUnderline?: boolean;
+
   /**
   /**
    * If child node supports ref forwarding, you can skip apply a wrapper
    * If child node supports ref forwarding, you can skip apply a wrapper
    */
    */
@@ -137,6 +143,7 @@ export function DO_NOT_USE_TOOLTIP({
   forceVisible,
   forceVisible,
   isHoverable,
   isHoverable,
   popperStyle,
   popperStyle,
+  showUnderline,
   showOnlyOnOverflow,
   showOnlyOnOverflow,
   skipWrapper,
   skipWrapper,
   title,
   title,
@@ -146,6 +153,7 @@ export function DO_NOT_USE_TOOLTIP({
 }: InternalTooltipProps) {
 }: InternalTooltipProps) {
   const [visible, setVisible] = useState(false);
   const [visible, setVisible] = useState(false);
   const tooltipId = useMemo(() => domId('tooltip-'), []);
   const tooltipId = useMemo(() => domId('tooltip-'), []);
+  const theme = useTheme();
 
 
   // Delayed open and close time handles
   // Delayed open and close time handles
   const delayOpenTimeoutRef = useRef<number | undefined>(undefined);
   const delayOpenTimeoutRef = useRef<number | undefined>(undefined);
@@ -234,13 +242,25 @@ export function DO_NOT_USE_TOOLTIP({
       (skipWrapper || typeof triggerChildren.type === 'string')
       (skipWrapper || typeof triggerChildren.type === 'string')
     ) {
     ) {
       // Basic DOM nodes can be cloned and have more props applied.
       // Basic DOM nodes can be cloned and have more props applied.
-      return cloneElement(triggerChildren, {...containerProps, ref: setRef});
+      return cloneElement(triggerChildren, {
+        ...containerProps,
+        style: {
+          ...triggerChildren.props.style,
+          ...(showUnderline && theme.tooltipUnderline),
+        },
+        ref: setRef,
+      });
     }
     }
 
 
     containerProps.containerDisplayMode = containerDisplayMode;
     containerProps.containerDisplayMode = containerDisplayMode;
 
 
     return (
     return (
-      <Container {...containerProps} className={className} ref={setRef}>
+      <Container
+        {...containerProps}
+        style={showUnderline ? theme.tooltipUnderline : undefined}
+        className={className}
+        ref={setRef}
+      >
         {triggerChildren}
         {triggerChildren}
       </Container>
       </Container>
     );
     );

+ 1 - 1
static/app/styles/global.tsx

@@ -14,7 +14,7 @@ const styles = (theme: Theme, isDark: boolean) => css`
   }
   }
 
 
   abbr {
   abbr {
-    border-bottom: 1px dotted ${theme.gray300};
+    ${theme.tooltipUnderline};
   }
   }
 
 
   a {
   a {

+ 10 - 0
static/app/utils/theme.tsx

@@ -564,6 +564,14 @@ const generateButtonTheme = (colors: BaseColors, alias: Aliases) => ({
   },
   },
 });
 });
 
 
+const generateUtils = (colors: BaseColors) => ({
+  tooltipUnderline: {
+    textDecoration: `underline dotted ${colors.gray300}`,
+    textDecorationThickness: '0.75px',
+    textUnderlineOffset: '1.25px',
+  },
+});
+
 const iconSizes = {
 const iconSizes = {
   xs: '12px',
   xs: '12px',
   sm: '16px',
   sm: '16px',
@@ -812,6 +820,7 @@ export const lightTheme = {
     ...darkColors,
     ...darkColors,
     ...darkAliases,
     ...darkAliases,
   },
   },
+  ...generateUtils(lightColors),
   alert: generateAlertTheme(lightColors, lightAliases),
   alert: generateAlertTheme(lightColors, lightAliases),
   badge: generateBadgeTheme(lightColors),
   badge: generateBadgeTheme(lightColors),
   button: generateButtonTheme(lightColors, lightAliases),
   button: generateButtonTheme(lightColors, lightAliases),
@@ -834,6 +843,7 @@ export const darkTheme: Theme = {
     ...lightColors,
     ...lightColors,
     ...lightAliases,
     ...lightAliases,
   },
   },
+  ...generateUtils(darkColors),
   alert: generateAlertTheme(darkColors, darkAliases),
   alert: generateAlertTheme(darkColors, darkAliases),
   badge: generateBadgeTheme(darkColors),
   badge: generateBadgeTheme(darkColors),
   button: generateButtonTheme(darkColors, darkAliases),
   button: generateButtonTheme(darkColors, darkAliases),

+ 1 - 2
static/app/views/organizationGroupDetails/eventToolbar.tsx

@@ -138,7 +138,7 @@ class GroupEventToolbar extends Component<Props> {
             </ExternalLink>
             </ExternalLink>
           </LinkContainer>
           </LinkContainer>
         </Heading>
         </Heading>
-        <Tooltip title={this.getDateTooltip()} disableForVisualTest>
+        <Tooltip title={this.getDateTooltip()} showUnderline disableForVisualTest>
           <StyledDateTime
           <StyledDateTime
             format={is24Hours ? 'MMM D, YYYY HH:mm:ss zz' : 'll LTS z'}
             format={is24Hours ? 'MMM D, YYYY HH:mm:ss zz' : 'll LTS z'}
             date={getDynamicText({
             date={getDynamicText({
@@ -198,7 +198,6 @@ const StyledIconWarning = styled(IconWarning)`
 `;
 `;
 
 
 const StyledDateTime = styled(DateTime)`
 const StyledDateTime = styled(DateTime)`
-  border-bottom: 1px dotted #dfe3ea;
   color: ${p => p.theme.subText};
   color: ${p => p.theme.subText};
 `;
 `;
 
 

+ 1 - 0
static/app/views/organizationGroupDetails/header.tsx

@@ -198,6 +198,7 @@ class GroupHeader extends React.Component<Props, State> {
                       <h6 className="nav-header">
                       <h6 className="nav-header">
                         <Tooltip
                         <Tooltip
                           className="help-link"
                           className="help-link"
+                          showUnderline
                           title={t(
                           title={t(
                             'This identifier is unique across your organization, and can be used to reference an issue in various places, like commit messages.'
                             'This identifier is unique across your organization, and can be used to reference an issue in various places, like commit messages.'
                           )}
                           )}

+ 0 - 1
static/less/shared-components.less

@@ -1074,7 +1074,6 @@ header + .alert {
   a.help-link,
   a.help-link,
   span.help-link a {
   span.help-link a {
     color: inherit;
     color: inherit;
-    border-bottom: 1px dotted @gray-light;
   }
   }
 
 
   .view-more {
   .view-more {