@openmrs/esm-styleguide 10.0.1-pre.4966 → 10.0.1-pre.4968
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/.turbo/turbo-build.log +1 -1
- package/package.json +25 -25
- package/src/datepicker/datepicker.test.tsx +133 -19
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
[0] Successfully compiled: 123 files, copied 164 files with swc (
|
|
1
|
+
[0] Successfully compiled: 123 files, copied 164 files with swc (251.02ms)
|
|
2
2
|
[0] swc --strip-leading-paths --copy-files src -d dist && svgo -rf dist --quiet exited with code 0
|
|
3
3
|
[1] tsc --project tsconfig.build.json exited with code 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openmrs/esm-styleguide",
|
|
3
|
-
"version": "10.0.1-pre.
|
|
3
|
+
"version": "10.0.1-pre.4968",
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
5
|
"description": "The styleguide for OpenMRS SPA",
|
|
6
6
|
"module": "dist/internal.js",
|
|
@@ -77,18 +77,18 @@
|
|
|
77
77
|
"react-aria-components": "^1.7.1"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
|
-
"@openmrs/esm-api": "^10.0.1-pre.
|
|
81
|
-
"@openmrs/esm-config": "^10.0.1-pre.
|
|
82
|
-
"@openmrs/esm-emr-api": "^10.0.1-pre.
|
|
83
|
-
"@openmrs/esm-error-handling": "^10.0.1-pre.
|
|
84
|
-
"@openmrs/esm-extensions": "^10.0.1-pre.
|
|
85
|
-
"@openmrs/esm-globals": "^10.0.1-pre.
|
|
86
|
-
"@openmrs/esm-navigation": "^10.0.1-pre.
|
|
87
|
-
"@openmrs/esm-react-utils": "^10.0.1-pre.
|
|
88
|
-
"@openmrs/esm-routes": "^10.0.1-pre.
|
|
89
|
-
"@openmrs/esm-state": "^10.0.1-pre.
|
|
90
|
-
"@openmrs/esm-translations": "^10.0.1-pre.
|
|
91
|
-
"@openmrs/esm-utils": "^10.0.1-pre.
|
|
80
|
+
"@openmrs/esm-api": "^10.0.1-pre.4968",
|
|
81
|
+
"@openmrs/esm-config": "^10.0.1-pre.4968",
|
|
82
|
+
"@openmrs/esm-emr-api": "^10.0.1-pre.4968",
|
|
83
|
+
"@openmrs/esm-error-handling": "^10.0.1-pre.4968",
|
|
84
|
+
"@openmrs/esm-extensions": "^10.0.1-pre.4968",
|
|
85
|
+
"@openmrs/esm-globals": "^10.0.1-pre.4968",
|
|
86
|
+
"@openmrs/esm-navigation": "^10.0.1-pre.4968",
|
|
87
|
+
"@openmrs/esm-react-utils": "^10.0.1-pre.4968",
|
|
88
|
+
"@openmrs/esm-routes": "^10.0.1-pre.4968",
|
|
89
|
+
"@openmrs/esm-state": "^10.0.1-pre.4968",
|
|
90
|
+
"@openmrs/esm-translations": "^10.0.1-pre.4968",
|
|
91
|
+
"@openmrs/esm-utils": "^10.0.1-pre.4968",
|
|
92
92
|
"dayjs": "1.x",
|
|
93
93
|
"i18next": "25.x",
|
|
94
94
|
"react": "18.x",
|
|
@@ -98,18 +98,18 @@
|
|
|
98
98
|
"swr": "2.x"
|
|
99
99
|
},
|
|
100
100
|
"devDependencies": {
|
|
101
|
-
"@openmrs/esm-api": "10.0.1-pre.
|
|
102
|
-
"@openmrs/esm-config": "10.0.1-pre.
|
|
103
|
-
"@openmrs/esm-emr-api": "10.0.1-pre.
|
|
104
|
-
"@openmrs/esm-error-handling": "10.0.1-pre.
|
|
105
|
-
"@openmrs/esm-extensions": "10.0.1-pre.
|
|
106
|
-
"@openmrs/esm-globals": "10.0.1-pre.
|
|
107
|
-
"@openmrs/esm-navigation": "10.0.1-pre.
|
|
108
|
-
"@openmrs/esm-react-utils": "10.0.1-pre.
|
|
109
|
-
"@openmrs/esm-routes": "10.0.1-pre.
|
|
110
|
-
"@openmrs/esm-state": "10.0.1-pre.
|
|
111
|
-
"@openmrs/esm-translations": "10.0.1-pre.
|
|
112
|
-
"@openmrs/esm-utils": "10.0.1-pre.
|
|
101
|
+
"@openmrs/esm-api": "10.0.1-pre.4968",
|
|
102
|
+
"@openmrs/esm-config": "10.0.1-pre.4968",
|
|
103
|
+
"@openmrs/esm-emr-api": "10.0.1-pre.4968",
|
|
104
|
+
"@openmrs/esm-error-handling": "10.0.1-pre.4968",
|
|
105
|
+
"@openmrs/esm-extensions": "10.0.1-pre.4968",
|
|
106
|
+
"@openmrs/esm-globals": "10.0.1-pre.4968",
|
|
107
|
+
"@openmrs/esm-navigation": "10.0.1-pre.4968",
|
|
108
|
+
"@openmrs/esm-react-utils": "10.0.1-pre.4968",
|
|
109
|
+
"@openmrs/esm-routes": "10.0.1-pre.4968",
|
|
110
|
+
"@openmrs/esm-state": "10.0.1-pre.4968",
|
|
111
|
+
"@openmrs/esm-translations": "10.0.1-pre.4968",
|
|
112
|
+
"@openmrs/esm-utils": "10.0.1-pre.4968",
|
|
113
113
|
"@swc/cli": "0.8.1",
|
|
114
114
|
"@swc/core": "1.15.21",
|
|
115
115
|
"@types/geopattern": "^1.2.9",
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import type { i18n } from 'i18next';
|
|
3
3
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
4
|
import '@testing-library/jest-dom/vitest';
|
|
5
|
-
import { render, screen, within } from '@testing-library/react';
|
|
5
|
+
import { render, screen, waitFor, within } from '@testing-library/react';
|
|
6
6
|
import userEvent from '@testing-library/user-event';
|
|
7
7
|
import { useConfig } from '@openmrs/esm-react-utils/mock';
|
|
8
8
|
import { OpenmrsDatePicker } from './index';
|
|
@@ -41,26 +41,20 @@ describe('OpenmrsDatePicker', () => {
|
|
|
41
41
|
it('should render RTL layout for Arabic locale', () => {
|
|
42
42
|
window.i18next = { language: 'ar' } as i18n;
|
|
43
43
|
useConfig.mockReturnValue({ preferredDateLocale: {} });
|
|
44
|
-
|
|
45
44
|
render(<OpenmrsDatePicker aria-label="datepicker" />);
|
|
46
45
|
const input = screen.getByLabelText('datepicker');
|
|
47
46
|
const text = input.textContent?.replace(/\u200F/g, '');
|
|
48
|
-
|
|
49
47
|
expect(text).toBe('يوم/شهر/سنة');
|
|
50
|
-
|
|
51
48
|
window.i18next = { language: 'en' } as i18n;
|
|
52
49
|
});
|
|
53
50
|
|
|
54
51
|
it('should render RTL layout for Amharic locale', () => {
|
|
55
52
|
window.i18next = { language: 'am' } as i18n;
|
|
56
53
|
useConfig.mockReturnValue({ preferredDateLocale: {} });
|
|
57
|
-
|
|
58
54
|
render(<OpenmrsDatePicker aria-label="datepicker" />);
|
|
59
55
|
const input = screen.getByLabelText('datepicker');
|
|
60
56
|
const text = input.textContent?.replace(/\u200F/g, '');
|
|
61
|
-
|
|
62
57
|
expect(text).toBe('ቀቀ/ሚሜ/ዓዓዓዓ');
|
|
63
|
-
|
|
64
58
|
window.i18next = { language: 'en' } as i18n;
|
|
65
59
|
});
|
|
66
60
|
});
|
|
@@ -90,6 +84,24 @@ describe('OpenmrsDatePicker', () => {
|
|
|
90
84
|
);
|
|
91
85
|
consoleWarnSpy.mockRestore();
|
|
92
86
|
});
|
|
87
|
+
|
|
88
|
+
it('should support the deprecated label prop', () => {
|
|
89
|
+
render(<OpenmrsDatePicker label="Legacy label" />);
|
|
90
|
+
expect(screen.getByText('Legacy label')).toBeInTheDocument();
|
|
91
|
+
expect(screen.getByText('Legacy label')).toHaveClass('cds--label');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should not warn when aria-labelledby is provided', () => {
|
|
95
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
96
|
+
render(
|
|
97
|
+
<>
|
|
98
|
+
<span id="my-label">External label</span>
|
|
99
|
+
<OpenmrsDatePicker aria-labelledby="my-label" labelText="" />
|
|
100
|
+
</>,
|
|
101
|
+
);
|
|
102
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
103
|
+
consoleWarnSpy.mockRestore();
|
|
104
|
+
});
|
|
93
105
|
});
|
|
94
106
|
|
|
95
107
|
describe('value display', () => {
|
|
@@ -104,6 +116,18 @@ describe('OpenmrsDatePicker', () => {
|
|
|
104
116
|
const input = screen.getByLabelText('datepicker');
|
|
105
117
|
expect(input).toHaveTextContent('18/06/2025');
|
|
106
118
|
});
|
|
119
|
+
|
|
120
|
+
it('should display placeholder when value is undefined', () => {
|
|
121
|
+
render(<OpenmrsDatePicker aria-label="datepicker" value={undefined} />);
|
|
122
|
+
const input = screen.getByLabelText('datepicker');
|
|
123
|
+
expect(input).toHaveTextContent('dd/mm/yyyy');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should display placeholder when value is null', () => {
|
|
127
|
+
render(<OpenmrsDatePicker aria-label="datepicker" value={null} />);
|
|
128
|
+
const input = screen.getByLabelText('datepicker');
|
|
129
|
+
expect(input).toHaveTextContent('dd/mm/yyyy');
|
|
130
|
+
});
|
|
107
131
|
});
|
|
108
132
|
|
|
109
133
|
describe('invalid state', () => {
|
|
@@ -121,6 +145,11 @@ describe('OpenmrsDatePicker', () => {
|
|
|
121
145
|
render(<OpenmrsDatePicker aria-label="datepicker" isInvalid={true} invalidText="Bad date" />);
|
|
122
146
|
expect(screen.getByText('Bad date')).toBeInTheDocument();
|
|
123
147
|
});
|
|
148
|
+
|
|
149
|
+
it('should be invalid when either invalid or isInvalid is true', () => {
|
|
150
|
+
render(<OpenmrsDatePicker aria-label="datepicker" invalid={true} isInvalid={false} invalidText="Error" />);
|
|
151
|
+
expect(screen.getByText('Error')).toBeInTheDocument();
|
|
152
|
+
});
|
|
124
153
|
});
|
|
125
154
|
|
|
126
155
|
describe('disabled state', () => {
|
|
@@ -129,6 +158,12 @@ describe('OpenmrsDatePicker', () => {
|
|
|
129
158
|
const label = screen.getByText('Date');
|
|
130
159
|
expect(label).toHaveClass('cds--label--disabled');
|
|
131
160
|
});
|
|
161
|
+
|
|
162
|
+
it('should not show disabled styling when isDisabled is false', () => {
|
|
163
|
+
render(<OpenmrsDatePicker labelText="Date" isDisabled={false} />);
|
|
164
|
+
const label = screen.getByText('Date');
|
|
165
|
+
expect(label).not.toHaveClass('cds--label--disabled');
|
|
166
|
+
});
|
|
132
167
|
});
|
|
133
168
|
|
|
134
169
|
describe('onChange callbacks', () => {
|
|
@@ -136,57 +171,105 @@ describe('OpenmrsDatePicker', () => {
|
|
|
136
171
|
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
137
172
|
const onChange = vi.fn();
|
|
138
173
|
const onChangeRaw = vi.fn();
|
|
139
|
-
|
|
140
174
|
render(<OpenmrsDatePicker aria-label="datepicker" onChange={onChange} onChangeRaw={onChangeRaw} />);
|
|
141
|
-
|
|
142
175
|
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
143
176
|
'An OpenmrsDatePicker component was created with both onChange and onChangeRaw handlers defined. Only onChangeRaw will be used.',
|
|
144
177
|
);
|
|
145
178
|
consoleErrorSpy.mockRestore();
|
|
146
179
|
});
|
|
180
|
+
|
|
181
|
+
it('should not log an error when only onChange is provided', () => {
|
|
182
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
183
|
+
render(<OpenmrsDatePicker aria-label="datepicker" onChange={vi.fn()} />);
|
|
184
|
+
expect(consoleErrorSpy).not.toHaveBeenCalled();
|
|
185
|
+
consoleErrorSpy.mockRestore();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should not log an error when only onChangeRaw is provided', () => {
|
|
189
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
190
|
+
render(<OpenmrsDatePicker aria-label="datepicker" onChangeRaw={vi.fn()} />);
|
|
191
|
+
expect(consoleErrorSpy).not.toHaveBeenCalled();
|
|
192
|
+
consoleErrorSpy.mockRestore();
|
|
193
|
+
});
|
|
147
194
|
});
|
|
148
195
|
|
|
149
196
|
describe('size prop', () => {
|
|
150
197
|
/* eslint-disable testing-library/no-container, testing-library/no-node-access */
|
|
151
198
|
it('should apply md size classes by default', () => {
|
|
152
199
|
const { container } = render(<OpenmrsDatePicker aria-label="datepicker" />);
|
|
153
|
-
const wrapper = container.querySelector('.cds--date-picker-input__wrapper')
|
|
200
|
+
const wrapper = container.querySelector('.cds--date-picker-input__wrapper');
|
|
201
|
+
expect(wrapper).not.toBeNull();
|
|
154
202
|
expect(wrapper).toHaveClass(styles.inputWrapperMd);
|
|
155
203
|
expect(screen.getByRole('button')).toHaveClass(styles.flatButtonMd);
|
|
156
204
|
});
|
|
157
205
|
|
|
158
206
|
it('should apply sm size classes when size="sm"', () => {
|
|
159
207
|
const { container } = render(<OpenmrsDatePicker aria-label="datepicker" size="sm" />);
|
|
160
|
-
const wrapper = container.querySelector('.cds--date-picker-input__wrapper')
|
|
208
|
+
const wrapper = container.querySelector('.cds--date-picker-input__wrapper');
|
|
209
|
+
expect(wrapper).not.toBeNull();
|
|
161
210
|
expect(wrapper).toHaveClass(styles.inputWrapperSm);
|
|
162
211
|
expect(screen.getByRole('button')).toHaveClass(styles.flatButtonSm);
|
|
163
212
|
});
|
|
164
213
|
|
|
165
214
|
it('should apply md size classes when size="md"', () => {
|
|
166
215
|
const { container } = render(<OpenmrsDatePicker aria-label="datepicker" size="md" />);
|
|
167
|
-
const wrapper = container.querySelector('.cds--date-picker-input__wrapper')
|
|
216
|
+
const wrapper = container.querySelector('.cds--date-picker-input__wrapper');
|
|
217
|
+
expect(wrapper).not.toBeNull();
|
|
168
218
|
expect(wrapper).toHaveClass(styles.inputWrapperMd);
|
|
169
219
|
expect(screen.getByRole('button')).toHaveClass(styles.flatButtonMd);
|
|
170
220
|
});
|
|
171
221
|
|
|
172
222
|
it('should apply lg size classes when size="lg"', () => {
|
|
173
223
|
const { container } = render(<OpenmrsDatePicker aria-label="datepicker" size="lg" />);
|
|
174
|
-
const wrapper = container.querySelector('.cds--date-picker-input__wrapper')
|
|
224
|
+
const wrapper = container.querySelector('.cds--date-picker-input__wrapper');
|
|
225
|
+
expect(wrapper).not.toBeNull();
|
|
175
226
|
expect(wrapper).toHaveClass(styles.inputWrapperLg);
|
|
176
227
|
expect(screen.getByRole('button')).toHaveClass(styles.flatButtonLg);
|
|
177
228
|
});
|
|
178
229
|
/* eslint-enable testing-library/no-container, testing-library/no-node-access */
|
|
179
230
|
});
|
|
180
231
|
|
|
232
|
+
describe('CSS class variants', () => {
|
|
233
|
+
/* eslint-disable testing-library/no-container, testing-library/no-node-access */
|
|
234
|
+
it('should apply short class when short prop is true', () => {
|
|
235
|
+
const { container } = render(<OpenmrsDatePicker aria-label="datepicker" short={true} />);
|
|
236
|
+
const datePicker = container.querySelector('.cds--date-picker');
|
|
237
|
+
expect(datePicker).not.toBeNull();
|
|
238
|
+
expect(datePicker).toHaveClass('cds--date-picker--short');
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should not apply short class when short prop is false', () => {
|
|
242
|
+
const { container } = render(<OpenmrsDatePicker aria-label="datepicker" short={false} />);
|
|
243
|
+
const datePicker = container.querySelector('.cds--date-picker');
|
|
244
|
+
expect(datePicker).not.toBeNull();
|
|
245
|
+
expect(datePicker).not.toHaveClass('cds--date-picker--short');
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('should apply light class when light prop is true', () => {
|
|
249
|
+
const { container } = render(<OpenmrsDatePicker aria-label="datepicker" light={true} />);
|
|
250
|
+
const datePicker = container.querySelector('.cds--date-picker');
|
|
251
|
+
expect(datePicker).not.toBeNull();
|
|
252
|
+
expect(datePicker).toHaveClass('cds--date-picker--light');
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('should apply custom className to the outer div', () => {
|
|
256
|
+
const { container } = render(<OpenmrsDatePicker aria-label="datepicker" className="my-custom-class" />);
|
|
257
|
+
const outerDiv = container.querySelector('.cds--form-item');
|
|
258
|
+
expect(outerDiv).not.toBeNull();
|
|
259
|
+
expect(outerDiv).toHaveClass('my-custom-class');
|
|
260
|
+
});
|
|
261
|
+
/* eslint-enable testing-library/no-container, testing-library/no-node-access */
|
|
262
|
+
});
|
|
263
|
+
|
|
181
264
|
describe('calendar popover', () => {
|
|
182
265
|
it('should open the calendar popover when the calendar button is clicked', async () => {
|
|
183
266
|
const user = userEvent.setup();
|
|
184
267
|
render(<OpenmrsDatePicker aria-label="datepicker" />);
|
|
185
|
-
|
|
186
268
|
const button = screen.getByRole('button');
|
|
187
269
|
await user.click(button);
|
|
188
|
-
|
|
189
|
-
|
|
270
|
+
await waitFor(() => {
|
|
271
|
+
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
272
|
+
});
|
|
190
273
|
expect(screen.getByRole('grid')).toBeInTheDocument();
|
|
191
274
|
});
|
|
192
275
|
|
|
@@ -198,14 +281,45 @@ describe('OpenmrsDatePicker', () => {
|
|
|
198
281
|
value={new Date(DEFAULT_MIN_DATE_FLOOR.year, DEFAULT_MIN_DATE_FLOOR.month - 1, DEFAULT_MIN_DATE_FLOOR.day)}
|
|
199
282
|
/>,
|
|
200
283
|
);
|
|
201
|
-
|
|
202
284
|
await user.click(screen.getByRole('button'));
|
|
203
|
-
|
|
285
|
+
await waitFor(() => {
|
|
286
|
+
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
287
|
+
});
|
|
204
288
|
const dialog = screen.getByRole('dialog');
|
|
205
289
|
const previousButton = within(dialog).getByRole('button', { name: /previous/i });
|
|
206
|
-
|
|
207
290
|
expect(previousButton).toBeInTheDocument();
|
|
208
291
|
expect(previousButton).toBeDisabled();
|
|
209
292
|
});
|
|
293
|
+
|
|
294
|
+
it('should respect maxDate and disable next-month navigation at the max boundary', async () => {
|
|
295
|
+
const user = userEvent.setup();
|
|
296
|
+
render(
|
|
297
|
+
<OpenmrsDatePicker aria-label="datepicker" value={new Date(2025, 5, 15)} maxDate={new Date(2025, 5, 30)} />,
|
|
298
|
+
);
|
|
299
|
+
await user.click(screen.getByRole('button'));
|
|
300
|
+
await waitFor(() => {
|
|
301
|
+
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
302
|
+
});
|
|
303
|
+
const dialog = screen.getByRole('dialog');
|
|
304
|
+
const nextButtons = within(dialog).getAllByRole('button', { name: /next/i });
|
|
305
|
+
expect(nextButtons[0]).toBeDisabled();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should select a date and call onChange', async () => {
|
|
309
|
+
const user = userEvent.setup();
|
|
310
|
+
const onChange = vi.fn();
|
|
311
|
+
render(<OpenmrsDatePicker aria-label="datepicker" defaultValue={new Date(2025, 5, 1)} onChange={onChange} />);
|
|
312
|
+
await user.click(screen.getByRole('button'));
|
|
313
|
+
await waitFor(() => {
|
|
314
|
+
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
315
|
+
});
|
|
316
|
+
const dialog = screen.getByRole('dialog');
|
|
317
|
+
const grid = within(dialog).getByRole('grid');
|
|
318
|
+
const dayCell = within(grid).getByRole('button', { name: /15/ });
|
|
319
|
+
await user.click(dayCell);
|
|
320
|
+
await waitFor(() => {
|
|
321
|
+
expect(onChange).toHaveBeenCalledWith(new Date(2025, 5, 15));
|
|
322
|
+
});
|
|
323
|
+
});
|
|
210
324
|
});
|
|
211
325
|
});
|