@seeqdev/qomponents 0.0.183 → 0.0.185
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.
- package/README.md +5 -19
- package/dist/example/.eslintrc.cjs +2 -6
- package/dist/example/README.md +0 -3
- package/dist/example/src/Example.tsx +7 -7
- package/dist/example/tsconfig.json +2 -8
- package/dist/example/tsconfig.node.json +1 -3
- package/dist/example/vite.config.ts +1 -1
- package/dist/index.esm.js +784 -523
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +781 -520
- package/dist/index.js.map +1 -1
- package/dist/src/Accordion/Accordion.js +8 -0
- package/dist/src/Accordion/Accordion.stories.js +96 -0
- package/dist/src/Accordion/Accordion.test.js +54 -0
- package/dist/src/Accordion/Accordion.types.js +1 -0
- package/dist/src/Accordion/index.js +1 -0
- package/dist/src/Alert/Alert.js +33 -0
- package/dist/src/Alert/Alert.stories.d.ts +13 -5
- package/dist/src/Alert/Alert.stories.js +72 -0
- package/dist/src/Alert/Alert.test.js +49 -0
- package/dist/src/Alert/Alert.types.js +1 -0
- package/dist/src/Alert/index.js +1 -0
- package/dist/src/Button/Button.js +107 -0
- package/dist/src/Button/Button.stories.js +74 -0
- package/dist/src/Button/Button.test.js +65 -0
- package/dist/src/Button/Button.types.d.ts +4 -4
- package/dist/src/Button/Button.types.js +4 -0
- package/dist/src/Button/index.d.ts +1 -1
- package/dist/src/Button/index.js +1 -0
- package/dist/src/ButtonGroup/ButtonGroup.js +34 -0
- package/dist/src/ButtonGroup/ButtonGroup.stories.js +328 -0
- package/dist/src/ButtonGroup/ButtonGroup.test.js +65 -0
- package/dist/src/ButtonGroup/ButtonGroup.types.js +1 -0
- package/dist/src/ButtonGroup/index.js +1 -0
- package/dist/src/ButtonWithDropdown/ButtonWithDropdown.js +46 -0
- package/dist/src/ButtonWithDropdown/ButtonWithDropdown.stories.js +119 -0
- package/dist/src/ButtonWithDropdown/ButtonWithDropdown.test.js +92 -0
- package/dist/src/ButtonWithDropdown/ButtonWithDropdown.types.js +1 -0
- package/dist/src/ButtonWithDropdown/index.js +1 -0
- package/dist/src/ButtonWithPopover/ButtonWithPopover.js +54 -0
- package/dist/src/ButtonWithPopover/ButtonWithPopover.stories.js +51 -0
- package/dist/src/ButtonWithPopover/ButtonWithPopover.test.js +80 -0
- package/dist/src/ButtonWithPopover/ButtonWithPopover.types.js +1 -0
- package/dist/src/ButtonWithPopover/index.js +1 -0
- package/dist/src/Carousel/Carousel.js +80 -0
- package/dist/src/Carousel/Carousel.stories.js +100 -0
- package/dist/src/Carousel/Carousel.test.js +47 -0
- package/dist/src/Carousel/Carousel.types.js +1 -0
- package/dist/src/Carousel/index.js +1 -0
- package/dist/src/Checkbox/Checkbox.js +24 -0
- package/dist/src/Checkbox/Checkbox.stories.js +21 -0
- package/dist/src/Checkbox/Checkbox.test.js +93 -0
- package/dist/src/Checkbox/Checkbox.types.js +1 -0
- package/dist/src/Checkbox/index.js +1 -0
- package/dist/src/Collapse/Collapse.js +16 -0
- package/dist/src/Collapse/Collapse.stories.js +28 -0
- package/dist/src/Collapse/Collapse.test.js +16 -0
- package/dist/src/Collapse/Collapse.types.js +1 -0
- package/dist/src/Collapse/index.js +1 -0
- package/dist/src/Icon/Icon.js +55 -0
- package/dist/src/Icon/Icon.stories.js +31 -0
- package/dist/src/Icon/Icon.test.js +54 -0
- package/dist/src/Icon/Icon.types.d.ts +1 -1
- package/dist/src/Icon/Icon.types.js +15 -0
- package/dist/src/Icon/index.js +1 -0
- package/dist/src/InputGroup/InputGroup.js +33 -0
- package/dist/src/InputGroup/InputGroup.stories.js +144 -0
- package/dist/src/InputGroup/InputGroup.test.js +41 -0
- package/dist/src/InputGroup/InputGroup.types.js +1 -0
- package/dist/src/InputGroup/index.js +1 -0
- package/dist/src/Modal/Modal.js +86 -0
- package/dist/src/Modal/Modal.stories.js +79 -0
- package/dist/src/Modal/Modal.test.js +107 -0
- package/dist/src/Modal/Modal.types.js +1 -0
- package/dist/src/Modal/index.js +1 -0
- package/dist/src/ProgressBar/ProgressBar.js +71 -0
- package/dist/src/ProgressBar/ProgressBar.stories.js +46 -0
- package/dist/src/ProgressBar/ProgressBar.test.js +42 -0
- package/dist/src/ProgressBar/ProgressBar.types.js +1 -0
- package/dist/src/ProgressBar/index.js +1 -0
- package/dist/src/SeeqActionDropdown/SeeqActionDropdown.js +41 -0
- package/dist/src/SeeqActionDropdown/SeeqActionDropdown.stories.js +73 -0
- package/dist/src/SeeqActionDropdown/SeeqActionDropdown.test.js +72 -0
- package/dist/src/SeeqActionDropdown/SeeqActionDropdown.types.d.ts +0 -5
- package/dist/src/SeeqActionDropdown/SeeqActionDropdown.types.js +1 -0
- package/dist/src/SeeqActionDropdown/index.js +1 -0
- package/dist/src/SeeqActionDropdown/variants.js +34 -0
- package/dist/src/Select/Select.js +171 -0
- package/dist/src/Select/Select.stories.js +50 -0
- package/dist/src/Select/Select.test.js +176 -0
- package/dist/src/Select/Select.types.js +1 -0
- package/dist/src/Select/index.js +2 -0
- package/dist/src/Slider/Slider.js +11 -0
- package/dist/src/Slider/Slider.stories.js +37 -0
- package/dist/src/Slider/Slider.test.js +31 -0
- package/dist/src/Slider/Slider.types.js +1 -0
- package/dist/src/Slider/index.js +1 -0
- package/dist/src/SvgIcon/SvgIcon.js +27 -0
- package/dist/src/SvgIcon/SvgIcon.stories.js +26 -0
- package/dist/src/SvgIcon/SvgIcon.test.js +40 -0
- package/dist/src/SvgIcon/SvgIcon.types.d.ts +1 -1
- package/dist/src/SvgIcon/SvgIcon.types.js +2 -0
- package/dist/src/SvgIcon/index.js +1 -0
- package/dist/src/Tabs/Tabs.js +18 -0
- package/dist/src/Tabs/Tabs.stories.js +83 -0
- package/dist/src/Tabs/Tabs.test.js +91 -0
- package/dist/src/Tabs/Tabs.types.js +1 -0
- package/dist/src/Tabs/index.js +1 -0
- package/dist/src/TextArea/TextArea.js +54 -0
- package/dist/src/TextArea/TextArea.stories.js +21 -0
- package/dist/src/TextArea/TextArea.test.js +129 -0
- package/dist/src/TextArea/TextArea.types.js +1 -0
- package/dist/src/TextArea/index.js +1 -0
- package/dist/src/TextField/TextField.js +85 -0
- package/dist/src/TextField/TextField.stories.js +30 -0
- package/dist/src/TextField/TextField.test.js +40 -0
- package/dist/src/TextField/TextField.types.js +1 -0
- package/dist/src/TextField/index.js +1 -0
- package/dist/src/ToolbarButton/ToolbarButton.js +58 -0
- package/dist/src/ToolbarButton/ToolbarButton.stories.js +63 -0
- package/dist/src/ToolbarButton/ToolbarButton.test.js +89 -0
- package/dist/src/ToolbarButton/ToolbarButton.types.d.ts +1 -1
- package/dist/src/ToolbarButton/ToolbarButton.types.js +1 -0
- package/dist/src/ToolbarButton/index.js +1 -0
- package/dist/src/Tooltip/QTip.stories.js +22 -0
- package/dist/src/Tooltip/QTip.types.js +1 -0
- package/dist/src/Tooltip/QTipPerformance.stories.js +27 -0
- package/dist/src/Tooltip/Qtip.js +168 -0
- package/dist/src/Tooltip/Tooltip.js +34 -0
- package/dist/src/Tooltip/Tooltip.stories.js +20 -0
- package/dist/src/Tooltip/Tooltip.types.d.ts +1 -1
- package/dist/src/Tooltip/Tooltip.types.js +2 -0
- package/dist/src/Tooltip/TooltipPerformance.stories.js +25 -0
- package/dist/src/Tooltip/index.js +2 -0
- package/dist/src/Tooltip/qTip.utilities.js +10 -0
- package/dist/src/index.js +47 -0
- package/dist/src/setupTests.js +5 -0
- package/dist/src/types.js +25 -0
- package/dist/src/utils/browserId.d.ts +3 -3
- package/dist/src/utils/browserId.js +28 -0
- package/dist/src/utils/svg.js +19 -0
- package/dist/src/utils/validateStyleDimension.js +13 -0
- package/dist/src/utils/validateStyleDimension.test.js +19 -0
- package/dist/styles.css +3410 -2791
- package/package.json +22 -17
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { TextField } from './TextField';
|
|
3
|
+
import { QTip } from '../Tooltip';
|
|
4
|
+
export default {
|
|
5
|
+
title: 'TextField',
|
|
6
|
+
};
|
|
7
|
+
export const AllTextFields = () => {
|
|
8
|
+
const renderAllVariations = () => (_jsxs(_Fragment, { children: [
|
|
9
|
+
_jsxs("div", { className: "tw-p-4 light", children: [
|
|
10
|
+
_jsx("div", { className: "tw-p-4", children: _jsx(TextField, { value: "value provided" }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { placeholder: "placeholder text" }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { placeholder: "with error", showError: true }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { placeholder: "with error text", showError: true, errorText: "this is a major test error and it is long on purpose to see its looks" }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { value: "read-only", readonly: true, onFocus: (e) => e.currentTarget.select() }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { value: "disabled", disabled: true, onFocus: (e) => e.currentTarget.select() }) }), _jsxs("div", { className: "tw-p-4", children: [
|
|
11
|
+
_jsx("div", { children: "max value = 10" }), _jsx(TextField, { type: "number", value: 20, max: 10 })
|
|
12
|
+
] }), _jsxs("div", { className: "tw-p-4", children: [
|
|
13
|
+
_jsx("div", { children: "min value = 2" }), _jsx(TextField, { type: "number", value: 1, min: 2 })
|
|
14
|
+
] }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { value: "large", size: "lg" }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { value: "with tooltip", tooltip: "small tooltip" }) })
|
|
15
|
+
] }), _jsxs("div", { className: "tw-p-4 tw-dark tw-bg-sq-dark-background", children: [
|
|
16
|
+
_jsx("div", { className: "tw-p-4", children: _jsx(TextField, { value: "value provided" }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { placeholder: "placeholder text" }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { placeholder: "with error", showError: true }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { placeholder: "with error text", showError: true, errorText: "this is a major test error and it is long on purpose to see its looks" }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { value: "read-only", readonly: true, onFocus: (e) => e.currentTarget.select() }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { value: "disabled", disabled: true, onFocus: (e) => e.currentTarget.select() }) }), _jsxs("div", { className: "tw-p-4", children: [
|
|
17
|
+
_jsx("div", { className: "tw-text-sq-color-dark dark:tw-text-sq-color-dark-dark", children: "max value = 10" }), _jsx(TextField, { type: "number", value: 8, max: 10 })
|
|
18
|
+
] }), _jsxs("div", { className: "tw-p-4", children: [
|
|
19
|
+
_jsx("div", { className: "tw-text-sq-color-dark dark:tw-text-sq-color-dark-dark", children: "min value = 2" }), _jsx(TextField, { type: "number", value: 10, min: 2 })
|
|
20
|
+
] }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { value: "large", size: "lg" }) }), _jsx("div", { className: "tw-p-4", children: _jsx(TextField, { value: "with tooltip", tooltip: "small tooltip" }) })
|
|
21
|
+
] })
|
|
22
|
+
] }));
|
|
23
|
+
return (_jsxs("div", { className: "tw-grid tw-grid-cols-4 tw-gap-4", children: [
|
|
24
|
+
_jsx(QTip, {}), _jsxs("div", { className: "color_topic", children: [
|
|
25
|
+
_jsx("b", { children: "Topic Colors" }), renderAllVariations()] }), _jsxs("div", { className: "color_analysis", children: [
|
|
26
|
+
_jsx("b", { children: "Analysis Colors" }), renderAllVariations()] }), _jsxs("div", { className: "color_datalab", children: [
|
|
27
|
+
_jsx("b", { children: "Datalab Colors" }), renderAllVariations()] }), _jsxs("div", { className: "color_vantage", children: [
|
|
28
|
+
_jsx("b", { children: "Vantage Colors" }), renderAllVariations()] })
|
|
29
|
+
] }));
|
|
30
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import '@testing-library/jest-dom';
|
|
3
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import { TextField } from './TextField';
|
|
6
|
+
describe('TextField', () => {
|
|
7
|
+
class Context {
|
|
8
|
+
testId = 'textFieldTestId';
|
|
9
|
+
props = {
|
|
10
|
+
onChange: jest.fn(),
|
|
11
|
+
testId: this.testId,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
let tc;
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
tc = new Context();
|
|
17
|
+
});
|
|
18
|
+
const renderTextField = (props) => render(_jsx(TextField, { ...props }));
|
|
19
|
+
it('renders the provided value', () => {
|
|
20
|
+
const value = 'hello';
|
|
21
|
+
renderTextField({ ...tc.props, value });
|
|
22
|
+
expect(screen.getByDisplayValue(value)).toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
it('renders the provided placeholder', () => {
|
|
25
|
+
const placeholder = 'Prompt to enter';
|
|
26
|
+
renderTextField({ ...tc.props, placeholder });
|
|
27
|
+
expect(screen.getByPlaceholderText(placeholder)).toBeInTheDocument();
|
|
28
|
+
});
|
|
29
|
+
it('calls onChange handler', async () => {
|
|
30
|
+
renderTextField({ ...tc.props });
|
|
31
|
+
await userEvent.type(screen.getByTestId(tc.testId), 'trigger');
|
|
32
|
+
expect(tc.props.onChange).toHaveBeenCalled();
|
|
33
|
+
});
|
|
34
|
+
it('autofocuses the textfield', async () => {
|
|
35
|
+
renderTextField({ ...tc.props, autoFocus: true });
|
|
36
|
+
await waitFor(() => {
|
|
37
|
+
expect(screen.getByTestId(tc.testId)).toHaveFocus();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TextField as default } from './TextField';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
4
|
+
import Icon from '../Icon';
|
|
5
|
+
import { DEFAULT_TOOL_TIP_DELAY } from '../Tooltip/Tooltip.types';
|
|
6
|
+
import { getSvgIconPath } from '../utils/svg';
|
|
7
|
+
const popoverBorderStyles = [
|
|
8
|
+
'tw-border-solid',
|
|
9
|
+
'tw-border',
|
|
10
|
+
'tw-rounded-md',
|
|
11
|
+
'tw-border-sq-disabled-gray',
|
|
12
|
+
'dark:tw-border-gray-500',
|
|
13
|
+
].join(' ');
|
|
14
|
+
const triggerBackgroundStyles = [
|
|
15
|
+
'tw-bg-transparent',
|
|
16
|
+
'hover:tw-bg-sq-worksheetspanel-gray',
|
|
17
|
+
'active:tw-bg-sq-worksheetspanel-gray',
|
|
18
|
+
'dark:tw-bg-transparent',
|
|
19
|
+
'dark:hover:tw-bg-sq-field-disabled-gray',
|
|
20
|
+
'dark:active:tw-bg-sq-field-disabled-gray',
|
|
21
|
+
].join(' ');
|
|
22
|
+
const activeBackgroundStyles = [
|
|
23
|
+
'active',
|
|
24
|
+
'tw-bg-sq-overlay-gray',
|
|
25
|
+
'hover:tw-bg-sq-overlay-gray',
|
|
26
|
+
'active:tw-bg-sq-overlay-gray',
|
|
27
|
+
'dark:tw-bg-sq-dark-disabled-gray',
|
|
28
|
+
'dark:hover:tw-border-sq-dark-disabled-gray',
|
|
29
|
+
'dark:active:tw-bg-sq-dark-disabled-gray',
|
|
30
|
+
].join(' ');
|
|
31
|
+
const bgStyles = ['tw-bg-sq-white', 'dark:tw-bg-sq-dark-background'].join(' ');
|
|
32
|
+
const disabledClasses = ['tw-opacity-50', 'tw-cursor-not-allowed'].join(' ');
|
|
33
|
+
const PopoverContent = React.forwardRef(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (_jsx(PopoverPrimitive.Content, { ref: ref, align: align, sideOffset: sideOffset, ...props, asChild: true })));
|
|
34
|
+
export const ToolbarButton = ({ isSmall = false, label, icon, secondIcon, forceSmallIcon = false, tooltipText, id, popoverContent, extraClassNames, testId, tooltipTestId, tooltipOptions, isActive = false, isHtmlTooltip = false, hasArrow = false, disabled = false, isPrimaryAnSvg = false, primaryIconExtraClassNames = false, iconHeight = 24, iconWidth = 24, primaryIconViewbox = '0 0 24 24', onClick, onHide, }) => {
|
|
35
|
+
let tooltipData = undefined;
|
|
36
|
+
if (tooltipText) {
|
|
37
|
+
tooltipData = {
|
|
38
|
+
'data-qtip-text': tooltipText,
|
|
39
|
+
'data-qtip-placement': tooltipOptions?.position,
|
|
40
|
+
'data-qtip-is-html': isHtmlTooltip,
|
|
41
|
+
'data-qtip-testid': tooltipTestId,
|
|
42
|
+
'data-qtip-delay': tooltipOptions?.delay ?? DEFAULT_TOOL_TIP_DELAY,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const onOpenChange = (open) => {
|
|
46
|
+
if (!open) {
|
|
47
|
+
onHide && onHide();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
return (_jsxs(PopoverPrimitive.Root, { defaultOpen: false, onOpenChange: onOpenChange, children: [
|
|
51
|
+
_jsx(PopoverPrimitive.Trigger, { id: id, className: `tw-border-none tw-mx-[1.5px] first:tw-ml-0 last:tw-mr-0 ${isActive ? 'active' : ''}`, disabled: disabled, "data-testid": testId, onClick: (e) => {
|
|
52
|
+
onClick && onClick(e);
|
|
53
|
+
}, children: _jsxs("div", { ...tooltipData, className: `tw-flex tw-flex-col tw-items-center tw-rounded-md ${isSmall ? 'tw-py-[1px] tw-px-1' : 'tw-px-2 tw-py-[5px]'} ${disabled ? disabledClasses : isActive ? activeBackgroundStyles : triggerBackgroundStyles} ${extraClassNames || ''}`, children: [
|
|
54
|
+
_jsxs("span", { className: "tw-nowrap", children: [isPrimaryAnSvg ? (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: `tw-text-sq-text-color dark:tw-text-sq-dark-text ${primaryIconExtraClassNames || ''}`, viewBox: primaryIconViewbox, height: `${iconHeight}px`, width: `${iconWidth}px`, fill: "currentColor", "data-testid": testId, children: _jsx("path", { d: getSvgIconPath(icon) }) })) : (_jsx(Icon, { icon: icon, testId: "firstIcon", type: "text", large: !isSmall && !label && !secondIcon && !forceSmallIcon, extraClassNames: `tw-text-sq-text-color dark:tw-text-sq-dark-text` })), secondIcon && _jsx(Icon, { icon: secondIcon, type: "text", testId: "secondIcon" }), popoverContent ? (_jsx(Icon, { icon: "fc-arrow-dropdown", extraClassNames: "tw-text-sq-text-color dark:tw-text-sq-dark-text tw-ml-[3px] tw-text-[0.5rem]", type: "text" })) : undefined] }), !isSmall && (_jsx("small", { className: "tw-text-sq-text-color dark:tw-text-sq-dark-text tw-text-[0.625rem]", children: label }))] }) }), !!popoverContent ? (_jsx(PopoverContent, { sideOffset: 2, align: "start", children: _jsxs("div", { className: bgStyles +
|
|
55
|
+
' tw-relative tw-z-[1000] tw-min-w-6 tw-p-[0.5rem] focus-visible:tw-outline-none tw-outline-none data-[state=open]:animate-in data-[state=closed]:animate-out' +
|
|
56
|
+
' data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2' +
|
|
57
|
+
popoverBorderStyles, children: [hasArrow && (_jsx(PopoverPrimitive.Arrow, { asChild: true, children: _jsx("div", { className: " tw-fill-transparent tw-bg-white tw-w-[15px] tw-h-[15px] tw-mt-[-7px] tw-rotate-45 dark:tw-bg-sq-dark-background tw-border-b tw-border-r tw-border-sq-disabled-gray dark:tw-border-gray-500" }) })), popoverContent] }) })) : undefined] }));
|
|
58
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ToolbarButton } from './ToolbarButton';
|
|
3
|
+
import Icon from '../Icon';
|
|
4
|
+
import { QTip } from '../Tooltip/Qtip';
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Toolbar Button',
|
|
7
|
+
};
|
|
8
|
+
export const AllToolbarButtonVariants = () => {
|
|
9
|
+
const options = [
|
|
10
|
+
{ value: 'a', label: 'Chocolate' },
|
|
11
|
+
{ value: 'b', label: 'Strawberry' },
|
|
12
|
+
{ value: 'c', label: 'Vanilla' },
|
|
13
|
+
{ value: 'd', label: 'Rocky Road' },
|
|
14
|
+
{ value: 'e', label: 'Crazy Cow' },
|
|
15
|
+
{ value: 'f', label: 'Almond Joy' },
|
|
16
|
+
{ value: 'g', label: 'All of the above' },
|
|
17
|
+
];
|
|
18
|
+
const colorOptions = [
|
|
19
|
+
{ value: 'h', label: 'pink' },
|
|
20
|
+
{ value: 'i', label: 'purple' },
|
|
21
|
+
{ value: 'j', label: 'green' },
|
|
22
|
+
{ value: 'k', label: 'red' },
|
|
23
|
+
];
|
|
24
|
+
const allToolbarButtons = (isDark) => (_jsxs("div", { className: isDark ? 'tw-dark tw-bg-sq-dark-background' : '', children: [
|
|
25
|
+
_jsx("div", { className: "tw-p-5 ", children: _jsx(ToolbarButton, { icon: "fc-y-axis", label: "Popover", testId: "basic-popover1", tooltipText: "This is a small popover.", tooltipOptions: { position: 'top', delay: 0 }, isHtmlTooltip: false, isSmall: true, popoverContent: _jsxs("div", { className: "tw-text-sm dark:tw-text-white", children: [
|
|
26
|
+
_jsx("p", { className: "tw-bg-gray-200 dark:tw-bg-gray-700 tw-p-2", children: "Flavors" }), options.map((option) => (_jsxs("div", { className: "tw-flex tw-items-center tw-p-2 hover:tw-bg-green-500", children: [
|
|
27
|
+
_jsx(Icon, { type: "color", color: "green", icon: "fc-check", extraClassNames: "tw-mr-2" }), _jsx("span", { children: option.label })
|
|
28
|
+
] }, option.value))), _jsx("p", { className: "tw-bg-gray-200 dark:tw-bg-gray-700 tw-p-2", children: "Colors" }), colorOptions.map((option) => (_jsxs("div", { className: "tw-flex tw-items-center tw-p-2", children: [
|
|
29
|
+
_jsx(Icon, { type: "color", color: "green", icon: "fc-check", extraClassNames: "tw-mr-2" }), _jsx("span", { children: option.label })
|
|
30
|
+
] }, option.value)))] }) }) }), _jsx("div", { className: "tw-p-5 ", children: _jsx(ToolbarButton, { icon: "fc-y-axis", label: "Normal", testId: "basic-popover1", tooltipText: "This is a normal popover.", tooltipOptions: { position: 'top', delay: 0 }, isHtmlTooltip: false, popoverContent: _jsxs("div", { className: "tw-text-sm dark:tw-text-white", children: [
|
|
31
|
+
_jsx("p", { className: "tw-bg-gray-200 dark:tw-bg-gray-700 tw-p-2", children: "Flavors" }), options.map((option) => (_jsxs("div", { className: "tw-flex tw-items-center tw-p-2 hover:tw-bg-green-500", children: [
|
|
32
|
+
_jsx(Icon, { type: "color", color: "green", icon: "fc-check", extraClassNames: "tw-mr-2" }), _jsx("span", { children: option.label })
|
|
33
|
+
] }, option.value))), _jsx("p", { className: "tw-bg-gray-200 dark:tw-bg-gray-700 tw-p-2", children: "Colors" }), colorOptions.map((option) => (_jsxs("div", { className: "tw-flex tw-items-center tw-p-2", children: [
|
|
34
|
+
_jsx(Icon, { type: "color", color: "green", icon: "fc-check", extraClassNames: "tw-mr-2" }), _jsx("span", { children: option.label })
|
|
35
|
+
] }, option.value)))] }) }) }), _jsxs("div", { className: "tw-p-5 tw-flex", children: [
|
|
36
|
+
_jsx(ToolbarButton, { icon: "fc-y-axis", label: "With arrow", testId: "basic-popover1", tooltipText: "This is a normal popover.", tooltipOptions: { position: 'top', delay: 0 }, hasArrow: true, isHtmlTooltip: false, popoverContent: _jsxs("div", { className: "tw-text-sm dark:tw-text-white", children: [
|
|
37
|
+
_jsx("p", { className: "tw-bg-gray-200 dark:tw-bg-gray-700 tw-p-2", children: "Flavors" }), options.map((option) => (_jsxs("div", { className: "tw-flex tw-items-center tw-p-2 hover:tw-bg-green-500", children: [
|
|
38
|
+
_jsx(Icon, { type: "color", color: "green", icon: "fc-check", extraClassNames: "tw-mr-2" }), _jsx("span", { children: option.label })
|
|
39
|
+
] }, option.value))), _jsx("p", { className: "tw-bg-gray-200 dark:tw-bg-gray-700 tw-p-2", children: "Colors" }), colorOptions.map((option) => (_jsxs("div", { className: "tw-flex tw-items-center tw-p-2", children: [
|
|
40
|
+
_jsx(Icon, { type: "color", color: "green", icon: "fc-check", extraClassNames: "tw-mr-2" }), _jsx("span", { children: option.label })
|
|
41
|
+
] }, option.value)))] }) }), _jsx(ToolbarButton, { icon: "fc-y-axis", tooltipText: "This is a large popover icon", tooltipOptions: { position: 'top', delay: 0 }, testId: "basic-popover4", isHtmlTooltip: false })
|
|
42
|
+
] }), _jsxs("div", { className: "tw-p-5 ", children: [
|
|
43
|
+
_jsx("p", { className: "tw-text-sq-color-dark tw-text-[0.625rem]", children: "Without popup" }), _jsx(ToolbarButton, { icon: "fc-y-axis", label: "Button", testId: "basic-popover3", isHtmlTooltip: false })
|
|
44
|
+
] }), _jsx("div", { className: "tw-p-5 tw-flex", children: _jsx(ToolbarButton, { icon: "fc-y-axis", label: "Disabled", testId: "basic-popover2", tooltipText: "This is a popover.", tooltipOptions: { position: 'top' }, isHtmlTooltip: false, disabled: true, popoverContent: _jsxs("div", { className: "tw-border tw-border-gray-100 tw-p-2 tw-rounded-sm tw-text-sm dark:tw-bg-sq-dark-background", children: [
|
|
45
|
+
_jsx("p", { className: "tw-bg-gray-200 dark:tw-bg-gray-700 tw-p-2", children: "Flavors" }), options.map((option) => (_jsxs("div", { className: "tw-flex tw-items-center tw-p-2 hover:tw-bg-green-500", children: [
|
|
46
|
+
_jsx(Icon, { type: "color", color: "green", icon: "fc-check", extraClassNames: "tw-mr-2" }), _jsx("span", { children: option.label })
|
|
47
|
+
] }, option.value))), _jsx("p", { className: "tw-bg-gray-200 dark:tw-bg-gray-700 tw-p-2", children: "Colors" }), colorOptions.map((option) => (_jsxs("div", { className: "tw-flex tw-items-center tw-p-2", children: [
|
|
48
|
+
_jsx(Icon, { type: "color", color: "green", icon: "fc-check", extraClassNames: "tw-mr-2" }), _jsx("span", { children: option.label })
|
|
49
|
+
] }, option.value)))] }) }) }), _jsx("div", { className: "tw-p-5 ", children: _jsx(ToolbarButton, { id: "scatterPlotFlipXY", isActive: false, disabled: true, testId: "scatterPlotFlipXY", label: "", icon: "fc-grab-window", tooltipText: "This is a large disabled popover.", tooltipOptions: { position: 'top' }, extraClassNames: "no-border ml5" }) }), _jsx("div", { className: "tw-p-5 ", children: _jsx(ToolbarButton, { icon: "fc-y-axis", tooltipText: "This is a large popover icon", tooltipOptions: { position: 'top', delay: 0 }, testId: "basic-popover4", isHtmlTooltip: false }) }), _jsx("div", { className: "tw-p-5 ", children: _jsx(ToolbarButton, { icon: "fc-y-axis", label: "Active", testId: "basic-popover5", tooltipText: "This is a button.", tooltipOptions: { position: 'top', delay: 0 }, isHtmlTooltip: false, isActive: true }) }), _jsx("div", { className: "tw-p-5 ", children: _jsx(ToolbarButton, { isPrimaryAnSvg: true, iconHeight: 13, iconWidth: 13, icon: "svgpath:M 19 2.76 L 18.016 4.066 L 7.678 17.765 L 6.745 19 L 5.812 17.765 L 0.984 11.373 L 0 10.067 L 1.867 7.307 L 2.851 8.613 L 6.745 13.771 L 16.149 1.306 L 17.133 0 L 19 2.76 Z", label: "Active", testId: "basic-popover5", tooltipText: "This is a button.", tooltipOptions: { position: 'top', delay: 0 }, isHtmlTooltip: false, isActive: true }) })
|
|
50
|
+
] }));
|
|
51
|
+
const renderAllVariations = () => (_jsxs("div", { className: "tw-grid tw-grid-cols-2 tw-gap-4 tw-p-4", children: [allToolbarButtons(), allToolbarButtons(true)] }));
|
|
52
|
+
return (_jsxs("div", { children: [
|
|
53
|
+
_jsxs("div", { className: "tw-grid tw-grid-cols-2 tw-gap-4", children: [
|
|
54
|
+
_jsx(QTip, {}), _jsxs("div", { className: "color_topic", children: [
|
|
55
|
+
_jsx("b", { children: "Topic Colors" }), renderAllVariations()] }), _jsxs("div", { className: "color_analysis", children: [
|
|
56
|
+
_jsx("b", { children: "Analysis Colors" }), renderAllVariations()] })
|
|
57
|
+
] }), _jsxs("div", { className: "tw-grid tw-grid-cols-2 tw-gap-4", children: [
|
|
58
|
+
_jsxs("div", { className: "color_datalab", children: [
|
|
59
|
+
_jsx("b", { children: "Datalab Colors" }), renderAllVariations()] }), _jsxs("div", { className: "color_vantage", children: [
|
|
60
|
+
_jsx("b", { children: "Vantage Colors" }), renderAllVariations()] })
|
|
61
|
+
] })
|
|
62
|
+
] }));
|
|
63
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import '@testing-library/jest-dom';
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import { ToolbarButton } from './ToolbarButton';
|
|
6
|
+
import Icon from '../Icon';
|
|
7
|
+
describe('ToolbarButton', () => {
|
|
8
|
+
const options = [
|
|
9
|
+
{ value: 'a', label: 'Chocolate' },
|
|
10
|
+
{ value: 'b', label: 'Strawberry' },
|
|
11
|
+
{ value: 'c', label: 'Vanilla' },
|
|
12
|
+
{ value: 'd', label: 'Rocky Road' },
|
|
13
|
+
{ value: 'e', label: 'Crazy Cow' },
|
|
14
|
+
{ value: 'f', label: 'Almond Joy' },
|
|
15
|
+
{ value: 'g', label: 'All of the above' },
|
|
16
|
+
];
|
|
17
|
+
const colorOptions = [
|
|
18
|
+
{ value: 'h', label: 'pink' },
|
|
19
|
+
{ value: 'i', label: 'purple' },
|
|
20
|
+
{ value: 'j', label: 'green' },
|
|
21
|
+
{ value: 'k', label: 'red' },
|
|
22
|
+
];
|
|
23
|
+
const popoverContent = (_jsxs("div", { className: "tw-text-sm", children: [
|
|
24
|
+
_jsx("p", { className: "tw-bg-gray-200 tw-p-2", children: "Flavors" }), options.map((option) => (_jsxs("div", { className: "tw-flex tw-items-center tw-p-2 hover:tw-bg-green-500", children: [
|
|
25
|
+
_jsx(Icon, { type: "color", color: "green", icon: "fc-check", extraClassNames: "tw-mr-2" }), _jsx("span", { children: option.label })
|
|
26
|
+
] }, option.value))), _jsx("p", { children: "Colors" }), colorOptions.map((option) => (_jsxs("div", { className: "tw-flex tw-items-center tw-p-2", children: [
|
|
27
|
+
_jsx(Icon, { type: "color", color: "green", icon: "fc-check", extraClassNames: "tw-mr-2" }), _jsx("span", { children: option.label })
|
|
28
|
+
] }, option.value)))] }));
|
|
29
|
+
const mockOnClick = jest.fn();
|
|
30
|
+
class Context {
|
|
31
|
+
testId = 'popoverTestId';
|
|
32
|
+
label = 'popover label';
|
|
33
|
+
props = {
|
|
34
|
+
label: this.label,
|
|
35
|
+
onClick: mockOnClick,
|
|
36
|
+
testId: this.testId,
|
|
37
|
+
icon: 'fc-y-axis',
|
|
38
|
+
tooltipText: 'This is a popover.',
|
|
39
|
+
tooltipOptions: { position: 'top', delay: 0 },
|
|
40
|
+
isHtmlTooltip: false,
|
|
41
|
+
isSmall: false,
|
|
42
|
+
popoverContent,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
let tc;
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
tc = new Context();
|
|
48
|
+
jest.clearAllMocks();
|
|
49
|
+
});
|
|
50
|
+
const renderToolbarButton = (props) => render(_jsx(ToolbarButton, { ...props }));
|
|
51
|
+
const openPopover = async () => {
|
|
52
|
+
const popover = screen.getByTestId(tc.testId);
|
|
53
|
+
await userEvent.click(popover);
|
|
54
|
+
};
|
|
55
|
+
it('renders popover icon without label when small', () => {
|
|
56
|
+
tc.props.isSmall = true;
|
|
57
|
+
renderToolbarButton(tc.props);
|
|
58
|
+
const popoverLabel = screen.queryByText(tc.label);
|
|
59
|
+
expect(popoverLabel).not.toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
it('renders popover trigger button', () => {
|
|
62
|
+
renderToolbarButton(tc.props);
|
|
63
|
+
expect(screen.getByText(tc.label)).toBeInTheDocument();
|
|
64
|
+
});
|
|
65
|
+
it('opens popover on click', async () => {
|
|
66
|
+
renderToolbarButton(tc.props);
|
|
67
|
+
await openPopover();
|
|
68
|
+
expect(screen.getByText(options[2].label)).toBeVisible();
|
|
69
|
+
});
|
|
70
|
+
it('renders disabled popover trigger', async () => {
|
|
71
|
+
renderToolbarButton({ ...tc.props, disabled: true });
|
|
72
|
+
await openPopover();
|
|
73
|
+
expect(mockOnClick).not.toHaveBeenCalled();
|
|
74
|
+
});
|
|
75
|
+
it('handles click events', async () => {
|
|
76
|
+
const handleClick = jest.fn();
|
|
77
|
+
tc.props.onClick = handleClick;
|
|
78
|
+
const { getByTestId } = renderToolbarButton(tc.props);
|
|
79
|
+
await userEvent.click(getByTestId(tc.testId));
|
|
80
|
+
expect(handleClick).toHaveBeenCalled();
|
|
81
|
+
});
|
|
82
|
+
it('handles click events within popover and does not close the popover', async () => {
|
|
83
|
+
renderToolbarButton(tc.props);
|
|
84
|
+
expect(screen.queryByText(options[4].label)).not.toBeInTheDocument();
|
|
85
|
+
await openPopover();
|
|
86
|
+
await userEvent.click(screen.getByText(options[3].label));
|
|
87
|
+
expect(screen.getByText(options[4].label)).toBeInTheDocument();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { MouseEvent } from 'react';
|
|
2
2
|
import { TooltipProps } from '../Tooltip/Tooltip.types';
|
|
3
3
|
export declare const toolbarButtonVariants: readonly ["outline", "theme", "theme-light", "warning", "danger", "no-border"];
|
|
4
|
-
export type ToolbarButtonVariant =
|
|
4
|
+
export type ToolbarButtonVariant = typeof toolbarButtonVariants[number];
|
|
5
5
|
/**
|
|
6
6
|
* Props for the ToolbarButton component that creates toolbar-style buttons with optional popovers.
|
|
7
7
|
* Designed for use in toolbars, action bars, and similar UI contexts.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const toolbarButtonVariants = ['outline', 'theme', 'theme-light', 'warning', 'danger', 'no-border'];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ToolbarButton as default } from './ToolbarButton';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Button from '../Button';
|
|
3
|
+
import { tooltipPositions } from './Tooltip.types';
|
|
4
|
+
import { QTip } from './Qtip';
|
|
5
|
+
import Icon from '../Icon';
|
|
6
|
+
export default {
|
|
7
|
+
title: 'Tooltip',
|
|
8
|
+
};
|
|
9
|
+
export const AllQTips = () => {
|
|
10
|
+
const renderSvgWithTooltip = () => tooltipPositions.map((position) => (_jsx("div", { className: "tw-w-full tw-flex tw-justify-center tw-items-center", children: _jsx("div", { "data-qtip-text": "this is a test tooltip", "data-qtip-placement": position, children: _jsx("svg", { viewBox: "0 0 19 19", xmlns: "http://www.w3.org/2000/svg", className: "seeq-icon p2", height: "19", width: "19", children: _jsx("path", { d: "M 2.714 1.188 L 2.714 0 L 0 0 L 0 1.188 L 0 2.375 L 0 13.656 L 0 17.813 L 0 19 L 2.714 19 L 2.714 17.813 L 2.714 13.063 L 5.441 12.465 C 7.184 12.083 9.029 12.261 10.637 12.962 C 12.511 13.782 14.687 13.883 16.646 13.237 L 19 12.469 L 19 0.594 L 16.693 1.603 C 14.729 2.464 12.418 2.464 10.454 1.603 C 8.966 0.95 7.256 0.787 5.641 1.139 L 2.714 1.781 L 2.714 1.188 Z", fill: "red" }) }) }, `${position}_svg`) })));
|
|
11
|
+
const renderButtonsWithTooltip = () => tooltipPositions.map((position) => (_jsx("div", { children: _jsx(Button, { tooltip: `Tooltip on the ${position}`, label: position, tooltipOptions: { position } }) }, `${position}_button`)));
|
|
12
|
+
const renderIconsWithHtmlTooltip = () => tooltipPositions.map((position) => (_jsx("div", { children: _jsx(Icon, { tooltip: "<div><h2>Fancy Tooltip</h2> This is a special tooltip. Why? <br />Because it supports <b>HTML!</b></div>", tooltipPlacement: position, icon: "fc-sun", isHtmlTooltip: true }) }, `${position}_icon`)));
|
|
13
|
+
const renderTextTooltipOnText = () => tooltipPositions.map((position) => (_jsx("div", { children: _jsxs("span", { "data-qtip-text": "Helpful information provided here", "data-qtip-placement": position, children: ["Hover for Tooltip (on the ", position, ")"] }) }, `${position}_text`)));
|
|
14
|
+
const renderTextTooltipOnTextWithDelay = () => tooltipPositions.map((position) => (_jsx("div", { children: _jsxs("span", { "data-qtip-text": "Helpful information provided here", "data-qtip-placement": position, "data-qtip-delay": 1500, children: ["Hover for Tooltip (on the ", position, ") - with delay (1500ms)"] }) }, `${position}_text`)));
|
|
15
|
+
const renderTextTooltipOnOverflowHiddenText = () => tooltipPositions.map((position) => (_jsx("div", { className: "tw-overflow-hidden", children: _jsxs("span", { "data-qtip-text": "Helpful information provided here", "data-qtip-placement": position, children: ["Hover for Tooltip (on the ", position, ")",
|
|
16
|
+
_jsx("br", {}),
|
|
17
|
+
"(Overflow set to hidden)"] }) }, `${position}_text`)));
|
|
18
|
+
const renderTextTooltipToRealignAtScreenEdge = () => tooltipPositions.map((position) => (_jsx("div", { className: "tw-overflow-hidden", children: _jsx("span", { "data-qtip-text": "This tooltip will realign its arrow and position if the data to be displayed within it goes beyond the screen edge. The text after this will be dummy text just to show a lot of text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", "data-qtip-placement": position, children: "Hover for Tooltip (Should avoid screen edges)" }) }, `${position}_text`)));
|
|
19
|
+
return (_jsxs(_Fragment, { children: [
|
|
20
|
+
_jsx(QTip, {}), _jsxs("div", { className: "tw-grid tw-grid-cols-4 tw-gap-4 tw-text-center", children: [renderSvgWithTooltip(), renderButtonsWithTooltip(), renderIconsWithHtmlTooltip(), renderTextTooltipOnText(), renderTextTooltipOnTextWithDelay(), renderTextTooltipOnOverflowHiddenText(), renderTextTooltipToRealignAtScreenEdge()] })
|
|
21
|
+
] }));
|
|
22
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { QTip } from './Qtip';
|
|
3
|
+
import Icon from '../Icon';
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Tooltip',
|
|
6
|
+
};
|
|
7
|
+
const renderManyQTips = () => {
|
|
8
|
+
const colsCount = 100;
|
|
9
|
+
const rowCount = 100;
|
|
10
|
+
const rows = [];
|
|
11
|
+
const getCols = (rowId) => {
|
|
12
|
+
const cols = [];
|
|
13
|
+
for (let i = 0; i < colsCount; i++) {
|
|
14
|
+
cols.push(_jsx("td", { children: _jsx(Icon, { icon: "fc-sun", tooltip: `hello there ${i}` }) }, 'col_' + i + '_row_' + rowId));
|
|
15
|
+
}
|
|
16
|
+
return cols;
|
|
17
|
+
};
|
|
18
|
+
for (let i = 0; i < rowCount; i++) {
|
|
19
|
+
rows.push(_jsxs("tr", { children: [...getCols(i)] }, 'row_' + i));
|
|
20
|
+
}
|
|
21
|
+
return (_jsx("table", { children: _jsx("tbody", { children: rows.map((row) => row) }) }));
|
|
22
|
+
};
|
|
23
|
+
export const QtipPerformance = () => {
|
|
24
|
+
return (_jsxs(_Fragment, { children: [
|
|
25
|
+
_jsx(QTip, {}), _jsx("div", { className: "tw-grid tw-grid-cols-4 tw-gap-4 tw-text-center", children: renderManyQTips() })
|
|
26
|
+
] }));
|
|
27
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import DOMPurify from 'dompurify';
|
|
3
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { arrow, computePosition, offset, flip, shift } from '@floating-ui/dom';
|
|
5
|
+
import { useIntervalWhen } from 'rooks';
|
|
6
|
+
import { DEFAULT_TOOL_TIP_DELAY } from './Tooltip.types';
|
|
7
|
+
const getArrowStyle = (position, x, arrowWidth, tooltipHeight, tooltipWidth) => {
|
|
8
|
+
switch (position) {
|
|
9
|
+
case 'bottom':
|
|
10
|
+
return {
|
|
11
|
+
left: `${x}px`,
|
|
12
|
+
top: `${-arrowWidth}px`,
|
|
13
|
+
};
|
|
14
|
+
case 'left':
|
|
15
|
+
return {
|
|
16
|
+
left: `${tooltipWidth - arrowWidth}px`,
|
|
17
|
+
top: `${tooltipHeight / 2 - arrowWidth}px`,
|
|
18
|
+
};
|
|
19
|
+
case 'right':
|
|
20
|
+
return {
|
|
21
|
+
left: `${-arrowWidth}px`,
|
|
22
|
+
top: `${tooltipHeight / 2 - arrowWidth}px`,
|
|
23
|
+
};
|
|
24
|
+
default: // 'top':
|
|
25
|
+
return {
|
|
26
|
+
left: `${x}px`,
|
|
27
|
+
top: `${tooltipHeight - 10 + arrowWidth}px`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const HTMLTip = ({ text }) => {
|
|
32
|
+
return React.createElement('div', {
|
|
33
|
+
dangerouslySetInnerHTML: { __html: DOMPurify.sanitize(text) },
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
/** Tooltips can cause SystemTests to be flaky, so we suppress them in Playwright and Protractor */
|
|
37
|
+
const suppressTooltip = () => {
|
|
38
|
+
return window.navigator.userAgent === 'Playwright' || window.navigator.userAgent.includes('Protractor');
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* QTip
|
|
42
|
+
*
|
|
43
|
+
* QTip is a Singleton Tooltip component that guarantees high-performance and reduces component wrappers!
|
|
44
|
+
*
|
|
45
|
+
* QTip is used by all qomponents that support the display of tooltips.
|
|
46
|
+
* If you want to add a Tooltip to your application you can do so by adding the following html data-attributes:
|
|
47
|
+
*
|
|
48
|
+
* 'data-qtip-text': the tooltip text to display; this can also be a string containing valid HTML
|
|
49
|
+
* 'data-qtip-placement': one of TooltipPosition (top, bottom, right, or left)
|
|
50
|
+
* 'data-qtip-is-html': set this to true if you provided a text that contains HTML,
|
|
51
|
+
* 'data-qtip-delay': this can be used to delay the showing of the tooltip. this should be a number representing
|
|
52
|
+
* the # of milliseconds you want to delay the tooltip for. If no delay is provided the DEFAULT_TOOL_TIP_DELAY is
|
|
53
|
+
* applied!
|
|
54
|
+
* 'data-qtip-testid': use this attribute to provide a value for a data-testid of your tooltip; this is useful
|
|
55
|
+
* for tests
|
|
56
|
+
*
|
|
57
|
+
* In order for QTip to be able to display Tooltips you must add the QTip component to your top-most component
|
|
58
|
+
* (often that's App or Application) - simply add:
|
|
59
|
+
*
|
|
60
|
+
* <QTip />
|
|
61
|
+
*
|
|
62
|
+
* and enjoy beautiful & performant tooltips!
|
|
63
|
+
*/
|
|
64
|
+
export const QTip = () => {
|
|
65
|
+
const tooltipRef = useRef(null);
|
|
66
|
+
const tooltipTarget = useRef(null);
|
|
67
|
+
const tooltipArrowRef = useRef(null);
|
|
68
|
+
const [tooltipText, setTooltipText] = useState('');
|
|
69
|
+
const [tooltipTargetOriginalPosition, setTooltipTargetOriginalPosition] = useState(null);
|
|
70
|
+
const [show, setShow] = useState(false);
|
|
71
|
+
const [html, setHtml] = useState(false);
|
|
72
|
+
const [tooltipTestId, setTooltipTestId] = useState('');
|
|
73
|
+
const [overTooltip, setOverTooltip] = useState(false);
|
|
74
|
+
// Check every 300ms if the tooltip target position has changed and hide the tooltip if this happens.
|
|
75
|
+
useIntervalWhen(() => {
|
|
76
|
+
const currentPosition = tooltipTarget?.current?.getBoundingClientRect();
|
|
77
|
+
if (overTooltip) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (tooltipTargetOriginalPosition?.y !== currentPosition?.y ||
|
|
81
|
+
tooltipTargetOriginalPosition?.x !== currentPosition?.x) {
|
|
82
|
+
setShow(false);
|
|
83
|
+
}
|
|
84
|
+
}, 300, show);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
!suppressTooltip() && document.body.addEventListener('mousemove', onMouseMove);
|
|
87
|
+
return () => {
|
|
88
|
+
!suppressTooltip() && document.removeEventListener('mousemove', onMouseMove);
|
|
89
|
+
};
|
|
90
|
+
}, []);
|
|
91
|
+
const ttTimeout = useRef(null);
|
|
92
|
+
const findTooltipData = (element) => {
|
|
93
|
+
// Traverse up to 8 levels to find the tooltip data
|
|
94
|
+
for (let i = 0; i < 8 && element; i++) {
|
|
95
|
+
if (!element) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
if (element?.dataset?.qtipText) {
|
|
99
|
+
return element.dataset;
|
|
100
|
+
}
|
|
101
|
+
element = element?.parentElement;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
};
|
|
105
|
+
const onMouseMove = (e) => {
|
|
106
|
+
if (ttTimeout.current) {
|
|
107
|
+
clearTimeout(ttTimeout.current);
|
|
108
|
+
}
|
|
109
|
+
// Find tooltip data on the current or parent elements
|
|
110
|
+
const target = e.target;
|
|
111
|
+
const dataset = findTooltipData(target);
|
|
112
|
+
if (!dataset) {
|
|
113
|
+
// No tooltip data found
|
|
114
|
+
tooltipTarget.current = null;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
tooltipTarget.current = target;
|
|
118
|
+
const text = dataset.qtipText;
|
|
119
|
+
if (text) {
|
|
120
|
+
const delay = parseInt(dataset.qtipDelay || `${DEFAULT_TOOL_TIP_DELAY}`, 10);
|
|
121
|
+
const placement = dataset.qtipPlacement || 'top';
|
|
122
|
+
const isHtml = dataset.qtipIsHtml === 'true';
|
|
123
|
+
const testId = dataset.qtipTestid;
|
|
124
|
+
// Debounced tooltip display
|
|
125
|
+
ttTimeout.current = setTimeout(() => {
|
|
126
|
+
makeTooltip(text, placement, isHtml, testId, delay);
|
|
127
|
+
}, delay);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const makeTooltip = (tooltipText, position = 'top', isHtml, dataTestId, delay) => {
|
|
131
|
+
if (tooltipText && tooltipTarget.current) {
|
|
132
|
+
setHtml(isHtml);
|
|
133
|
+
setTooltipText(tooltipText);
|
|
134
|
+
setTooltipTestId(dataTestId);
|
|
135
|
+
setTooltipTargetOriginalPosition(tooltipTarget.current.getBoundingClientRect());
|
|
136
|
+
const positionTooltip = () => {
|
|
137
|
+
if (tooltipRef.current && tooltipTarget.current) {
|
|
138
|
+
computePosition(tooltipTarget.current, tooltipRef.current, {
|
|
139
|
+
placement: position,
|
|
140
|
+
middleware: [offset(10), flip(), shift({ padding: 8 }), arrow({ element: tooltipArrowRef.current })],
|
|
141
|
+
}).then(({ x, y, middlewareData, placement: finalPlacement }) => {
|
|
142
|
+
Object.assign(tooltipRef.current?.style, {
|
|
143
|
+
left: `${x}px`,
|
|
144
|
+
top: `${y}px`,
|
|
145
|
+
});
|
|
146
|
+
if (middlewareData.arrow) {
|
|
147
|
+
const { x, y } = middlewareData.arrow;
|
|
148
|
+
const arrowWidth = tooltipArrowRef.current?.offsetHeight / 2;
|
|
149
|
+
const tooltipHeight = tooltipRef.current?.offsetHeight;
|
|
150
|
+
const tooltipWidth = tooltipRef.current?.offsetWidth;
|
|
151
|
+
const style = getArrowStyle(finalPlacement, x || y, arrowWidth, tooltipHeight, tooltipWidth);
|
|
152
|
+
Object.assign(tooltipArrowRef.current.style, style);
|
|
153
|
+
}
|
|
154
|
+
setShow(true);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
delay > 0
|
|
159
|
+
? requestAnimationFrame(() => {
|
|
160
|
+
positionTooltip();
|
|
161
|
+
})
|
|
162
|
+
: positionTooltip();
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
return (_jsx(_Fragment, { children: _jsxs("div", { onMouseEnter: () => setOverTooltip(true), onMouseLeave: () => setOverTooltip(false), "data-testid": tooltipTestId, ref: tooltipRef, className: 'tw-absolute tw-rounded tw-bg-black tw-max-w-96 tw-p-2 tw-text-xs tw-text-white tw-z-[9999] fade-in ' +
|
|
166
|
+
(show ? 'tw-visible' : 'tw-invisible tw-pointer-events-none'), children: [html ? _jsx(HTMLTip, { text: tooltipText }) : tooltipText, _jsx("div", { className: "tw-absolute tw-w-[10px] tw-h-[10px] tw-rotate-45 tw-bg-black", ref: tooltipArrowRef })
|
|
167
|
+
] }) }));
|
|
168
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import '../styles.css';
|
|
3
|
+
import { DEFAULT_TOOL_TIP_DELAY } from './Tooltip.types';
|
|
4
|
+
/**
|
|
5
|
+
* @deprecated
|
|
6
|
+
* Note: Tooltip has been replaced by QTip - a singleton JS tooltip that behaves well even with overflow settings :)
|
|
7
|
+
* See @QTip for more info!
|
|
8
|
+
*
|
|
9
|
+
* This component displays a Tooltip for the provided children. It is a CSS only tooltip that will not display
|
|
10
|
+
* correctly if a parent element has an overflow CSS property assigned. Please use QTip to ensure your tooltips
|
|
11
|
+
* display correctly.
|
|
12
|
+
*/
|
|
13
|
+
export const Tooltip = ({ position = 'bottom', children, text, delay = DEFAULT_TOOL_TIP_DELAY, }) => {
|
|
14
|
+
const arrowBaseClasses = "before:tw-content-[''] before:tw-absolute before:tw-border-8";
|
|
15
|
+
const centerArrowVertically = 'before:tw-top-1/2 before:-tw-translate-y-1/2';
|
|
16
|
+
const centerArrowHorizontally = 'before:tw-left-1/2 before:-tw-translate-x-1/2';
|
|
17
|
+
const arrowRight = `${arrowBaseClasses} ${centerArrowVertically} before:tw-right-[100%] before:tw-border-y-transparent
|
|
18
|
+
before:tw-border-l-transparent before:tw-border-r-black`;
|
|
19
|
+
const arrowLeft = `${arrowBaseClasses} ${centerArrowVertically} before:tw-left-[100%] before:tw-border-y-transparent
|
|
20
|
+
before:tw-border-l-black before:tw-border-r-transparent`;
|
|
21
|
+
const arrowBottom = `${arrowBaseClasses} ${centerArrowHorizontally} before:-tw-top-4 before:tw-border-b-black
|
|
22
|
+
before:tw-border-r-transparent before:tw-border-l-transparent before:tw-border-t-transparent`;
|
|
23
|
+
const arrowTop = `${arrowBaseClasses} ${centerArrowHorizontally} before:-tw-bottom-4 before:tw-border-b-transparent
|
|
24
|
+
before:tw-border-t-black before:tw-border-l-transparent before:tw-border-r-transparent`;
|
|
25
|
+
const placements = {
|
|
26
|
+
top: `-tw-top-2 -tw-translate-y-full tw-left-1/2 -tw-translate-x-1/2 ${arrowTop}`,
|
|
27
|
+
left: `-tw-translate-x-full -tw-left-3 -tw-translate-y-1/2 tw-top-1/2 ${arrowLeft}`,
|
|
28
|
+
right: `tw-translate-x-full -tw-right-3 -tw-translate-y-1/2 tw-top-1/2 ${arrowRight}`,
|
|
29
|
+
bottom: `-tw-bottom-2 tw-translate-y-full tw-left-1/2 -tw-translate-x-1/2 ${arrowBottom}`,
|
|
30
|
+
};
|
|
31
|
+
return (_jsxs("div", { className: "tw-group tw-relative tw-inline-block", children: [children, _jsx("div", { className: `tw-z-50 tw-whitespace-nowrap tw-hidden group-hover:tw-inline-block group-hover:tw-delay-[${delay}ms]
|
|
32
|
+
tw-absolute tw-opacity-0 group-hover:tw-opacity-100 tw-rounded tw-bg-black tw-p-2 tw-text-xs tw-text-white ${placements[position]}`, children: text })
|
|
33
|
+
] }));
|
|
34
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Tooltip } from './Tooltip';
|
|
3
|
+
import Icon from '../Icon';
|
|
4
|
+
import Button from '../Button';
|
|
5
|
+
import { tooltipPositions } from './Tooltip.types';
|
|
6
|
+
export default {
|
|
7
|
+
title: 'Tooltip',
|
|
8
|
+
};
|
|
9
|
+
export const AllTooltips = () => {
|
|
10
|
+
const renderButtonsWithTooltip = () => tooltipPositions.map((position) => (_jsx("div", { children: _jsx(Tooltip, { text: `Tooltip on the ${position}`, position: position, children: _jsx(Button, { label: position }) }) }, `${position}_button`)));
|
|
11
|
+
const renderIconsWithHtmlTooltip = () => tooltipPositions.map((position) => (_jsx("div", { children: _jsx(Tooltip, { text: _jsxs("div", { children: [
|
|
12
|
+
_jsx("h2", { children: "Fancy Tooltip" }),
|
|
13
|
+
" This is a special tooltip. Why?",
|
|
14
|
+
_jsx("br", {}),
|
|
15
|
+
"Because it supports ",
|
|
16
|
+
_jsx("b", { children: "HTML!" })
|
|
17
|
+
] }), position: position, children: _jsx(Icon, { icon: "fc-sun" }) }) }, `${position}_icon`)));
|
|
18
|
+
const renderTextTooltipOnText = () => tooltipPositions.map((position) => (_jsx("div", { children: _jsx(Tooltip, { text: "Helpful information provided here", position: position, children: _jsxs("span", { children: ["Hover for Tooltip (on the ", position, ")"] }) }) }, `${position}_text`)));
|
|
19
|
+
return (_jsxs("div", { className: "tw-grid tw-grid-cols-4 tw-gap-4 tw-text-center", children: [renderButtonsWithTooltip(), renderIconsWithHtmlTooltip(), renderTextTooltipOnText()] }));
|
|
20
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const DEFAULT_TOOL_TIP_DELAY = 500;
|
|
2
2
|
export declare const tooltipPositions: readonly ["top", "left", "right", "bottom"];
|
|
3
3
|
import React from 'react';
|
|
4
|
-
export type TooltipPosition =
|
|
4
|
+
export type TooltipPosition = typeof tooltipPositions[number];
|
|
5
5
|
export interface TooltipProps {
|
|
6
6
|
position?: TooltipPosition;
|
|
7
7
|
children?: React.JSX.Element | string;
|