@tpzdsp/next-toolkit 1.14.3 → 1.15.1
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/package.json +2 -1
- package/src/assets/styles/globals.css +5 -1
- package/src/components/ErrorBoundary/ErrorBoundary.stories.tsx +1 -1
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +1 -1
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
- package/src/components/InfoBox/InfoBox.tsx +2 -2
- package/src/components/accordion/Accordion.tsx +29 -14
- package/src/components/divider/RuleDivider.test.tsx +4 -4
- package/src/components/form/Input.test.tsx +3 -11
- package/src/components/form/Input.tsx +2 -2
- package/src/components/form/TextArea.test.tsx +3 -5
- package/src/components/form/TextArea.tsx +2 -2
- package/src/components/layout/header/Header.stories.tsx +3 -3
- package/src/components/layout/header/Header.test.tsx +3 -3
- package/src/components/layout/header/HeaderNavClient.test.tsx +3 -3
- package/src/components/select/Select.stories.tsx +5 -5
- package/src/components/select/Select.test.tsx +2 -2
- package/src/components/select/Select.tsx +3 -4
- package/src/components/select/SelectSkeleton.test.tsx +1 -2
- package/src/components/select/SelectSkeleton.tsx +3 -3
- package/src/components/select/common.ts +2 -3
- package/src/http/constants.ts +1 -0
- package/src/http/logger.test.ts +346 -0
- package/src/http/logger.ts +412 -76
- package/src/map/MapComponent.tsx +31 -7
- package/src/map/Popup.tsx +6 -3
- package/src/map/useKeyboardDrawing.ts +8 -4
- package/src/utils/constants.ts +8 -0
- package/src/utils/utils.ts +4 -4
- package/src/components/theme/ThemeProvider.tsx +0 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tpzdsp/next-toolkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"description": "A reusable React component library for Next.js applications",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">= 24.12.0",
|
|
@@ -119,6 +119,7 @@
|
|
|
119
119
|
"preview": "vite preview",
|
|
120
120
|
"dev:publish": "yalc publish",
|
|
121
121
|
"dev:push": "yalc push",
|
|
122
|
+
"tsc:check": "tsc -b --noEmit",
|
|
122
123
|
"storybook": "storybook dev -p 6006",
|
|
123
124
|
"release": "semantic-release"
|
|
124
125
|
},
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
/* Component-specific styles */
|
|
46
46
|
@layer components {
|
|
47
47
|
.focus-yellow {
|
|
48
|
-
@apply focus:border-
|
|
48
|
+
@apply focus:border-focus focus:outline focus:outline-2 focus:outline-focus;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
.library-button {
|
|
@@ -83,6 +83,10 @@
|
|
|
83
83
|
top: calc(calc(var(--border) + var(--shadow)) * -1);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
.input-height {
|
|
88
|
+
@apply h-[38px];
|
|
89
|
+
}
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
/* Utilities */
|
|
@@ -14,7 +14,7 @@ const CustomFallback = ({ error, resetErrorBoundary }: FallbackProps) => (
|
|
|
14
14
|
<div role="alert" className="p-4 border border-red-300 rounded-lg bg-red-50">
|
|
15
15
|
<p className="font-medium text-red-700">Custom Fallback:</p>
|
|
16
16
|
|
|
17
|
-
<p className="text-red-600">{error.message}</p>
|
|
17
|
+
<p className="text-red-600">{error instanceof Error ? error.message : 'Unknown'}</p>
|
|
18
18
|
|
|
19
19
|
<button
|
|
20
20
|
className="px-3 py-1 mt-2 text-white bg-red-600 rounded hover:bg-red-700"
|
|
@@ -12,7 +12,7 @@ const Bomb = () => {
|
|
|
12
12
|
// Custom fallback with button for reset
|
|
13
13
|
const CustomFallback = ({ error, resetErrorBoundary }: FallbackProps) => (
|
|
14
14
|
<div role="alert">
|
|
15
|
-
<p>Custom fallback: {error.message}</p>
|
|
15
|
+
<p>Custom fallback: {error instanceof Error ? error.message : 'Unknown'}</p>
|
|
16
16
|
|
|
17
17
|
<button onClick={resetErrorBoundary}>Try again</button>
|
|
18
18
|
</div>
|
|
@@ -12,7 +12,7 @@ type ErrorBoundaryProps = {
|
|
|
12
12
|
|
|
13
13
|
export const ErrorBoundary = ({ children, fallback, onReset }: ErrorBoundaryProps) => {
|
|
14
14
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
15
|
-
const logError = (
|
|
15
|
+
const logError = (_error: unknown, _info: ErrorInfo) => {
|
|
16
16
|
// send error to third party api, etc
|
|
17
17
|
};
|
|
18
18
|
|
|
@@ -98,8 +98,8 @@ export const InfoBox = ({
|
|
|
98
98
|
const iconClasses = cn(
|
|
99
99
|
// Icon size
|
|
100
100
|
'w-5 h-5',
|
|
101
|
-
// Icon color - yellow when open,
|
|
102
|
-
isOpen ? 'text-focus' : '
|
|
101
|
+
// Icon color - yellow when open, auto when closed
|
|
102
|
+
isOpen ? 'text-focus' : '',
|
|
103
103
|
// Hover state - yellow
|
|
104
104
|
'hover:text-focus',
|
|
105
105
|
// Focus state - yellow
|
|
@@ -11,6 +11,7 @@ type Props = {
|
|
|
11
11
|
title: string;
|
|
12
12
|
children: ReactNode;
|
|
13
13
|
defaultOpen?: boolean;
|
|
14
|
+
disabled?: boolean;
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
export type AccordionProps = ExtendProps<'div', Props>;
|
|
@@ -19,6 +20,7 @@ export const Accordion = ({
|
|
|
19
20
|
title,
|
|
20
21
|
children,
|
|
21
22
|
defaultOpen = false,
|
|
23
|
+
disabled = false,
|
|
22
24
|
className,
|
|
23
25
|
...props
|
|
24
26
|
}: AccordionProps) => {
|
|
@@ -28,14 +30,25 @@ export const Accordion = ({
|
|
|
28
30
|
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
29
31
|
|
|
30
32
|
return (
|
|
31
|
-
<div
|
|
33
|
+
<div
|
|
34
|
+
className={cn(
|
|
35
|
+
'flex flex-col border-l-2 border-neutral-100',
|
|
36
|
+
disabled ? 'opacity-50' : '',
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
32
41
|
<button
|
|
33
|
-
aria-expanded={isOpen}
|
|
34
|
-
aria-controls={contentId}
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
aria-expanded={disabled ? undefined : isOpen}
|
|
43
|
+
aria-controls={disabled ? undefined : contentId}
|
|
44
|
+
disabled={disabled}
|
|
45
|
+
className={cn(
|
|
46
|
+
`flex justify-between items-center px-2 py-1 bg-[#fefefefe] text-[color:#000000]
|
|
47
|
+
border-y-2 border-neutral-100`,
|
|
48
|
+
disabled ? 'cursor-not-allowed' : 'focus-yellow',
|
|
49
|
+
)}
|
|
37
50
|
id={buttonId}
|
|
38
|
-
onClick={() => setIsOpen(!isOpen)}
|
|
51
|
+
onClick={disabled ? undefined : () => setIsOpen(!isOpen)}
|
|
39
52
|
type="button"
|
|
40
53
|
>
|
|
41
54
|
<span>{title}</span>
|
|
@@ -45,14 +58,16 @@ export const Accordion = ({
|
|
|
45
58
|
</span>
|
|
46
59
|
</button>
|
|
47
60
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
{!disabled ? (
|
|
62
|
+
<section
|
|
63
|
+
id={contentId}
|
|
64
|
+
aria-labelledby={buttonId}
|
|
65
|
+
aria-hidden={!isOpen}
|
|
66
|
+
className={cn('p-2 bg-[#efefef]', isOpen ? 'block' : 'hidden')}
|
|
67
|
+
>
|
|
68
|
+
{children}
|
|
69
|
+
</section>
|
|
70
|
+
) : null}
|
|
56
71
|
</div>
|
|
57
72
|
);
|
|
58
73
|
};
|
|
@@ -93,9 +93,9 @@ describe('RuleDivider', () => {
|
|
|
93
93
|
|
|
94
94
|
const [firstHr, span, secondHr] = containerElement.children;
|
|
95
95
|
|
|
96
|
-
expect(firstHr
|
|
97
|
-
expect(span
|
|
98
|
-
expect(secondHr
|
|
96
|
+
expect(firstHr?.tagName).toBe('HR');
|
|
97
|
+
expect(span?.tagName).toBe('SPAN');
|
|
98
|
+
expect(secondHr?.tagName).toBe('HR');
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
it('should have proper structure when no children provided', () => {
|
|
@@ -107,7 +107,7 @@ describe('RuleDivider', () => {
|
|
|
107
107
|
|
|
108
108
|
const hrElement = containerElement.children[0];
|
|
109
109
|
|
|
110
|
-
expect(hrElement
|
|
110
|
+
expect(hrElement?.tagName).toBe('HR');
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
it('should handle empty string as children', () => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Input } from './Input';
|
|
2
2
|
import { render, screen } from '../../test/renderers';
|
|
3
3
|
|
|
4
|
-
const ROUNDED_MD = 'rounded-md';
|
|
5
4
|
const BORDER_ERROR = 'border-error';
|
|
6
5
|
|
|
7
6
|
describe('Input', () => {
|
|
@@ -19,13 +18,7 @@ describe('Input', () => {
|
|
|
19
18
|
|
|
20
19
|
const input = screen.getByRole('textbox');
|
|
21
20
|
|
|
22
|
-
expect(input).toHaveClass(
|
|
23
|
-
ROUNDED_MD,
|
|
24
|
-
'border',
|
|
25
|
-
'p-1',
|
|
26
|
-
'disabled:opacity-60',
|
|
27
|
-
'disabled:bg-gray-100',
|
|
28
|
-
);
|
|
21
|
+
expect(input).toHaveClass('border', 'p-1', 'disabled:opacity-60', 'disabled:bg-gray-100');
|
|
29
22
|
});
|
|
30
23
|
|
|
31
24
|
it('applies error styling when hasError is true', () => {
|
|
@@ -57,7 +50,7 @@ describe('Input', () => {
|
|
|
57
50
|
|
|
58
51
|
const input = screen.getByRole('textbox');
|
|
59
52
|
|
|
60
|
-
expect(input).toHaveClass(
|
|
53
|
+
expect(input).toHaveClass('border', 'p-1', 'custom-class');
|
|
61
54
|
});
|
|
62
55
|
|
|
63
56
|
it('allows custom className to override default classes', () => {
|
|
@@ -105,7 +98,7 @@ describe('Input', () => {
|
|
|
105
98
|
|
|
106
99
|
const input = screen.getByRole('textbox');
|
|
107
100
|
|
|
108
|
-
expect(input).toHaveClass(BORDER_ERROR, 'w-full',
|
|
101
|
+
expect(input).toHaveClass(BORDER_ERROR, 'w-full', 'border', 'p-1');
|
|
109
102
|
});
|
|
110
103
|
|
|
111
104
|
it('handles different input types', () => {
|
|
@@ -174,7 +167,6 @@ describe('Input', () => {
|
|
|
174
167
|
|
|
175
168
|
// Custom classes should override defaults due to twMerge
|
|
176
169
|
expect(input).toHaveClass('border-green-500', 'p-4');
|
|
177
|
-
expect(input).toHaveClass(ROUNDED_MD); // Default class preserved
|
|
178
170
|
expect(input).toHaveClass('border'); // Base border class is still present
|
|
179
171
|
expect(input).not.toHaveClass('p-1'); // Overridden by p-4
|
|
180
172
|
expect(input).not.toHaveClass(BORDER_ERROR); // Overridden by border-green-500
|
|
@@ -14,8 +14,8 @@ export const Input = ({ hasError, className, ...props }: InputProps) => {
|
|
|
14
14
|
<input
|
|
15
15
|
{...props}
|
|
16
16
|
className={cn(
|
|
17
|
-
'
|
|
18
|
-
hasError ? 'border-error' : '',
|
|
17
|
+
'border p-1 disabled:opacity-60 disabled:bg-gray-100 input-height focus-yellow',
|
|
18
|
+
hasError ? 'border-error' : 'border-black',
|
|
19
19
|
className,
|
|
20
20
|
)}
|
|
21
21
|
/>
|
|
@@ -3,7 +3,6 @@ import { render, screen } from '@testing-library/react';
|
|
|
3
3
|
import { TextArea } from './TextArea';
|
|
4
4
|
|
|
5
5
|
// Constants for repeated class names
|
|
6
|
-
const ROUNDED_MD = 'rounded-md';
|
|
7
6
|
const BORDER = 'border';
|
|
8
7
|
const P_1 = 'p-1';
|
|
9
8
|
const DISABLED_OPACITY = 'disabled:opacity-60';
|
|
@@ -26,7 +25,7 @@ describe('TextArea', () => {
|
|
|
26
25
|
|
|
27
26
|
const textarea = screen.getByRole('textbox');
|
|
28
27
|
|
|
29
|
-
expect(textarea).toHaveClass(
|
|
28
|
+
expect(textarea).toHaveClass(BORDER, P_1, DISABLED_OPACITY, DISABLED_BG);
|
|
30
29
|
});
|
|
31
30
|
|
|
32
31
|
it('applies error styling when hasError is true', () => {
|
|
@@ -58,7 +57,7 @@ describe('TextArea', () => {
|
|
|
58
57
|
|
|
59
58
|
const textarea = screen.getByRole('textbox');
|
|
60
59
|
|
|
61
|
-
expect(textarea).toHaveClass(
|
|
60
|
+
expect(textarea).toHaveClass(BORDER, P_1, 'custom-class');
|
|
62
61
|
});
|
|
63
62
|
|
|
64
63
|
it('allows custom className to override default classes', () => {
|
|
@@ -109,7 +108,7 @@ describe('TextArea', () => {
|
|
|
109
108
|
|
|
110
109
|
const textarea = screen.getByRole('textbox');
|
|
111
110
|
|
|
112
|
-
expect(textarea).toHaveClass(BORDER_ERROR, 'w-full',
|
|
111
|
+
expect(textarea).toHaveClass(BORDER_ERROR, 'w-full', BORDER, P_1);
|
|
113
112
|
});
|
|
114
113
|
|
|
115
114
|
it('supports required attribute', () => {
|
|
@@ -143,7 +142,6 @@ describe('TextArea', () => {
|
|
|
143
142
|
|
|
144
143
|
// Custom classes should override defaults due to twMerge
|
|
145
144
|
expect(textarea).toHaveClass('border-green-500', 'p-4');
|
|
146
|
-
expect(textarea).toHaveClass(ROUNDED_MD); // Default class preserved
|
|
147
145
|
expect(textarea).toHaveClass(BORDER); // Base border class is still present
|
|
148
146
|
expect(textarea).not.toHaveClass(P_1); // Overridden by p-4
|
|
149
147
|
expect(textarea).not.toHaveClass(BORDER_ERROR); // Overridden by border-green-500
|
|
@@ -14,8 +14,8 @@ export const TextArea = ({ hasError, className, ...props }: TextAreaProps) => {
|
|
|
14
14
|
<textarea
|
|
15
15
|
{...props}
|
|
16
16
|
className={cn(
|
|
17
|
-
'
|
|
18
|
-
hasError ? 'border-error' : '',
|
|
17
|
+
'border p-1 disabled:opacity-60 disabled:bg-gray-100',
|
|
18
|
+
hasError ? 'border-error' : 'border-black',
|
|
19
19
|
className,
|
|
20
20
|
)}
|
|
21
21
|
/>
|
|
@@ -5,9 +5,9 @@ import type { Credentials } from '../../../types/auth';
|
|
|
5
5
|
import type { NavLink } from '../../../types/navigation';
|
|
6
6
|
|
|
7
7
|
const navLinks: NavLink[] = [
|
|
8
|
-
{ label: 'Home', url: '/',
|
|
9
|
-
{ label: 'API', url: '/api-docs',
|
|
10
|
-
{ label: 'Support', url: 'https://example.com/support',
|
|
8
|
+
{ label: 'Home', url: '/', openInNewTab: false },
|
|
9
|
+
{ label: 'API', url: '/api-docs', openInNewTab: false },
|
|
10
|
+
{ label: 'Support', url: 'https://example.com/support', openInNewTab: true },
|
|
11
11
|
];
|
|
12
12
|
|
|
13
13
|
const authenticatedCredentials: Credentials = {
|
|
@@ -3,9 +3,9 @@ import { render, screen } from '../../../test/renderers';
|
|
|
3
3
|
import type { NavLink } from '../../../types/navigation';
|
|
4
4
|
|
|
5
5
|
const NAV_LINKS: NavLink[] = [
|
|
6
|
-
{ label: 'Home', url: '/',
|
|
7
|
-
{ label: 'API', url: '/api-docs',
|
|
8
|
-
{ label: 'Support', url: 'https://example.com/support',
|
|
6
|
+
{ label: 'Home', url: '/', openInNewTab: false },
|
|
7
|
+
{ label: 'API', url: '/api-docs', openInNewTab: false },
|
|
8
|
+
{ label: 'Support', url: 'https://example.com/support', openInNewTab: true },
|
|
9
9
|
];
|
|
10
10
|
|
|
11
11
|
describe('Header', () => {
|
|
@@ -3,9 +3,9 @@ import { render, screen, userEvent } from '../../../test/renderers';
|
|
|
3
3
|
import type { NavLink } from '../../../types/navigation';
|
|
4
4
|
|
|
5
5
|
const NAV_LINKS: NavLink[] = [
|
|
6
|
-
{ label: 'Home', url: '/',
|
|
7
|
-
{ label: 'API', url: '/api-docs',
|
|
8
|
-
{ label: 'Support', url: 'https://example.com/support',
|
|
6
|
+
{ label: 'Home', url: '/', openInNewTab: false },
|
|
7
|
+
{ label: 'API', url: '/api-docs', openInNewTab: false },
|
|
8
|
+
{ label: 'Support', url: 'https://example.com/support', openInNewTab: true },
|
|
9
9
|
];
|
|
10
10
|
|
|
11
11
|
describe('HeaderNavClient', () => {
|
|
@@ -27,7 +27,7 @@ const OPTIONS = [
|
|
|
27
27
|
{ value: 'vanilla', label: 'Vanilla' },
|
|
28
28
|
{ value: 'mint', label: 'Mint' },
|
|
29
29
|
{ value: 'cookies', label: 'Cookies & Cream' },
|
|
30
|
-
];
|
|
30
|
+
] as const;
|
|
31
31
|
|
|
32
32
|
const GROUPED_OPTIONS = [
|
|
33
33
|
{
|
|
@@ -46,16 +46,16 @@ const GROUPED_OPTIONS = [
|
|
|
46
46
|
{ value: 'spinach', label: 'Spinach' },
|
|
47
47
|
],
|
|
48
48
|
},
|
|
49
|
-
];
|
|
49
|
+
] as const;
|
|
50
50
|
|
|
51
51
|
const USERS = [
|
|
52
52
|
{ value: 'john', label: 'John Doe', email: 'john@example.com' },
|
|
53
53
|
{ value: 'jane', label: 'Jane Smith', email: 'jane@example.com' },
|
|
54
54
|
{ value: 'bob', label: 'Bob Johnson', email: 'bob@example.com' },
|
|
55
|
-
];
|
|
55
|
+
] as const;
|
|
56
56
|
|
|
57
|
-
const FLAVOUR_PLACEHOLDER_TEXT = 'Select a flavour...';
|
|
58
|
-
const FLAVOUR_MULTI_PLACEHOLDER_TEXT = 'Select multiple flavours...';
|
|
57
|
+
const FLAVOUR_PLACEHOLDER_TEXT = 'Select a flavour...' as const;
|
|
58
|
+
const FLAVOUR_MULTI_PLACEHOLDER_TEXT = 'Select multiple flavours...' as const;
|
|
59
59
|
|
|
60
60
|
export const Default: Story = {
|
|
61
61
|
args: {
|
|
@@ -346,11 +346,11 @@ describe('Select', () => {
|
|
|
346
346
|
|
|
347
347
|
// Check which option was actually selected
|
|
348
348
|
const selectedCall = onChangeMock.mock.calls[0];
|
|
349
|
-
const selectedOption = selectedCall[0];
|
|
349
|
+
const selectedOption = selectedCall?.[0];
|
|
350
350
|
|
|
351
351
|
// Verify the selected option is displayed
|
|
352
352
|
await waitFor(() => {
|
|
353
|
-
expect(screen.getByText(selectedOption
|
|
353
|
+
expect(screen.getByText(selectedOption?.label)).toBeInTheDocument();
|
|
354
354
|
});
|
|
355
355
|
});
|
|
356
356
|
|
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
} from 'react-select';
|
|
14
14
|
import { components, default as ReactSelect } from 'react-select';
|
|
15
15
|
|
|
16
|
-
import { SELECT_CONTAINER_CLASSES, SELECT_CONTROL_CLASSES
|
|
16
|
+
import { SELECT_CONTAINER_CLASSES, SELECT_CONTROL_CLASSES } from './common';
|
|
17
17
|
import { cn } from '../../utils';
|
|
18
18
|
|
|
19
19
|
// extends the react-select props with some of our own
|
|
@@ -30,7 +30,6 @@ const getClassNames = <Option, IsMulti extends boolean, Group extends GroupBase<
|
|
|
30
30
|
control: (props) =>
|
|
31
31
|
cn(
|
|
32
32
|
SELECT_CONTROL_CLASSES,
|
|
33
|
-
SELECT_MIN_HEIGHT,
|
|
34
33
|
props.isDisabled ? '!cursor-not-allowed bg-gray-100' : 'bg-white',
|
|
35
34
|
props.isFocused
|
|
36
35
|
? 'shadow-[0px_0px_0px_theme(borderWidth.form)_theme(colors.focus)] border-focus'
|
|
@@ -41,7 +40,7 @@ const getClassNames = <Option, IsMulti extends boolean, Group extends GroupBase<
|
|
|
41
40
|
placeholder: (props) => cn('text-text-secondary', userClassNames?.placeholder?.(props)),
|
|
42
41
|
menu: (props) =>
|
|
43
42
|
cn(
|
|
44
|
-
'bg-white
|
|
43
|
+
'bg-white border border-black mt-1 overflow-hidden shadow-sm shadow-[0px_0px_6px_0px_#00000044]',
|
|
45
44
|
userClassNames?.menu?.(props),
|
|
46
45
|
),
|
|
47
46
|
menuList: (props) => cn('flex flex-col', userClassNames?.menuList?.(props)),
|
|
@@ -64,7 +63,7 @@ const getClassNames = <Option, IsMulti extends boolean, Group extends GroupBase<
|
|
|
64
63
|
),
|
|
65
64
|
multiValue: (props) =>
|
|
66
65
|
cn(
|
|
67
|
-
'flex gap-2 items-center justify-center px-2 bg-brand text-white
|
|
66
|
+
'flex gap-2 items-center justify-center px-2 bg-brand text-white m-[2px]',
|
|
68
67
|
userClassNames?.multiValue?.(props),
|
|
69
68
|
),
|
|
70
69
|
multiValueRemove: (props) => cn('w-3 h-3', userClassNames?.multiValueRemove?.(props)),
|
|
@@ -27,7 +27,6 @@ describe('SelectSkeleton', () => {
|
|
|
27
27
|
'h-full',
|
|
28
28
|
'bg-gray-100',
|
|
29
29
|
'animate-pulse',
|
|
30
|
-
'rounded-md',
|
|
31
30
|
'col-span-2',
|
|
32
31
|
);
|
|
33
32
|
});
|
|
@@ -70,7 +69,7 @@ describe('SelectSkeleton', () => {
|
|
|
70
69
|
expect(containerElement.tagName).toBe('DIV');
|
|
71
70
|
});
|
|
72
71
|
|
|
73
|
-
it('should apply SELECT_CONTROL_CLASSES
|
|
72
|
+
it('should apply SELECT_CONTROL_CLASSES to control element', () => {
|
|
74
73
|
const { container } = render(<SelectSkeleton />);
|
|
75
74
|
|
|
76
75
|
const controlElement = container.firstChild?.firstChild as HTMLElement;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SELECT_CONTAINER_CLASSES, SELECT_CONTROL_CLASSES
|
|
1
|
+
import { SELECT_CONTAINER_CLASSES, SELECT_CONTROL_CLASSES } from './common';
|
|
2
2
|
import type { ExtendProps } from '../../types';
|
|
3
3
|
import { cn } from '../../utils';
|
|
4
4
|
|
|
@@ -12,9 +12,9 @@ export type SelectSkeletonProps = ExtendProps<'div', Props>;
|
|
|
12
12
|
export const SelectSkeleton = ({ className, ...props }: SelectSkeletonProps = {}) => {
|
|
13
13
|
return (
|
|
14
14
|
<div className={cn(SELECT_CONTAINER_CLASSES, className)} {...props}>
|
|
15
|
-
<div className={cn(SELECT_CONTROL_CLASSES,
|
|
15
|
+
<div className={cn(SELECT_CONTROL_CLASSES, 'p-2')}>
|
|
16
16
|
<div
|
|
17
|
-
className="w-full h-full bg-gray-100 animate-pulse
|
|
17
|
+
className="w-full h-full bg-gray-100 animate-pulse col-span-2"
|
|
18
18
|
aria-label="Loading options"
|
|
19
19
|
></div>
|
|
20
20
|
</div>
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export const
|
|
2
|
-
export const SELECT_CONTAINER_CLASSES = 'w-full h-max select-none !pointer-events-auto';
|
|
1
|
+
export const SELECT_CONTAINER_CLASSES = 'w-full select-none !pointer-events-auto input-height';
|
|
3
2
|
export const SELECT_CONTROL_CLASSES =
|
|
4
|
-
'
|
|
3
|
+
'p-1 !grid gap-4 grid-cols-[1fr_min-content] w-full border input-height border-black';
|