@ndlib/component-library 0.0.118 → 0.0.120

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 (46) hide show
  1. package/dist/components/composites/DragDropList/DragDrop.test.d.ts +1 -0
  2. package/dist/components/composites/DragDropList/DragDrop.test.js +20 -0
  3. package/dist/components/composites/Modal/Modal.test.d.ts +1 -0
  4. package/dist/components/composites/Modal/Modal.test.js +37 -0
  5. package/dist/components/composites/Modal/index.d.ts +2 -3
  6. package/dist/components/composites/StructuredData/sdEvent/sdEvent.test.d.ts +1 -0
  7. package/dist/components/composites/StructuredData/sdEvent/sdEvent.test.js +32 -0
  8. package/dist/components/composites/StructuredData/sdNews/sdNews.test.d.ts +1 -0
  9. package/dist/components/composites/StructuredData/sdNews/sdNews.test.js +25 -0
  10. package/dist/components/elements/Alerts/index.js +1 -1
  11. package/dist/components/elements/Button/Button.test.js +6 -0
  12. package/dist/components/elements/Button/index.js +2 -3
  13. package/dist/components/elements/Fields/AutoComplete/AutoComplete.test.d.ts +1 -0
  14. package/dist/components/elements/Fields/AutoComplete/AutoComplete.test.js +52 -0
  15. package/dist/components/elements/Fields/AutoComplete/index.d.ts +2 -2
  16. package/dist/components/elements/Fields/Checkbox/Checkbox.test.d.ts +1 -0
  17. package/dist/components/elements/Fields/Checkbox/Checkbox.test.js +35 -0
  18. package/dist/components/elements/Fields/CheckboxGroup/CheckboxGroup.test.d.ts +1 -0
  19. package/dist/components/elements/Fields/CheckboxGroup/CheckboxGroup.test.js +34 -0
  20. package/dist/components/elements/Fields/CheckboxGroup/index.d.ts +1 -1
  21. package/dist/components/elements/Fields/DatePicker/DatePicker.test.d.ts +1 -0
  22. package/dist/components/elements/Fields/DatePicker/DatePicker.test.js +52 -0
  23. package/dist/components/elements/Fields/DatePicker/index.d.ts +1 -0
  24. package/dist/components/elements/Fields/DatePicker/index.js +1 -1
  25. package/dist/components/elements/Fields/MonthPicker/MonthPicker.test.d.ts +1 -0
  26. package/dist/components/elements/Fields/MonthPicker/MonthPicker.test.js +47 -0
  27. package/dist/components/elements/Fields/Radio/Radio.test.d.ts +1 -0
  28. package/dist/components/elements/Fields/Radio/Radio.test.js +46 -0
  29. package/dist/components/elements/Fields/Radio/index.d.ts +1 -2
  30. package/dist/components/elements/Fields/Radio/index.js +4 -5
  31. package/dist/components/elements/Fields/RadioGroup/RadioGroup.test.d.ts +1 -0
  32. package/dist/components/elements/Fields/RadioGroup/RadioGroup.test.js +35 -0
  33. package/dist/components/elements/Fields/RadioGroup/index.d.ts +1 -1
  34. package/dist/components/elements/Fields/Select/Select.test.d.ts +1 -0
  35. package/dist/components/elements/Fields/Select/Select.test.js +52 -0
  36. package/dist/components/elements/Fields/Select/index.js +7 -10
  37. package/dist/components/elements/Fields/TextInput/TextInput.test.d.ts +1 -0
  38. package/dist/components/elements/Fields/TextInput/TextInput.test.js +61 -0
  39. package/dist/components/elements/Fields/TextInput/index.js +4 -5
  40. package/dist/components/elements/Icon/Icon.test.d.ts +1 -1
  41. package/dist/components/elements/Icon/Icon.test.js +55 -27
  42. package/dist/components/elements/text/ReadMore/ReadMore.test.d.ts +1 -0
  43. package/dist/components/elements/text/ReadMore/ReadMore.test.js +35 -0
  44. package/dist/theme/index.d.ts +11 -1
  45. package/dist/theme/index.js +13 -1
  46. package/package.json +4 -2
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render } from '@testing-library/react';
3
+ import { DragDropList } from './';
4
+ describe('DragDropList component', () => {
5
+ const items = [
6
+ { id: 1, accessibleLabel: 'Item 1' },
7
+ { id: 2, accessibleLabel: 'Item 2' },
8
+ { id: 3, accessibleLabel: 'Item 3' },
9
+ ];
10
+ it('should render without errors', () => {
11
+ const { container } = render(_jsx(DragDropList, { items: items, onReorder: () => { }, children: () => _jsx("div", { children: "Child" }) }));
12
+ expect(container).toBeInTheDocument();
13
+ });
14
+ it('should render items correctly', () => {
15
+ const { getByText } = render(_jsx(DragDropList, { items: items, onReorder: () => { }, children: ({ item }) => _jsx("div", { children: item.accessibleLabel }) }));
16
+ items.forEach((item) => {
17
+ expect(getByText(item.accessibleLabel)).toBeInTheDocument();
18
+ });
19
+ });
20
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { fireEvent } from '@testing-library/react';
3
+ import { render } from '../../../utils/test';
4
+ import { vi } from 'vitest';
5
+ import { Modal } from './';
6
+ describe('Modal', () => {
7
+ const handleClose = vi.fn();
8
+ const mockActions = [
9
+ { label: 'Action 1', onClick: vi.fn() },
10
+ { label: 'Action 2', onClick: vi.fn() },
11
+ ];
12
+ it('renders modal with heading and content', () => {
13
+ const { getByText } = render(_jsx(Modal, Object.assign({ isOpen: true, close: handleClose, heading: "Test Heading" }, { children: _jsx("div", { children: "Modal Content" }) })));
14
+ expect(getByText('Test Heading')).toBeInTheDocument();
15
+ expect(getByText('Modal Content')).toBeInTheDocument();
16
+ });
17
+ it('calls close handler when close button is clicked', () => {
18
+ const { getByText } = render(_jsx(Modal, Object.assign({ isOpen: true, close: handleClose, heading: "Test Heading" }, { children: _jsx("div", { children: "Modal Content" }) })));
19
+ const closeButton = getByText('Close');
20
+ fireEvent.click(closeButton);
21
+ expect(handleClose).toHaveBeenCalledTimes(1);
22
+ });
23
+ it('renders actions correctly', () => {
24
+ const { getByText } = render(_jsx(Modal, Object.assign({ isOpen: true, close: handleClose, heading: "Test Heading", actions: mockActions }, { children: _jsx("div", { children: "Modal Content" }) })));
25
+ expect(getByText('Action 1')).toBeInTheDocument();
26
+ expect(getByText('Action 2')).toBeInTheDocument();
27
+ });
28
+ it('calls action handler when action button is clicked', () => {
29
+ const { getByText } = render(_jsx(Modal, Object.assign({ isOpen: true, close: handleClose, heading: "Test Heading", actions: mockActions }, { children: _jsx("div", { children: "Modal Content" }) })));
30
+ const actionButton1 = getByText('Action 1');
31
+ fireEvent.click(actionButton1);
32
+ expect(mockActions[0].onClick).toHaveBeenCalledTimes(1);
33
+ const actionButton2 = getByText('Action 2');
34
+ fireEvent.click(actionButton2);
35
+ expect(mockActions[1].onClick).toHaveBeenCalledTimes(1);
36
+ });
37
+ });
@@ -1,7 +1,7 @@
1
1
  import { StyledElementProps, StylesProp } from '../../../theme';
2
2
  import React from 'react';
3
3
  import { ButtonProps } from '../../elements/Button';
4
- type ModalProps = StyledElementProps<HTMLDivElement, {
4
+ export type ModalProps = StyledElementProps<HTMLDivElement, {
5
5
  isOpen: boolean;
6
6
  close: (e: React.MouseEvent) => void;
7
7
  heading: string;
@@ -12,11 +12,10 @@ type ModalProps = StyledElementProps<HTMLDivElement, {
12
12
  wrapWithTheme?: boolean;
13
13
  bodyStyles?: StylesProp;
14
14
  }>;
15
- type ModalAction = {
15
+ export type ModalAction = {
16
16
  label: string;
17
17
  isPrimary?: boolean;
18
18
  buttonProps?: Partial<ButtonProps>;
19
19
  onClick: (e: React.MouseEvent) => void;
20
20
  };
21
21
  export declare const Modal: React.FC<ModalProps>;
22
- export {};
@@ -0,0 +1,32 @@
1
+ import stringifyEventSchema from './';
2
+ describe('stringifyEventSchema', () => {
3
+ it('generates the correct event schema', () => {
4
+ const props = {
5
+ title: 'Test Event',
6
+ startDate: '2024-05-20T10:00:00Z',
7
+ endDate: '2024-05-20T12:00:00Z',
8
+ description: 'This is a test event',
9
+ eventLocation: '123 Main St, City, Country',
10
+ eventLocationName: 'Main Hall',
11
+ image: 'https://example.com/event-image.jpg',
12
+ url: 'https://example.com/event',
13
+ siteUrl: 'https://example.com',
14
+ };
15
+ const expectedSchema = {
16
+ '@type': 'Event',
17
+ name: 'Test Event',
18
+ startDate: '2024-05-20T10:00:00Z',
19
+ endDate: '2024-05-20T12:00:00Z',
20
+ description: 'This is a test event',
21
+ image: 'https://example.com/event-image.jpg',
22
+ url: 'https://example.com/event/https://example.com/event',
23
+ location: {
24
+ '@type': 'Place',
25
+ name: 'Main Hall',
26
+ address: '123 Main St, City, Country',
27
+ },
28
+ };
29
+ const generatedSchema = stringifyEventSchema(props);
30
+ expect(JSON.parse(generatedSchema)).toEqual(expectedSchema);
31
+ });
32
+ });
@@ -0,0 +1,25 @@
1
+ import { makeNewsSchema } from './makeNewsSchema';
2
+ describe('makeNewsSchema', () => {
3
+ it('generates the correct news schema with all fields provided', () => {
4
+ const props = {
5
+ title: 'Test News',
6
+ author: 'John Doe',
7
+ abstract: 'This is a test news article',
8
+ image: 'https://example.com/news-image.jpg',
9
+ url: 'https://example.com/news',
10
+ publishedDate: '2024-05-20T10:00:00Z',
11
+ siteUrl: 'https://example.com',
12
+ };
13
+ const expectedSchema = {
14
+ '@type': 'NewsArticle',
15
+ name: 'Test News',
16
+ creator: 'John Doe',
17
+ abstract: 'This is a test news article',
18
+ image: 'https://example.com/news-image.jpg',
19
+ url: 'https://example.com/news/https://example.com/news',
20
+ datePublished: '2024-05-20T10:00:00Z',
21
+ };
22
+ const generatedSchema = makeNewsSchema(props);
23
+ expect(generatedSchema).toEqual(expectedSchema);
24
+ });
25
+ });
@@ -65,7 +65,7 @@ export const Alert = (_a) => {
65
65
  [ALERT_TYPE.INFORMATIONAL]: InfoIcon,
66
66
  [ALERT_TYPE.SUCCESS]: SuccessIcon,
67
67
  }[type];
68
- return (_jsx(Row, Object.assign({ sx: Object.assign({}, style) }, rest, { centered: true }, { children: _jsxs(Row, Object.assign({ sx: {
68
+ return (_jsx(Row, Object.assign({ sx: Object.assign({}, style) }, rest, { children: _jsxs(Row, Object.assign({ sx: {
69
69
  boxSizing: 'border-box',
70
70
  margin: '0 auto',
71
71
  pl: '.5rem',
@@ -21,6 +21,12 @@ describe('Button', () => {
21
21
  expect(getByText('Mock Left Icon')).toBeDefined();
22
22
  expect(getByText('Mock Right Icon')).toBeDefined();
23
23
  });
24
+ it('renders without icons if not passed', () => {
25
+ const { queryByText } = render(_jsx(Button, { children: "Click Me" }));
26
+ expect(queryByText('Mock Primary Icon')).toBeNull();
27
+ expect(queryByText('Mock Left Icon')).toBeNull();
28
+ expect(queryByText('Mock Right Icon')).toBeNull();
29
+ });
24
30
  it('responds to click handlers', () => {
25
31
  const { getByRole } = render(_jsx(Button, { onClick: mockClickHandler }));
26
32
  fireEvent.click(getByRole('button'));
@@ -11,12 +11,12 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "theme-ui/jsx-runtime";
13
13
  import React from 'react';
14
- import { useTheme } from '../../../theme';
15
14
  import { COLOR } from '../../../theme/colors';
16
15
  import { TYPOGRAPHY_TYPE, getIconSize, getTypographyStyles, } from '../../../theme/typography';
17
16
  import { Icon } from '../Icon';
18
17
  import { useEnvironment } from '../../providers/env';
19
18
  import { SPINNER_SIZE, Spinner } from '../Spinner';
19
+ import { BOX_SHADOW } from '../../../theme/custom';
20
20
  export var BUTTON_SIZE;
21
21
  (function (BUTTON_SIZE) {
22
22
  BUTTON_SIZE["SM"] = "sm";
@@ -52,13 +52,12 @@ export var BUTTON_TYPE;
52
52
  export const Button = React.forwardRef((_a, ref) => {
53
53
  var { size: sizeParam, type: typeParam, color, customColor, textColor: textColorParam, primaryIcon, leftIcon, rightIcon, disabled: disabledParam, children, sx, loading, disableFocusStyles } = _a, rest = __rest(_a, ["size", "type", "color", "customColor", "textColor", "primaryIcon", "leftIcon", "rightIcon", "disabled", "children", "sx", "loading", "disableFocusStyles"]);
54
54
  const disabled = disabledParam || loading;
55
- const theme = useTheme();
56
55
  const { flagInDevelopment } = useEnvironment();
57
56
  let bg = COLOR.PRIMARY;
58
57
  let borderColor = COLOR.TRANSPARENT;
59
58
  let cursor = 'pointer';
60
59
  let textColor = COLOR.BACKGROUND;
61
- let hoverShadow = theme.boxShadow.NORMAL;
60
+ let hoverShadow = BOX_SHADOW.NORMAL;
62
61
  let hoverDecoration = undefined;
63
62
  let hoverTransform = 'scale(1.03)';
64
63
  const isTextButton = typeParam === BUTTON_TYPE.TEXT;
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import { describe, it, expect, vi } from 'vitest';
5
+ import { AutoComplete } from './';
6
+ describe('AutoComplete Component', () => {
7
+ const options = [
8
+ { label: 'Apple', value: 'apple' },
9
+ { label: 'Banana', value: 'banana' },
10
+ { label: 'Cherry', value: 'cherry' },
11
+ ];
12
+ const setup = (props = {}) => {
13
+ const initialProps = Object.assign({ value: '', options: options, onSelect: vi.fn() }, props);
14
+ return render(_jsx(AutoComplete, Object.assign({}, initialProps)));
15
+ };
16
+ it('renders the component with initial value', () => {
17
+ setup({ value: 'Apple' });
18
+ expect(screen.getByDisplayValue('Apple')).toBeInTheDocument();
19
+ });
20
+ it('filters options based on input value', () => {
21
+ setup();
22
+ const input = screen.getByRole('textbox');
23
+ fireEvent.change(input, { target: { value: 'Ba' } });
24
+ expect(screen.getByText('Banana')).toBeInTheDocument();
25
+ expect(screen.queryByText('Apple')).not.toBeInTheDocument();
26
+ });
27
+ it('shows no options when input does not match any option', () => {
28
+ setup();
29
+ const input = screen.getByRole('textbox');
30
+ fireEvent.change(input, { target: { value: 'xyz' } });
31
+ expect(screen.queryByText('Apple')).not.toBeInTheDocument();
32
+ expect(screen.queryByText('Banana')).not.toBeInTheDocument();
33
+ expect(screen.queryByText('Cherry')).not.toBeInTheDocument();
34
+ });
35
+ it('calls onSelect with the correct value when an option is selected', () => {
36
+ const mockOnSelect = vi.fn();
37
+ setup({ onSelect: mockOnSelect });
38
+ const input = screen.getByRole('textbox');
39
+ fireEvent.change(input, { target: { value: 'Ap' } });
40
+ const option = screen.getByText('Apple');
41
+ fireEvent.mouseDown(option);
42
+ expect(mockOnSelect).toHaveBeenCalledWith('apple');
43
+ });
44
+ it('hides options after selection', () => {
45
+ setup();
46
+ const input = screen.getByRole('textbox');
47
+ fireEvent.change(input, { target: { value: 'Ap' } });
48
+ const option = screen.getByText('Apple');
49
+ fireEvent.mouseDown(option);
50
+ expect(screen.queryByText('Apple')).not.toBeInTheDocument();
51
+ });
52
+ });
@@ -1,8 +1,8 @@
1
- interface Option {
1
+ export interface Option {
2
2
  label: string;
3
3
  value: string;
4
4
  }
5
- interface AutoCompleteProps {
5
+ export interface AutoCompleteProps {
6
6
  value: string;
7
7
  options: Option[];
8
8
  onSelect: (option: string) => any;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, fireEvent } from '@testing-library/react';
3
+ import { Checkbox } from './';
4
+ import { vi } from 'vitest';
5
+ describe('Checkbox', () => {
6
+ it('renders unchecked checkbox', () => {
7
+ const { getByRole } = render(_jsx(Checkbox, { checked: false, onChange: () => { } }));
8
+ const checkbox = getByRole('checkbox');
9
+ expect(checkbox).toBeInTheDocument();
10
+ expect(checkbox).not.toBeChecked();
11
+ });
12
+ it('renders checked checkbox', () => {
13
+ const { getByRole } = render(_jsx(Checkbox, { checked: true, onChange: () => { } }));
14
+ const checkbox = getByRole('checkbox');
15
+ expect(checkbox).toBeInTheDocument();
16
+ expect(checkbox).toBeChecked();
17
+ });
18
+ it('calls onChange handler when checkbox is clicked', () => {
19
+ const onChangeMock = vi.fn();
20
+ const { getByRole } = render(_jsx(Checkbox, { checked: false, onChange: onChangeMock }));
21
+ const checkbox = getByRole('checkbox');
22
+ fireEvent.click(checkbox);
23
+ expect(onChangeMock).toHaveBeenCalledWith(true);
24
+ });
25
+ it('applies disabled styles when disabled', () => {
26
+ const { getByRole } = render(_jsx(Checkbox, { checked: false, onChange: () => { }, disabled: true }));
27
+ const checkbox = getByRole('checkbox');
28
+ expect(checkbox).toHaveStyle('cursor: not-allowed;');
29
+ });
30
+ it('applies pointer cursor style when not disabled', () => {
31
+ const { getByRole } = render(_jsx(Checkbox, { checked: false, onChange: () => { } }));
32
+ const checkbox = getByRole('checkbox');
33
+ expect(checkbox).toHaveStyle('cursor: pointer;');
34
+ });
35
+ });
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, fireEvent } from '@testing-library/react';
3
+ import { CheckboxGroup } from './';
4
+ import { vi } from 'vitest';
5
+ const mockOptions = [
6
+ { value: 'option1', label: 'Option 1' },
7
+ { value: 'option2', label: 'Option 2', disabled: true },
8
+ { value: 'option3', label: 'Option 3' },
9
+ ];
10
+ describe('CheckboxGroup', () => {
11
+ it('renders checkboxes with options correctly', () => {
12
+ const { getByText } = render(_jsx(CheckboxGroup, { options: mockOptions, onChange: () => { } }));
13
+ mockOptions.forEach((option) => {
14
+ const label = getByText(option.label);
15
+ expect(label).toBeInTheDocument();
16
+ });
17
+ });
18
+ it('calls onChange handler with correct value when checkbox is clicked', () => {
19
+ const onChangeMock = vi.fn();
20
+ const { getByLabelText } = render(_jsx(CheckboxGroup, { options: mockOptions, onChange: onChangeMock }));
21
+ const option1Checkbox = getByLabelText('Option 1');
22
+ fireEvent.click(option1Checkbox);
23
+ expect(onChangeMock).toHaveBeenCalledWith(new Set(['option1']));
24
+ });
25
+ it('disables checkboxes correctly', () => {
26
+ const { getByLabelText } = render(_jsx(CheckboxGroup, { options: mockOptions, onChange: () => { } }));
27
+ const option1Checkbox = getByLabelText('Option 1');
28
+ expect(option1Checkbox).not.toBeDisabled();
29
+ const option2Checkbox = getByLabelText('Option 2');
30
+ expect(option2Checkbox).toBeDisabled();
31
+ const option3Checkbox = getByLabelText('Option 3');
32
+ expect(option3Checkbox).not.toBeDisabled();
33
+ });
34
+ });
@@ -1,6 +1,6 @@
1
1
  import { StyledElementProps, StylesProp } from '../../../../theme';
2
2
  import { Key } from '../option';
3
- type Option<K extends Key = string> = {
3
+ export type Option<K extends Key = string> = {
4
4
  value: K;
5
5
  label: string;
6
6
  disabled?: boolean;
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, fireEvent, screen } from '@testing-library/react';
3
+ import { describe, it, expect, vi } from 'vitest';
4
+ import { DatePicker, areDatesEqual } from './';
5
+ describe('DatePicker Component', () => {
6
+ it('renders correctly', () => {
7
+ const mockDate = new Date(2023, 4, 23);
8
+ render(_jsx(DatePicker, { value: mockDate, onChange: vi.fn() }));
9
+ expect(screen.getByDisplayValue('05/23/2023')).toBeInTheDocument();
10
+ });
11
+ it('triggers onChange when a date is selected', () => {
12
+ const mockDate = new Date(2023, 4, 23);
13
+ const handleChange = vi.fn();
14
+ render(_jsx(DatePicker, { value: mockDate, onChange: handleChange }));
15
+ fireEvent.click(screen.getByDisplayValue('05/23/2023'));
16
+ fireEvent.click(screen.getByText('24'));
17
+ expect(handleChange).toHaveBeenCalled();
18
+ });
19
+ it('applies custom input props to TextInput', () => {
20
+ const mockDate = new Date(2023, 4, 23);
21
+ render(_jsx(DatePicker, { value: mockDate, onChange: vi.fn(), inputProps: { placeholder: 'Select date' } }));
22
+ expect(screen.getByPlaceholderText('Select date')).toBeInTheDocument();
23
+ });
24
+ it('highlights specified dates', () => {
25
+ const mockDate = new Date(2023, 4, 23);
26
+ const highlightDate = new Date(2023, 4, 24);
27
+ render(_jsx(DatePicker, { value: mockDate, onChange: vi.fn(), highlightDates: [highlightDate] }));
28
+ fireEvent.click(screen.getByDisplayValue('05/23/2023'));
29
+ const dayElement = screen.getByText((content, element) => {
30
+ return ((element === null || element === void 0 ? void 0 : element.classList.contains('ndlib-date-picker-highlight')) &&
31
+ content === '24');
32
+ });
33
+ expect(dayElement).toBeInTheDocument();
34
+ });
35
+ it('applies custom wrapper props to Box', () => {
36
+ const mockDate = new Date(2023, 4, 23);
37
+ render(_jsx(DatePicker, { value: mockDate, onChange: vi.fn(), wrapperProps: { 'data-testid': 'wrapper' } }));
38
+ expect(screen.getByTestId('wrapper')).toBeInTheDocument();
39
+ });
40
+ });
41
+ describe('areDatesEqual Function', () => {
42
+ it('returns true for equal dates', () => {
43
+ const date1 = new Date(2023, 4, 23);
44
+ const date2 = new Date(2023, 4, 23);
45
+ expect(areDatesEqual(date1, date2)).toBe(true);
46
+ });
47
+ it('returns false for different dates', () => {
48
+ const date1 = new Date(2023, 4, 23);
49
+ const date2 = new Date(2023, 4, 24);
50
+ expect(areDatesEqual(date1, date2)).toBe(false);
51
+ });
52
+ });
@@ -8,6 +8,7 @@ export declare const InputWrapper: React.ForwardRefExoticComponent<{
8
8
  value?: string | undefined;
9
9
  children: React.ReactElement;
10
10
  } & React.RefAttributes<any>>;
11
+ export declare const areDatesEqual: (d1: Date, d2: Date) => boolean;
11
12
  export declare const DatePicker: React.FC<{
12
13
  value: Date;
13
14
  onChange: (date: Date) => void;
@@ -29,7 +29,7 @@ export const InputWrapper = React.forwardRef(({ onChange, value, children, onCli
29
29
  });
30
30
  });
31
31
  const PermissiveTextInput = TextInput;
32
- const areDatesEqual = (d1, d2) => {
32
+ export const areDatesEqual = (d1, d2) => {
33
33
  const serializeDate = (d) => `${d.getFullYear()} ${d.getMonth()} ${d.getDate()}`;
34
34
  return serializeDate(d1) === serializeDate(d2);
35
35
  };
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen } from '@testing-library/react';
3
+ import { describe, it, expect, vi, afterEach } from 'vitest';
4
+ import { MonthPicker } from './';
5
+ describe('MonthPicker component', () => {
6
+ const mockOnChange = vi.fn();
7
+ const selectedDate = new Date(Date.UTC(96, 1, 2, 3, 4, 5));
8
+ afterEach(() => {
9
+ vi.clearAllMocks();
10
+ });
11
+ it('renders without crashing', () => {
12
+ render(_jsx(MonthPicker, { value: selectedDate, onChange: mockOnChange }));
13
+ expect(screen.getByRole('textbox')).toBeInTheDocument();
14
+ });
15
+ it('displays the correct selected date', () => {
16
+ render(_jsx(MonthPicker, { value: selectedDate, onChange: mockOnChange }));
17
+ expect(screen.getByRole('textbox')).toHaveValue('02/02/1996');
18
+ });
19
+ it('passes inputProps to TextInput component', () => {
20
+ const inputProps = { placeholder: 'Select month' };
21
+ render(_jsx(MonthPicker, { value: selectedDate, onChange: mockOnChange, inputProps: inputProps }));
22
+ expect(screen.getByPlaceholderText('Select month')).toBeInTheDocument();
23
+ });
24
+ it('passes wrapperProps to Box component', () => {
25
+ const wrapperProps = {
26
+ 'data-testid': 'box-wrapper',
27
+ className: 'custom-class',
28
+ };
29
+ render(_jsx(MonthPicker, { value: selectedDate, onChange: mockOnChange, wrapperProps: wrapperProps }));
30
+ const box = screen.getByTestId('box-wrapper');
31
+ expect(box).toBeInTheDocument();
32
+ expect(box).toHaveClass('custom-class');
33
+ });
34
+ it('renders the date picker inline if inline prop is true', () => {
35
+ render(_jsx(MonthPicker, { value: selectedDate, onChange: mockOnChange, inline: true }));
36
+ const datePicker = screen.getByRole('alert');
37
+ screen.debug();
38
+ expect(datePicker).toHaveClass('react-datepicker__aria-live');
39
+ });
40
+ it('respects minDate and maxDate props', () => {
41
+ const minDate = new Date('2021-01-01');
42
+ const maxDate = new Date('2023-01-01');
43
+ render(_jsx(MonthPicker, { value: selectedDate, onChange: mockOnChange, minDate: minDate, maxDate: maxDate }));
44
+ const datePicker = screen.getByRole('textbox');
45
+ expect(datePicker).toBeInTheDocument();
46
+ });
47
+ });
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx } from "theme-ui/jsx-runtime";
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
4
+ import { Radio } from './';
5
+ import { useTheme } from '../../../../theme';
6
+ vi.mock('../../../../theme', () => ({
7
+ useTheme: vi.fn(),
8
+ }));
9
+ describe('Radio Component', () => {
10
+ const mockUseTheme = useTheme;
11
+ const theme = { boxShadow: '0 0 5px rgba(0,0,0,0.2)' };
12
+ beforeEach(() => {
13
+ mockUseTheme.mockReturnValue(theme);
14
+ });
15
+ afterEach(() => {
16
+ vi.clearAllMocks();
17
+ });
18
+ const setup = (props = {}) => {
19
+ const initialProps = Object.assign({ checked: false, onChange: vi.fn(), disabled: false, sx: {} }, props);
20
+ return render(_jsx(Radio, Object.assign({}, initialProps)));
21
+ };
22
+ it('renders the component with initial checked state', () => {
23
+ setup({ checked: true });
24
+ const radio = screen.getByRole('radio');
25
+ expect(radio).toBeChecked();
26
+ });
27
+ it('calls onChange with the correct value when clicked', () => {
28
+ const mockOnChange = vi.fn();
29
+ setup({ onChange: mockOnChange });
30
+ const radio = screen.getByRole('radio');
31
+ fireEvent.click(radio);
32
+ expect(mockOnChange).toHaveBeenCalledWith(true);
33
+ });
34
+ it('applies the disabled state correctly', () => {
35
+ setup({ disabled: true });
36
+ const radio = screen.getByRole('radio');
37
+ expect(radio).toBeDisabled();
38
+ expect(radio).toHaveStyle('cursor: not-allowed');
39
+ });
40
+ it('applies custom styles from sx prop', () => {
41
+ const customStyles = { backgroundColor: 'red' };
42
+ setup({ sx: customStyles });
43
+ const radio = screen.getByRole('radio');
44
+ expect(radio).toHaveStyle('background-color: rgb(255, 0, 0)');
45
+ });
46
+ });
@@ -1,9 +1,8 @@
1
1
  import React from 'react';
2
2
  import { StyledElementProps } from '../../../../theme';
3
- type RadioProps = StyledElementProps<HTMLInputElement, {
3
+ export type RadioProps = StyledElementProps<HTMLInputElement, {
4
4
  checked: boolean;
5
5
  onChange: (value: boolean) => void;
6
6
  disabled?: boolean;
7
7
  }, string>;
8
8
  export declare const Radio: React.FC<RadioProps>;
9
- export {};
@@ -10,13 +10,12 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { jsx as _jsx } from "theme-ui/jsx-runtime";
13
- import { useTheme } from '../../../../theme';
13
+ import { BOX_SHADOW } from '../../../../theme/custom';
14
14
  export const Radio = (_a) => {
15
15
  var { checked, onChange, disabled, sx } = _a, rest = __rest(_a, ["checked", "onChange", "disabled", "sx"]);
16
- const theme = useTheme();
17
16
  return (_jsx("input", Object.assign({ type: "radio", onChange: (e) => {
18
17
  onChange(e.target.checked);
19
- }, checked: checked, sx: Object.assign({ margin: '0px', height: '1.25rem', width: '1.25rem', cursor: disabled ? 'not-allowed' : 'pointer', ':hover': {
20
- boxShadow: theme.boxShadow,
21
- } }, sx), disabled: disabled }, rest)));
18
+ }, checked: checked, disabled: disabled, sx: Object.assign({ margin: '0px', height: '1.25rem', width: '1.25rem', cursor: disabled ? 'not-allowed' : 'pointer', ':hover': {
19
+ boxShadow: disabled ? null : BOX_SHADOW.NORMAL,
20
+ } }, sx) }, rest)));
22
21
  };
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import { describe, it, expect, vi } from 'vitest';
4
+ import '@testing-library/jest-dom';
5
+ import { RadioGroup } from './';
6
+ describe('RadioGroup', () => {
7
+ const options = [
8
+ { value: 'option1', label: 'Option 1' },
9
+ { value: 'option2', label: 'Option 2', disabled: true },
10
+ { value: 'option3', label: 'Option 3' },
11
+ ];
12
+ const onChange = vi.fn();
13
+ it('renders all options', () => {
14
+ render(_jsx(RadioGroup, { options: options, onChange: onChange }));
15
+ options.forEach((option) => {
16
+ expect(screen.getByText(option.label)).toBeInTheDocument();
17
+ });
18
+ });
19
+ it('renders checked option correctly', () => {
20
+ render(_jsx(RadioGroup, { options: options, checked: "option1", onChange: onChange }));
21
+ const radio = screen.getByLabelText('Option 1');
22
+ expect(radio.checked).toBe(true);
23
+ });
24
+ it('calls onChange when an option is clicked', () => {
25
+ render(_jsx(RadioGroup, { options: options, onChange: onChange }));
26
+ const radio = screen.getByLabelText('Option 3');
27
+ fireEvent.click(radio);
28
+ expect(onChange).toHaveBeenCalledWith('option3');
29
+ });
30
+ it('disables the correct options', () => {
31
+ render(_jsx(RadioGroup, { options: options, onChange: onChange }));
32
+ const disabledRadio = screen.getByLabelText('Option 2');
33
+ expect(disabledRadio.disabled).toBe(true);
34
+ });
35
+ });
@@ -5,7 +5,7 @@ type Option<K extends Key = string> = {
5
5
  label: string;
6
6
  disabled?: boolean;
7
7
  };
8
- type RadioGroupProps<K extends Key = string> = StyledElementProps<HTMLDivElement, {
8
+ export type RadioGroupProps<K extends Key = string> = StyledElementProps<HTMLDivElement, {
9
9
  onChange: (value: K) => void;
10
10
  options: Option<K>[];
11
11
  columnStyles?: StylesProp;
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import { describe, it, expect, vi } from 'vitest';
4
+ import { Select } from './';
5
+ describe('Select', () => {
6
+ const options = [
7
+ { value: 'option1', label: 'Option 1' },
8
+ { value: 'option2', label: 'Option 2' },
9
+ { value: 'option3', label: 'Option 3' },
10
+ ];
11
+ const onSelectOption = vi.fn();
12
+ it('renders placeholder when no value is selected', () => {
13
+ render(_jsx(Select, { options: options, value: "", placeholder: "Select an option", onSelectOption: onSelectOption }));
14
+ expect(screen.getByRole('combobox')).toHaveTextContent('Select an option');
15
+ });
16
+ it('renders the selected value', () => {
17
+ render(_jsx(Select, { options: options, value: "option2", onSelectOption: onSelectOption }));
18
+ expect(screen.getByRole('combobox')).toHaveTextContent('Option 2');
19
+ });
20
+ it('opens the dropdown on click and displays options', () => {
21
+ render(_jsx(Select, { options: options, value: "", onSelectOption: onSelectOption }));
22
+ const combobox = screen.getByRole('combobox');
23
+ fireEvent.mouseDown(combobox);
24
+ options.forEach((option) => {
25
+ expect(screen.getByText(option.label)).toBeInTheDocument();
26
+ });
27
+ });
28
+ it('calls onSelectOption with correct value on option click', () => {
29
+ render(_jsx(Select, { options: options, value: "", onSelectOption: onSelectOption }));
30
+ const combobox = screen.getByRole('combobox');
31
+ fireEvent.mouseDown(combobox);
32
+ const option = screen.getByText('Option 1');
33
+ fireEvent.mouseDown(option);
34
+ expect(onSelectOption).toHaveBeenCalledWith('option1');
35
+ });
36
+ it('handles keyboard navigation and selection', () => {
37
+ render(_jsx(Select, { options: options, value: "", onSelectOption: onSelectOption }));
38
+ const combobox = screen.getByRole('combobox');
39
+ fireEvent.focus(combobox);
40
+ fireEvent.keyDown(combobox, { key: 'ArrowDown' });
41
+ fireEvent.keyDown(combobox, { key: 'Enter' });
42
+ expect(onSelectOption).toHaveBeenCalledWith('option2');
43
+ });
44
+ it('closes the dropdown on blur', () => {
45
+ render(_jsx(Select, { options: options, value: "", onSelectOption: onSelectOption }));
46
+ const combobox = screen.getByRole('combobox');
47
+ fireEvent.mouseDown(combobox);
48
+ expect(screen.getByText('Option 1')).toBeInTheDocument();
49
+ fireEvent.blur(combobox);
50
+ expect(screen.queryByText('Option 1')).not.toBeInTheDocument();
51
+ });
52
+ });
@@ -3,7 +3,6 @@ import { useMemo, useState } from 'react';
3
3
  import _ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
4
4
  import { useFloating, size as sizeMiddleware, offset, autoPlacement, } from '@floating-ui/react';
5
5
  import { TYPOGRAPHY_TYPE, getTypographyStyles, } from '../../../../theme/typography';
6
- import { useTheme } from '../../../../theme';
7
6
  import { INPUT_SIZE, labelOffsetMap, labelTypographyMap } from '../TextInput';
8
7
  import { ListBox } from '../../ListBox';
9
8
  import { BUTTON_SIZE, BUTTON_TYPE, Button } from '../../Button';
@@ -13,6 +12,7 @@ import { COLOR } from '../../../../theme/colors';
13
12
  import { Box } from '../../layout/Box';
14
13
  import { Label } from '../../text/Label';
15
14
  import { useUniqueId } from '../../../providers/uniqueIds';
15
+ import { BOX_SHADOW, Z_INDEX } from '../../../../theme/custom';
16
16
  const ArrowDropDownIcon = importedDefaultComponentShim(_ArrowDropDownIcon);
17
17
  const typographyMap = {
18
18
  [INPUT_SIZE.SM]: TYPOGRAPHY_TYPE.PARAGRAPH_SMALL,
@@ -31,7 +31,6 @@ const buttonSizeMap = {
31
31
  };
32
32
  const DEFAULT_WIDTH = '16rem';
33
33
  export function Select({ size: sizeParam, placeholder, leftIcon, value, label, onSelectOption, options, renderOption, inverted, renderOptionLabel: renderOptionLabelParam, sx, }) {
34
- const theme = useTheme();
35
34
  const listboxId = useUniqueId('select-list-box');
36
35
  const inputId = useUniqueId('select-input');
37
36
  const [isOpen, setIsOpen] = useState(false);
@@ -117,7 +116,7 @@ export function Select({ size: sizeParam, placeholder, leftIcon, value, label, o
117
116
  const typographyStyles = getTypographyStyles(typography);
118
117
  const renderOptionLabel = renderOptionLabelParam || defaultRenderOptionLabel;
119
118
  const focusStyles = {
120
- boxShadow: inverted ? theme.boxShadow.INVERTED : theme.boxShadow.NORMAL,
119
+ boxShadow: inverted ? BOX_SHADOW.INVERTED : BOX_SHADOW.NORMAL,
121
120
  };
122
121
  return (_jsxs(Box, Object.assign({ sx: Object.assign({ width: DEFAULT_WIDTH, position: 'relative', borderRadius: '4px', backgroundColor: inverted ? COLOR.DARK_GRAY : COLOR.WHITE, ':hover fieldset': focusStyles, ':focus fieldset': focusStyles }, sx) }, { children: [_jsx("fieldset", Object.assign({ sx: {
123
122
  textAlign: 'left',
@@ -132,7 +131,7 @@ export function Select({ size: sizeParam, placeholder, leftIcon, value, label, o
132
131
  overflow: 'hidden',
133
132
  minWidth: '0%',
134
133
  borderColor: inverted ? COLOR.WHITE : COLOR.LIGHT_GRAY,
135
- zIndex: theme.zIndex.ELEVATED,
134
+ zIndex: Z_INDEX.ELEVATED,
136
135
  } }, { children: _jsx("legend", Object.assign({ style: {
137
136
  width: 'auto',
138
137
  float: 'unset',
@@ -154,7 +153,7 @@ export function Select({ size: sizeParam, placeholder, leftIcon, value, label, o
154
153
  position: 'absolute',
155
154
  left: '0.5rem',
156
155
  lineHeight: 1,
157
- zIndex: theme.zIndex.ELEVATED,
156
+ zIndex: Z_INDEX.ELEVATED,
158
157
  color: inverted ? COLOR.WHITE : COLOR.PRIMARY,
159
158
  px: 1,
160
159
  top: labelOffsetMap[size],
@@ -167,12 +166,10 @@ export function Select({ size: sizeParam, placeholder, leftIcon, value, label, o
167
166
  else {
168
167
  close();
169
168
  }
170
- }, color: inverted ? COLOR.WHITE : COLOR.TEXT, sx: Object.assign({ height, width: '100%', border: 'solid 0px', flexDirection: 'row', alignItems: 'center', position: 'relative', zIndex: theme.zIndex.NORMAL, background: 'none' }, typographyStyles), leftIcon: leftIcon, rightIcon: ArrowDropDownIcon }, { children: currentOption ? renderOptionLabel(currentOption) : placeholder })), isOpen && (_jsx(ListBox, { id: listboxId, options: options, selected: value, focused: stagedOptionValue, selectOption: (option) => {
169
+ }, color: inverted ? COLOR.WHITE : COLOR.TEXT, sx: Object.assign({ height, width: '100%', border: 'solid 0px', flexDirection: 'row', alignItems: 'center', position: 'relative', zIndex: Z_INDEX.NORMAL, background: 'none' }, typographyStyles), leftIcon: leftIcon, rightIcon: ArrowDropDownIcon }, { children: currentOption ? renderOptionLabel(currentOption) : placeholder })), isOpen && (_jsx(ListBox, { id: listboxId, options: options, selected: value, focused: stagedOptionValue, selectOption: (option) => {
171
170
  onSelectOption && onSelectOption(option.value);
172
171
  close();
173
172
  }, ref: refs.setFloating, renderOption: renderOption, sx: {
174
- zIndex: theme.zIndex.DIALOG,
175
- }, style: Object.assign(Object.assign({ minWidth: dropdownMinWidth }, floatingStyles), { boxShadow: inverted
176
- ? theme.boxShadow.INVERTED
177
- : theme.boxShadow.NORMAL }) }))] })));
173
+ zIndex: Z_INDEX.DIALOG,
174
+ }, style: Object.assign(Object.assign({ minWidth: dropdownMinWidth }, floatingStyles), { boxShadow: inverted ? BOX_SHADOW.INVERTED : BOX_SHADOW.NORMAL }) }))] })));
178
175
  }
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx } from "theme-ui/jsx-runtime";
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import { describe, it, expect, vi } from 'vitest';
4
+ import { TextInput, INPUT_SIZE } from './';
5
+ import { ThemeProvider } from 'theme-ui';
6
+ import { theme } from '../../../../theme';
7
+ import '@testing-library/jest-dom';
8
+ vi.mock('../../../providers/uniqueIds', () => ({
9
+ useUniqueId: (prefix) => `${prefix}-mock-id`,
10
+ }));
11
+ describe('TextInput Component', () => {
12
+ const setup = (props = {}) => {
13
+ const initialProps = Object.assign({ value: '', onChange: vi.fn(), onChangeRaw: vi.fn(), size: INPUT_SIZE.MD }, props);
14
+ return render(_jsx(ThemeProvider, Object.assign({ theme: theme }, { children: _jsx(TextInput, Object.assign({}, initialProps)) })));
15
+ };
16
+ it('renders with the correct initial value', () => {
17
+ setup({ value: 'Initial value' });
18
+ expect(screen.getByDisplayValue('Initial value')).toBeInTheDocument();
19
+ });
20
+ it('calls onChange when input value changes', () => {
21
+ const mockOnChange = vi.fn();
22
+ setup({ value: 'Initial value', onChange: mockOnChange });
23
+ const input = screen.getByRole('textbox');
24
+ fireEvent.change(input, { target: { value: 'New value' } });
25
+ expect(mockOnChange).toHaveBeenCalledWith('New value');
26
+ });
27
+ it('calls onChangeRaw with the event when input value changes', () => {
28
+ const mockOnChangeRaw = vi.fn();
29
+ setup({ value: 'Initial value', onChangeRaw: mockOnChangeRaw });
30
+ const input = screen.getByRole('textbox');
31
+ fireEvent.change(input, { target: { value: 'New value' } });
32
+ expect(mockOnChangeRaw).toHaveBeenCalled();
33
+ const value = mockOnChangeRaw.mock.calls[0][0];
34
+ expect(value).toBe('New value');
35
+ });
36
+ it('renders the left icon if provided', () => {
37
+ const MockIcon = () => _jsx("span", Object.assign({ "data-testid": "mock-icon" }, { children: "Icon" }));
38
+ setup({ leftIcon: MockIcon });
39
+ expect(screen.getByTestId('mock-icon')).toBeInTheDocument();
40
+ });
41
+ it('renders the label if provided', () => {
42
+ setup({ label: 'Test Label' });
43
+ const labelElements = screen.getAllByText('Test Label');
44
+ expect(labelElements.length).toBeGreaterThan(0);
45
+ });
46
+ it('applies the correct styles for the input size', () => {
47
+ setup({ size: INPUT_SIZE.SM });
48
+ const inputContainer = screen.getByRole('textbox');
49
+ expect(inputContainer).toHaveStyle({ height: '2.25rem;' });
50
+ });
51
+ it('disables the input if the disabled prop is true', () => {
52
+ setup({ disabled: true });
53
+ const input = screen.getByRole('textbox');
54
+ expect(input).toBeDisabled();
55
+ });
56
+ it('renders with inverted styles if the inverted prop is true', () => {
57
+ setup({ inverted: true });
58
+ const input = screen.getByRole('textbox');
59
+ expect(input).toHaveStyle({ backgroundColor: 'rgb(51, 51, 51);' });
60
+ });
61
+ });
@@ -17,6 +17,7 @@ import { Icon } from '../../Icon';
17
17
  import { COLOR } from '../../../../theme/colors';
18
18
  import { Label } from '../../text/Label';
19
19
  import { useUniqueId } from '../../../providers/uniqueIds';
20
+ import { BOX_SHADOW, Z_INDEX } from '../../../../theme/custom';
20
21
  export var INPUT_SIZE;
21
22
  (function (INPUT_SIZE) {
22
23
  INPUT_SIZE["SM"] = "sm";
@@ -61,9 +62,7 @@ export const TextInput = React.forwardRef((_a, ref) => {
61
62
  const focusStyles = disabled
62
63
  ? {}
63
64
  : {
64
- boxShadow: inverted
65
- ? theme.boxShadow.INVERTED
66
- : theme.boxShadow.NORMAL,
65
+ boxShadow: inverted ? BOX_SHADOW.INVERTED : BOX_SHADOW.NORMAL,
67
66
  };
68
67
  return (_jsxs("div", Object.assign({ ref: ref, onClick: onClick, id: id, sx: Object.assign(Object.assign({ height,
69
68
  display, px: paddingX, backgroundColor: inverted ? COLOR.DARK_GRAY : COLOR.WHITE, borderRadius: '4px', flexDirection: 'row', position: 'relative', alignItems: 'center', ':hover': focusStyles, ':focus': focusStyles }, typographyStyles), sx) }, { children: [_jsx("fieldset", Object.assign({ sx: {
@@ -79,7 +78,7 @@ export const TextInput = React.forwardRef((_a, ref) => {
79
78
  overflow: 'hidden',
80
79
  minWidth: '0%',
81
80
  borderColor: inverted ? COLOR.GRAY : COLOR.LIGHT_GRAY,
82
- zIndex: theme.zIndex.ELEVATED,
81
+ zIndex: Z_INDEX.ELEVATED,
83
82
  } }, { children: _jsx("legend", Object.assign({ style: {
84
83
  width: 'auto',
85
84
  float: 'unset',
@@ -102,7 +101,7 @@ export const TextInput = React.forwardRef((_a, ref) => {
102
101
  fontSize: getIconSize(typographyStyles.fontSize),
103
102
  } })), _jsx("input", Object.assign({ value: value, onChange: (event) => {
104
103
  onChange && onChange(event.target.value);
105
- onChangeRaw && onChangeRaw(event);
104
+ onChangeRaw && onChangeRaw(event.target.value);
106
105
  }, sx: Object.assign(Object.assign({ px: 0, py: 0, width: '100%', border: 'none', backgroundColor: inverted ? COLOR.DARK_GRAY : COLOR.WHITE, color: inverted ? COLOR.EXTRA_LIGHT_GRAY : COLOR.TEXT }, typographyStyles), { '::placeholder': {
107
106
  color: inverted ? COLOR.LIGHT_GRAY : COLOR.GRAY,
108
107
  }, ':focus': {
@@ -1 +1 @@
1
- export {};
1
+ import '@testing-library/jest-dom';
@@ -1,34 +1,62 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { render } from '../../../utils/test';
3
- import { Icon } from '.';
4
- import { SPACING } from '../../../theme';
5
- import { FONT_SIZE, fontSizeMap } from '../../../theme/typography';
6
- import { vitest } from 'vitest';
7
- const MockIcon = () => {
8
- return _jsx("div", { children: "Mock Icon" });
9
- };
10
- describe('Icon', () => {
11
- it('renders component passed to it', () => {
12
- const { getByText } = render(_jsx(Icon, { icon: MockIcon }));
13
- expect(getByText('Mock Icon')).toBeInTheDocument();
14
- });
15
- it('passes font size to outer wrapper', () => {
16
- const { container } = render(_jsx(Icon, { icon: MockIcon, size: FONT_SIZE.MD }));
17
- expect(container.childNodes[0]).toHaveStyle({
18
- fontSize: fontSizeMap[FONT_SIZE.MD],
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import { vi, describe, it, beforeEach, afterEach, expect } from 'vitest';
5
+ import { Icon } from './';
6
+ import { COLOR } from '../../../theme/colors';
7
+ import { FONT_SIZE } from '../../../theme/typography';
8
+ import { useEnvironment } from '../../providers/env';
9
+ vi.mock('../../providers/env', () => ({
10
+ useEnvironment: vi.fn(),
11
+ }));
12
+ describe('Icon Component', () => {
13
+ const MockIcon = () => _jsx("svg", { "data-testid": "mock-icon" });
14
+ const mockFlagInDevelopment = vi.fn();
15
+ beforeEach(() => {
16
+ ;
17
+ useEnvironment.mockReturnValue({
18
+ flagInDevelopment: mockFlagInDevelopment,
19
19
  });
20
20
  });
21
- it('passes custom styles to outer wrapper', () => {
22
- const { container } = render(_jsx(Icon, { icon: MockIcon, sx: { p: 1 } }));
23
- expect(container.childNodes[0]).toHaveStyle({
24
- padding: SPACING[1],
21
+ afterEach(() => {
22
+ vi.clearAllMocks();
23
+ });
24
+ it('renders the icon component', () => {
25
+ render(_jsx(Icon, { icon: MockIcon }));
26
+ expect(screen.getByTestId('mock-icon')).toBeInTheDocument();
27
+ });
28
+ it('applies the correct size and color', () => {
29
+ render(_jsx(Icon, { icon: MockIcon, size: FONT_SIZE.LG, color: COLOR.SECONDARY }));
30
+ const iconElement = screen.getByTestId('mock-icon');
31
+ expect(iconElement).toHaveStyle({
32
+ fontSize: 'inherit',
33
+ color: 'color: rgb(211, 159, 16)',
25
34
  });
26
35
  });
27
- it('icon can be clicked', () => {
28
- const onClick = vitest.fn();
29
- const { getByLabelText } = render(_jsx(Icon, { icon: MockIcon, sx: { p: 1 }, onClick: onClick, "aria-label": "My Icon" }));
30
- const icon = getByLabelText('My Icon');
31
- icon.click();
32
- expect(onClick).toHaveBeenCalled();
36
+ it('calls flagInDevelopment if onClick is provided without aria-label', () => {
37
+ render(_jsx(Icon, { icon: MockIcon, onClick: vi.fn() }));
38
+ expect(mockFlagInDevelopment).toHaveBeenCalledWith('Icon component with onClick should have an aria-label and tabIndex={0}');
39
+ });
40
+ it('does not call flagInDevelopment if onClick is provided with aria-label', () => {
41
+ render(_jsx(Icon, { icon: MockIcon, onClick: vi.fn(), "aria-label": "icon" }));
42
+ expect(mockFlagInDevelopment).not.toHaveBeenCalled();
43
+ });
44
+ it('renders with tabIndex and role when onClick is provided', () => {
45
+ render(_jsx(Icon, { icon: MockIcon, onClick: vi.fn(), "aria-label": "icon" }));
46
+ const divElement = screen.getByRole('button');
47
+ expect(divElement).toHaveAttribute('tabIndex', '0');
48
+ });
49
+ it('does not render with tabIndex and role when onClick is not provided', () => {
50
+ render(_jsx(Icon, { icon: MockIcon }));
51
+ const divElement = screen.getByTestId('mock-icon').parentElement;
52
+ expect(divElement).not.toHaveAttribute('tabIndex');
53
+ expect(divElement).not.toHaveAttribute('role', 'button');
54
+ });
55
+ it('handles onClick event', () => {
56
+ const handleClick = vi.fn();
57
+ render(_jsx(Icon, { icon: MockIcon, onClick: handleClick, "aria-label": "icon" }));
58
+ const divElement = screen.getByRole('button');
59
+ fireEvent.click(divElement);
60
+ expect(handleClick).toHaveBeenCalledTimes(1);
33
61
  });
34
62
  });
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { screen } from '@testing-library/react';
3
+ import { describe, it, expect, vi, afterEach } from 'vitest';
4
+ import { ReadMore } from './';
5
+ import { TYPOGRAPHY_TYPE } from '../../../../theme/typography';
6
+ import { render } from '../../../../utils/test';
7
+ describe('ReadMore Component', () => {
8
+ const mockText = 'This is a test content for the ReadMore component. It should truncate properly if it exceeds the given number of lines.';
9
+ const defaultProps = {
10
+ typography: TYPOGRAPHY_TYPE.PARAGRAPH_MEDIUM,
11
+ lines: 3,
12
+ fixedHeight: false,
13
+ };
14
+ afterEach(() => {
15
+ vi.clearAllMocks();
16
+ });
17
+ it('renders without crashing', () => {
18
+ render(_jsx(ReadMore, Object.assign({}, defaultProps, { children: mockText })));
19
+ expect(screen.getByRole('paragraph')).toBeInTheDocument();
20
+ });
21
+ it('displays the correct text content', () => {
22
+ render(_jsx(ReadMore, Object.assign({}, defaultProps, { children: mockText })));
23
+ expect(screen.getByText(mockText)).toBeInTheDocument();
24
+ });
25
+ it('applies the correct typography styles', () => {
26
+ render(_jsx(ReadMore, Object.assign({}, defaultProps)));
27
+ const paragraph = screen.getByRole('paragraph');
28
+ expect(paragraph).toHaveStyle('font-size: 1rem');
29
+ });
30
+ it('does not show ellipsis if content fits within allotted space', () => {
31
+ const shortText = 'Short text';
32
+ render(_jsx(ReadMore, Object.assign({}, defaultProps, { children: shortText })));
33
+ expect(screen.queryByText('Read More...')).not.toBeInTheDocument();
34
+ });
35
+ });
@@ -2,8 +2,18 @@ import { PropsWithChildren } from 'react';
2
2
  import { ThemeUICSSObject } from 'theme-ui';
3
3
  import { FONT, FONT_SIZE, FONT_WEIGHT, LINE_HEIGHT } from './typography';
4
4
  import { COLOR } from './colors';
5
- import { BOX_SHADOW, Z_INDEX } from './custom';
6
5
  export declare const BREAKPOINTS: string[];
6
+ export declare enum BOX_SHADOW {
7
+ NORMAL = "0 0 8px #dfdfdf",
8
+ EMPHASIZED = "0 0 12px #d3d3d3",
9
+ INVERTED = "0 0 8px #767676"
10
+ }
11
+ export declare enum Z_INDEX {
12
+ NORMAL = 1,
13
+ ELEVATED = 2,
14
+ DIALOG = 3,
15
+ MODAL = 4
16
+ }
7
17
  export declare const SPACING: string[];
8
18
  export declare const theme: {
9
19
  colors: {
@@ -2,8 +2,20 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { ThemeUIProvider, useThemeUI } from 'theme-ui';
3
3
  import { fontStyles, } from './typography';
4
4
  import { colors } from './colors';
5
- import { BOX_SHADOW, Z_INDEX } from './custom';
6
5
  export const BREAKPOINTS = ['576px', '768px', '992px', '1200px', '1400px'];
6
+ export var BOX_SHADOW;
7
+ (function (BOX_SHADOW) {
8
+ BOX_SHADOW["NORMAL"] = "0 0 8px #dfdfdf";
9
+ BOX_SHADOW["EMPHASIZED"] = "0 0 12px #d3d3d3";
10
+ BOX_SHADOW["INVERTED"] = "0 0 8px #767676";
11
+ })(BOX_SHADOW || (BOX_SHADOW = {}));
12
+ export var Z_INDEX;
13
+ (function (Z_INDEX) {
14
+ Z_INDEX[Z_INDEX["NORMAL"] = 1] = "NORMAL";
15
+ Z_INDEX[Z_INDEX["ELEVATED"] = 2] = "ELEVATED";
16
+ Z_INDEX[Z_INDEX["DIALOG"] = 3] = "DIALOG";
17
+ Z_INDEX[Z_INDEX["MODAL"] = 4] = "MODAL";
18
+ })(Z_INDEX || (Z_INDEX = {}));
7
19
  export const SPACING = [
8
20
  '0rem',
9
21
  '0.25rem',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ndlib/component-library",
3
- "version": "0.0.118",
3
+ "version": "0.0.120",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "files": [
@@ -63,6 +63,7 @@
63
63
  "@typescript-eslint/eslint-plugin": "^5.57.1",
64
64
  "@typescript-eslint/parser": "^5.57.1",
65
65
  "@vitejs/plugin-react-swc": "^3.0.0",
66
+ "@vitest/coverage-c8": "^0.33.0",
66
67
  "eslint": "^8.38.0",
67
68
  "eslint-plugin-react-hooks": "^4.6.0",
68
69
  "eslint-plugin-react-refresh": "^0.3.4",
@@ -77,7 +78,7 @@
77
78
  "storybook": "^7.0.17",
78
79
  "theme-ui": "^0.16.1",
79
80
  "typescript": "^5.0.2",
80
- "vite": "^4.5.2",
81
+ "vite": "^4.5.3",
81
82
  "vitest": "^0.31.4"
82
83
  },
83
84
  "prettier": {
@@ -87,6 +88,7 @@
87
88
  },
88
89
  "dependencies": {
89
90
  "@floating-ui/react": "^0.24.5",
91
+ "@vitest/coverage-istanbul": "^1.6.0",
90
92
  "react-beautiful-dnd": "^13.1.1",
91
93
  "react-datepicker": "^4.16.0",
92
94
  "react-markdown": "^8.0.7",