feat: add calendar widget
This commit is contained in:
parent
326d503f7f
commit
078e2c987f
1
src/widgets/CalendarWidget/index.js
Normal file
1
src/widgets/CalendarWidget/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default as CalendarWidget } from './ui/CalendarWidget.ui';
|
||||
4
src/widgets/CalendarWidget/ui/CalendarWidget.module.css
Normal file
4
src/widgets/CalendarWidget/ui/CalendarWidget.module.css
Normal file
@ -0,0 +1,4 @@
|
||||
.wrapper {
|
||||
border: 1px solid black;
|
||||
width: 372px;
|
||||
}
|
||||
27
src/widgets/CalendarWidget/ui/CalendarWidget.ui.jsx
Normal file
27
src/widgets/CalendarWidget/ui/CalendarWidget.ui.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classes from './CalendarWidget.module.css';
|
||||
import { CalendarWidgetProvider } from './CalendarWidgetContext';
|
||||
import { DateWrapper } from './components/DateWrapper';
|
||||
import { DayOfWeekRow } from './components/DayOfWeekRow';
|
||||
import { HeaderRow } from './components/HeaderRow';
|
||||
|
||||
const CalendarWidget = (props) => (
|
||||
<CalendarWidgetProvider {...props}>
|
||||
<div className={classes.wrapper}>
|
||||
<HeaderRow />
|
||||
|
||||
<DayOfWeekRow />
|
||||
|
||||
<DateWrapper />
|
||||
</div>
|
||||
</CalendarWidgetProvider>
|
||||
);
|
||||
|
||||
CalendarWidget.propTypes = {
|
||||
maxDate: PropTypes.instanceOf(Date),
|
||||
minDate: PropTypes.instanceOf(Date),
|
||||
value: PropTypes.instanceOf(Date),
|
||||
};
|
||||
|
||||
export default CalendarWidget;
|
||||
41
src/widgets/CalendarWidget/ui/CalendarWidgetContext.jsx
Normal file
41
src/widgets/CalendarWidget/ui/CalendarWidgetContext.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { createContext, useMemo, useState } from 'react';
|
||||
|
||||
export const CalendarWidgetContext = createContext({});
|
||||
|
||||
export const CalendarWidgetProvider = (props) => {
|
||||
const {
|
||||
children,
|
||||
maxDate = new Date(2030, 0, 1),
|
||||
minDate = new Date(1990, 0, 1),
|
||||
locale = 'ru',
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const [targetDate, setTargetDate] = useState(new Date());
|
||||
|
||||
const contextValue = useMemo(() => ({
|
||||
targetYear: targetDate.getFullYear(),
|
||||
targetMonthIndex: targetDate.getMonth(),
|
||||
targetDate,
|
||||
setTargetDate,
|
||||
minDate,
|
||||
maxDate,
|
||||
locale,
|
||||
...restProps,
|
||||
}), [maxDate, minDate, targetDate, locale, restProps]);
|
||||
|
||||
return (
|
||||
<CalendarWidgetContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</CalendarWidgetContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
CalendarWidgetProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
|
||||
maxDate: PropTypes.instanceOf(Date),
|
||||
minDate: PropTypes.instanceOf(Date),
|
||||
locale: PropTypes.oneOf(['ru', 'en']),
|
||||
};
|
||||
@ -0,0 +1 @@
|
||||
export { default as DateCell } from './ui/DateCell.ui';
|
||||
@ -0,0 +1,37 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import DateCell from './DateCell.ui';
|
||||
|
||||
describe('Base Render', () => {
|
||||
const setup = (date) => {
|
||||
render(<DateCell date={date} />);
|
||||
};
|
||||
|
||||
test('Check base render', () => {
|
||||
setup(new Date(2024, 3, 15));
|
||||
|
||||
const dateCellElem = screen.getByTestId('date-cell');
|
||||
expect(dateCellElem).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Check base class', () => {
|
||||
setup(new Date(2024, 3, 15));
|
||||
|
||||
const dateCellElem = screen.getByTestId('date-cell');
|
||||
expect(dateCellElem).toHaveClass('dateCell');
|
||||
});
|
||||
|
||||
test('Is correct date', () => {
|
||||
setup(new Date(2024, 3, 15));
|
||||
|
||||
const dateCellElem = screen.getByTestId('date-cell');
|
||||
expect(dateCellElem).toHaveTextContent('15');
|
||||
});
|
||||
|
||||
test('Have data-date attribute', () => {
|
||||
setup(new Date(2024, 3, 15));
|
||||
|
||||
const dateCellElem = screen.getByTestId('date-cell');
|
||||
expect(dateCellElem).toHaveAttribute('data-date', '4/15/2024');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,28 @@
|
||||
.dateCell {
|
||||
width: calc((100% / 7));
|
||||
height: 40px;
|
||||
background: aquamarine;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&:nth-child(odd):not(:last-child) {
|
||||
width: calc((100% / 7) - 1px);
|
||||
border-right: 1px solid black;
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
width: calc((100% / 7) - 1px);
|
||||
border-right: 1px solid black;
|
||||
}
|
||||
|
||||
&.today {
|
||||
color: red;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&.outMothRange {
|
||||
color: darkgrey;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
import classnames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { CalendarWidgetContext } from '../../../CalendarWidgetContext';
|
||||
|
||||
import classes from './DateCell.style.module.css';
|
||||
|
||||
const DateCell = (props) => {
|
||||
const {
|
||||
date,
|
||||
} = props;
|
||||
const format = 'en-En';
|
||||
|
||||
const { targetMonthIndex } = useContext(CalendarWidgetContext);
|
||||
|
||||
const todayDate = new Date();
|
||||
const dataDate = new Intl.DateTimeFormat(format).format(date);
|
||||
const dataToday = new Intl.DateTimeFormat(format).format(todayDate);
|
||||
const isOutMonthRange = date.getMonth() !== targetMonthIndex;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={dataDate}
|
||||
className={classnames(
|
||||
{
|
||||
[classes.dateCell]: true,
|
||||
[classes.today]: dataDate === dataToday,
|
||||
[classes.outMothRange]: isOutMonthRange,
|
||||
},
|
||||
)}
|
||||
data-date={dataDate}
|
||||
data-testid="date-cell"
|
||||
>
|
||||
{date.getDate()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
DateCell.propTypes = {
|
||||
date: PropTypes.instanceOf(Date).isRequired,
|
||||
};
|
||||
|
||||
export default DateCell;
|
||||
@ -0,0 +1 @@
|
||||
export { default as DateRow } from './ui/DateRow.ui';
|
||||
@ -0,0 +1,65 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import DateRow from './DateRow.ui';
|
||||
|
||||
describe('Test Date Row', () => {
|
||||
const setup = (cells) => {
|
||||
render(<DateRow cells={cells} />);
|
||||
};
|
||||
|
||||
const setupWeek = () => {
|
||||
const cells = [
|
||||
new Date(2024, 3, 15),
|
||||
new Date(2024, 3, 16),
|
||||
new Date(2024, 3, 17),
|
||||
new Date(2024, 3, 18),
|
||||
new Date(2024, 3, 19),
|
||||
new Date(2024, 3, 20),
|
||||
new Date(2024, 3, 21),
|
||||
];
|
||||
setup(cells);
|
||||
};
|
||||
|
||||
test('base render', () => {
|
||||
setupWeek();
|
||||
|
||||
const dateRowElem = screen.getByTestId('date-row');
|
||||
expect(dateRowElem).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Correct data attribute', () => {
|
||||
setupWeek();
|
||||
|
||||
const dateRowElem = screen.getByTestId('date-row');
|
||||
expect(dateRowElem).toHaveAttribute('data-row', '4/15/2024-4/21/2024');
|
||||
});
|
||||
|
||||
test('Correct style class', () => {
|
||||
setupWeek();
|
||||
|
||||
const dateRowElem = screen.getByTestId('date-row');
|
||||
expect(dateRowElem).toHaveClass('dateRow');
|
||||
});
|
||||
|
||||
describe('Test cells', () => {
|
||||
test('base render', () => {
|
||||
setupWeek();
|
||||
|
||||
const dateCellElems = screen.getAllByTestId('date-cell');
|
||||
expect(dateCellElems).toHaveLength(7);
|
||||
});
|
||||
|
||||
test('is correct cells', () => {
|
||||
setupWeek();
|
||||
|
||||
const dateCellElems = screen.getAllByTestId('date-cell');
|
||||
expect(dateCellElems[0]).toHaveTextContent(15);
|
||||
expect(dateCellElems[1]).toHaveTextContent(16);
|
||||
expect(dateCellElems[2]).toHaveTextContent(17);
|
||||
expect(dateCellElems[3]).toHaveTextContent(18);
|
||||
expect(dateCellElems[4]).toHaveTextContent(19);
|
||||
expect(dateCellElems[5]).toHaveTextContent(20);
|
||||
expect(dateCellElems[6]).toHaveTextContent(21);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,10 @@
|
||||
.dateRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { DateCell } from '../../DateCell';
|
||||
|
||||
import classes from './DateRow.style.module.css';
|
||||
|
||||
const DateRow = (props) => {
|
||||
const {
|
||||
cells,
|
||||
} = props;
|
||||
|
||||
const format = 'en-En';
|
||||
|
||||
const firstDate = new Intl.DateTimeFormat(format).format(cells[0]);
|
||||
const lastDate = new Intl.DateTimeFormat(format).format(cells[cells.length - 1]);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`${firstDate}-${lastDate}`}
|
||||
className={classes.dateRow}
|
||||
data-row={`${firstDate}-${lastDate}`}
|
||||
data-testid="date-row"
|
||||
>
|
||||
{cells.map((item) => (
|
||||
<DateCell key={item.toString()} date={item} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
DateRow.propTypes = {
|
||||
cells: PropTypes.arrayOf(PropTypes.instanceOf(Date)),
|
||||
};
|
||||
|
||||
export default DateRow;
|
||||
@ -0,0 +1 @@
|
||||
export { default as DateWrapper } from './ui/DateWrapper.ui';
|
||||
@ -0,0 +1,24 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { CalendarWidgetProvider } from 'widgets/CalendarWidget/ui/CalendarWidgetContext';
|
||||
|
||||
import { withProvider } from 'shared/hoc/withProvider';
|
||||
|
||||
import DateWrapper from './DateWrapper.ui';
|
||||
|
||||
describe('Base render DateWrapper', () => {
|
||||
const setup = () => {
|
||||
const DateWrapperWithProvider = withProvider(CalendarWidgetProvider)(DateWrapper);
|
||||
|
||||
render(<DateWrapperWithProvider />);
|
||||
};
|
||||
|
||||
test('Base render', () => {
|
||||
setup();
|
||||
|
||||
const dateWrapperElem = screen.getByTestId('date-wrapper');
|
||||
expect(dateWrapperElem).toBeInTheDocument();
|
||||
|
||||
expect(dateWrapperElem).toHaveClass('dateWrapper');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,3 @@
|
||||
.dateWrapper {
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
import { useCallback, useContext } from 'react';
|
||||
|
||||
import DateRow from 'widgets/CalendarWidget/ui/components/DateRow/ui/DateRow.ui';
|
||||
|
||||
import { DAYS_IN_WEEK } from 'shared/constants';
|
||||
|
||||
// todo fix eslint - no empty line
|
||||
import { CalendarWidgetContext } from '../../../CalendarWidgetContext';
|
||||
|
||||
import classes from './DateWrapper.styles.module.css';
|
||||
|
||||
const DateWrapper = () => {
|
||||
const { targetYear, targetMonthIndex } = useContext(CalendarWidgetContext);
|
||||
|
||||
const renderDateWrapper = useCallback(() => {
|
||||
const rowsCount = 6;
|
||||
const days = [];
|
||||
|
||||
const lastDatePrevMonth = new Date(targetYear, targetMonthIndex, 0);
|
||||
const startOffset = lastDatePrevMonth.getDay();
|
||||
if (startOffset) {
|
||||
for (let i = 0; i < startOffset; i++) {
|
||||
const newDate = new Date(targetYear, targetMonthIndex, i - startOffset + 1);
|
||||
days.push(newDate);
|
||||
}
|
||||
}
|
||||
|
||||
const daysPerMonth = new Date(targetYear, targetMonthIndex + 1, 0).getDate();
|
||||
// eslint-disable-next-line @stylistic/padding-line-between-statements
|
||||
for (let i = 1; i <= daysPerMonth; i++) {
|
||||
const newDate = new Date(targetYear, targetMonthIndex, i);
|
||||
days.push(newDate);
|
||||
}
|
||||
|
||||
const endOffset = rowsCount * DAYS_IN_WEEK - days.length;
|
||||
if (endOffset) {
|
||||
for (let i = 1; i <= endOffset; i++) {
|
||||
const newDate = new Date(targetYear, targetMonthIndex + 1, i);
|
||||
days.push(newDate);
|
||||
}
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
// eslint-disable-next-line @stylistic/padding-line-between-statements
|
||||
for (let i = 0; i < rowsCount; i++) {
|
||||
rows.push(days.slice(i * 7, (i + 1) * 7));
|
||||
}
|
||||
|
||||
return rows.map((item) => (
|
||||
<DateRow key={item.toString()} cells={item} />
|
||||
));
|
||||
}, [targetMonthIndex, targetYear]);
|
||||
|
||||
return (
|
||||
<div className={classes.dateWrapper} data-testid="date-wrapper">
|
||||
{renderDateWrapper()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DateWrapper;
|
||||
@ -0,0 +1 @@
|
||||
export { default as DayOfWeekCell } from './ui/DayOfWeekCell.ui';
|
||||
@ -0,0 +1,136 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { WEEK_DAYS } from 'shared/constants';
|
||||
import { withProvider } from 'shared/hoc/withProvider';
|
||||
|
||||
import { CalendarWidgetProvider } from '../../../CalendarWidgetContext';
|
||||
|
||||
import DayOfWeekCell from './DayOfWeekCell.ui';
|
||||
|
||||
describe('Test DayOfWeekCell', () => {
|
||||
const setup = (day, locale) => {
|
||||
const DayOfWeekCellComponent = () => (
|
||||
<DayOfWeekCell day={day} />
|
||||
);
|
||||
|
||||
const Provider = (props) => (
|
||||
<CalendarWidgetProvider {...props} locale={locale} />
|
||||
);
|
||||
|
||||
const DayOfWeekCellWithProvider = withProvider(Provider)(DayOfWeekCellComponent);
|
||||
|
||||
render(<DayOfWeekCellWithProvider />);
|
||||
};
|
||||
|
||||
describe('Basic render sunday', () => {
|
||||
test('en locale', () => {
|
||||
setup(WEEK_DAYS.SUNDAY, 'en');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Sun.');
|
||||
});
|
||||
|
||||
test('ru locale', () => {
|
||||
setup(WEEK_DAYS.SUNDAY, 'ru');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Вс.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Basic render monday', () => {
|
||||
test('en locale', () => {
|
||||
setup(WEEK_DAYS.MONDAY, 'en');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Mon.');
|
||||
});
|
||||
|
||||
test('ru locale', () => {
|
||||
setup(WEEK_DAYS.MONDAY, 'ru');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Пн.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Basic render tuesday', () => {
|
||||
test('en locale', () => {
|
||||
setup(WEEK_DAYS.TUESDAY, 'en');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Tue.');
|
||||
});
|
||||
|
||||
test('ru locale', () => {
|
||||
setup(WEEK_DAYS.TUESDAY, 'ru');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Вт.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Basic render wednesday', () => {
|
||||
test('en locale', () => {
|
||||
setup(WEEK_DAYS.WEDNESDAY, 'en');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Wed.');
|
||||
});
|
||||
|
||||
test('ru locale', () => {
|
||||
setup(WEEK_DAYS.WEDNESDAY, 'ru');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Ср.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Basic render thursday', () => {
|
||||
test('en locale', () => {
|
||||
setup(WEEK_DAYS.THURSDAY, 'en');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Thu.');
|
||||
});
|
||||
|
||||
test('ru locale', () => {
|
||||
setup(WEEK_DAYS.THURSDAY, 'ru');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Чт.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Basic render friday', () => {
|
||||
test('en locale', () => {
|
||||
setup(WEEK_DAYS.FRIDAY, 'en');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Fri.');
|
||||
});
|
||||
|
||||
test('ru locale', () => {
|
||||
setup(WEEK_DAYS.FRIDAY, 'ru');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Пт.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Basic render saturday', () => {
|
||||
test('en locale', () => {
|
||||
setup(WEEK_DAYS.SATURDAY, 'en');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Sat.');
|
||||
});
|
||||
|
||||
test('ru locale', () => {
|
||||
setup(WEEK_DAYS.SATURDAY, 'ru');
|
||||
|
||||
const dayOfWeekCellElem = screen.getByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElem).toHaveTextContent('Сб.');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,4 @@
|
||||
.content {
|
||||
max-width: 100%;
|
||||
font-weight: 600;
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { WEEK_DAYS_LABEL, WEEK_DAYS } from 'shared/constants';
|
||||
|
||||
import { CalendarWidgetContext } from '../../../CalendarWidgetContext';
|
||||
|
||||
import classes from './DayOfWeekCell.style.module.css';
|
||||
|
||||
const DayOfWeekCell = (props) => {
|
||||
const { day } = props;
|
||||
const { locale } = useContext(CalendarWidgetContext);
|
||||
const content = WEEK_DAYS_LABEL[day][locale]?.short;
|
||||
|
||||
return (
|
||||
<span className={classes.content} data-testid="day-of-week-cell">
|
||||
{content}
|
||||
.
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
DayOfWeekCell.propTypes = {
|
||||
day: PropTypes.oneOf(Object.values(WEEK_DAYS)).isRequired,
|
||||
};
|
||||
|
||||
export default DayOfWeekCell;
|
||||
@ -0,0 +1 @@
|
||||
export { default as DayOfWeekRow } from './ui/DayOfWeekRow.ui';
|
||||
@ -0,0 +1,29 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { withProvider } from 'shared/hoc/withProvider';
|
||||
|
||||
import { CalendarWidgetProvider } from '../../../CalendarWidgetContext';
|
||||
|
||||
import DayOfWeekRow from './DayOfWeekRow.ui';
|
||||
|
||||
describe('Test DayOfWeekRow', () => {
|
||||
const setup = () => {
|
||||
const DayOfWeekRowWithProvider = withProvider(CalendarWidgetProvider)(DayOfWeekRow);
|
||||
|
||||
render(<DayOfWeekRowWithProvider />);
|
||||
};
|
||||
|
||||
test('Basic render row', () => {
|
||||
setup();
|
||||
|
||||
const dayOfWeekRowElem = screen.getByTestId('day-of-week-row');
|
||||
expect(dayOfWeekRowElem).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Basic render cells', () => {
|
||||
setup();
|
||||
|
||||
const dayOfWeekCellElems = screen.getAllByTestId('day-of-week-cell');
|
||||
expect(dayOfWeekCellElems).toHaveLength(7);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,24 @@
|
||||
.dayOfWeekRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1px solid black;
|
||||
|
||||
>div {
|
||||
width: calc(100% / 7 - 1px);
|
||||
max-width: calc(100% / 7 - 1px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
&:last-child {
|
||||
width: calc(100% / 7);
|
||||
max-width: calc(100% / 7);
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-right: 1px solid black;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
import { WEEK_DAYS } from 'shared/constants';
|
||||
|
||||
import { DayOfWeekCell } from '../../DayOfWeekCell';
|
||||
|
||||
import classes from './DayOfWeekRow.style.module.css';
|
||||
|
||||
const DayOfWeekRow = () => (
|
||||
<div className={classes.dayOfWeekRow} data-testid="day-of-week-row">
|
||||
<div>
|
||||
<DayOfWeekCell day={WEEK_DAYS.MONDAY} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<DayOfWeekCell day={WEEK_DAYS.TUESDAY} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<DayOfWeekCell day={WEEK_DAYS.WEDNESDAY} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<DayOfWeekCell day={WEEK_DAYS.THURSDAY} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<DayOfWeekCell day={WEEK_DAYS.FRIDAY} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<DayOfWeekCell day={WEEK_DAYS.SATURDAY} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<DayOfWeekCell day={WEEK_DAYS.SUNDAY} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default DayOfWeekRow;
|
||||
@ -0,0 +1 @@
|
||||
export { default as HeaderRow } from './ui/HeaderRow.ui';
|
||||
@ -0,0 +1,77 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { withProvider } from 'shared/hoc/withProvider';
|
||||
|
||||
import { CalendarWidgetProvider } from '../../../CalendarWidgetContext';
|
||||
|
||||
import HeaderRow from './HeaderRow.ui';
|
||||
|
||||
describe('Test HeaderRow', () => {
|
||||
const setup = () => {
|
||||
const HeaderRowWithProvider = withProvider(CalendarWidgetProvider)(HeaderRow);
|
||||
|
||||
render(<HeaderRowWithProvider />);
|
||||
};
|
||||
|
||||
test('Basic render', () => {
|
||||
setup();
|
||||
|
||||
const headerRowElem = screen.getByTestId('header-row');
|
||||
expect(headerRowElem).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Render month select', () => {
|
||||
setup();
|
||||
|
||||
const headerRowElem = screen.getByTestId('header-row');
|
||||
const monthSelectElem = screen.getByTestId('month-select');
|
||||
|
||||
expect(headerRowElem).toContainElement(monthSelectElem);
|
||||
expect(monthSelectElem).toBeInTheDocument();
|
||||
expect(monthSelectElem).toBeVisible();
|
||||
});
|
||||
|
||||
test('Render prev month button', () => {
|
||||
setup();
|
||||
|
||||
const headerRowElem = screen.getByTestId('header-row');
|
||||
const prevMonthButtonElem = screen.getByTestId('prev-month-button');
|
||||
|
||||
expect(headerRowElem).toContainElement(prevMonthButtonElem);
|
||||
expect(prevMonthButtonElem).toBeInTheDocument();
|
||||
expect(prevMonthButtonElem).toBeVisible();
|
||||
});
|
||||
|
||||
test('Render today button', () => {
|
||||
setup();
|
||||
|
||||
const headerRowElem = screen.getByTestId('header-row');
|
||||
const todayButtonElem = screen.getByTestId('today-button');
|
||||
|
||||
expect(headerRowElem).toContainElement(todayButtonElem);
|
||||
expect(todayButtonElem).toBeInTheDocument();
|
||||
expect(todayButtonElem).toBeVisible();
|
||||
});
|
||||
|
||||
test('Render next month button', () => {
|
||||
setup();
|
||||
|
||||
const headerRowElem = screen.getByTestId('header-row');
|
||||
const nextMonthButtonElem = screen.getByTestId('next-month-button');
|
||||
|
||||
expect(headerRowElem).toContainElement(nextMonthButtonElem);
|
||||
expect(nextMonthButtonElem).toBeInTheDocument();
|
||||
expect(nextMonthButtonElem).toBeVisible();
|
||||
});
|
||||
|
||||
test('Render year select', () => {
|
||||
setup();
|
||||
|
||||
const headerRowElem = screen.getByTestId('header-row');
|
||||
const yearSelectElem = screen.getByTestId('year-select');
|
||||
|
||||
expect(headerRowElem).toContainElement(yearSelectElem);
|
||||
expect(yearSelectElem).toBeInTheDocument();
|
||||
expect(yearSelectElem).toBeVisible();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,9 @@
|
||||
.headerRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
>div {
|
||||
background: dimgrey;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
import { MonthSelect } from '../../MonthSelect';
|
||||
import { NextMonthButton } from '../../NextMonthButton';
|
||||
import { PrevMonthButton } from '../../PrevMonthButton';
|
||||
import { TodayButton } from '../../TodayButton';
|
||||
import { YearSelect } from '../../YearSelect';
|
||||
|
||||
import classes from './HeaderRow.styles.module.css';
|
||||
|
||||
const HeaderRow = () => (
|
||||
<div className={classes.headerRow} data-testid="header-row">
|
||||
<div>
|
||||
<MonthSelect />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<PrevMonthButton>
|
||||
<
|
||||
</PrevMonthButton>
|
||||
|
||||
<TodayButton>
|
||||
Сегодня
|
||||
</TodayButton>
|
||||
|
||||
<NextMonthButton>
|
||||
>
|
||||
</NextMonthButton>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<YearSelect />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default HeaderRow;
|
||||
@ -0,0 +1 @@
|
||||
export { default as MonthSelect } from './ui/MonthSelect.ui';
|
||||
@ -0,0 +1,52 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { userEvent } from '@testing-library/user-event';
|
||||
|
||||
import { MONTHS } from 'shared/constants';
|
||||
import { withProvider } from 'shared/hoc/withProvider';
|
||||
|
||||
import { CalendarWidgetProvider } from '../../../CalendarWidgetContext';
|
||||
|
||||
import MonthSelect from './MonthSelect.ui';
|
||||
|
||||
describe('Test MonthSelect', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = userEvent.setup();
|
||||
});
|
||||
|
||||
const setup = (props) => {
|
||||
const MonthSelectComponent = () => (
|
||||
<MonthSelect {...props} />
|
||||
);
|
||||
const MonthSelectWithProvider = withProvider(CalendarWidgetProvider)(MonthSelectComponent);
|
||||
|
||||
render(<MonthSelectWithProvider />);
|
||||
};
|
||||
|
||||
test('Basic render', async () => {
|
||||
setup();
|
||||
|
||||
const monthSelectElem = screen.getByTestId('month-select');
|
||||
|
||||
expect(monthSelectElem).toBeInTheDocument();
|
||||
expect(monthSelectElem).toBeVisible();
|
||||
|
||||
await user.selectOptions(monthSelectElem, String(MONTHS.JULY));
|
||||
expect(monthSelectElem).toHaveValue('6');
|
||||
expect(monthSelectElem).toHaveDisplayValue(['Июль']);
|
||||
});
|
||||
|
||||
test('onChange handler', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
setup({ onChange });
|
||||
|
||||
const monthSelectElem = screen.getByTestId('month-select');
|
||||
await user.selectOptions(monthSelectElem, String(MONTHS.JULY));
|
||||
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
const newDate = new Date(2024, 6);
|
||||
expect(onChange).toHaveBeenCalledWith(newDate);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,61 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { MONTHS, MONTHS_LABEL } from 'shared/constants';
|
||||
import { lastDayOfMonthDate } from 'shared/utils/date';
|
||||
|
||||
import { CalendarWidgetContext } from '../../../CalendarWidgetContext';
|
||||
|
||||
const MonthSelect = (props) => {
|
||||
const {
|
||||
onChange,
|
||||
...restProps
|
||||
} = props;
|
||||
// todo eslint sort
|
||||
const {
|
||||
targetYear,
|
||||
targetMonthIndex,
|
||||
setTargetDate,
|
||||
minDate,
|
||||
maxDate,
|
||||
locale,
|
||||
} = useContext(CalendarWidgetContext);
|
||||
|
||||
const onChangeHandler = (e) => {
|
||||
const newDate = new Date(targetYear, Number(e.target.value));
|
||||
setTargetDate(newDate);
|
||||
onChange?.(newDate);
|
||||
};
|
||||
|
||||
const createOption = (value) => {
|
||||
const newDate = new Date(targetYear, value);
|
||||
|
||||
return (
|
||||
<option
|
||||
key={value}
|
||||
disabled={lastDayOfMonthDate(newDate) < minDate || newDate > maxDate}
|
||||
value={value}
|
||||
>
|
||||
{MONTHS_LABEL[value][locale]}
|
||||
</option>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<select
|
||||
data-testid="month-select"
|
||||
name="month"
|
||||
value={targetMonthIndex}
|
||||
onChange={onChangeHandler}
|
||||
{...restProps}
|
||||
>
|
||||
{Object.values(MONTHS).map(createOption)}
|
||||
</select>
|
||||
);
|
||||
};
|
||||
|
||||
MonthSelect.propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
export default MonthSelect;
|
||||
@ -0,0 +1 @@
|
||||
export { default as NextMonthButton } from './ui/NextMonthButton.ui';
|
||||
@ -0,0 +1,43 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { userEvent } from '@testing-library/user-event';
|
||||
|
||||
import { withProvider } from 'shared/hoc/withProvider';
|
||||
|
||||
import { CalendarWidgetProvider } from '../../../CalendarWidgetContext';
|
||||
|
||||
import NextMonthButton from './NextMonthButton.ui';
|
||||
|
||||
describe('Test NextMonthButton', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = userEvent.setup();
|
||||
});
|
||||
|
||||
const setup = (props) => {
|
||||
const NextMonthButtonComponent = () => (
|
||||
<NextMonthButton {...props} />
|
||||
);
|
||||
const NextMonthButtonWithProvider = withProvider(CalendarWidgetProvider)(NextMonthButtonComponent);
|
||||
|
||||
render(<NextMonthButtonWithProvider />);
|
||||
};
|
||||
|
||||
test('Basic render', () => {
|
||||
setup();
|
||||
|
||||
const nextMontButtonElem = screen.getByTestId('next-month-button');
|
||||
expect(nextMontButtonElem).toBeInTheDocument();
|
||||
expect(nextMontButtonElem).toBeVisible();
|
||||
});
|
||||
|
||||
test('onClick handler', async () => {
|
||||
const onClick = jest.fn();
|
||||
setup({ onClick });
|
||||
|
||||
const nextMontButtonElem = screen.getByTestId('next-month-button');
|
||||
await user.click(nextMontButtonElem);
|
||||
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,46 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { CalendarWidgetContext } from '../../../CalendarWidgetContext';
|
||||
|
||||
const NextMonthButton = (props) => {
|
||||
const {
|
||||
children = 'next',
|
||||
disabled,
|
||||
onClick,
|
||||
...restProps
|
||||
} = props;
|
||||
const {
|
||||
targetYear,
|
||||
targetMonthIndex,
|
||||
setTargetDate,
|
||||
maxDate,
|
||||
} = useContext(CalendarWidgetContext);
|
||||
|
||||
const nextDate = new Date(targetYear, targetMonthIndex + 1);
|
||||
|
||||
const onClickHandler = () => {
|
||||
setTargetDate(nextDate);
|
||||
onClick?.(nextDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
data-testid="next-month-button"
|
||||
disabled={disabled || maxDate < nextDate}
|
||||
type="button"
|
||||
onClick={onClickHandler}
|
||||
{...restProps}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
NextMonthButton.propTypes = {
|
||||
children: PropTypes.node,
|
||||
onClick: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default NextMonthButton;
|
||||
@ -0,0 +1 @@
|
||||
export { default as PrevMonthButton } from './ui/PrevMonthButton.ui';
|
||||
@ -0,0 +1,43 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { userEvent } from '@testing-library/user-event';
|
||||
|
||||
import { withProvider } from 'shared/hoc/withProvider';
|
||||
|
||||
import { CalendarWidgetProvider } from '../../../CalendarWidgetContext';
|
||||
|
||||
import PrevMonthButton from './PrevMonthButton.ui';
|
||||
|
||||
describe('Test NextMonthButton', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = userEvent.setup();
|
||||
});
|
||||
|
||||
const setup = (props) => {
|
||||
const PrevMonthButtonComponent = () => (
|
||||
<PrevMonthButton {...props} />
|
||||
);
|
||||
const PrevMonthButtonWithProvider = withProvider(CalendarWidgetProvider)(PrevMonthButtonComponent);
|
||||
|
||||
render(<PrevMonthButtonWithProvider />);
|
||||
};
|
||||
|
||||
test('Basic render', () => {
|
||||
setup();
|
||||
|
||||
const prevMontButtonElem = screen.getByTestId('prev-month-button');
|
||||
expect(prevMontButtonElem).toBeInTheDocument();
|
||||
expect(prevMontButtonElem).toBeVisible();
|
||||
});
|
||||
|
||||
test('onClick handler', async () => {
|
||||
const onClick = jest.fn();
|
||||
setup({ onClick });
|
||||
|
||||
const prevMontButtonElem = screen.getByTestId('prev-month-button');
|
||||
await user.click(prevMontButtonElem);
|
||||
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,46 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { CalendarWidgetContext } from '../../../CalendarWidgetContext';
|
||||
|
||||
const PrevMonthButton = (props) => {
|
||||
const {
|
||||
children = 'prev',
|
||||
onClick,
|
||||
disabled,
|
||||
...restProps
|
||||
} = props;
|
||||
const {
|
||||
targetYear,
|
||||
targetMonthIndex,
|
||||
setTargetDate,
|
||||
minDate,
|
||||
} = useContext(CalendarWidgetContext);
|
||||
|
||||
const prevDate = new Date(targetYear, targetMonthIndex, 0);
|
||||
|
||||
const onClickHandler = () => {
|
||||
setTargetDate(prevDate);
|
||||
onClick?.(prevDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
data-testid="prev-month-button"
|
||||
disabled={disabled || prevDate < minDate}
|
||||
type="button"
|
||||
onClick={onClickHandler}
|
||||
{...restProps}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
PrevMonthButton.propTypes = {
|
||||
children: PropTypes.node,
|
||||
onClick: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default PrevMonthButton;
|
||||
@ -0,0 +1 @@
|
||||
export { default as TodayButton } from './ui/TodayButton.ui';
|
||||
@ -0,0 +1,56 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { userEvent } from '@testing-library/user-event';
|
||||
|
||||
import { withProvider } from 'shared/hoc/withProvider';
|
||||
|
||||
import { CalendarWidgetProvider } from '../../../CalendarWidgetContext';
|
||||
|
||||
import TodayButton from './TodayButton.ui';
|
||||
|
||||
describe('Test NextMonthButton', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = userEvent.setup();
|
||||
});
|
||||
|
||||
const setup = (props) => {
|
||||
const TodayButtonComponent = () => (
|
||||
<TodayButton {...props} />
|
||||
);
|
||||
const TodayButtonWithProvider = withProvider(CalendarWidgetProvider)(TodayButtonComponent);
|
||||
|
||||
render(<TodayButtonWithProvider />);
|
||||
};
|
||||
|
||||
test('Basic render', () => {
|
||||
setup();
|
||||
|
||||
const todayButtonElem = screen.getByTestId('today-button');
|
||||
expect(todayButtonElem).toBeInTheDocument();
|
||||
expect(todayButtonElem).toBeVisible();
|
||||
});
|
||||
|
||||
test('onClick handler', async () => {
|
||||
const onClick = jest.fn();
|
||||
setup({ onClick });
|
||||
|
||||
const todayButtonElem = screen.getByTestId('today-button');
|
||||
await user.click(todayButtonElem);
|
||||
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
|
||||
const onClickArgument = onClick.mock.calls[0][0];
|
||||
const calledDate = new Date(onClickArgument);
|
||||
const calledYear = calledDate.getFullYear();
|
||||
const calledMonthIndex = calledDate.getMonth();
|
||||
const calledDateNumber = calledDate.getDate();
|
||||
const calledString = `${calledYear}-${calledMonthIndex}-${calledDateNumber}`;
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
const currentMonthIndex = new Date().getMonth();
|
||||
const currentDateNumber = new Date().getDate();
|
||||
const expectedString = `${currentYear}-${currentMonthIndex}-${currentDateNumber}`;
|
||||
expect(calledString).toBe(expectedString);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,38 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { CalendarWidgetContext } from '../../../CalendarWidgetContext';
|
||||
|
||||
const TodayButton = (props) => {
|
||||
const {
|
||||
children = 'today',
|
||||
onClick,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const { setTargetDate } = useContext(CalendarWidgetContext);
|
||||
|
||||
const onClickHandler = () => {
|
||||
const newDate = new Date();
|
||||
setTargetDate(newDate);
|
||||
onClick?.(newDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
data-testid="today-button"
|
||||
type="button"
|
||||
onClick={onClickHandler}
|
||||
{...restProps}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
TodayButton.propTypes = {
|
||||
children: PropTypes.node,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export default TodayButton;
|
||||
@ -0,0 +1 @@
|
||||
export { default as YearSelect } from './ui/YearSelect.ui';
|
||||
@ -0,0 +1,53 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { userEvent } from '@testing-library/user-event';
|
||||
|
||||
import { withProvider } from 'shared/hoc/withProvider';
|
||||
|
||||
import { CalendarWidgetProvider } from '../../../CalendarWidgetContext';
|
||||
|
||||
import YearSelect from './YearSelect.ui';
|
||||
|
||||
describe('Test YearSelect', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = userEvent.setup();
|
||||
});
|
||||
|
||||
const setup = (props) => {
|
||||
const YearSelectComponent = () => (
|
||||
<YearSelect {...props} />
|
||||
);
|
||||
const YearSelectWithProvider = withProvider(CalendarWidgetProvider)(YearSelectComponent);
|
||||
|
||||
render(<YearSelectWithProvider />);
|
||||
};
|
||||
|
||||
test('Basic render', async () => {
|
||||
setup();
|
||||
|
||||
const yearSelectElem = screen.getByTestId('year-select');
|
||||
|
||||
expect(yearSelectElem).toBeInTheDocument();
|
||||
expect(yearSelectElem).toBeVisible();
|
||||
|
||||
await user.selectOptions(yearSelectElem, '2020');
|
||||
expect(yearSelectElem).toHaveValue('2020');
|
||||
expect(yearSelectElem).toHaveDisplayValue(['2020']);
|
||||
});
|
||||
|
||||
test('onChange handler', async () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
setup({ onChange });
|
||||
|
||||
const yearSelectElem = screen.getByTestId('year-select');
|
||||
await user.selectOptions(yearSelectElem, '2020');
|
||||
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
const currentDate = new Date();
|
||||
const currentMonthIndex = currentDate.getMonth();
|
||||
const newDate = new Date(2020, currentMonthIndex);
|
||||
expect(onChange).toHaveBeenCalledWith(newDate);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,72 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { CalendarWidgetContext } from '../../../CalendarWidgetContext';
|
||||
|
||||
const YearSelect = (props) => {
|
||||
const {
|
||||
onChange,
|
||||
...restProps
|
||||
} = props;
|
||||
// todo eslint fix
|
||||
const {
|
||||
targetYear, targetMonthIndex, setTargetDate, minDate, maxDate,
|
||||
} = useContext(CalendarWidgetContext);
|
||||
|
||||
const onChangeHandler = (event) => {
|
||||
let newDate = new Date(event.target.value, targetMonthIndex);
|
||||
|
||||
if (newDate < minDate) {
|
||||
newDate = minDate;
|
||||
}
|
||||
|
||||
if (newDate > maxDate) {
|
||||
newDate = maxDate;
|
||||
}
|
||||
|
||||
setTargetDate(newDate);
|
||||
onChange?.(newDate);
|
||||
};
|
||||
|
||||
const renderOptions = () => {
|
||||
const minYear = minDate.getFullYear();
|
||||
const maxYear = maxDate.getFullYear();
|
||||
const options = [];
|
||||
|
||||
for (let i = minYear; i <= maxYear; i++) {
|
||||
options.push(i);
|
||||
}
|
||||
|
||||
return options.map((item) => {
|
||||
// todo refactor
|
||||
const newDate = new Date(item, targetMonthIndex + 1, 0);
|
||||
const newDate2 = new Date(item, targetMonthIndex);
|
||||
const disabled = newDate2 > maxDate || newDate < minDate;
|
||||
|
||||
return (
|
||||
<option key={item} disabled={disabled} value={item}>
|
||||
{item}
|
||||
</option>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<select
|
||||
data-testid="year-select"
|
||||
id=""
|
||||
name="year"
|
||||
value={targetYear}
|
||||
onChange={onChangeHandler}
|
||||
{...restProps}
|
||||
>
|
||||
{renderOptions()}
|
||||
</select>
|
||||
);
|
||||
};
|
||||
|
||||
YearSelect.propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
};
|
||||
|
||||
export default YearSelect;
|
||||
Loading…
x
Reference in New Issue
Block a user