@astral/ui 1.45.0 → 1.47.0
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/DatePicker/DateCalendar/DateCalendarBtn/DateCalendarBtn.js +1 -1
- package/DatePicker/DatePicker.d.ts +22 -2
- package/DatePicker/DatePicker.js +16 -31
- package/DatePicker/DatePicker.test.d.ts +1 -0
- package/DatePicker/DatePicker.test.js +77 -0
- package/DatePicker/hooks/index.d.ts +3 -0
- package/DatePicker/hooks/index.js +19 -0
- package/DatePicker/hooks/useMaskedValue/index.d.ts +1 -0
- package/DatePicker/hooks/useMaskedValue/index.js +17 -0
- package/DatePicker/hooks/useMaskedValue/useMaskedValue.d.ts +27 -0
- package/DatePicker/hooks/useMaskedValue/useMaskedValue.js +44 -0
- package/DatePicker/hooks/useSelectedBaseDate/useSelectedBaseDate.d.ts +1 -1
- package/DatePicker/hooks/useSelectedBaseDate/useSelectedBaseDate.js +5 -1
- package/DropdownButton/DropdownButton.test.d.ts +1 -0
- package/DropdownButton/DropdownButton.test.js +37 -0
- package/esm/DatePicker/DateCalendar/DateCalendarBtn/DateCalendarBtn.js +1 -1
- package/esm/DatePicker/DatePicker.d.ts +22 -2
- package/esm/DatePicker/DatePicker.js +16 -31
- package/esm/DatePicker/DatePicker.test.d.ts +1 -0
- package/esm/DatePicker/DatePicker.test.js +75 -0
- package/esm/DatePicker/hooks/index.d.ts +3 -0
- package/esm/DatePicker/hooks/index.js +3 -0
- package/esm/DatePicker/hooks/useMaskedValue/index.d.ts +1 -0
- package/esm/DatePicker/hooks/useMaskedValue/index.js +1 -0
- package/esm/DatePicker/hooks/useMaskedValue/useMaskedValue.d.ts +27 -0
- package/esm/DatePicker/hooks/useMaskedValue/useMaskedValue.js +40 -0
- package/esm/DatePicker/hooks/useSelectedBaseDate/useSelectedBaseDate.d.ts +1 -1
- package/esm/DatePicker/hooks/useSelectedBaseDate/useSelectedBaseDate.js +6 -2
- package/esm/DropdownButton/DropdownButton.test.d.ts +1 -0
- package/esm/DropdownButton/DropdownButton.test.js +35 -0
- package/package.json +2 -2
|
@@ -18,5 +18,5 @@ const Tooltip_1 = require("../../../Tooltip");
|
|
|
18
18
|
const styles_1 = require("./styles");
|
|
19
19
|
exports.DateCalendarBtn = (0, react_1.forwardRef)((_a, ref) => {
|
|
20
20
|
var { title = '', disabled, selected } = _a, props = __rest(_a, ["title", "disabled", "selected"]);
|
|
21
|
-
return disabled ? ((0, jsx_runtime_1.jsx)(styles_1.DateCalendarBtnWrapper, Object.assign({ ref: ref, variant: selected ? 'contained' : 'text', disabled: true }, props))) : ((0, jsx_runtime_1.jsx)(Tooltip_1.Tooltip, Object.assign({ title: title, disableInteractive: true }, { children: (0, jsx_runtime_1.jsx)(styles_1.DateCalendarBtnWrapper, Object.assign({ ref: ref, variant: selected ? 'contained' : 'text' }, props)) })));
|
|
21
|
+
return disabled ? ((0, jsx_runtime_1.jsx)(styles_1.DateCalendarBtnWrapper, Object.assign({ ref: ref, variant: selected ? 'contained' : 'text', "aria-selected": selected, disabled: true }, props))) : ((0, jsx_runtime_1.jsx)(Tooltip_1.Tooltip, Object.assign({ title: title, disableInteractive: true }, { children: (0, jsx_runtime_1.jsx)(styles_1.DateCalendarBtnWrapper, Object.assign({ ref: ref, variant: selected ? 'contained' : 'text', "aria-selected": selected }, props)) })));
|
|
22
22
|
});
|
|
@@ -5,22 +5,42 @@ import { Reason } from '../types';
|
|
|
5
5
|
import { MinMaxDate } from './types';
|
|
6
6
|
import { MondayFirst } from './DayPicker';
|
|
7
7
|
export type DatePickerProps = MondayFirst & Partial<MinMaxDate> & {
|
|
8
|
+
/**
|
|
9
|
+
* @description Маска для инпута даты
|
|
10
|
+
* @default 'DD.MM.YYYY'
|
|
11
|
+
* */
|
|
8
12
|
mask?: DateMask;
|
|
9
|
-
|
|
13
|
+
/**
|
|
14
|
+
* @description Обработчик изменения состояния. Передает только Date object, если дата невалидна, то будет Invalid date
|
|
15
|
+
* */
|
|
16
|
+
onChange?: (date?: Date) => void;
|
|
10
17
|
onBlur?: (e: FocusEvent<HTMLInputElement>) => void;
|
|
11
18
|
onOpen?: () => void;
|
|
12
19
|
onClose?: (event?: SyntheticEvent<Element, Event> | Event, reason?: Reason) => void;
|
|
13
20
|
inputProps?: Omit<TextFieldProps, 'ref' | 'value' | 'onChange'>;
|
|
14
21
|
disabled?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* @description Принимает только Date object, включая невалидную дату Invalid date
|
|
24
|
+
* */
|
|
15
25
|
value?: Date;
|
|
16
26
|
};
|
|
17
27
|
export declare const DatePicker: import("react").ForwardRefExoticComponent<MondayFirst & Partial<MinMaxDate> & {
|
|
28
|
+
/**
|
|
29
|
+
* @description Маска для инпута даты
|
|
30
|
+
* @default 'DD.MM.YYYY'
|
|
31
|
+
* */
|
|
18
32
|
mask?: string | undefined;
|
|
19
|
-
|
|
33
|
+
/**
|
|
34
|
+
* @description Обработчик изменения состояния. Передает только Date object, если дата невалидна, то будет Invalid date
|
|
35
|
+
* */
|
|
36
|
+
onChange?: ((date?: Date) => void) | undefined;
|
|
20
37
|
onBlur?: ((e: FocusEvent<HTMLInputElement>) => void) | undefined;
|
|
21
38
|
onOpen?: (() => void) | undefined;
|
|
22
39
|
onClose?: ((event?: SyntheticEvent<Element, Event> | Event, reason?: Reason) => void) | undefined;
|
|
23
40
|
inputProps?: Omit<TextFieldProps, "ref" | "value" | "onChange"> | undefined;
|
|
24
41
|
disabled?: boolean | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* @description Принимает только Date object, включая невалидную дату Invalid date
|
|
44
|
+
* */
|
|
25
45
|
value?: Date | undefined;
|
|
26
46
|
} & import("react").RefAttributes<HTMLInputElement>>;
|
package/DatePicker/DatePicker.js
CHANGED
|
@@ -15,53 +15,38 @@ exports.DatePicker = void 0;
|
|
|
15
15
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
16
16
|
const react_1 = require("react");
|
|
17
17
|
const hooks_1 = require("../hooks");
|
|
18
|
-
const date_1 = require("../utils/date");
|
|
19
18
|
const DatePickerClickAwayListener_1 = require("./DatePickerClickAwayListener");
|
|
20
19
|
const DatePickerInput_1 = require("./DatePickerInput");
|
|
21
20
|
const DatePickerPopper_1 = require("./DatePickerPopper");
|
|
22
21
|
const MinMaxDateContext_1 = require("./MinMaxDateContext");
|
|
23
22
|
const YearMonthDayPicker_1 = require("./YearMonthDayPicker");
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const DatePickerInner = (0, react_1.forwardRef)(({ onChange, onOpen, onBlur, onClose, mask = 'DD.MM.YYYY', isMondayFirst, inputProps, disabled, value: parentValue, }, forwardedRef) => {
|
|
23
|
+
const hooks_2 = require("./hooks");
|
|
24
|
+
const DatePickerInner = (0, react_1.forwardRef)(({ onChange, onOpen, onBlur, onClose, mask = 'DD.MM.YYYY', isMondayFirst, inputProps, disabled, value, }, forwardedRef) => {
|
|
27
25
|
const { maxDate, minDate } = (0, react_1.useContext)(MinMaxDateContext_1.MinMaxDateContext);
|
|
28
26
|
const ref = (0, hooks_1.useForwardedRef)(forwardedRef);
|
|
29
27
|
const [isActive, openPopper, closePopper] = (0, hooks_1.useToggle)({
|
|
30
28
|
onActive: onOpen,
|
|
31
29
|
onInactive: onClose,
|
|
32
30
|
});
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const date = isString ? (0, date_1.parseDate)(value, mask) : value;
|
|
41
|
-
if (value === '' || !(0, date_1.isDate)(date)) {
|
|
42
|
-
setSelectedDate(null);
|
|
43
|
-
}
|
|
44
|
-
else if (!(0, date_1.areDatesSame)(date, selectedDate)) {
|
|
45
|
-
setSelectedDate(date);
|
|
46
|
-
}
|
|
47
|
-
}, [mask, selectedDate]);
|
|
31
|
+
const { maskedValue, onChangeMaskedValue, onChangeMaskedDate } = (0, hooks_2.useMaskedValue)({
|
|
32
|
+
currentValue: value,
|
|
33
|
+
mask,
|
|
34
|
+
onChangeValue: onChange,
|
|
35
|
+
});
|
|
36
|
+
const baseDate = (0, hooks_2.useBaseDateInRange)({ minDate, maxDate });
|
|
37
|
+
const selectedBaseDate = (0, hooks_2.useSelectedBaseDate)(value);
|
|
48
38
|
const handleDayPick = (date) => {
|
|
49
|
-
|
|
50
|
-
setSelectedDate(date);
|
|
39
|
+
onChangeMaskedDate(date);
|
|
51
40
|
closePopper(undefined, 'selectOption');
|
|
52
41
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}, [selectedDate, maskedDate]);
|
|
56
|
-
(0, react_1.useEffect)(() => {
|
|
57
|
-
checkValue(parentValue || '');
|
|
58
|
-
}, [parentValue]);
|
|
59
|
-
const blurHandler = (e) => {
|
|
60
|
-
checkValue(e.target.value);
|
|
42
|
+
const handleBlurMaskInput = (e) => {
|
|
43
|
+
onChangeMaskedValue(e.target.value);
|
|
61
44
|
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
|
|
62
45
|
};
|
|
63
|
-
const
|
|
64
|
-
|
|
46
|
+
const handleChangeMaskInput = (e) => {
|
|
47
|
+
onChangeMaskedValue(e.target.value);
|
|
48
|
+
};
|
|
49
|
+
return ((0, jsx_runtime_1.jsxs)(DatePickerClickAwayListener_1.DatePickerClickAwayListener, Object.assign({ onClickAway: closePopper }, { children: [(0, jsx_runtime_1.jsx)(DatePickerInput_1.DatePickerInput, Object.assign({}, inputProps, { mask: mask, onNativeChange: handleChangeMaskInput, onBlur: handleBlurMaskInput, disabled: disabled, value: maskedValue, ref: ref, onFocus: openPopper })), (0, jsx_runtime_1.jsx)(DatePickerPopper_1.DatePickerPopper, Object.assign({ open: isActive, onClose: closePopper, anchorEl: ref === null || ref === void 0 ? void 0 : ref.current }, { children: (0, jsx_runtime_1.jsx)(YearMonthDayPicker_1.YearMonthDayPicker, { isMondayFirst: isMondayFirst, selectedDate: selectedBaseDate, onChange: handleDayPick, date: selectedBaseDate || baseDate }) }))] })));
|
|
65
50
|
});
|
|
66
51
|
exports.DatePicker = (0, react_1.forwardRef)((_a, forwardedRef) => {
|
|
67
52
|
var { minDate, maxDate } = _a, props = __rest(_a, ["minDate", "maxDate"]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
13
|
+
const tests_1 = require("@astral/tests");
|
|
14
|
+
const vitest_1 = require("vitest");
|
|
15
|
+
const react_1 = require("react");
|
|
16
|
+
const DatePicker_1 = require("./DatePicker");
|
|
17
|
+
// Протестировать любые кейсы, связанные с MaskFiled невозможно из-за того, что MaskField использует методы, не эмулируемые в rtl
|
|
18
|
+
describe('DatePicker', () => {
|
|
19
|
+
beforeAll(() => {
|
|
20
|
+
vitest_1.vi.useFakeTimers({ toFake: ['Date'] });
|
|
21
|
+
vitest_1.vi.setSystemTime(new Date('2022-02-10'));
|
|
22
|
+
});
|
|
23
|
+
afterAll(() => {
|
|
24
|
+
vitest_1.vi.useRealTimers();
|
|
25
|
+
});
|
|
26
|
+
it('Props:onChange: при выборе даты в пикере в onChange передается объект Date', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
|
+
const onChange = vitest_1.vi.fn();
|
|
28
|
+
(0, tests_1.renderWithTheme)((0, jsx_runtime_1.jsx)(DatePicker_1.DatePicker, { onChange: onChange }));
|
|
29
|
+
tests_1.fireEvent.focus(tests_1.screen.getByRole('textbox'));
|
|
30
|
+
const dateBtn = tests_1.screen.getAllByText('10')[0];
|
|
31
|
+
yield tests_1.userEvents.click(dateBtn);
|
|
32
|
+
expect(onChange.mock.calls[0][0] instanceof Date).toBeTruthy();
|
|
33
|
+
expect(onChange.mock.calls[0][0].toISOString()).toBe('2022-02-10T00:00:00.000Z');
|
|
34
|
+
}));
|
|
35
|
+
it('Props:minDate: в пикере нельзя выбрать дату меньше minDate', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
|
+
const onChange = vitest_1.vi.fn();
|
|
37
|
+
(0, tests_1.renderWithTheme)((0, jsx_runtime_1.jsx)(DatePicker_1.DatePicker, { onChange: onChange, minDate: new Date('2022-02-09') }));
|
|
38
|
+
tests_1.fireEvent.focus(tests_1.screen.getByRole('textbox'));
|
|
39
|
+
const btn = tests_1.screen.getAllByText('8')[0];
|
|
40
|
+
expect(btn).toBeDisabled();
|
|
41
|
+
}));
|
|
42
|
+
it('Props:minDate: в пикере можно выбрать дату равной minDate', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
43
|
+
(0, tests_1.renderWithTheme)((0, jsx_runtime_1.jsx)(DatePicker_1.DatePicker, { minDate: new Date('2022-02-09') }));
|
|
44
|
+
tests_1.fireEvent.focus(tests_1.screen.getByRole('textbox'));
|
|
45
|
+
const btn = tests_1.screen.getAllByText('9')[0];
|
|
46
|
+
expect(btn).not.toBeDisabled();
|
|
47
|
+
}));
|
|
48
|
+
it('Props:maxDate: в пикере нельзя выбрать дату больше maxDate', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
|
+
(0, tests_1.renderWithTheme)((0, jsx_runtime_1.jsx)(DatePicker_1.DatePicker, { maxDate: new Date('2022-02-09') }));
|
|
50
|
+
tests_1.fireEvent.focus(tests_1.screen.getByRole('textbox'));
|
|
51
|
+
const btn = tests_1.screen.getAllByText('10')[0];
|
|
52
|
+
expect(btn).toBeDisabled();
|
|
53
|
+
}));
|
|
54
|
+
it('Props:maxDate: в пикере можно выбрать дату равной maxDate', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
55
|
+
(0, tests_1.renderWithTheme)((0, jsx_runtime_1.jsx)(DatePicker_1.DatePicker, { maxDate: new Date('2022-02-09') }));
|
|
56
|
+
tests_1.fireEvent.focus(tests_1.screen.getByRole('textbox'));
|
|
57
|
+
const btn = tests_1.screen.getAllByText('9')[0];
|
|
58
|
+
expect(btn).not.toBeDisabled();
|
|
59
|
+
}));
|
|
60
|
+
it('Props:value: при изменении меняется выбранная дата в календаре', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
61
|
+
const callbacks = {
|
|
62
|
+
setValue: () => undefined,
|
|
63
|
+
};
|
|
64
|
+
const TestComponent = () => {
|
|
65
|
+
const [value, setValue] = (0, react_1.useState)();
|
|
66
|
+
callbacks.setValue = setValue;
|
|
67
|
+
return (0, jsx_runtime_1.jsx)(DatePicker_1.DatePicker, { value: value, onChange: setValue });
|
|
68
|
+
};
|
|
69
|
+
(0, tests_1.renderWithTheme)((0, jsx_runtime_1.jsx)(TestComponent, {}));
|
|
70
|
+
yield (0, tests_1.act)(() => {
|
|
71
|
+
callbacks.setValue(new Date('2022-03-09'));
|
|
72
|
+
});
|
|
73
|
+
tests_1.fireEvent.focus(tests_1.screen.getByRole('textbox'));
|
|
74
|
+
const selected = tests_1.screen.getAllByText('9')[0].getAttribute('aria-selected');
|
|
75
|
+
expect(selected).toBeTruthy();
|
|
76
|
+
}));
|
|
77
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./useMaskedValue"), exports);
|
|
18
|
+
__exportStar(require("./useSelectedBaseDate"), exports);
|
|
19
|
+
__exportStar(require("./useBaseDateInRange"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useMaskedValue';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./useMaskedValue"), exports);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type Params = {
|
|
2
|
+
currentValue?: Date;
|
|
3
|
+
mask: string;
|
|
4
|
+
/**
|
|
5
|
+
* @description Изменение value для DatePicker
|
|
6
|
+
* */
|
|
7
|
+
onChangeValue?: (date?: Date) => void;
|
|
8
|
+
};
|
|
9
|
+
type Returned = {
|
|
10
|
+
/**
|
|
11
|
+
* @description Value для MaskField
|
|
12
|
+
* */
|
|
13
|
+
maskedValue: string;
|
|
14
|
+
/**
|
|
15
|
+
* @description Изменение maskedValue по Date
|
|
16
|
+
* */
|
|
17
|
+
onChangeMaskedDate: (date: Date) => void;
|
|
18
|
+
/**
|
|
19
|
+
* @description Изменение maskedValue
|
|
20
|
+
* */
|
|
21
|
+
onChangeMaskedValue: (value: string) => void;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* @description Хук для управления значением для MaskField
|
|
25
|
+
*/
|
|
26
|
+
export declare const useMaskedValue: ({ currentValue, mask, onChangeValue, }: Params) => Returned;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMaskedValue = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const date_1 = require("../../../utils/date");
|
|
6
|
+
/**
|
|
7
|
+
* @description Хук для управления значением для MaskField
|
|
8
|
+
*/
|
|
9
|
+
const useMaskedValue = ({ currentValue, mask, onChangeValue, }) => {
|
|
10
|
+
const [maskedValue, setMaskedValue] = (0, react_1.useState)(() => currentValue ? (0, date_1.formatDate)(currentValue, mask) : '');
|
|
11
|
+
const handleChangeMaskedValue = (value) => {
|
|
12
|
+
setMaskedValue(value);
|
|
13
|
+
onChangeValue === null || onChangeValue === void 0 ? void 0 : onChangeValue((0, date_1.parseDate)(value, mask));
|
|
14
|
+
};
|
|
15
|
+
const handleChangeMaskedDate = (date) => {
|
|
16
|
+
setMaskedValue((0, date_1.formatDate)(date, mask));
|
|
17
|
+
onChangeValue === null || onChangeValue === void 0 ? void 0 : onChangeValue(date);
|
|
18
|
+
};
|
|
19
|
+
// здесь происходит реакция на изменение value из вне (управляемый компонент)
|
|
20
|
+
(0, react_1.useEffect)(() => {
|
|
21
|
+
// если новое значение пустое, то сбрасываем значение MaskField
|
|
22
|
+
if (!currentValue) {
|
|
23
|
+
setMaskedValue('');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// здесь обрабатывается сценарий, когда в инпут вводится невалидная дата и при этом currentValue становится Invalid Date
|
|
27
|
+
// в таком случае сравнить даты напрямую не получится
|
|
28
|
+
if (!(0, date_1.isDate)(currentValue) && maskedValue) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// проверяем равны ли даты
|
|
32
|
+
const isEqualValueAndMaskedDate = currentValue.getTime() === (0, date_1.parseDate)(maskedValue, mask).getTime();
|
|
33
|
+
// если даты не равны, то значит изменился currentValue из вне и надо синхронизировать maskedValue
|
|
34
|
+
if (!isEqualValueAndMaskedDate) {
|
|
35
|
+
setMaskedValue((0, date_1.formatDate)(currentValue, mask));
|
|
36
|
+
}
|
|
37
|
+
}, [currentValue]);
|
|
38
|
+
return {
|
|
39
|
+
maskedValue,
|
|
40
|
+
onChangeMaskedValue: handleChangeMaskedValue,
|
|
41
|
+
onChangeMaskedDate: handleChangeMaskedDate,
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
exports.useMaskedValue = useMaskedValue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const useSelectedBaseDate: (selectedDate?: Date
|
|
1
|
+
export declare const useSelectedBaseDate: (selectedDate?: Date) => Date | undefined;
|
|
@@ -8,6 +8,10 @@ const useSelectedBaseDate = (selectedDate) => {
|
|
|
8
8
|
const { minDate, maxDate } = (0, react_1.useContext)(MinMaxDateContext_1.MinMaxDateContext);
|
|
9
9
|
const [baseDate, setBaseDate] = (0, react_1.useState)(selectedDate);
|
|
10
10
|
(0, react_1.useEffect)(() => {
|
|
11
|
+
if (!(0, date_1.isDate)(selectedDate)) {
|
|
12
|
+
setBaseDate(undefined);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
11
15
|
if ((0, date_1.areDatesSame)(baseDate, selectedDate)) {
|
|
12
16
|
return;
|
|
13
17
|
}
|
|
@@ -18,7 +22,7 @@ const useSelectedBaseDate = (selectedDate) => {
|
|
|
18
22
|
maxDate,
|
|
19
23
|
deep: date_1.DateCompareDeep.day,
|
|
20
24
|
})) {
|
|
21
|
-
setBaseDate(
|
|
25
|
+
setBaseDate(undefined);
|
|
22
26
|
}
|
|
23
27
|
else {
|
|
24
28
|
setBaseDate(selectedDate);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
13
|
+
const tests_1 = require("@astral/tests");
|
|
14
|
+
const vitest_1 = require("vitest");
|
|
15
|
+
const MenuItem_1 = require("../MenuItem");
|
|
16
|
+
const DropdownButton_1 = require("./DropdownButton");
|
|
17
|
+
describe('DropdownButton', () => {
|
|
18
|
+
it('Props:disabled: нельзя нажать кнопку', () => {
|
|
19
|
+
(0, tests_1.renderWithTheme)((0, jsx_runtime_1.jsx)(DropdownButton_1.DropdownButton, Object.assign({ disabled: true, name: "btn" }, { children: (0, jsx_runtime_1.jsx)(MenuItem_1.MenuItem, { children: "Item" }) })));
|
|
20
|
+
const button = tests_1.screen.getByRole('button', { name: 'btn' });
|
|
21
|
+
expect(button).toBeDisabled();
|
|
22
|
+
});
|
|
23
|
+
it('При клике на option закрывается popover', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
24
|
+
(0, tests_1.renderWithTheme)((0, jsx_runtime_1.jsx)(DropdownButton_1.DropdownButton, Object.assign({ name: "btn" }, { children: (0, jsx_runtime_1.jsx)(MenuItem_1.MenuItem, { children: "Item" }) })));
|
|
25
|
+
yield tests_1.userEvents.click(tests_1.screen.getByRole('button'));
|
|
26
|
+
yield tests_1.userEvents.click(tests_1.screen.getByText('Item'));
|
|
27
|
+
const menuItem = tests_1.screen.queryByRole('menuitem');
|
|
28
|
+
expect(menuItem).not.toBeInTheDocument();
|
|
29
|
+
}));
|
|
30
|
+
it('При клике на option вызывается onClick', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
31
|
+
const onClick = vitest_1.vi.fn();
|
|
32
|
+
(0, tests_1.renderWithTheme)((0, jsx_runtime_1.jsx)(DropdownButton_1.DropdownButton, Object.assign({ name: "btn" }, { children: (0, jsx_runtime_1.jsx)(MenuItem_1.MenuItem, Object.assign({ onClick: onClick }, { children: "Item" })) })));
|
|
33
|
+
yield tests_1.userEvents.click(tests_1.screen.getByRole('button'));
|
|
34
|
+
yield tests_1.userEvents.click(tests_1.screen.getByRole('menuitem'));
|
|
35
|
+
expect(onClick).toBeCalled();
|
|
36
|
+
}));
|
|
37
|
+
});
|
|
@@ -15,5 +15,5 @@ import { Tooltip } from '../../../Tooltip';
|
|
|
15
15
|
import { DateCalendarBtnWrapper } from './styles';
|
|
16
16
|
export const DateCalendarBtn = forwardRef((_a, ref) => {
|
|
17
17
|
var { title = '', disabled, selected } = _a, props = __rest(_a, ["title", "disabled", "selected"]);
|
|
18
|
-
return disabled ? (_jsx(DateCalendarBtnWrapper, Object.assign({ ref: ref, variant: selected ? 'contained' : 'text', disabled: true }, props))) : (_jsx(Tooltip, Object.assign({ title: title, disableInteractive: true }, { children: _jsx(DateCalendarBtnWrapper, Object.assign({ ref: ref, variant: selected ? 'contained' : 'text' }, props)) })));
|
|
18
|
+
return disabled ? (_jsx(DateCalendarBtnWrapper, Object.assign({ ref: ref, variant: selected ? 'contained' : 'text', "aria-selected": selected, disabled: true }, props))) : (_jsx(Tooltip, Object.assign({ title: title, disableInteractive: true }, { children: _jsx(DateCalendarBtnWrapper, Object.assign({ ref: ref, variant: selected ? 'contained' : 'text', "aria-selected": selected }, props)) })));
|
|
19
19
|
});
|
|
@@ -5,22 +5,42 @@ import { Reason } from '../types';
|
|
|
5
5
|
import { MinMaxDate } from './types';
|
|
6
6
|
import { MondayFirst } from './DayPicker';
|
|
7
7
|
export type DatePickerProps = MondayFirst & Partial<MinMaxDate> & {
|
|
8
|
+
/**
|
|
9
|
+
* @description Маска для инпута даты
|
|
10
|
+
* @default 'DD.MM.YYYY'
|
|
11
|
+
* */
|
|
8
12
|
mask?: DateMask;
|
|
9
|
-
|
|
13
|
+
/**
|
|
14
|
+
* @description Обработчик изменения состояния. Передает только Date object, если дата невалидна, то будет Invalid date
|
|
15
|
+
* */
|
|
16
|
+
onChange?: (date?: Date) => void;
|
|
10
17
|
onBlur?: (e: FocusEvent<HTMLInputElement>) => void;
|
|
11
18
|
onOpen?: () => void;
|
|
12
19
|
onClose?: (event?: SyntheticEvent<Element, Event> | Event, reason?: Reason) => void;
|
|
13
20
|
inputProps?: Omit<TextFieldProps, 'ref' | 'value' | 'onChange'>;
|
|
14
21
|
disabled?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* @description Принимает только Date object, включая невалидную дату Invalid date
|
|
24
|
+
* */
|
|
15
25
|
value?: Date;
|
|
16
26
|
};
|
|
17
27
|
export declare const DatePicker: import("react").ForwardRefExoticComponent<MondayFirst & Partial<MinMaxDate> & {
|
|
28
|
+
/**
|
|
29
|
+
* @description Маска для инпута даты
|
|
30
|
+
* @default 'DD.MM.YYYY'
|
|
31
|
+
* */
|
|
18
32
|
mask?: string | undefined;
|
|
19
|
-
|
|
33
|
+
/**
|
|
34
|
+
* @description Обработчик изменения состояния. Передает только Date object, если дата невалидна, то будет Invalid date
|
|
35
|
+
* */
|
|
36
|
+
onChange?: ((date?: Date) => void) | undefined;
|
|
20
37
|
onBlur?: ((e: FocusEvent<HTMLInputElement>) => void) | undefined;
|
|
21
38
|
onOpen?: (() => void) | undefined;
|
|
22
39
|
onClose?: ((event?: SyntheticEvent<Element, Event> | Event, reason?: Reason) => void) | undefined;
|
|
23
40
|
inputProps?: Omit<TextFieldProps, "ref" | "value" | "onChange"> | undefined;
|
|
24
41
|
disabled?: boolean | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* @description Принимает только Date object, включая невалидную дату Invalid date
|
|
44
|
+
* */
|
|
25
45
|
value?: Date | undefined;
|
|
26
46
|
} & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -10,55 +10,40 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
-
import { forwardRef,
|
|
13
|
+
import { forwardRef, useContext, } from 'react';
|
|
14
14
|
import { useForwardedRef, useToggle } from '../hooks';
|
|
15
|
-
import { areDatesSame, formatDate, isDate, parseDate, } from '../utils/date';
|
|
16
15
|
import { DatePickerClickAwayListener } from './DatePickerClickAwayListener';
|
|
17
16
|
import { DatePickerInput } from './DatePickerInput';
|
|
18
17
|
import { DatePickerPopper } from './DatePickerPopper';
|
|
19
18
|
import { MinMaxDateContext, MinMaxDateContextProvider, } from './MinMaxDateContext';
|
|
20
19
|
import { YearMonthDayPicker } from './YearMonthDayPicker';
|
|
21
|
-
import { useBaseDateInRange } from './hooks
|
|
22
|
-
|
|
23
|
-
const DatePickerInner = forwardRef(({ onChange, onOpen, onBlur, onClose, mask = 'DD.MM.YYYY', isMondayFirst, inputProps, disabled, value: parentValue, }, forwardedRef) => {
|
|
20
|
+
import { useBaseDateInRange, useMaskedValue, useSelectedBaseDate, } from './hooks';
|
|
21
|
+
const DatePickerInner = forwardRef(({ onChange, onOpen, onBlur, onClose, mask = 'DD.MM.YYYY', isMondayFirst, inputProps, disabled, value, }, forwardedRef) => {
|
|
24
22
|
const { maxDate, minDate } = useContext(MinMaxDateContext);
|
|
25
23
|
const ref = useForwardedRef(forwardedRef);
|
|
26
24
|
const [isActive, openPopper, closePopper] = useToggle({
|
|
27
25
|
onActive: onOpen,
|
|
28
26
|
onInactive: onClose,
|
|
29
27
|
});
|
|
30
|
-
const
|
|
31
|
-
|
|
28
|
+
const { maskedValue, onChangeMaskedValue, onChangeMaskedDate } = useMaskedValue({
|
|
29
|
+
currentValue: value,
|
|
30
|
+
mask,
|
|
31
|
+
onChangeValue: onChange,
|
|
32
|
+
});
|
|
32
33
|
const baseDate = useBaseDateInRange({ minDate, maxDate });
|
|
33
|
-
const selectedBaseDate = useSelectedBaseDate(
|
|
34
|
-
const checkValue = useCallback((value) => {
|
|
35
|
-
const isString = typeof value === 'string';
|
|
36
|
-
setMaskedDate(isString ? value : formatDate(value, mask));
|
|
37
|
-
const date = isString ? parseDate(value, mask) : value;
|
|
38
|
-
if (value === '' || !isDate(date)) {
|
|
39
|
-
setSelectedDate(null);
|
|
40
|
-
}
|
|
41
|
-
else if (!areDatesSame(date, selectedDate)) {
|
|
42
|
-
setSelectedDate(date);
|
|
43
|
-
}
|
|
44
|
-
}, [mask, selectedDate]);
|
|
34
|
+
const selectedBaseDate = useSelectedBaseDate(value);
|
|
45
35
|
const handleDayPick = (date) => {
|
|
46
|
-
|
|
47
|
-
setSelectedDate(date);
|
|
36
|
+
onChangeMaskedDate(date);
|
|
48
37
|
closePopper(undefined, 'selectOption');
|
|
49
38
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}, [selectedDate, maskedDate]);
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
checkValue(parentValue || '');
|
|
55
|
-
}, [parentValue]);
|
|
56
|
-
const blurHandler = (e) => {
|
|
57
|
-
checkValue(e.target.value);
|
|
39
|
+
const handleBlurMaskInput = (e) => {
|
|
40
|
+
onChangeMaskedValue(e.target.value);
|
|
58
41
|
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
|
|
59
42
|
};
|
|
60
|
-
const
|
|
61
|
-
|
|
43
|
+
const handleChangeMaskInput = (e) => {
|
|
44
|
+
onChangeMaskedValue(e.target.value);
|
|
45
|
+
};
|
|
46
|
+
return (_jsxs(DatePickerClickAwayListener, Object.assign({ onClickAway: closePopper }, { children: [_jsx(DatePickerInput, Object.assign({}, inputProps, { mask: mask, onNativeChange: handleChangeMaskInput, onBlur: handleBlurMaskInput, disabled: disabled, value: maskedValue, ref: ref, onFocus: openPopper })), _jsx(DatePickerPopper, Object.assign({ open: isActive, onClose: closePopper, anchorEl: ref === null || ref === void 0 ? void 0 : ref.current }, { children: _jsx(YearMonthDayPicker, { isMondayFirst: isMondayFirst, selectedDate: selectedBaseDate, onChange: handleDayPick, date: selectedBaseDate || baseDate }) }))] })));
|
|
62
47
|
});
|
|
63
48
|
export const DatePicker = forwardRef((_a, forwardedRef) => {
|
|
64
49
|
var { minDate, maxDate } = _a, props = __rest(_a, ["minDate", "maxDate"]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
import { act, fireEvent, renderWithTheme, screen, userEvents, } from '@astral/tests';
|
|
12
|
+
import { vi } from 'vitest';
|
|
13
|
+
import { useState } from 'react';
|
|
14
|
+
import { DatePicker } from './DatePicker';
|
|
15
|
+
// Протестировать любые кейсы, связанные с MaskFiled невозможно из-за того, что MaskField использует методы, не эмулируемые в rtl
|
|
16
|
+
describe('DatePicker', () => {
|
|
17
|
+
beforeAll(() => {
|
|
18
|
+
vi.useFakeTimers({ toFake: ['Date'] });
|
|
19
|
+
vi.setSystemTime(new Date('2022-02-10'));
|
|
20
|
+
});
|
|
21
|
+
afterAll(() => {
|
|
22
|
+
vi.useRealTimers();
|
|
23
|
+
});
|
|
24
|
+
it('Props:onChange: при выборе даты в пикере в onChange передается объект Date', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
+
const onChange = vi.fn();
|
|
26
|
+
renderWithTheme(_jsx(DatePicker, { onChange: onChange }));
|
|
27
|
+
fireEvent.focus(screen.getByRole('textbox'));
|
|
28
|
+
const dateBtn = screen.getAllByText('10')[0];
|
|
29
|
+
yield userEvents.click(dateBtn);
|
|
30
|
+
expect(onChange.mock.calls[0][0] instanceof Date).toBeTruthy();
|
|
31
|
+
expect(onChange.mock.calls[0][0].toISOString()).toBe('2022-02-10T00:00:00.000Z');
|
|
32
|
+
}));
|
|
33
|
+
it('Props:minDate: в пикере нельзя выбрать дату меньше minDate', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
34
|
+
const onChange = vi.fn();
|
|
35
|
+
renderWithTheme(_jsx(DatePicker, { onChange: onChange, minDate: new Date('2022-02-09') }));
|
|
36
|
+
fireEvent.focus(screen.getByRole('textbox'));
|
|
37
|
+
const btn = screen.getAllByText('8')[0];
|
|
38
|
+
expect(btn).toBeDisabled();
|
|
39
|
+
}));
|
|
40
|
+
it('Props:minDate: в пикере можно выбрать дату равной minDate', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
41
|
+
renderWithTheme(_jsx(DatePicker, { minDate: new Date('2022-02-09') }));
|
|
42
|
+
fireEvent.focus(screen.getByRole('textbox'));
|
|
43
|
+
const btn = screen.getAllByText('9')[0];
|
|
44
|
+
expect(btn).not.toBeDisabled();
|
|
45
|
+
}));
|
|
46
|
+
it('Props:maxDate: в пикере нельзя выбрать дату больше maxDate', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
|
+
renderWithTheme(_jsx(DatePicker, { maxDate: new Date('2022-02-09') }));
|
|
48
|
+
fireEvent.focus(screen.getByRole('textbox'));
|
|
49
|
+
const btn = screen.getAllByText('10')[0];
|
|
50
|
+
expect(btn).toBeDisabled();
|
|
51
|
+
}));
|
|
52
|
+
it('Props:maxDate: в пикере можно выбрать дату равной maxDate', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
|
+
renderWithTheme(_jsx(DatePicker, { maxDate: new Date('2022-02-09') }));
|
|
54
|
+
fireEvent.focus(screen.getByRole('textbox'));
|
|
55
|
+
const btn = screen.getAllByText('9')[0];
|
|
56
|
+
expect(btn).not.toBeDisabled();
|
|
57
|
+
}));
|
|
58
|
+
it('Props:value: при изменении меняется выбранная дата в календаре', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
|
+
const callbacks = {
|
|
60
|
+
setValue: () => undefined,
|
|
61
|
+
};
|
|
62
|
+
const TestComponent = () => {
|
|
63
|
+
const [value, setValue] = useState();
|
|
64
|
+
callbacks.setValue = setValue;
|
|
65
|
+
return _jsx(DatePicker, { value: value, onChange: setValue });
|
|
66
|
+
};
|
|
67
|
+
renderWithTheme(_jsx(TestComponent, {}));
|
|
68
|
+
yield act(() => {
|
|
69
|
+
callbacks.setValue(new Date('2022-03-09'));
|
|
70
|
+
});
|
|
71
|
+
fireEvent.focus(screen.getByRole('textbox'));
|
|
72
|
+
const selected = screen.getAllByText('9')[0].getAttribute('aria-selected');
|
|
73
|
+
expect(selected).toBeTruthy();
|
|
74
|
+
}));
|
|
75
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useMaskedValue';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useMaskedValue';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
type Params = {
|
|
2
|
+
currentValue?: Date;
|
|
3
|
+
mask: string;
|
|
4
|
+
/**
|
|
5
|
+
* @description Изменение value для DatePicker
|
|
6
|
+
* */
|
|
7
|
+
onChangeValue?: (date?: Date) => void;
|
|
8
|
+
};
|
|
9
|
+
type Returned = {
|
|
10
|
+
/**
|
|
11
|
+
* @description Value для MaskField
|
|
12
|
+
* */
|
|
13
|
+
maskedValue: string;
|
|
14
|
+
/**
|
|
15
|
+
* @description Изменение maskedValue по Date
|
|
16
|
+
* */
|
|
17
|
+
onChangeMaskedDate: (date: Date) => void;
|
|
18
|
+
/**
|
|
19
|
+
* @description Изменение maskedValue
|
|
20
|
+
* */
|
|
21
|
+
onChangeMaskedValue: (value: string) => void;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* @description Хук для управления значением для MaskField
|
|
25
|
+
*/
|
|
26
|
+
export declare const useMaskedValue: ({ currentValue, mask, onChangeValue, }: Params) => Returned;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { formatDate, isDate, parseDate } from '../../../utils/date';
|
|
3
|
+
/**
|
|
4
|
+
* @description Хук для управления значением для MaskField
|
|
5
|
+
*/
|
|
6
|
+
export const useMaskedValue = ({ currentValue, mask, onChangeValue, }) => {
|
|
7
|
+
const [maskedValue, setMaskedValue] = useState(() => currentValue ? formatDate(currentValue, mask) : '');
|
|
8
|
+
const handleChangeMaskedValue = (value) => {
|
|
9
|
+
setMaskedValue(value);
|
|
10
|
+
onChangeValue === null || onChangeValue === void 0 ? void 0 : onChangeValue(parseDate(value, mask));
|
|
11
|
+
};
|
|
12
|
+
const handleChangeMaskedDate = (date) => {
|
|
13
|
+
setMaskedValue(formatDate(date, mask));
|
|
14
|
+
onChangeValue === null || onChangeValue === void 0 ? void 0 : onChangeValue(date);
|
|
15
|
+
};
|
|
16
|
+
// здесь происходит реакция на изменение value из вне (управляемый компонент)
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
// если новое значение пустое, то сбрасываем значение MaskField
|
|
19
|
+
if (!currentValue) {
|
|
20
|
+
setMaskedValue('');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// здесь обрабатывается сценарий, когда в инпут вводится невалидная дата и при этом currentValue становится Invalid Date
|
|
24
|
+
// в таком случае сравнить даты напрямую не получится
|
|
25
|
+
if (!isDate(currentValue) && maskedValue) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// проверяем равны ли даты
|
|
29
|
+
const isEqualValueAndMaskedDate = currentValue.getTime() === parseDate(maskedValue, mask).getTime();
|
|
30
|
+
// если даты не равны, то значит изменился currentValue из вне и надо синхронизировать maskedValue
|
|
31
|
+
if (!isEqualValueAndMaskedDate) {
|
|
32
|
+
setMaskedValue(formatDate(currentValue, mask));
|
|
33
|
+
}
|
|
34
|
+
}, [currentValue]);
|
|
35
|
+
return {
|
|
36
|
+
maskedValue,
|
|
37
|
+
onChangeMaskedValue: handleChangeMaskedValue,
|
|
38
|
+
onChangeMaskedDate: handleChangeMaskedDate,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const useSelectedBaseDate: (selectedDate?: Date
|
|
1
|
+
export declare const useSelectedBaseDate: (selectedDate?: Date) => Date | undefined;
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { useContext, useEffect, useState } from 'react';
|
|
2
2
|
import { MinMaxDateContext } from '../../MinMaxDateContext';
|
|
3
|
-
import { DateCompareDeep, areDatesSame, isDateOutOfRange, } from '../../../utils/date';
|
|
3
|
+
import { DateCompareDeep, areDatesSame, isDate, isDateOutOfRange, } from '../../../utils/date';
|
|
4
4
|
export const useSelectedBaseDate = (selectedDate) => {
|
|
5
5
|
const { minDate, maxDate } = useContext(MinMaxDateContext);
|
|
6
6
|
const [baseDate, setBaseDate] = useState(selectedDate);
|
|
7
7
|
useEffect(() => {
|
|
8
|
+
if (!isDate(selectedDate)) {
|
|
9
|
+
setBaseDate(undefined);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
8
12
|
if (areDatesSame(baseDate, selectedDate)) {
|
|
9
13
|
return;
|
|
10
14
|
}
|
|
@@ -15,7 +19,7 @@ export const useSelectedBaseDate = (selectedDate) => {
|
|
|
15
19
|
maxDate,
|
|
16
20
|
deep: DateCompareDeep.day,
|
|
17
21
|
})) {
|
|
18
|
-
setBaseDate(
|
|
22
|
+
setBaseDate(undefined);
|
|
19
23
|
}
|
|
20
24
|
else {
|
|
21
25
|
setBaseDate(selectedDate);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
import { renderWithTheme, screen, userEvents } from '@astral/tests';
|
|
12
|
+
import { vi } from 'vitest';
|
|
13
|
+
import { MenuItem } from '../MenuItem';
|
|
14
|
+
import { DropdownButton } from './DropdownButton';
|
|
15
|
+
describe('DropdownButton', () => {
|
|
16
|
+
it('Props:disabled: нельзя нажать кнопку', () => {
|
|
17
|
+
renderWithTheme(_jsx(DropdownButton, Object.assign({ disabled: true, name: "btn" }, { children: _jsx(MenuItem, { children: "Item" }) })));
|
|
18
|
+
const button = screen.getByRole('button', { name: 'btn' });
|
|
19
|
+
expect(button).toBeDisabled();
|
|
20
|
+
});
|
|
21
|
+
it('При клике на option закрывается popover', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
+
renderWithTheme(_jsx(DropdownButton, Object.assign({ name: "btn" }, { children: _jsx(MenuItem, { children: "Item" }) })));
|
|
23
|
+
yield userEvents.click(screen.getByRole('button'));
|
|
24
|
+
yield userEvents.click(screen.getByText('Item'));
|
|
25
|
+
const menuItem = screen.queryByRole('menuitem');
|
|
26
|
+
expect(menuItem).not.toBeInTheDocument();
|
|
27
|
+
}));
|
|
28
|
+
it('При клике на option вызывается onClick', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
29
|
+
const onClick = vi.fn();
|
|
30
|
+
renderWithTheme(_jsx(DropdownButton, Object.assign({ name: "btn" }, { children: _jsx(MenuItem, Object.assign({ onClick: onClick }, { children: "Item" })) })));
|
|
31
|
+
yield userEvents.click(screen.getByRole('button'));
|
|
32
|
+
yield userEvents.click(screen.getByRole('menuitem'));
|
|
33
|
+
expect(onClick).toBeCalled();
|
|
34
|
+
}));
|
|
35
|
+
});
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astral/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.47.0",
|
|
4
4
|
"browser": "./esm/index.js",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@astral/icons": "^1.
|
|
7
|
+
"@astral/icons": "^1.47.0",
|
|
8
8
|
"@emotion/cache": "11.7.1",
|
|
9
9
|
"@emotion/react": "11.9.0",
|
|
10
10
|
"@emotion/server": "11.4.0",
|