@arbor-education/design-system.components 0.1.0 → 0.1.2

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 (120) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/components/button/Button.d.ts +1 -0
  3. package/dist/components/button/Button.d.ts.map +1 -1
  4. package/dist/components/button/Button.js +2 -1
  5. package/dist/components/button/Button.js.map +1 -1
  6. package/dist/components/formField/inputs/number/NumberInput.d.ts +2 -0
  7. package/dist/components/formField/inputs/number/NumberInput.d.ts.map +1 -1
  8. package/dist/components/formField/inputs/number/NumberInput.js +9 -4
  9. package/dist/components/formField/inputs/number/NumberInput.js.map +1 -1
  10. package/dist/components/table/BulkActionsDropdown.js +2 -2
  11. package/dist/components/table/BulkActionsDropdown.js.map +1 -1
  12. package/dist/components/table/GridApiContext.d.ts +1 -2
  13. package/dist/components/table/GridApiContext.d.ts.map +1 -1
  14. package/dist/components/table/GridApiContext.js +1 -1
  15. package/dist/components/table/GridApiContext.js.map +1 -1
  16. package/dist/components/table/Table.d.ts +9 -1
  17. package/dist/components/table/Table.d.ts.map +1 -1
  18. package/dist/components/table/Table.js +13 -5
  19. package/dist/components/table/Table.js.map +1 -1
  20. package/dist/components/table/Table.stories.d.ts +5 -1
  21. package/dist/components/table/Table.stories.d.ts.map +1 -1
  22. package/dist/components/table/Table.stories.js +36 -1
  23. package/dist/components/table/Table.stories.js.map +1 -1
  24. package/dist/components/table/Table.test.js +2 -0
  25. package/dist/components/table/Table.test.js.map +1 -1
  26. package/dist/components/table/pagination/PageSizeSelector.d.ts +7 -0
  27. package/dist/components/table/pagination/PageSizeSelector.d.ts.map +1 -0
  28. package/dist/components/table/pagination/PageSizeSelector.js +46 -0
  29. package/dist/components/table/pagination/PageSizeSelector.js.map +1 -0
  30. package/dist/components/table/pagination/Pagination.test.d.ts +2 -0
  31. package/dist/components/table/pagination/Pagination.test.d.ts.map +1 -0
  32. package/dist/components/table/pagination/Pagination.test.js +616 -0
  33. package/dist/components/table/pagination/Pagination.test.js.map +1 -0
  34. package/dist/components/table/pagination/PaginationControls.d.ts +6 -0
  35. package/dist/components/table/pagination/PaginationControls.d.ts.map +1 -0
  36. package/dist/components/table/pagination/PaginationControls.js +47 -0
  37. package/dist/components/table/pagination/PaginationControls.js.map +1 -0
  38. package/dist/components/table/pagination/PaginationPanel.d.ts +9 -0
  39. package/dist/components/table/pagination/PaginationPanel.d.ts.map +1 -0
  40. package/dist/components/table/pagination/PaginationPanel.js +11 -0
  41. package/dist/components/table/pagination/PaginationPanel.js.map +1 -0
  42. package/dist/components/table/pagination/RowCountInfo.d.ts +5 -0
  43. package/dist/components/table/pagination/RowCountInfo.d.ts.map +1 -0
  44. package/dist/components/table/pagination/RowCountInfo.js +53 -0
  45. package/dist/components/table/pagination/RowCountInfo.js.map +1 -0
  46. package/dist/components/tooltip/Tooltip.d.ts +7 -0
  47. package/dist/components/tooltip/Tooltip.d.ts.map +1 -0
  48. package/dist/components/tooltip/Tooltip.js +11 -0
  49. package/dist/components/tooltip/Tooltip.js.map +1 -0
  50. package/dist/components/tooltip/Tooltip.stories.d.ts +10 -0
  51. package/dist/components/tooltip/Tooltip.stories.d.ts.map +1 -0
  52. package/dist/components/tooltip/Tooltip.stories.js +24 -0
  53. package/dist/components/tooltip/Tooltip.stories.js.map +1 -0
  54. package/dist/components/tooltip/Tooltip.test.d.ts +2 -0
  55. package/dist/components/tooltip/Tooltip.test.d.ts.map +1 -0
  56. package/dist/components/tooltip/Tooltip.test.js +23 -0
  57. package/dist/components/tooltip/Tooltip.test.js.map +1 -0
  58. package/dist/components/tooltip/TooltipContent.d.ts +8 -0
  59. package/dist/components/tooltip/TooltipContent.d.ts.map +1 -0
  60. package/dist/components/tooltip/TooltipContent.js +11 -0
  61. package/dist/components/tooltip/TooltipContent.js.map +1 -0
  62. package/dist/components/tooltip/TooltipTrigger.d.ts +3 -0
  63. package/dist/components/tooltip/TooltipTrigger.d.ts.map +1 -0
  64. package/dist/components/tooltip/TooltipTrigger.js +8 -0
  65. package/dist/components/tooltip/TooltipTrigger.js.map +1 -0
  66. package/dist/components/tooltip/TooltipWrapper.d.ts +11 -0
  67. package/dist/components/tooltip/TooltipWrapper.d.ts.map +1 -0
  68. package/dist/components/tooltip/TooltipWrapper.js +8 -0
  69. package/dist/components/tooltip/TooltipWrapper.js.map +1 -0
  70. package/dist/components/tooltip/TooltipWrapper.stories.d.ts +11 -0
  71. package/dist/components/tooltip/TooltipWrapper.stories.d.ts.map +1 -0
  72. package/dist/components/tooltip/TooltipWrapper.stories.js +23 -0
  73. package/dist/components/tooltip/TooltipWrapper.stories.js.map +1 -0
  74. package/dist/components/tooltip/TooltipWrapper.test.d.ts +2 -0
  75. package/dist/components/tooltip/TooltipWrapper.test.d.ts.map +1 -0
  76. package/dist/components/tooltip/TooltipWrapper.test.js +42 -0
  77. package/dist/components/tooltip/TooltipWrapper.test.js.map +1 -0
  78. package/dist/index.css +41 -0
  79. package/dist/index.css.map +1 -1
  80. package/dist/index.d.ts +2 -0
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +2 -0
  83. package/dist/index.js.map +1 -1
  84. package/dist/utils/hooks/useGridApi.d.ts +5 -0
  85. package/dist/utils/hooks/useGridApi.d.ts.map +1 -0
  86. package/dist/utils/hooks/useGridApi.js +13 -0
  87. package/dist/utils/hooks/useGridApi.js.map +1 -0
  88. package/dist/utils/hooks/useIsMounted.d.ts +2 -0
  89. package/dist/utils/hooks/useIsMounted.d.ts.map +1 -0
  90. package/dist/utils/hooks/useIsMounted.js +12 -0
  91. package/dist/utils/hooks/useIsMounted.js.map +1 -0
  92. package/package.json +1 -1
  93. package/release/design-system.components.tgz +0 -0
  94. package/src/components/button/Button.tsx +3 -0
  95. package/src/components/button/button.scss +4 -0
  96. package/src/components/formField/inputs/number/NumberInput.tsx +39 -24
  97. package/src/components/table/BulkActionsDropdown.tsx +2 -2
  98. package/src/components/table/GridApiContext.ts +2 -2
  99. package/src/components/table/Table.stories.tsx +64 -2
  100. package/src/components/table/Table.test.tsx +2 -0
  101. package/src/components/table/Table.tsx +14 -4
  102. package/src/components/table/pagination/PageSizeSelector.tsx +73 -0
  103. package/src/components/table/pagination/Pagination.test.tsx +846 -0
  104. package/src/components/table/pagination/PaginationControls.tsx +116 -0
  105. package/src/components/table/pagination/PaginationPanel.tsx +30 -0
  106. package/src/components/table/pagination/RowCountInfo.tsx +67 -0
  107. package/src/components/table/pagination/pagination.scss +26 -0
  108. package/src/components/tooltip/Tooltip.stories.tsx +35 -0
  109. package/src/components/tooltip/Tooltip.test.tsx +44 -0
  110. package/src/components/tooltip/Tooltip.tsx +18 -0
  111. package/src/components/tooltip/TooltipContent.tsx +40 -0
  112. package/src/components/tooltip/TooltipTrigger.tsx +11 -0
  113. package/src/components/tooltip/TooltipWrapper.stories.tsx +24 -0
  114. package/src/components/tooltip/TooltipWrapper.test.tsx +100 -0
  115. package/src/components/tooltip/TooltipWrapper.tsx +32 -0
  116. package/src/components/tooltip/tooltip.scss +15 -0
  117. package/src/index.scss +2 -0
  118. package/src/index.ts +2 -0
  119. package/src/utils/hooks/useGridApi.ts +15 -0
  120. package/src/utils/hooks/useIsMounted.ts +12 -0
