Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ui-parts/tooltip/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
},
"dependencies": {
"@atls-ui-parts/condition": "workspace:*",
"@atls-ui-parts/theme": "workspace:*",
"@atls-utils/use-float": "workspace:*",
"@floating-ui/react": "0.27.8",
"framer-motion": "12.23.22"
Expand Down
42 changes: 29 additions & 13 deletions ui-parts/tooltip/src/component.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,51 @@
import type { ReactNode } from 'react'
import type { ReactNode } from 'react'

import type { TooltipProps } from './interfaces.js'
import type { TooltipProps } from './interfaces.js'

import { FloatingPortal } from '@floating-ui/react'
import { AnimatePresence } from 'framer-motion'
import { motion } from 'framer-motion'
import { cloneElement } from 'react'
import { FloatingPortal } from '@floating-ui/react'
import { AnimatePresence } from 'framer-motion'
import { motion } from 'framer-motion'
import { cloneElement } from 'react'

import { useFloat } from '@atls-utils/use-float'
import { useFloat } from '@atls-utils/use-float'

import { Arrow } from './arrow/index.js'
import { Container } from './container/index.js'
import { animateProps } from './constants.js'
import { Arrow } from './arrow/index.js'
import { Container } from './container/index.js'
import { animateProps } from './constants.js'
import { tooltipAppearances } from './styles/index.js'
import { tooltipShapes } from './styles/index.js'

export const Tooltip = ({
appearance,
children,
text,
open,
container = <Container />,
container,
animated = true,
arrow = true,
shape,
...props
}: TooltipProps): ReactNode => {
const { arrowRef, refs, isOpen, context, floatingStyles, getFloatingProps, getReferenceProps } =
useFloat({ open, role: 'tooltip', ...props })

const containerElement = container ?? <Container />
const defaultAppearance = container === undefined ? tooltipAppearances.default : undefined
const defaultShape = container === undefined ? tooltipShapes.default : undefined
const containerAppearance = appearance ?? defaultAppearance
const containerShape = shape ?? defaultShape
Comment on lines +35 to +36

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Merge custom style slots with defaults

When consumers use the new public API to style the default container, e.g. appearance={{ container: customClass }} or shape={{ container: customClass }}, this replaces the corresponding default class instead of appending to it. That means a class meant to tweak just one property drops the built-in defaults from the old container (color/background/shadow for appearance, or padding/radius/z-index for shape), which violates the issue requirement that the default container preserve its internal styles while accepting a user class.

Useful? React with 👍 / 👎.

const TriggerElement = cloneElement(children, { ref: refs.setReference, ...getReferenceProps() })
const containerClassName = [
containerElement.props.className,
containerAppearance?.container,
containerShape?.container,
]
.filter(Boolean)
.join(' ')

const ContainerElement = cloneElement(
container,
{ open: isOpen },
containerElement,
{ className: containerClassName || undefined, open: isOpen },
<>
<Arrow ref={arrowRef} context={context} arrow={arrow} />
{text}
Expand Down
6 changes: 6 additions & 0 deletions ui-parts/tooltip/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { MotionProps } from 'framer-motion'

export const DEFAULT_CONTAINER_HEIGHT = 32

export const DEFAULT_CONTAINER_MIN_WIDTH = 30

export const DEFAULT_CONTAINER_Z_INDEX = 1000

export const animateProps: MotionProps = {
initial: { opacity: 0, scale: 0.8 },
animate: { opacity: 1, scale: 1 },
Expand Down
7 changes: 4 additions & 3 deletions ui-parts/tooltip/src/container/component.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { HTMLAttributes } from 'react'
import type { ReactNode } from 'react'

import { baseContainerStyles } from './styles.css.js'
import { baseContainerStyles } from './container.css.js'

export const Container = (props: {}): ReactNode => (
<div className={baseContainerStyles} {...props} />
export const Container = ({ className, ...props }: HTMLAttributes<HTMLDivElement>): ReactNode => (
<div {...props} className={[baseContainerStyles, className].filter(Boolean).join(' ')} />
)
9 changes: 9 additions & 0 deletions ui-parts/tooltip/src/container/container.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { style } from '@vanilla-extract/css'

export const baseContainerStyles = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: 0,
wordWrap: 'break-word',
})
17 changes: 0 additions & 17 deletions ui-parts/tooltip/src/container/styles.css.ts

This file was deleted.

1 change: 1 addition & 0 deletions ui-parts/tooltip/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './arrow/index.js'
export * from './component.js'
export * from './styles/index.js'
export type * from './interfaces.js'
14 changes: 14 additions & 0 deletions ui-parts/tooltip/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,24 @@ import type { MotionProps } from 'framer-motion'
import type { JSX } from 'react'
import type { ReactNode } from 'react'

export interface TooltipAppearance {
container: string
}

export type TooltipAppearanceName = 'default'

export interface TooltipShape {
container: string
}

export type TooltipShapeName = 'default'

export interface TooltipProps extends UseFloatProps {
appearance?: TooltipAppearance
children: JSX.Element
text?: ReactNode
container?: JSX.Element
animated?: Omit<MotionProps, 'children'> | boolean
arrow?: Omit<FloatingArrowProps, 'context'> | boolean
shape?: TooltipShape
}
22 changes: 22 additions & 0 deletions ui-parts/tooltip/src/styles/appearance.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { TooltipAppearance } from '../interfaces.js'
import type { TooltipAppearanceName } from '../interfaces.js'

import { style } from '@vanilla-extract/css'

import { vars } from '@atls-ui-parts/theme'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid importing page-wide globals from Tooltip

Whenever a consumer imports @atls-ui-parts/tooltip, this new dependency import loads @atls-ui-parts/theme; that package's theme.css.ts imports global.css, which emits globalStyle rules for html, body, and #__next (margin/padding reset, overflowX: hidden, etc.). A leaf tooltip component should not mutate the host document, because embedding it in an existing app can unexpectedly change page layout even when the app did not opt into the theme globals.

Useful? React with 👍 / 👎.


const containerDefaultAppearanceStyles = style({
color: vars.colors.white,
backgroundColor: vars.colors.blackThreeQuarters,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep default tooltip styles standalone

When @atls-ui-parts/tooltip is consumed outside the repo Storybook decorators, nothing sets id={globalThemeId} for @atls-ui-parts/theme (repo-wide search only found this in ui-parts/design/src/preview.tsx and ui/design/src/preview.tsx). These default values therefore compile to CSS variables scoped under #atls-ui-parts-theme and resolve as invalid in a standalone app, so the default tooltip loses its dark background/color and related shape tokens; before this change the same defaults were literal CSS values and worked without a theme container.

Useful? React with 👍 / 👎.

boxShadow: vars.shadows.gordonsgreen,
})

export const appearanceVariant = {
default: {
container: containerDefaultAppearanceStyles,
},
}

export const tooltipAppearances: Record<TooltipAppearanceName, TooltipAppearance> = {
default: appearanceVariant.default,
}
2 changes: 2 additions & 0 deletions ui-parts/tooltip/src/styles/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './appearance.css.js'
export * from './shape.css.js'
26 changes: 26 additions & 0 deletions ui-parts/tooltip/src/styles/shape.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { TooltipShape } from '../interfaces.js'
import type { TooltipShapeName } from '../interfaces.js'

import { style } from '@vanilla-extract/css'

import { vars } from '@atls-ui-parts/theme'

import { DEFAULT_CONTAINER_HEIGHT } from '../constants.js'
import { DEFAULT_CONTAINER_MIN_WIDTH } from '../constants.js'
import { DEFAULT_CONTAINER_Z_INDEX } from '../constants.js'

const containerDefaultShapeStyles = style({
minWidth: DEFAULT_CONTAINER_MIN_WIDTH,
minHeight: DEFAULT_CONTAINER_HEIGHT,
padding: `${vars.space.g8} ${vars.space.g12}`,
borderRadius: vars.radii.f8,
zIndex: DEFAULT_CONTAINER_Z_INDEX,
})

export const shapeStyles = {
default: {
container: containerDefaultShapeStyles,
},
}

export const tooltipShapes: Record<TooltipShapeName, TooltipShape> = shapeStyles
1 change: 1 addition & 0 deletions ui-parts/tooltip/stories/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import type { TooltipProps } from '@atls-ui-parts/tooltip'
export interface StoryTooltipProps
extends Pick<TooltipProps, 'animated' | 'arrow' | 'offset' | 'placement' | 'trigger'> {
customContainer: boolean
styledContainer: boolean
}
22 changes: 15 additions & 7 deletions ui-parts/tooltip/stories/story-tooltip.tsx

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

топонимическая редупликация

Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
/* eslint-disable react/jsx-no-leaked-render */

import type { ReactElement } from 'react'
import type { ReactElement } from 'react'

import type { StoryTooltipProps } from './interfaces.js'
import type { StoryTooltipProps } from './interfaces.js'

import { Tooltip } from '@atls-ui-parts/tooltip'
import { Tooltip } from '@atls-ui-parts/tooltip'

import { storyTriggerStyles } from './styles.css.js'
import { storyContainerStyles } from './styles.css.js'
import { storyContainerAppearanceStyles } from './styles.css.js'
import { storyContainerShapeStyles } from './styles.css.js'
import { storyTriggerStyles } from './styles.css.js'

const storyContainerClassName = [storyContainerAppearanceStyles, storyContainerShapeStyles].join(
' '
)

export const StoryTooltip = ({
animated,
arrow,
customContainer,
offset,
placement,
styledContainer,
trigger,
}: StoryTooltipProps): ReactElement => (
<Tooltip
animated={animated}
arrow={arrow && customContainer ? { fill: 'green' } : arrow}
arrow={arrow && (customContainer || styledContainer) ? { fill: 'green' } : arrow}
appearance={styledContainer ? { container: storyContainerAppearanceStyles } : undefined}
offset={offset}
placement={placement}
shape={styledContainer ? { container: storyContainerShapeStyles } : undefined}
trigger={trigger}
text='Tooltip text'
container={customContainer ? <div className={storyContainerStyles} /> : undefined}
container={customContainer ? <div className={storyContainerClassName} /> : undefined}
>
<div className={storyTriggerStyles}>Trigger</div>
</Tooltip>
Expand Down
8 changes: 6 additions & 2 deletions ui-parts/tooltip/stories/styles.css.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { style } from '@vanilla-extract/css'

export const storyContainerStyles = style({
export const storyContainerAppearanceStyles = style({
background: 'green',
color: 'white',
})

export const storyContainerShapeStyles = style({
width: '150px',
height: '30px',
background: 'green',
margin: 0,
})

Expand Down
12 changes: 12 additions & 0 deletions ui-parts/tooltip/stories/tooltip.stories.tsx

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ui-parts/tooltip/stories/story-tooltip.tsx и ui-parts/tooltip/stories/tooltip.stories.tsx…ничего не понимаю

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ const meta: Meta<StoryTooltipProps> = {
description: 'Свой контейнер',
control: { type: 'boolean' },
},
styledContainer: {
description: 'Стили дефолтного контейнера',
control: { type: 'boolean' },
},
offset: {
description: 'Офсет',
control: { type: 'number' },
Expand Down Expand Up @@ -56,6 +60,7 @@ const meta: Meta<StoryTooltipProps> = {
animated: true,
arrow: true,
customContainer: false,
styledContainer: false,
offset: 10,
placement: 'top',
trigger: 'click',
Expand All @@ -67,3 +72,10 @@ export default meta
export const Base: StoryObj<StoryTooltipProps> = {
render: StoryTooltip,
}

export const StyledContainer: StoryObj<StoryTooltipProps> = {
args: {
styledContainer: true,
},
render: StoryTooltip,
}
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,7 @@ __metadata:
dependencies:
"@atls-ui-parts/condition": "workspace:*"
"@atls-ui-parts/layout": "workspace:*"
"@atls-ui-parts/theme": "workspace:*"
"@atls-utils/use-float": "workspace:*"
"@floating-ui/react": "npm:0.27.8"
"@storybook/react": "npm:8.6.12"
Expand Down