@loadsmart/loadsmart-ui 5.19.2 → 5.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/components/DatePicker/DatePicker.types.d.ts +3 -0
  2. package/dist/components/Dropdown/Dropdown.d.ts +2 -2
  3. package/dist/components/Dropdown/Dropdown.types.d.ts +5 -1
  4. package/dist/components/Popover/Popover.d.ts +9 -3
  5. package/dist/components/Popover/Popover.stories.d.ts +1 -1
  6. package/dist/components/Popover/Popover.types.d.ts +33 -0
  7. package/dist/components/Popover/index.d.ts +2 -2
  8. package/dist/components/TablePagination/RowsPerPage.d.ts +1 -1
  9. package/dist/components/TablePagination/TablePagination.types.d.ts +7 -1
  10. package/dist/components/Text/Text.d.ts +1 -1
  11. package/dist/components/Tooltip/Tooltip.d.ts +3 -2
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.js +442 -481
  14. package/dist/index.js.map +1 -1
  15. package/dist/miranda-compatibility.theme-f37aba71.js +2 -0
  16. package/dist/miranda-compatibility.theme-f37aba71.js.map +1 -0
  17. package/dist/prop-ee1dfc7f.js +2 -0
  18. package/dist/{prop-a330029f.js.map → prop-ee1dfc7f.js.map} +1 -1
  19. package/dist/testing/index.js +1 -1
  20. package/dist/testing/index.js.map +1 -1
  21. package/dist/theming/index.js +1 -1
  22. package/dist/theming/themes/alice.theme.d.ts +4 -0
  23. package/dist/theming/themes/loadsmart.theme.d.ts +4 -0
  24. package/dist/theming/themes/miranda-compatibility.theme.d.ts +4 -0
  25. package/dist/tools/index.js +1 -1
  26. package/package.json +2 -1
  27. package/src/components/DatePicker/DatePicker.tsx +8 -4
  28. package/src/components/DatePicker/DatePicker.types.ts +3 -0
  29. package/src/components/DatePicker/DateRangePicker.tsx +16 -12
  30. package/src/components/Dropdown/Dropdown.stories.tsx +41 -40
  31. package/src/components/Dropdown/Dropdown.tsx +35 -11
  32. package/src/components/Dropdown/Dropdown.types.ts +7 -1
  33. package/src/components/Dropdown/DropdownMenu.tsx +10 -14
  34. package/src/components/Dropdown/DropdownTrigger.tsx +8 -5
  35. package/src/components/Popover/Popover.stories.tsx +27 -4
  36. package/src/components/Popover/Popover.tsx +145 -13
  37. package/src/components/Popover/Popover.types.ts +41 -0
  38. package/src/components/Popover/index.ts +11 -2
  39. package/src/components/Select/Select.test.tsx +3 -3
  40. package/src/components/Select/SelectTrigger.tsx +18 -1
  41. package/src/components/Table/Table.stories.tsx +0 -1
  42. package/src/components/Table/Table.tsx +2 -2
  43. package/src/components/TablePagination/RowsPerPage.tsx +9 -4
  44. package/src/components/TablePagination/TablePagination.tsx +3 -0
  45. package/src/components/TablePagination/TablePagination.types.ts +12 -5
  46. package/src/components/Tooltip/Tooltip.tsx +59 -85
  47. package/src/components/TopNavigation/Menu/MenuItemDropdown.tsx +11 -8
  48. package/src/index.ts +10 -2
  49. package/src/testing/DatePickerEvent/DatePickerEvent.ts +1 -1
  50. package/src/theming/themes/alice.theme.ts +6 -0
  51. package/src/theming/themes/loadsmart.theme.ts +6 -0
  52. package/dist/miranda-compatibility.theme-4cecc6cf.js +0 -2
  53. package/dist/miranda-compatibility.theme-4cecc6cf.js.map +0 -1
  54. package/dist/prop-a330029f.js +0 -2
@@ -1,21 +1,153 @@
1
- import React from 'react'
2
- import styled from 'styled-components'
1
+ import React, { useMemo } from 'react'
2
+ import { useFloating, autoUpdate } from '@floating-ui/react-dom'
3
+ import { offset, flip, shift, arrow } from '@floating-ui/core'
4
+ import type { Placement, MiddlewareState } from '@floating-ui/core'
3
5
 