@@ -0,0 +1,116 @@
1
+ import { Button } from 'Components/button/Button';
2
+ import { NumberInput } from 'Components/formField/inputs/number/NumberInput';
3
+ import { useEffect, useState } from 'react';
4
+ import { useGridApi } from 'Utils/hooks/useGridApi';
5
+
6
+ export type PaginationControlsProps = {
7
+ onPageChange?: (newPage: number) => void;
8
+ totalPages?: number;
9
+ };
10
+
11
+ export const PaginationControls = (props: PaginationControlsProps) => {
12
+ const { onPageChange, totalPages: passedTotalPages } = props;
13
+ const { gridApi, isGridApiReady } = useGridApi();
14
+ const [inputValue, setInputValue] = useState(1);
15
+
16
+ // default to 1, because no content is still one page
17
+ const [totalPages, setTotalPages] = useState(() => passedTotalPages ?? gridApi?.paginationGetTotalPages() ?? 1);
18
+
19
+ useEffect(() => {
20
+ if (isGridApiReady && !passedTotalPages) {
21
+ gridApi?.addEventListener('paginationChanged', () => {
22
+ const newPageCount = gridApi.paginationGetTotalPages();
23
+ if (newPageCount) {
24
+ setTotalPages(newPageCount);
25
+ }
26
+ });
27
+ }
28
+ }, [isGridApiReady]);
29
+
30
+ useEffect(() => {
31
+ if (passedTotalPages) {
32
+ setTotalPages(passedTotalPages);
33
+ }
34
+ }, [passedTotalPages]);
35
+
36
+ const goToPage = (page: number) => {
37
+ if (page === inputValue) {
38
+ return;
39
+ }
40
+ const pageZeroIndexed = page - 1;
41
+ if (onPageChange) {
42
+ onPageChange(pageZeroIndexed);
43
+ }
44
+ else {
45
+ gridApi?.paginationGoToPage(pageZeroIndexed);
46
+ }
47
+ setInputValue(page);
48
+ };
49
+
50
+ return (
51
+ <div className="ds-table__pagination-controls">
52
+ <Button
53
+ variant="secondary"
54
+ borderless
55
+ size="S"
56
+ color="black"
57
+ iconLeftName="chevrons-left"
58
+ iconLeftScreenReaderText="Go to first page"
59
+ onClick={() => goToPage(1)}
60
+ disabled={inputValue === 1}
61
+ />
62
+ <Button
63
+ variant="secondary"
64
+ borderless
65
+ size="S"
66
+ color="black"
67
+ iconLeftName="chevron-left"
68
+ onClick={() => goToPage(inputValue - 1)}
69
+ iconLeftScreenReaderText="Go to previous page"
70
+ disabled={inputValue === 1}
71
+ />
72
+ <span>
73
+ Page
74
+ {' '}
75
+ <NumberInput
76
+ value={inputValue}
77
+ onChange={(e) => {
78
+ const newPage = Number(e.currentTarget.value);
79
+ if (newPage > 0) {
80
+ goToPage(newPage);
81
+ }
82
+ }}
83
+ max={totalPages}
84
+ min={1}
85
+ disableSpinners
86
+ containerClassName="ds-table__pagination-controls__number-input-container"
87
+ className="ds-table__pagination-controls__number-input"
88
+ aria-label="Current page"
89
+ aria-current="page"
90
+ />
91
+ {' '}
92
+ of
93
+ {' '}
94
+ {totalPages}
95
+ </span>
96
+ <Button
97
+ variant="secondary"
98
+ borderless
99
+ color="black"
100
+ iconLeftName="chevron-right"
101
+ onClick={() => goToPage(inputValue + 1)}
102
+ disabled={inputValue === totalPages}
103
+ iconLeftScreenReaderText="Go to next page"
104
+ />
105
+ <Button
106
+ variant="secondary"
107
+ borderless
108
+ color="black"
109
+ iconLeftName="chevrons-right"
110
+ onClick={() => goToPage(totalPages)}
111
+ disabled={inputValue === totalPages}
112
+ iconLeftScreenReaderText="Go to last page"
113
+ />
114
+ </div>
115
+ );
116
+ };
@@ -0,0 +1,30 @@
1
+ import { PageSizeSelector, type PageSizeSelectorProps } from './PageSizeSelector';
2
+ import { RowCountInfo, type RowCountInfoProps } from './RowCountInfo';
3
+ import { PaginationControls, type PaginationControlsProps } from './PaginationControls';
4
+ import classNames from 'classnames';
5
+
6
+ type PaginationPanelProps = {
7
+ className?: string;
8
+ } & RowCountInfoProps & PaginationControlsProps & PageSizeSelectorProps;
9
+
10
+ export const PaginationPanel = (props: PaginationPanelProps) => {
11
+ const {
12
+ className = '',
13
+ totalRows,
14
+ totalPages,
15
+ onPageChange,
16
+ availableSizes,
17
+ initialPageSize,
18
+ onPageSizeChange,
19
+ } = props;
20
+
21
+ const classes = classNames('ds-table__pagination-panel', className);
22
+
23
+ return (
24
+ <nav className={classes} aria-label="Pagination">
25
+ <RowCountInfo totalRows={totalRows} />
26
+ <PaginationControls totalPages={totalPages} onPageChange={onPageChange} />
27
+ <PageSizeSelector availableSizes={availableSizes} initialPageSize={initialPageSize} onPageSizeChange={onPageSizeChange} />
28
+ </nav>
29
+ );
30
+ };
@@ -0,0 +1,67 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { useGridApi } from 'Utils/hooks/useGridApi';
3
+
4
+ export type RowCountInfoProps = {
5
+ totalRows?: number;
6
+ };
7
+
8
+ export const RowCountInfo = (props: RowCountInfoProps) => {
9
+ const { totalRows: passedTotalRows } = props;
10
+
11
+ const { gridApi, isGridApiReady } = useGridApi();
12
+
13
+ const getTrueDisplayedRowCount = () => {
14
+ const displayedRowCount = gridApi?.getDisplayedRowCount() || 0;
15
+ const pageSize = gridApi?.paginationGetPageSize() || 0;
16
+ const result = Math.min(displayedRowCount, pageSize);
17
+ const currentPage = gridApi?.paginationGetCurrentPage() || 0;
18
+ const totalPages = gridApi?.paginationGetTotalPages();
19
+
20
+ if (totalPages === 1) {
21
+ return displayedRowCount;
22
+ }
23
+ if (gridApi && displayedRowCount && pageSize && currentPage + 1 === totalPages) {
24
+ return displayedRowCount % pageSize || pageSize;
25
+ }
26
+ return result;
27
+ };
28
+ const [displayedRowCount, setDisplayedRowCount] = useState(() => getTrueDisplayedRowCount());
29
+
30
+ useEffect(() => {
31
+ if (isGridApiReady) {
32
+ const result = getTrueDisplayedRowCount();
33
+ setDisplayedRowCount(result);
34
+ }
35
+ }, [isGridApiReady]);
36
+
37
+ if (!isGridApiReady) {
38
+ return null;
39
+ }
40
+
41
+ // TODO when writing tests, see how this behaves with or without pagination
42
+ let totalRows: number | undefined;
43
+ if (typeof passedTotalRows !== 'undefined') {
44
+ totalRows = passedTotalRows;
45
+ }
46
+ else if (gridApi && gridApi.paginationGetTotalPages() > 1) {
47
+ totalRows = gridApi.paginationGetRowCount();
48
+ }
49
+
50
+ gridApi?.addEventListener('paginationChanged', () => {
51
+ setDisplayedRowCount(getTrueDisplayedRowCount());
52
+ });
53
+ gridApi?.addEventListener('filterChanged', () => {
54
+ setDisplayedRowCount(getTrueDisplayedRowCount());
55
+ });
56
+ gridApi?.addEventListener('rowDataUpdated', () => {
57
+ setDisplayedRowCount(getTrueDisplayedRowCount());
58
+ });
59
+
60
+ const message = totalRows && totalRows !== displayedRowCount
61
+ ? `Showing ${displayedRowCount} of ${totalRows} results`
62
+ : `Showing ${displayedRowCount} results`;
63
+
64
+ return (
65
+ <span className="ds-table__row-count-info">{message}</span>
66
+ );
67
+ };
@@ -0,0 +1,26 @@
1
+ .ds-table__pagination-panel {
2
+ display: flex;
3
+ flex-direction: row;
4
+ align-items: center;
5
+ width: 100%;
6
+ justify-content: space-between;
7
+ }
8
+
9
+ .ds-table__pagination-controls {
10
+ display: flex;
11
+ flex-direction: row;
12
+ align-items: center;
13
+ gap: 3px;
14
+
15
+ &__number-input-container {
16
+ display: inline;
17
+ }
18
+
19
+ &__number-input {
20
+ width: 39px;
21
+ }
22
+ }
23
+
24
+ .ds-table__pagination-controls, .ds-table__row-count-info {
25
+ font-size: var(--font-size-2-13);
26
+ }
@@ -0,0 +1,35 @@
1
+ import type { Meta } from '@storybook/react-vite';
2
+ import { Tooltip } from './Tooltip';
3
+ import { Button } from 'Components/button/Button';
4
+
5
+ const meta: Meta<typeof Tooltip> = {
6
+ title: 'Components/Tooltip/DestructuredTooltip',
7
+ component: Tooltip,
8
+ tags: ['autodocs'],
9
+
10
+ parameters: {
11
+ docs: {
12
+ description: {
13
+ component: 'A composable set of components (Tooltip, Tooltip.Trigger, Tooltip.Content) that you can use to create a tooltip.',
14
+ },
15
+ },
16
+ },
17
+ };
18
+
19
+ export const Default = {
20
+ args: {
21
+ children: [
22
+ <>
23
+ <Tooltip.Trigger>
24
+ <Button type="primary">Click me!</Button>
25
+ </Tooltip.Trigger>
26
+
27
+ <Tooltip.Content>
28
+ <div>Content</div>
29
+ </Tooltip.Content>
30
+ </>,
31
+ ],
32
+ },
33
+ };
34
+
35
+ export default meta;
@@ -0,0 +1,44 @@
1
+ import { expect, test, describe } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom/vitest';
4
+ import { Tooltip } from './Tooltip';
5
+ import userEvent from '@testing-library/user-event';
6
+
7
+ describe('Tooltip component', () => {
8
+ test('renders tooltip trigger', () => {
9
+ render(
10
+ <Tooltip>
11
+ <Tooltip.Trigger>
12
+ <button>Hover me</button>
13
+ </Tooltip.Trigger>
14
+ <Tooltip.Content>
15
+ Tooltip content
16
+ </Tooltip.Content>
17
+ </Tooltip>,
18
+ );
19
+
20
+ expect(screen.getByText('Hover me')).toBeInTheDocument();
21
+ expect(screen.getByRole('button')).toHaveClass('ds-tooltip__trigger');
22
+ });
23
+
24
+ test('shows tooltip on hover', async () => {
25
+ render(
26
+ <Tooltip delayDuration={0}>
27
+ <Tooltip.Trigger>
28
+ <button>Hover me</button>
29
+ </Tooltip.Trigger>
30
+ <Tooltip.Content>
31
+ Tooltip content
32
+ </Tooltip.Content>
33
+ </Tooltip>,
34
+ );
35
+
36
+ await userEvent.hover(screen.getByText('Hover me'));
37
+ await expect(screen.getAllByText('Tooltip content')[0]).toBeInTheDocument();
38
+ });
39
+
40
+ test('exposes Content and Trigger as static properties', () => {
41
+ expect(Tooltip.Content).toBeDefined();
42
+ expect(Tooltip.Trigger).toBeDefined();
43
+ });
44
+ });
@@ -0,0 +1,18 @@
1
+ import { Tooltip as TooltipPrimitive } from 'radix-ui';
2
+ import { TooltipContent } from './TooltipContent';
3
+ import { TooltipTrigger } from './TooltipTrigger';
4
+
5
+ export const Tooltip = (props: TooltipPrimitive.TooltipProps) => {
6
+ const { children, delayDuration = 400, ...rest } = props;
7
+ return (
8
+ <TooltipPrimitive.Provider>
9
+ <TooltipPrimitive.Root delayDuration={delayDuration} {...rest}>
10
+ {children}
11
+ </TooltipPrimitive.Root>
12
+ </TooltipPrimitive.Provider>
13
+
14
+ );
15
+ };
16
+
17
+ Tooltip.Content = TooltipContent;
18
+ Tooltip.Trigger = TooltipTrigger;
@@ -0,0 +1,40 @@
1
+ import classNames from 'classnames';
2
+ import { Tooltip as TooltipPrimitive } from 'radix-ui';
3
+ import { useContext } from 'react';
4
+ import { PopupParentContext } from 'Utils/PopupParentContext';
5
+
6
+ export type TooltipContentProps = {
7
+ children: React.ReactNode;
8
+ portalProps?: TooltipPrimitive.TooltipPortalProps;
9
+ shouldShowArrow?: boolean;
10
+ } & TooltipPrimitive.TooltipContentProps;
11
+
12
+ export const TooltipContent = (props: TooltipContentProps) => {
13
+ const {
14
+ children,
15
+ portalProps,
16
+ shouldShowArrow = true,
17
+ className,
18
+ ...rest
19
+ } = props;
20
+ const popupParentRef = useContext(PopupParentContext);
21
+
22
+ return (
23
+ <TooltipPrimitive.Portal
24
+ container={popupParentRef.current}
25
+ {...portalProps}
26
+ >
27
+ <TooltipPrimitive.Content
28
+ {...rest}
29
+ className={classNames('ds-tooltip__content', className)}
30
+ >
31
+ {shouldShowArrow && (
32
+ <TooltipPrimitive.Arrow
33
+ className="ds-tooltip__content-arrow"
34
+ />
35
+ )}
36
+ {children}
37
+ </TooltipPrimitive.Content>
38
+ </TooltipPrimitive.Portal>
39
+ );
40
+ };
@@ -0,0 +1,11 @@
1
+ import classNames from 'classnames';
2
+ import { Tooltip as TooltipPrimitive } from 'radix-ui';
3
+
4
+ export const TooltipTrigger = (props: TooltipPrimitive.TooltipTriggerProps) => {
5
+ const { children, className = '', ...rest } = props;
6
+ return (
7
+ <TooltipPrimitive.Trigger asChild className={classNames('ds-tooltip__trigger', className)} {...rest}>
8
+ {children}
9
+ </TooltipPrimitive.Trigger>
10
+ );
11
+ };
@@ -0,0 +1,24 @@
1
+ import type { Meta } from '@storybook/react-vite';
2
+ import { Button } from 'Components/button/Button';
3
+ import { TooltipWrapper as TooltipWrapperComponent } from './TooltipWrapper';
4
+
5
+ const meta: Meta<typeof TooltipWrapperComponent> = {
6
+ title: 'Components/Tooltip/TooltipWrapper',
7
+ component: TooltipWrapperComponent,
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component: 'Instead of manually creating a tooltip (using Tooltip, Tooltip.TooltipTrigger, Tooltip.TooltipContent), you can use this wrapper to easily create a tooltip.',
12
+ },
13
+ } },
14
+ tags: ['autodocs'],
15
+ };
16
+
17
+ export const Default = {
18
+ args: {
19
+ tooltipContent: 'Tooltip content',
20
+ children: <Button type="primary">Click me!</Button>,
21
+ },
22
+ };
23
+
24
+ export default meta;
@@ -0,0 +1,100 @@
1
+ import { expect, test, describe } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom/vitest';
4
+ import { TooltipWrapper } from './TooltipWrapper';
5
+ import userEvent from '@testing-library/user-event';
6
+
7
+ describe('TooltipWrapper component', () => {
8
+ test('renders children with tooltip wrapper', () => {
9
+ render(
10
+ <TooltipWrapper tooltipContent="Helpful tooltip text">
11
+ <button>Click me</button>
12
+ </TooltipWrapper>,
13
+ );
14
+
15
+ expect(screen.getByText('Click me')).toBeInTheDocument();
16
+ expect(screen.getByRole('button')).toHaveClass('ds-tooltip__trigger');
17
+ });
18
+
19
+ test('accepts string tooltip content', () => {
20
+ render(
21
+ <TooltipWrapper tooltipContent="Tooltip text">
22
+ <button>Button</button>
23
+ </TooltipWrapper>,
24
+ );
25
+
26
+ expect(screen.getByText('Button')).toBeInTheDocument();
27
+ });
28
+
29
+ test('accepts JSX tooltip content', () => {
30
+ render(
31
+ <TooltipWrapper
32
+ tooltipContent={(
33
+ <div>
34
+ <strong>Bold text</strong>
35
+ </div>
36
+ )}
37
+ >
38
+ <button>Button</button>
39
+ </TooltipWrapper>,
40
+ );
41
+
42
+ expect(screen.getByText('Button')).toBeInTheDocument();
43
+ });
44
+
45
+ test('passes triggerProps to trigger', () => {
46
+ render(
47
+ <TooltipWrapper
48
+ tooltipContent="Tooltip text"
49
+ triggerProps={{ className: 'foobar' }}
50
+ >
51
+ <button>Tooltip trigger</button>
52
+ </TooltipWrapper>,
53
+ );
54
+
55
+ expect(screen.getByText('Tooltip trigger')).toBeInTheDocument();
56
+ expect(screen.getByText('Tooltip trigger')).toHaveClass('foobar');
57
+ });
58
+
59
+ test('shows tooltip on hover', async () => {
60
+ render(
61
+ <TooltipWrapper
62
+ tooltipContent="Tooltip text"
63
+ delayDuration={0}
64
+ >
65
+ <button>Tooltip trigger</button>
66
+ </TooltipWrapper>,
67
+ );
68
+
69
+ await userEvent.hover(screen.getByText('Tooltip trigger'));
70
+ await expect(screen.getAllByText('Tooltip text')[0]).toBeInTheDocument();
71
+ });
72
+
73
+ test('passes contentProps to content', async () => {
74
+ render(
75
+ <TooltipWrapper
76
+ tooltipContent="Tooltip text"
77
+ contentProps={{ className: 'foobar', shouldShowArrow: false }}
78
+ delayDuration={0}
79
+ >
80
+ <button>Tooltip trigger</button>
81
+ </TooltipWrapper>,
82
+ );
83
+
84
+ await userEvent.hover(screen.getByText('Tooltip trigger'));
85
+ await expect(screen.getAllByText('Tooltip text')[0]).toBeInTheDocument();
86
+ expect(screen.getAllByText('Tooltip text')[0]).toHaveClass('foobar');
87
+ });
88
+
89
+ test('renders with complex children', () => {
90
+ render(
91
+ <TooltipWrapper tooltipContent="Info">
92
+ <div className="wrapper">
93
+ <span>Content</span>
94
+ </div>
95
+ </TooltipWrapper>,
96
+ );
97
+
98
+ expect(screen.getByText('Content')).toBeInTheDocument();
99
+ });
100
+ });
@@ -0,0 +1,32 @@
1
+ import { Tooltip as TooltipPrimitive } from 'radix-ui';
2
+ import { Tooltip } from './Tooltip';
3
+ import type { TooltipContentProps } from './TooltipContent';
4
+
5
+ export type TooltipWrapperProps = {
6
+ children: TooltipPrimitive.TooltipTriggerProps['children'];
7
+ tooltipContent: TooltipPrimitive.TooltipContentProps['children'];
8
+ triggerProps?: TooltipPrimitive.TooltipTriggerProps;
9
+ contentProps?: Omit<TooltipContentProps, 'children'>;
10
+ delayDuration?: number;
11
+ };
12
+
13
+ export const TooltipWrapper = (props: TooltipWrapperProps) => {
14
+ const {
15
+ children,
16
+ tooltipContent,
17
+ triggerProps,
18
+ contentProps,
19
+ delayDuration = 400,
20
+ } = props;
21
+
22
+ return (
23
+ <Tooltip delayDuration={delayDuration}>
24
+ <Tooltip.Trigger {...triggerProps}>
25
+ {children}
26
+ </Tooltip.Trigger>
27
+ <Tooltip.Content {...contentProps}>
28
+ {tooltipContent}
29
+ </Tooltip.Content>
30
+ </Tooltip>
31
+ );
32
+ };
@@ -0,0 +1,15 @@
1
+ .ds-tooltip {
2
+ &__content {
3
+ color: var(--tooltip-color-text);
4
+ background: var(--tooltip-color-background);
5
+ padding: var(--tooltip-spacing-vertical) var(--tooltip-spacing-horizontal);
6
+ border-radius: var(--border-radius-small);
7
+ font-size: var(--type-body-p-size);
8
+ font-family: var(--type-body-p-family);
9
+ font-weight: var(--type-body-p-weight);
10
+
11
+ &-arrow {
12
+ fill: var(--tooltip-color-background);
13
+ }
14
+ }
15
+ }
package/src/index.scss CHANGED
@@ -18,4 +18,6 @@
18
18
  @use "./components/slideoverManager/slideoverManager.scss";