4
- import { getToken as token } from 'theming'
6
+ import type {
7
+ PopoverAlign,
8
+ PopoverFloatingProps,
9
+ PopoverPosition,
10
+ PopoverProps,
11
+ PopoverReferenceProps,
12
+ UsePopoverReturn,
13
+ } from './Popover.types'
5
14
 
6
- import type { HTMLAttributes } from 'react'
15
+ const PopoverContext = React.createContext<UsePopoverReturn | undefined>(undefined)
7
16
 
8
- export type PopoverProps = HTMLAttributes<HTMLDivElement>
17
+ export function usePopover(): UsePopoverReturn {
18
+ const value = React.useContext(PopoverContext)
9
19
 
10
- const PopoverWrapper = styled.div`
11
- background: ${token('popover-background')};
12
- border: 1px solid ${token('popover-border-color')};
13
- border-radius: ${token('popover-border-radius')};
14
- box-shadow: ${token('popover-shadow')};
15
- `
20
+ if (!value) {
21
+ throw new Error('usePopover must be used within a <Popover> Provider')
22
+ }
16
23
 
17
- function Popover({ children, ...others }: PopoverProps): JSX.Element {
18
- return <PopoverWrapper {...others}>{children}</PopoverWrapper>
24
+ return value
19
25
  }
20
26
 
27
+ function Popover(props: PopoverProps): JSX.Element {
28
+ const arrowRef = React.useRef<HTMLElement | null>(null)
29
+
30
+ const { position = 'bottom', align = 'start', strategy = 'fixed' } = props
31
+
32
+ const desiredPlacement = `${position}${align === 'center' ? '' : `-${align}`}` as Placement
33
+
34
+ const result = useFloating({
35
+ placement: desiredPlacement,
36
+ strategy,
37
+ middleware: [
38
+ offset(10),
39
+ flip(),
40
+ shift(),
41
+ {
42
+ name: 'arrow',
43
+ fn(args: MiddlewareState) {
44
+ if (arrowRef.current) {
45
+ return arrow({ element: arrowRef.current, padding: 8 }).fn(args)
46
+ }
47
+
48
+ return {}
49
+ },
50
+ },
51
+ ],
52
+ // TODO: FloatingUI docs states that `autoUpdate` is expensive.
53
+ // This should be properly investidated since it's the way to
54
+ // update fixed positions after scrolling.
55
+ whileElementsMounted: autoUpdate,
56
+ })
57
+
58
+ const registerArrow = React.useCallback(
59
+ (node: HTMLElement) => {
60
+ arrowRef.current = node
61
+ result.update()
62
+ },
63
+ [result]
64
+ )
65
+
66
+ const [resultPosition = position, resultAlign = align] = result.placement.split('-') as [
67
+ PopoverPosition,
68
+ PopoverAlign
69
+ ]
70
+
71
+ const value = useMemo(
72
+ () => ({
73
+ strategy,
74
+ register: {
75
+ reference: result.refs.setReference,
76
+ floating: result.refs.setFloating,
77
+ arrow: registerArrow,
78
+ },
79
+ result: {
80
+ floating: {
81
+ top: result.y ?? 0,
82
+ left: result.x ?? 0,
83
+ },
84
+ arrow: { top: result.middlewareData.arrow?.y, left: result.middlewareData.arrow?.x },
85
+ position: resultPosition,
86
+ align: resultAlign,
87
+ },
88
+ }),
89
+ [
90
+ registerArrow,
91
+ result.refs.setFloating,
92
+ result.middlewareData.arrow?.x,
93
+ result.middlewareData.arrow?.y,
94
+ result.refs.setReference,
95
+ result.x,
96
+ result.y,
97
+ resultAlign,
98
+ resultPosition,
99
+ strategy,
100
+ ]
101
+ )
102
+
103
+ return <PopoverContext.Provider value={value}>{props.children}</PopoverContext.Provider>
104
+ }
105
+
106
+ function PopoverReference({ children, ...others }: PopoverReferenceProps): JSX.Element {
107
+ const ref = React.useRef<HTMLDivElement | null>(null)
108
+ const { register } = usePopover()
109
+
110
+ React.useLayoutEffect(() => {
111
+ if (ref.current) {
112
+ register.reference(ref.current)
113
+ }
114
+ }, [register])
115
+
116
+ return (
117
+ <div ref={ref} {...others}>
118
+ {children}
119
+ </div>
120
+ )
121
+ }
122
+
123
+ function PopoverFloating({ children, style, ...others }: PopoverFloatingProps): JSX.Element {
124
+ const { register, result, strategy } = usePopover()
125
+
126
+ const ref = React.useRef<HTMLDivElement | null>(null)
127
+
128
+ React.useLayoutEffect(() => {
129
+ if (ref.current) {
130
+ register.floating(ref.current)
131
+ }
132
+ }, [register])
133
+
134
+ return (
135
+ <div
136
+ ref={ref}
137
+ {...others}
138
+ style={{
139
+ position: strategy,
140
+ top: result.floating.top,
141
+ left: result.floating.left,
142
+ ...style,
143
+ }}
144
+ >
145
+ {children}
146
+ </div>
147
+ )
148
+ }
149
+
150
+ Popover.Floating = PopoverFloating
151
+ Popover.Reference = PopoverReference
152
+
21
153
  export default Popover
@@ -0,0 +1,41 @@
1
+ import type { HTMLAttributes, MutableRefObject, PropsWithChildren } from 'react'
2
+
3
+ export interface UsePopoverReturn {
4
+ register: {
5
+ reference: (el: HTMLElement) => void
6
+ floating: (el: HTMLElement) => void
7
+ arrow: (el: HTMLElement) => void
8
+ }
9
+ result: {
10
+ floating: {
11
+ top: number
12
+ left: number
13
+ }
14
+ arrow: {
15
+ top: number | undefined
16
+ left: number | undefined
17
+ }
18
+ position: PopoverPosition
19
+ align: PopoverAlign
20
+ }
21
+ strategy: 'fixed' | 'absolute'
22
+ }
23
+
24
+ export type PopoverPosition = 'top' | 'bottom' | 'right' | 'left'
25
+ export type PopoverAlign = 'start' | 'center' | 'end'
26
+
27
+ export interface PopoverPlacement {
28
+ position?: PopoverPosition
29
+ align?: PopoverAlign
30
+ }
31
+
32
+ export type PopoverProps = PropsWithChildren<
33
+ PopoverPlacement & {
34
+ strategy?: UsePopoverReturn['strategy']
35
+ arrow?: MutableRefObject<HTMLElement | null>
36
+ }
37
+ >
38
+
39
+ export type PopoverReferenceProps = HTMLAttributes<HTMLDivElement>
40
+
41
+ export type PopoverFloatingProps = HTMLAttributes<HTMLDivElement>
@@ -1,2 +1,11 @@
1
- export { default as Popover } from './Popover'
2
- export type { PopoverProps } from './Popover'
1
+ export { default as Popover, usePopover } from './Popover'
2
+
3
+ export type {
4
+ PopoverProps,
5
+ PopoverFloatingProps,
6
+ PopoverReferenceProps,
7
+ PopoverPlacement,
8
+ PopoverPosition,
9
+ PopoverAlign,
10
+ UsePopoverReturn,
11
+ } from './Popover.types'
@@ -341,7 +341,7 @@ describe('Select', () => {
341
341
 
342
342
  expect(firstSelection).toHaveTextContent(firstOption.label)
343
343
  expect(secondSelection).toHaveTextContent(secondOption.label)
344
- })
344
+ }, 30_000)
345
345
 
346
346
  it('unselects a selected option keeping selected the others', async () => {
347
347
  const [firstOption, secondOption, thirdOption] = generator.pickset([...FRUITS], 3)
@@ -525,7 +525,7 @@ describe('Select', () => {
525
525
  const optionText = getOptionText(option)
526
526
  expect(selectedOptions[index]).toHaveTextContent(optionText)
527
527
  }
528
- })
528
+ }, 30_000)
529
529
 
530
530
  it.each([[{ multiple: true }], [{ multiple: false }]])(
531
531
  'overrides the empty component with %s',
@@ -1037,6 +1037,6 @@ describe('Select', () => {
1037
1037
 
1038
1038
  await selectEvent.expand(searchInput)
1039
1039
  expect(screen.getByText('No more options.')).toBeInTheDocument()
1040
- })
1040
+ }, 30_000)
1041
1041
  })
1042
1042
  })
@@ -7,6 +7,8 @@ import { TextField, Trailing } from 'components/TextField'
7
7
  import focusable from 'styles/focusable'
8
8
 
9
9
  import type { SelectTriggerProps } from './Select.types'
10
+ import { isFunction } from '@loadsmart/utils-function'
11
+ import { usePopover } from 'components/Popover'
10
12
 
11
13
  const GenericSelectTrigger = styled(GenericDropdownTrigger)`
12
14
  background: ${token('color-neutral-white')};
@@ -47,6 +49,15 @@ const SelectTrigger = forwardRef<HTMLInputElement, SelectTriggerProps>(function
47
49
  ) {
48
50
  const { className, ...others } = props
49
51
  const { disabled, expanded, toggle } = useContext(DropdownContext)
52
+ const triggerRef = React.useRef<HTMLElement | null>(null)
53
+
54
+ const { register } = usePopover()
55
+
56
+ React.useEffect(() => {
57
+ if (triggerRef.current) {
58
+ register.reference(triggerRef.current)
59
+ }
60
+ }, [register])
50
61
 
51
62
  function handleClick() {
52
63
  if (!expanded) {
@@ -59,7 +70,13 @@ const SelectTrigger = forwardRef<HTMLInputElement, SelectTriggerProps>(function
59
70
  <SelectTriggerSearchField
60
71
  data-testid="select-trigger-search-field"
61
72
  {...others}
62
- ref={ref}
73
+ ref={(node) => {
74
+ if (isFunction(ref)) {
75
+ ref(node)
76
+ }
77
+
78
+ triggerRef.current = node
79
+ }}
63
80
  type="search"
64
81
  disabled={disabled}
65
82
  onClick={handleClick}
@@ -12,7 +12,6 @@ import type { RowFixture } from './Table.fixtures'
12
12
  import { Button } from 'components/Button'
13
13
  import { Text } from 'components/Text'
14
14
  import { Switch } from 'components/Switch'
15
- import { Icon } from 'components/Icon'
16
15
 
17
16
  export default {
18
17
  title: 'Components/Table',
@@ -451,7 +451,7 @@ function TablePicker<T>({
451
451
  ...props
452
452
  }: TablePickerProps<T>): JSX.Element {
453
453
  return (
454
- <Dropdown>
454
+ <Dropdown align={align}>
455
455
  <StyledPickerTrigger trailing={null} scale="small" {...props}>
456
456
  {propsTrigger !== undefined ? (
457
457
  ({ expanded }) => (isFunction(propsTrigger) ? propsTrigger(expanded) : propsTrigger)
@@ -459,7 +459,7 @@ function TablePicker<T>({
459
459
  <TriggerIcon />
460
460
  )}
461
461
  </StyledPickerTrigger>
462
- <Dropdown.Menu align={align} role="listbox">
462
+ <Dropdown.Menu role="listbox">
463
463
  {children ||
464
464
  options?.map((option, i) => (
465
465
  <TablePickerItem
@@ -6,14 +6,17 @@ import { Icon } from 'components/Icon'
6
6
  import type { RowsPerPageProps } from './TablePagination.types'
7
7
  import type { ButtonProps } from 'components/Button'
8
8
  import { NoPaddingButton } from './TablePagination.styles'
9
+ import { Popover } from 'components/Popover'
9
10
 
10
11
  const TriggerButton = (props: Omit<ButtonProps, 'scale' | 'variant'>) => {
11
12
  const { toggle } = React.useContext(DropdownContext)
12
13
 
13
14
  return (
14
- <NoPaddingButton data-testid="rows-per-page-button" onClick={toggle} {...props}>
15
- <Icon name="caret-down" size={16} color="neutral-darker" />
16
- </NoPaddingButton>
15
+ <Popover.Reference>
16
+ <NoPaddingButton data-testid="rows-per-page-button" onClick={toggle} {...props}>
17
+ <Icon name="caret-down" size={16} color="neutral-darker" />
18
+ </NoPaddingButton>
19
+ </Popover.Reference>
17
20
  )
18
21
  }
19
22
 
@@ -25,6 +28,8 @@ function RowsPerPage({
25
28
  count,
26
29
  rowsPerPageOptions,
27
30
  disabled = false,
31
+ position = 'bottom',
32
+ align = 'start',
28
33
  }: RowsPerPageProps): JSX.Element {
29
34
  const getItemsRange = () => {
30
35
  if (!count) {
@@ -55,7 +60,7 @@ function RowsPerPage({
55
60
  {count}
56
61
  </Text>
57
62
  </Text>
58
- <Dropdown>
63
+ <Dropdown position={position} align={align}>
59
64
  <TriggerButton disabled={disabled} />
60
65
  <Dropdown.Menu>
61
66
  {rowsPerPageOptions.map((option) => (
@@ -17,6 +17,7 @@ function TablePagination(props: TablePaginationProps): JSX.Element {
17
17
  rowsPerPage = 50,
18
18
  rowsPerPageOptions = [10, 25, 50, 100],
19
19
  disabled = false,
20
+ rowsPerPagePlacement,
20
21
  ...rest
21
22
  } = props
22
23
 
@@ -30,6 +31,8 @@ function TablePagination(props: TablePaginationProps): JSX.Element {
30
31
  rowsPerPageOptions={rowsPerPageOptions}
31
32
  labelRowsPerPage={labelRowsPerPage}
32
33
  disabled={disabled || !count}
34
+ position={rowsPerPagePlacement?.position}
35
+ align={rowsPerPagePlacement?.align}
33
36
  />
34
37
  <TablePaginationActions
35
38
  variant={variant}
@@ -1,4 +1,5 @@
1
1
  import type { GroupProps } from 'components/Layout/Group'
2
+ import type { PopoverPlacement } from 'components/Popover'
2
3
 
3
4
  export interface TablePaginationProps extends GroupProps {
4
5
  /**
@@ -44,6 +45,11 @@ export interface TablePaginationProps extends GroupProps {
44
45
  * Disable all the pagination actions
45
46
  */
46
47
  disabled?: boolean
48
+ /**
49
+ * Customizes the placement of the rows per page select field.
50
+ * @default { position: 'bottom', align: 'start' }
51
+ */
52
+ rowsPerPagePlacement?: PopoverPlacement
47
53
  }
48
54
 
49
55
  export type TablePaginationActionsProps = Omit<
@@ -55,8 +61,9 @@ export type TablePaginationActionsProps = Omit<
55
61
 
56
62
  export type RowsPerPageProps = Omit<
57
63
  TablePaginationProps,
58
- 'rowsPerPageOptions' | 'onPageChange' | 'rowsPerPage'
59
- > & {
60
- rowsPerPageOptions: number[]
61
- rowsPerPage: number
62
- }
64
+ 'rowsPerPageOptions' | 'onPageChange' | 'rowsPerPage' | 'align'
65
+ > &
66
+ PopoverPlacement & {
67
+ rowsPerPageOptions: number[]
68
+ rowsPerPage: number
69
+ }
@@ -4,10 +4,12 @@ import styled, { css } from 'styled-components'
4
4
 
5
5
  import type ColorScheme from 'utils/types/ColorScheme'
6
6
  import focusable from 'styles/focusable'
7
- import transition from 'styles/transition'
8
- import font from 'styles/font'
9
7
  import conditional, { whenProps } from 'tools/conditional'
10
8
  import { getToken as token } from 'theming'
9
+ import { Popover, usePopover } from 'components/Popover'
10
+ import typography from 'styles/typography'
11
+
12
+ import type { PopoverAlign, PopoverPosition } from 'components/Popover'
11
13
 
12
14
  export enum TooltipPosition {
13
15
  Top = 'top',
@@ -25,8 +27,8 @@ export enum TooltipAlign {
25
27
  export interface TooltipProps extends HTMLAttributes<HTMLDivElement> {
26
28
  message: ReactNode
27
29
  scheme?: ColorScheme
28
- position?: TooltipPosition
29
- align?: TooltipAlign
30
+ position?: TooltipPosition | PopoverPosition
31
+ align?: TooltipAlign | PopoverAlign
30
32
  }
31
33
 
32
34
  type ContainerProps = Pick<TooltipProps, 'scheme'>
@@ -46,15 +48,8 @@ const Container = styled.div<ContainerProps>`
46
48
  `}
47
49
  `