19
19
  @use "./components/slideover/slideover.scss";
20
20
  @use "./components/table/table.scss";
21
+ @use "./components/table/pagination/pagination.scss";
22
+ @use "./components/tooltip/tooltip.scss";
21
23
  @import 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap';
package/src/index.ts CHANGED
@@ -16,3 +16,5 @@ export { SlideoverUtils } from 'Utils/SlideoverUtils';
16
16
  export { RadioButtonInput } from 'Components/formField/inputs/radio/RadioButtonInput';
17
17
  export { Table } from 'Components/table/Table';
18
18
  export { GridApiContext } from 'Components/table/GridApiContext';
19
+ export { Tooltip } from 'Components/tooltip/Tooltip';
20
+ export { TooltipWrapper } from 'Components/tooltip/TooltipWrapper';
@@ -0,0 +1,15 @@
1
+ import { GridApiContext } from 'Components/table/GridApiContext';
2
+ import { useContext, useEffect, useState } from 'react';
3
+
4
+ export const useGridApi = () => {
5
+ const gridApi = useContext(GridApiContext);
6
+ const [isGridApiReady, setIsGridApiReady] = useState(false);
7
+
8
+ useEffect(() => {
9
+ if (gridApi) {
10
+ setIsGridApiReady(true);
11
+ }
12
+ }, [gridApi]);
13
+
14
+ return { gridApi, isGridApiReady };
15
+ };
@@ -0,0 +1,12 @@
1
+ import { useRef } from 'react';
2
+ import { useComponentDidMount } from './useComponentDidMount';
3
+
4
+ export const useIsMounted = () => {
5
+ const isMounted = useRef(true);
6
+ useComponentDidMount(() => {
7
+ return () => {
8
+ isMounted.current = false;
9
+ };
10
+ });
11
+ return isMounted;
12
+ };