48
50
 
49
- type BubbleProps = Pick<TooltipProps, 'position' | 'align'>
50
-
51
- const Bubble = styled.span<BubbleProps>`
52
- ${font({
53
- weight: 'font-weight-medium',
54
- height: 'font-height-3',
55
- })}
56
-
57
- ${transition()}
51
+ const Bubble = styled.div`
52
+ ${typography('body')}
58
53
 
59
54
  white-space: initial;
60
55
 
@@ -62,7 +57,6 @@ const Bubble = styled.span<BubbleProps>`
62
57
  max-width: ${token('tooltip-max-width')};
63
58
  width: max-content;
64
59
 
65
- position: absolute;
66
60
  z-index: ${token('z-index-tooltip')};
67
61
 
68
62
  background: ${token('tooltip-background')};
@@ -74,108 +68,85 @@ const Bubble = styled.span<BubbleProps>`
74
68
  color: ${token('tooltip-color')};
75
69
  font-size: ${token('tooltip-font-size')};
76
70
  line-height: ${token('tooltip-font-height')};
71
+ `
77
72
 
78
- ${({ position }) =>
79
- position === TooltipPosition.Top &&
80
- css`
81
- top: -16px;
82
- left: 50%;
83
-
84
- transform: ${conditional({
85
- 'translate(-10%, -100%)': whenProps({ align: TooltipAlign.Start }),
86
- 'translate(-50%, -100%)': whenProps({ align: TooltipAlign.Center }),
87
- 'translate(-90%, -100%)': whenProps({ align: TooltipAlign.End }),
88
- })};
89
- `}
90
-
91
- ${({ position }) =>
92
- position === TooltipPosition.Bottom &&
93
- css`
94
- bottom: -16px;
95
- left: 50%;
96
-
97
- transform: ${conditional({
98
- 'translate(-10%, 100%)': whenProps({ align: TooltipAlign.Start }),
99
- 'translate(-50%, 100%)': whenProps({ align: TooltipAlign.Center }),
100
- 'translate(-90%, 100%)': whenProps({ align: TooltipAlign.End }),
101
- })};
102
- `};
103
-
104
- ${({ position }) =>
105
- position === TooltipPosition.Left &&
106
- css`
107
- top: 50%;
108
- left: -16px;
109
-
110
- transform: translate(-100%, -50%);
111
- `};
112
-
113
- ${({ position }) =>
114
- position === TooltipPosition.Right &&
115
- css`
116
- top: 50%;
117
- right: -16px;
73
+ type ArrowProps = Pick<TooltipProps, 'position'> & {
74
+ top?: number
75
+ left?: number
76
+ }
118
77
 
119
- transform: translate(100%, -50%);
120
- `};
121
- `
78
+ const StyledArrow = styled.span<ArrowProps>`
79
+ position: absolute;
122
80
 
123
- type ArrowProps = Pick<TooltipProps, 'position'>
124
- const Arrow = styled.span<ArrowProps>`
125
81
  width: 0;
126
82
  height: 0;
83
+ display: block;
127
84
 
128
85
  background: transparent;
129
86
  border-style: solid;
130
87
 
131
- position: absolute;
132
88
  z-index: 1;
133
89
 
90
+ ${({ top }) => top && `top: ${top}px;`}
91
+ ${({ left }) => left && `left: ${left}px;`}
92
+
134
93
  ${({ position }) =>
135
- position === TooltipPosition.Top &&
94
+ position === 'top' &&
136
95
  css`
137
- bottom: calc(100% - 4px);
138
- left: 50%;
96
+ bottom: -8px;
139
97
 
140
98
  border-color: ${token('tooltip-background')} transparent transparent transparent;
141
99
  border-width: 12px 10px 0 10px;
142
- transform: translate(-50%, -100%);
143
100
  `}
144
101
 
145
102
  ${({ position }) =>
146
- position === TooltipPosition.Bottom &&
103
+ position === 'bottom' &&
147
104
  css`
148
- top: calc(100% - 4px);
149
- left: 50%;
105
+ top: -8px;
150
106
 
151
107
  border-color: transparent transparent ${token('tooltip-background')} transparent;
152
108
  border-width: 0 10px 12px 10px;
153
- transform: translate(-50%, 100%);
154
109
  `}
155
110
 
156
111
  ${({ position }) =>
157
- position === TooltipPosition.Left &&
112
+ position === 'left' &&
158
113
  css`
159
- top: 50%;
160
- right: calc(100% - 4px);
114
+ right: -8px;
161
115
 
162
116
  border-color: transparent transparent transparent ${token('tooltip-background')};
163
117
  border-width: 10px 0 10px 12px;
164
- transform: translate(-100%, -50%);
165
118
  `}
166
119
 
167
120
  ${({ position }) =>
168
- position === TooltipPosition.Right &&
121
+ position === 'right' &&
169
122
  css`
170
- top: 50%;
171
- left: calc(100% - 4px);
123
+ left: -8px;
172
124
 
173
125
  border-color: transparent ${token('tooltip-background')} transparent transparent;
174
126
  border-width: 10px 12px 10px 0;
175
- transform: translate(100%, -50%);
176
127
  `}
177
128
  `
178
129
 
130
+ function Arrow() {
131
+ const { register, result } = usePopover()
132
+ const ref = React.useRef<HTMLDivElement | null>(null)
133
+
134
+ React.useLayoutEffect(() => {
135
+ if (ref.current) {
136
+ register.arrow(ref.current)
137
+ }
138
+ }, [register])
139
+
140
+ return (
141
+ <StyledArrow
142
+ ref={ref}
143
+ position={result.position}
144
+ top={result.arrow.top}
145
+ left={result.arrow.left}
146
+ />
147
+ )
148
+ }
149
+
179
150
  function Tooltip({
180
151
  children,
181
152
  message,
@@ -204,15 +175,18 @@ function Tooltip({
204
175
  tabIndex={0}
205
176
  scheme={scheme}
206
177
  >
207
- {visible && (
208
- <>
209
- <Bubble role="tooltip" position={position} align={align}>
210
- {message}
211
- </Bubble>
212
- <Arrow position={position} />
213
- </>
214
- )}
215
- {children}
178
+ <Popover position={position} align={align}>
179
+ {visible && (
180
+ <Popover.Floating>
181
+ <Bubble role="tooltip">
182
+ {message}
183
+ <Arrow />
184
+ </Bubble>
185
+ </Popover.Floating>
186
+ )}
187
+
188
+ <Popover.Reference>{children}</Popover.Reference>
189
+ </Popover>
216
190
  </Container>
217
191
  )
218
192
  }
@@ -11,6 +11,7 @@ import { BaseLink } from 'components/Link'
11
11
  import useID from 'hooks/useID'
12
12
  import ellipsizable from 'styles/ellipsizable'
13
13
  import typography from 'styles/typography'
14
+ import { Popover } from 'components/Popover'
14
15
 
15
16
  export const MenuDropdown = styled(Dropdown)({})
16
17
 
@@ -96,14 +97,16 @@ function MenuItemDropdown(props: MenuItemDropdownProps): JSX.Element {
96
97
  const labelId = useID()
97
98
 
98
99
  return (
99
- <MenuDropdown key={label}>
100
- <MenuDropdownLabel>
101
- <Ellipsizable $max={120} id={labelId}>
102
- {label}
103
- </Ellipsizable>
104
- <MenuDropdownTrigger {...rest} tabIndex={0} aria-labelledby={labelId} />
105
- </MenuDropdownLabel>
106
- <DropdownMenu align="end">{children}</DropdownMenu>
100
+ <MenuDropdown align="end" key={label}>
101
+ <Popover.Reference>
102
+ <MenuDropdownLabel>
103
+ <Ellipsizable $max={120} id={labelId}>
104
+ {label}
105
+ </Ellipsizable>
106
+ <MenuDropdownTrigger {...rest} tabIndex={0} aria-labelledby={labelId} />
107
+ </MenuDropdownLabel>
108
+ </Popover.Reference>
109
+ <DropdownMenu>{children}</DropdownMenu>
107
110
  </MenuDropdown>
108
111
  )
109
112
  }
package/src/index.ts CHANGED
@@ -83,8 +83,16 @@ export type { CardProps } from './components/Card'
83
83
  export { IconFactory } from './components/IconFactory'
84
84
  export type { IconProps, IconMapping } from './components/IconFactory'
85
85
 
86
- export { Popover } from './components/Popover'
87
- export type { PopoverProps } from './components/Popover'
86
+ export { Popover, usePopover } from './components/Popover'
87
+ export type {
88
+ PopoverProps,
89
+ PopoverAlign,
90
+ PopoverPosition,
91
+ PopoverFloatingProps,
92
+ PopoverPlacement,
93
+ PopoverReferenceProps,
94
+ UsePopoverReturn,
95
+ } from './components/Popover'
88
96
 
89
97
  export { Dropdown, useDropdown, DropdownContext } from './components/Dropdown'
90
98
  export type { DropdownProps, useDropdownProps } from './components/Dropdown'