@/utils#commonHelpers JavaScript Examples
The following examples show how to use
@/utils#commonHelpers.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: doubleDateFilter.js From what-front with MIT License | 5 votes |
DoubleDateFilter = (props) => {
const { rawItemsList, setFilteredItemsList, component } = props;
let initialStartDate = `${new Date().getFullYear()}-01-01`;
let initialFinishDate = `${commonHelpers.transformDateTime({}).reverseDate}`;
let rawList;
switch(component){
case 'lessons': {
rawList = rawItemsList.map(item => {
const lessonDate = commonHelpers.transformDateTime({dateTime: item.lessonDate}).formInitialValue;
return item = {...item,
startDate: lessonDate,
finishDate: lessonDate};
});
const d = new Date();
d.setDate(d.getDate() - 15);
initialStartDate = `${commonHelpers.transformDateTime({dateTime:d}).reverseDate}`;
break;
}
default: {
rawList = rawItemsList;
break;
}
}
const validate = ({ startDate, finishDate }) => startDate > finishDate && {startDate: ' ', finishDate: ' '};
const filterByDate = ({ startDate, finishDate }) => {
const newArray = rawList
.filter((item) => {
return (new Date(item.startDate.slice(0, 10)) >= new Date(startDate))
&& (new Date(item.finishDate.slice(0, 10)) <= new Date(finishDate))
}
);
setFilteredItemsList(newArray);
};
return (
<Formik
initialValues={{
startDate: initialStartDate,
finishDate: initialFinishDate,
}}
validate={validate}
onSubmit={filterByDate}
>
{({ errors }) => (
<Form name="start-group" className="row d-flex">
<div className="col-5">
<Field
className={classNames('form-control', { 'border-danger': errors.startDate })}
type="date"
name="startDate"
id="startDate"
/>
</div>
<div className="col-5">
<Field
className={classNames('form-control', { 'border-danger': errors.finishDate })}
type="date"
name="finishDate"
id="finishDate"
/>
</div>
<div className="col-2 text-right">
<Button type="submit">
Filter
</Button>
</div>
</Form>
)}
</Formik>
)
}
Example #2
Source File: schedule.js From what-front with MIT License | 4 votes |
Schedule = ({ groupsData, schedulesData }) => {
const { currentUser } = useSelector(currentUserSelector, shallowEqual);
const {
data: schedules,
isLoading: areSchedulesLoading,
isLoaded: areSchedulesLoaded,
} = schedulesData;
const {
data: groups,
isLoading: areGroupsLoading,
isLoaded: areGroupsLoaded,
} = groupsData;
const [currentWeek, setCurrentWeek] = useState([]);
const [chosenDate, setChosenDate] = useState(new Date());
const [inputDateValue, setInputDateValue] = useState('');
const history = useHistory();
const DAY_IN_MILLIS = 86400000;
const WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
useEffect(() => {
const addZero = (num) => (num < 10 ? `0${num}` : num);
if (areSchedulesLoaded && areGroupsLoaded) {
const weekDayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
const firstDayOfWeek = new Date(chosenDate.getTime()
- (chosenDate.getDay() - 1) * DAY_IN_MILLIS);
const weekDays = weekDayNames.map((day, index) => {
const currentDay = new Date(firstDayOfWeek.getTime() + index * DAY_IN_MILLIS);
const lessons = schedules
.filter((schedule) => schedule.dayNumber === index || schedule.repeatRate === 1)
.sort((lesson, nextLesson) => (nextLesson.lessonStart < lesson.lessonStart ? 1 : -1));
return {
id: index,
day,
lessons,
date: `${addZero(currentDay.getDate())}.${addZero(currentDay.getMonth() + 1)}`,
isToday: new Date().toDateString() === currentDay.toDateString(),
isPast: new Date(new Date().toDateString()).getTime() > currentDay.getTime(),
};
});
setCurrentWeek(weekDays);
}
}, [areGroupsLoaded, areSchedulesLoaded, chosenDate, schedules, setCurrentWeek]);
const handleNextWeek = () => {
setChosenDate(new Date(chosenDate.getTime() + WEEK_IN_MILLIS));
};
const handlePreviousWeek = () => {
setChosenDate(new Date(chosenDate.getTime() - WEEK_IN_MILLIS));
};
const handleInputDate = (event) => {
setChosenDate(new Date(event.target.value));
setInputDateValue(event.target.value);
};
const handleAddSchedule = () => {
history.push(paths.SCHEDULE_ADD);
};
const handleSetToday = () => {
const today = commonHelpers.transformDateTime({ isDayTime: false }).reverseDate;
setChosenDate(new Date(today));
setInputDateValue(today);
};
const handleEditSchedule = (id) => {
console.log(id);
history.push(`${paths.SCHEDULE_EDIT}/${id}`);
};
const handleGroupSchedule = useCallback((event) => {
const { groupId } = event.target.dataset;
history.push(`${paths.SCHEDULE_BY_GROUP_ID}/${groupId}`);
}, [history]);
return (
<div className="container pt-5">
<WithLoading isLoading={areGroupsLoading || areSchedulesLoading} className="d-block mx-auto">
<div className="row ml-2 mr-2 mb-5">
<div className="col-3 d-flex justify-content-start pl-0">
<input
type="date"
min={`${new Date().getFullYear() - 1}-${new Date().getMonth() + 1}-${new Date().getDate()}`}
max={`${new Date().getFullYear() + 1}-${new Date().getMonth() + 1}-${new Date().getDate()}`}
onChange={handleInputDate}
value={inputDateValue}
className={styles['date-input']}
/>
<Button
className="ml-2"
variant="info"
onClick={handleSetToday}
>
Today
</Button>
</div>
<div className="col-6 d-flex justify-content-center">
<button
type="button"
className={styles['change-week-btn']}
onClick={handlePreviousWeek}
>
<Icon icon="Arrow" className={classNames(styles.arrow, styles['arrow-left'])} />
</button>
<h4 className="mb-0">{currentWeek[0]?.date} - {currentWeek[currentWeek.length - 1]?.date}</h4>
<button
type="button"
className={styles['change-week-btn']}
onClick={handleNextWeek}
>
<Icon icon="Arrow" className={styles.arrow} />
</button>
</div>
{[8, 4].includes(currentUser.role) ? (
<div className="col-3 d-flex justify-content-end pr-0">
<Button variant="info" onClick={handleAddSchedule}>
Add schedule
</Button>
</div>
) : null}
</div>
<section className={classNames('row', 'justify-content-center')}>
{ currentWeek.map(({ id, isToday, day, date, lessons, isPast }) => (
<div key={id} className={classNames('col', 'px-0', { [styles['current-day']]: isToday }, styles['day-column'])}>
<hgroup className={styles['day-column-header']}>
<h5 className="text-center">{day}</h5>
<h5 className="text-center">{date}</h5>
</hgroup>
<ul className={styles['lessons-list']}>
{ lessons.map(({ id: lessonId, studentGroupId, eventStart, eventFinish, lessonEnd, lessonStart }) => (
<li key={lessonId} className={styles['lessons-list__item']}>
<p
className={styles['lessons-list__group-name']}
onClick={handleGroupSchedule}
data-group-id={studentGroupId}
>
{ Array.isArray(groups)
? groups.find((group) => studentGroupId === group.id).name
: groups.name }
</p>
<div className={styles['lessons-list__details']}>
<Badge
variant={classNames(
{ info: isToday },
{ secondary: isPast && !isToday },
)}
className={classNames({ [styles['future-lesson']]: !isToday && !isPast })}
>
{/* {lessonStart.substring(0, 5)} - {lessonEnd.substring(0, 5)} */}
{new Date(eventStart).toLocaleTimeString()} - {new Date(eventFinish).toLocaleTimeString()}
</Badge>
{[8, 4].includes(currentUser.role) ? (
<button
type="button"
className={styles['edit-button']}
onClick={() => handleEditSchedule(lessonId)}
>
<Icon icon="Edit" size={24} />
</button>
) : null}
</div>
</li>
)) }
</ul>
</div>
)) }
</section>
</WithLoading>
</div>
);
}
Example #3
Source File: blocks.js From what-front with MIT License | 4 votes |
Blocks = (
{ data, handleDetails, handleEdit, access = true, fieldsToShow },
custom
) =>
data.map(
({
id,
index,
firstName,
lastName,
email,
name,
themeName,
lessonShortDate,
lessonTime,
studentIds,
startDate,
finishDate,
}) => (
<div
className="card"
style={{
width: '31%',
margin: '1%',
}}
key={id}
>
<div className="card-body d-flex justify-content-between">
{fieldsToShow.includes('index') && index && <div>{index + 1}</div>}
<div>
{fieldsToShow.includes('firstName') && firstName && (
<div>{firstName}</div>
)}
{fieldsToShow.includes('name') && name && <div>{name}</div>}
{fieldsToShow.includes('studentIds') && studentIds && (
<div>Quantity of students: {studentIds.length}</div>
)}
{fieldsToShow.includes('startDate') && startDate && (
<div>
Start:{' '}
{
commonHelpers.transformDateTime({
isDayTime: false,
dateTime: startDate,
}).date
}
</div>
)}
{fieldsToShow.includes('finishDate') && finishDate && (
<div>
Finish:{' '}
{
commonHelpers.transformDateTime({
isDayTime: false,
dateTime: finishDate,
}).date
}
</div>
)}
{fieldsToShow.includes('themeName') && themeName && (
<div>{themeName}</div>
)}
{fieldsToShow.includes('lessonShortDate') && lessonShortDate && (
<div>{lessonShortDate}</div>
)}
{fieldsToShow.includes('lessonTime') && lessonTime && (
<div>{lessonTime}</div>
)}
{fieldsToShow.includes('lastName') && lastName && (
<div>{lastName}</div>
)}
{fieldsToShow.includes('email') && email && <div>{email}</div>}
{fieldsToShow.includes('custom') && custom && <div>{custom}</div>}
<Button onClick={handleDetails ? () => handleDetails(id) : null} className={styles.btnDetails}>
<span>Details</span>
</Button>
</div>
{fieldsToShow.includes('edit') && access && (
<div
onClick={handleEdit ? (event) => handleEdit(event, id) : null}
>
<Icon
icon="Edit"
className={styles.scale}
color="#2E3440"
size={30}
/>
</div>
)}
</div>
</div>
)
)
Example #4
Source File: edit-schedule.test.js From what-front with MIT License | 4 votes |
describe('Tests EditSchedule', () => {
describe('Render && Form of EditSchedule', () => {
let historyMock;
beforeEach(() => {
useSelector
.mockReturnValue(scheduleMockDataLoading)
.mockReturnValue(scheduleMockData)
.mockReturnValue(studentGroupMockDataLoading)
.mockReturnValue(studentGroupMockData)
.mockReturnValue(mentorsMockData)
.mockReturnValue(themesMockData);
commonHelpers.transformDateTime = jest
.fn()
.mockReturnValue({ formInitialValue: '2021-06-17T02:47' });
historyMock = { push: jest.fn(), location: jest.fn(), listen: jest.fn() };
});
afterEach(cleanup);
it('should loader appear when scheduleSelector .isLoading is true', () => {
const mockedScheduleSelector = {
data: scheduleMockDataLoading,
isLoading: true,
};
useSelector.mockReturnValue(mockedScheduleSelector);
const { container } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={mockedScheduleSelector}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const loader = container.querySelector('.spinner-border');
expect(loader).toBeInTheDocument();
});
it('should loader appear when groupSelector .isLoading is true', () => {
const mockedGroupSelector = {
data: studentGroupMockDataLoading,
isLoading: true,
};
useSelector.mockReturnValue(mockedGroupSelector);
const { container } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={mockedGroupSelector}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const loader = container.querySelector('.spinner-border');
expect(loader).toBeInTheDocument();
});
it('should the component be rendered', () => {
const { getByTestId } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
expect(getByTestId('editForm')).toBeInTheDocument();
});
it('should the Form be rendered correctly', () => {
const { getByTestId } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const groupName = getByTestId('groupName');
const eventStart = getByTestId('eventStart');
const eventFinish = getByTestId('eventFinish');
expect(getByTestId('editForm')).toBeInTheDocument();
expect(groupName.value).toBe('122-18-3');
expect(eventStart.value).toBe('2021-06-17T02:47');
expect(eventFinish.value).toBe('2021-06-17T02:47');
expect(groupName).toBeDisabled();
});
it('should weekDays list = 7 ', () => {
const { container } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const weekDaysList = container.querySelector('#weekDays-list');
fireEvent.click(weekDaysList);
expect(weekDaysList.children.length).toEqual(7);
expect(weekDaysList.children[0].value).toBe('Monday');
});
it('should day of week be chosen', () => {
const { getByPlaceholderText } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const weekDay = getByPlaceholderText("Select day('s) of week");
fireEvent.change(weekDay, {
target: { value: helpersData.daysOfWeek[3].name },
});
expect(weekDay.value).toBe('Thursday');
});
});
describe('Redirect correctly', () => {
let historyMock;
let data;
let mockEditedScheduleSelector;
let mockDeletedScheduleSelector;
beforeEach(() => {
data = scheduleMockData;
data.isLoading = false;
mockEditedScheduleSelector = {
isLoading: false,
isLoaded: true,
error: '',
};
mockDeletedScheduleSelector = {
isLoading: false,
isLoaded: true,
error: '',
};
useSelector
.mockReturnValue(mockEditedScheduleSelector)
.mockReturnValue(mockDeletedScheduleSelector)
.mockReturnValue(scheduleMockData)
.mockReturnValue(studentGroupMockData)
.mockReturnValue(mentorsMockData)
.mockReturnValue(themesMockData);
historyMock = { push: jest.fn(), location: jest.fn(), listen: jest.fn() };
});
afterEach(cleanup);
it('should redirrect to page 404 in case of error', () => {
const noData = {
data: '',
isLoading: true,
isLoaded: true,
};
useSelector.mockReturnValue(noData);
render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={noData}
groupData={noData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
expect(historyMock.push).toHaveBeenCalledWith(paths.NOT_FOUND);
});
it('should redirrect to schedule page and show success alert after successful editing', () => {
mockEditedScheduleSelector = {
...data,
isLoaded: true,
};
useSelector
.mockReturnValueOnce(mockEditedScheduleSelector)
.mockReturnValueOnce(mockDeletedScheduleSelector);
render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
expect(useActionsFns.dispatchAddAlert).toHaveBeenCalledWith(
'The schedule has been successfully edited',
'success'
);
});
it('should show error alert after unsuccessful editing', () => {
mockEditedScheduleSelector = {
...data,
isLoaded: false,
error: 'some error',
};
useSelector
.mockReturnValueOnce(mockEditedScheduleSelector)
.mockReturnValueOnce(mockDeletedScheduleSelector);
render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
expect(useActionsFns.dispatchAddAlert).toHaveBeenCalledWith(
mockEditedScheduleSelector.error
);
});
it('should redirrect to schedule page and show success alert after successful deleting', () => {
mockDeletedScheduleSelector = {
...data,
isLoaded: true,
};
useSelector
.mockReturnValueOnce(mockEditedScheduleSelector)
.mockReturnValueOnce(mockDeletedScheduleSelector);
render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
expect(useActionsFns.dispatchAddAlert).toHaveBeenCalledWith(
'The schedule has been successfully deleted',
'success'
);
});
it('should show error alert after unsuccessful deleting', () => {
mockDeletedScheduleSelector = {
...data,
isLoaded: false,
error: 'some error',
};
useSelector
.mockReturnValueOnce(mockEditedScheduleSelector)
.mockReturnValueOnce(mockDeletedScheduleSelector);
render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
expect(useActionsFns.dispatchAddAlert).toHaveBeenCalledWith(
mockDeletedScheduleSelector.error
);
});
it('should open modalWindow when click delete', async () => {
const { getByText } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const handleShowModal = jest.fn();
const removeBtn = getByText(/Delete/i);
await waitFor(() => {
fireEvent.click(removeBtn);
handleShowModal();
});
expect(handleShowModal).toHaveBeenCalledTimes(1);
expect(
getByText(/Are you sure you want to exclude this schedule?/i)
).toBeInTheDocument();
});
it('should close modalWindow and delete course when click delete on modalWindow', async () => {
const { getByText, getByRole } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const handleDelete = jest.fn();
const removeBtn = getByText('Delete');
await waitFor(() => {
fireEvent.click(removeBtn);
});
expect(getByRole('button', { name: 'Delete' })).toBeInTheDocument();
expect(getByRole('button', { name: 'Cancel' })).toBeInTheDocument();
const deleteBtn = getByRole('button', { name: 'Delete' });
await waitFor(() => {
fireEvent.click(deleteBtn);
handleDelete();
});
expect(handleDelete).toHaveBeenCalled();
expect(useActionsFns.removeSchedule).toHaveBeenCalled();
});
it('should day of week not Found', () => {
React.useState = jest
.fn()
.mockReturnValue(['', useStates.dayInputError.setDayInputError]);
const { getByPlaceholderText } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const weekDay = getByPlaceholderText("Select day('s) of week");
fireEvent.change(weekDay, { target: { value: 'day' } });
expect(useStates.dayInputError.setDayInputError).toHaveBeenCalled();
});
it('should call addDayOfWeek and change state', async () => {
React.useState = jest
.fn()
.mockReturnValue([[], useStates.weekDays.setWeekDays]);
const { container, getByPlaceholderText, getByText } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const addDayOfWeek = jest.fn();
const clearField = jest.fn();
const addDayOfWeekBtn = container.querySelector('#add-weekDay-btn');
const weekDay = getByPlaceholderText("Select day('s) of week");
await waitFor(() => {
fireEvent.change(weekDay, {
target: { value: helpersData.daysOfWeek[3].name },
});
});
await waitFor(() => {
fireEvent.click(addDayOfWeekBtn);
addDayOfWeek();
clearField();
});
expect(addDayOfWeek).toHaveBeenCalledTimes(1);
expect(useStates.weekDays.setWeekDays).toHaveBeenCalled();
expect(getByText(/Thursday/i)).toBeInTheDocument();
});
it('should call removeDayOfWeek and change state', async () => {
React.useState = jest
.fn()
.mockReturnValue([[], useStates.weekDays.setWeekDays]);
const { container, getByPlaceholderText } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const addDayOfWeek = jest.fn();
const removeDayOfWeek = jest.fn();
const addDayOfWeekBtn = container.querySelector('#add-weekDay-btn');
const weekDay = getByPlaceholderText("Select day('s) of week");
await waitFor(() => {
fireEvent.change(weekDay, {
target: { value: helpersData.daysOfWeek[3].name },
});
});
await waitFor(() => {
fireEvent.click(addDayOfWeekBtn);
addDayOfWeek();
});
const dayContainer = container.querySelector('#chosenWeekDay');
const removeBtn = dayContainer.children[0];
expect(removeBtn).toHaveTextContent('X');
await waitFor(() => {
fireEvent.click(removeBtn);
removeDayOfWeek();
});
expect(removeDayOfWeek).toHaveBeenCalledTimes(1);
expect(useStates.weekDays.setWeekDays).toHaveBeenCalled();
});
it('should change interval', async () => {
React.useState = jest
.fn()
.mockReturnValue([1, useStates.interval.setInterval]);
const { getByTestId } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const intervalInput = getByTestId('interval');
await waitFor(() => {
fireEvent.change(intervalInput, { target: { value: 3 } });
});
expect(useStates.interval.setInterval).toHaveBeenCalled();
expect(intervalInput.value).toBe('3');
});
it('should handleReset', async () => {
React.useState = jest
.fn()
.mockReturnValue([1, useStates.interval.setInterval]);
const { container, getByTestId, getByRole, getByPlaceholderText } = render(
<Router history={historyMock}>
<EditSchedule
id={1}
schedulesData={scheduleMockData}
groupData={studentGroupMockData}
themesData={themesMockData}
mentorsData={mentorsMockData}
/>
</Router>
);
const handleReset = jest.fn();
const setInterval = jest.fn();
const setWeekDays = jest.fn();
const intervalInput = getByTestId('interval');
const cancleBtn = getByRole('button', { name: 'Clear' });
const addDayOfWeekBtn = container.querySelector('#add-weekDay-btn');
const weekDay = getByPlaceholderText("Select day('s) of week");
await waitFor(() => {
fireEvent.change(intervalInput, { target: { value: 3 } });
fireEvent.change(weekDay, {
target: { value: helpersData.daysOfWeek[3].name },
});
fireEvent.click(addDayOfWeekBtn);
});
expect(intervalInput.value).toBe('3');
await waitFor(() => {
fireEvent.click(cancleBtn);
handleReset();
setInterval(1);
setWeekDays([]);
});
expect(handleReset).toHaveBeenCalled();
expect(setInterval).toHaveBeenCalled();
expect(setWeekDays).toHaveBeenCalled();
expect(useStates.interval.setInterval).toHaveBeenCalled();
expect(intervalInput.value).toBe('1');
});
});
});
Example #5
Source File: student-lessons.js From what-front with MIT License | 4 votes |
StudentLessons = () => {
const history = useHistory();
const [searchLessonsThemeValue, setSearchLessonsThemeValue] = useState('');
const [filteredLessonsList, setFilteredLessonsList] = useState([]);
const [visibleLessonsList, setVisibleLessonsList] = useState([]);
const [searchLessonsDateValue, setSearchLessonsDateValue] = useState('');
const [currentPage, setCurrentPage] = useState(1);
const [descendingSorts, setDescendingSorts] = useState({ id: true, themeName: false, lessonDate: false, lessonTime: false });
const [prevSort, setPrevSort] = useState('id');
const { data, isLoading } = useSelector(studentLessonsSelector, shallowEqual);
const { currentUser } = useSelector(currentUserSelector, shallowEqual);
const fetchStudentLessons = useActions(fetchLessonsByStudentId);
const lessonsPerPage = 10;
const indexOfLast = currentPage * lessonsPerPage;
const indexOfFirst = indexOfLast - lessonsPerPage;
useEffect(() => {
fetchStudentLessons(currentUser.id);
}, [fetchStudentLessons, currentUser]);
useEffect(() => {
if(data.length !== 0) {
const lessonsData = data.map((lesson) => {
const {date, time} = commonHelpers.transformDateTime({ dateTime: lesson.lessonDate });
return {
lessonShortDate: date,
lessonTime: time,
...lesson,
}
});
setFilteredLessonsList(lessonsData);
}
}, [data]);
useEffect(() => {
setVisibleLessonsList(filteredLessonsList.slice(indexOfFirst, indexOfLast));
}, [currentPage, filteredLessonsList]);
const handleSearchTheme = (inputValue) => setSearchLessonsThemeValue(inputValue);
const handleSearchDate = (event) => setSearchLessonsDateValue(event.target.value);
useEffect(() => {
const lessons = data.filter(
(lesson) => lesson.lessonShortDate === searchLessonsDateValue);
setFilteredLessonsList(lessons);
setCurrentPage(1);
}, [searchLessonsDateValue]);
useEffect(() => {
const lessons = data.filter(
(lesson) => lesson.themeName.toUpperCase().includes(searchLessonsThemeValue.toUpperCase()),
);
setFilteredLessonsList(lessons);
setCurrentPage(1);
}, [searchLessonsThemeValue]);
const lessonDetails = useCallback((id) => {
history.push(`${paths.LESSON_DETAILS}/${id}`);
}, [history]);
const handleSortByParam = (key) => {
if (prevSort === key) {
descendingSorts[key] = !descendingSorts[key];
setDescendingSorts(descendingSorts);
} else {
setDescendingSorts({ id: false, themeName: false, lessonDate: false, lessonTime: false });
}
setPrevSort(key);
const sortedLessons = [...filteredLessonsList].sort((a, b) => {
if (descendingSorts[key]) {
return a[key] <= b[key] ? -1 : 1;
}
return b[key] <= a[key] ? -1 : 1;
});
setFilteredLessonsList(sortedLessons);
setVisibleLessonsList(filteredLessonsList.slice(indexOfFirst, indexOfLast));
};
const paginate = (pageNumber) => {
if (currentPage !== pageNumber) {
setCurrentPage(pageNumber);
}
};
const nextPage = useCallback((pageNumber) => {
const totalPages = Math.ceil(filteredLessonsList?.length / lessonsPerPage);
setCurrentPage(currentPage === totalPages ? currentPage : pageNumber);
},[lessonsPerPage, currentPage]);
const prevPage = useCallback((pageNumber) => {
setCurrentPage(currentPage - 1 === 0 ? currentPage : pageNumber);
},[currentPage]);
const getLessonsList = useCallback(() => {
const lessonsList = visibleLessonsList.map((lesson) => (
<tr id={lesson.id} key={lesson.id} onClick={() => lessonDetails(lesson.id)} className={styles['table-row']}>
<td className="text-center">{lesson.id}</td>
<td>{lesson.themeName}</td>
<td>{lesson.lessonShortDate}</td>
<td>{lesson.lessonTime}</td>
</tr>
));
if (!lessonsList.length && searchLessonsDateValue || !lessonsList.length && searchLessonsThemeValue) {
return <tr><td colSpan="5" className="text-center">Lesson is not found</td></tr>;
}
return lessonsList;
}, [visibleLessonsList, searchLessonsDateValue, searchLessonsThemeValue]);
return (
<div className="container">
<div className="row justify-content-between align-items-center mb-3">
<h2 className="col-6">Lessons</h2>
{filteredLessonsList.length > lessonsPerPage ? <div className="col-2 text-right">{filteredLessonsList.length} lessons</div> : null}
<div className="col-4 d-flex align-items-center justify-content-end">
{filteredLessonsList.length > lessonsPerPage && !isLoading
&& (
<Pagination
itemsPerPage={lessonsPerPage}
totalItems={filteredLessonsList.length}
paginate={paginate}
prevPage={prevPage}
nextPage={nextPage}
page={currentPage}
/>
)}
</div>
</div>
<div className="row">
<div className="col-12 card shadow p-3 mb-5 bg-white">
<div className="row align-items-center mt-2 mb-3">
<div className="col-2">
<div className="btn-group">
<button type="button" className="btn btn-secondary" disabled><Icon icon="List" color="#2E3440" size={25} /></button>
<button type="button" className="btn btn-outline-secondary" disabled><Icon icon="Card" color="#2E3440" size={25} /></button>
</div>
</div>
<div className="col-3">
<Search onSearch={handleSearchTheme} className={classNames(styles.text)} placeholder="Theme's name" />
</div>
<div className="col-2">
<input
className={classNames(styles.date, 'form-control')}
type="date"
name="lesson_date"
required
onChange={handleSearchDate}
/>
</div>
</div>
<WithLoading isLoading={isLoading} className="d-block mx-auto mt-3">
<table className="table table-hover">
<thead>
<tr>
<th
scope="col"
className={classNames(styles['table-head'], `${descendingSorts.id ? classNames(styles.descending) : ''}`, 'text-center', 'align-middle')}
onClick={() => handleSortByParam('id')}
>
<button
className={classNames(styles['button-sort'], 'px-0')}
>
<span className="font-weight-bolder">#</span>
<span className="pl-2">
<Icon className={classNames(styles['arrow-down'])} icon="DropDown" color="#2E3440" size={25} />
<Icon className={classNames(styles['arrow-up'])} icon="DropUp" color="#2E3440" size={25} />
</span>
</button>
</th>
<th
scope="col"
className={classNames(styles['table-head'], `${descendingSorts.themeName ? classNames(styles.descending) : ''}`)}
onClick={() => handleSortByParam('themeName')}
>
<button
className={classNames(styles['button-sort'], 'px-0')}
>
<span className="font-weight-bolder">Theme Name</span>
<span className="pl-2">
<Icon className={classNames(styles['arrow-down'])} icon="DropDown" color="#2E3440" size={25} />
<Icon className={classNames(styles['arrow-up'])} icon="DropUp" color="#2E3440" size={25} />
</span>
</button>
</th>
<th
scope="col"
className={classNames(styles['table-head'], `${descendingSorts.lessonDate ? classNames(styles.descending) : ''}`)}
onClick={() => handleSortByParam('lessonDate')}
>
<button
className={classNames(styles['button-sort'], 'px-0')}
>
<span className="font-weight-bolder">Date</span>
<span className="pl-2">
<Icon className={classNames(styles['arrow-down'])} icon="DropDown" color="#2E3440" size={25} />
<Icon className={classNames(styles['arrow-up'])} icon="DropUp" color="#2E3440" size={25} />
</span>
</button>
</th>
<th
scope="col"
className={classNames(styles['table-head'], `${descendingSorts.lessonTime ? classNames(styles.descending) : ''}`)}
onClick={() => handleSortByParam('lessonTime')}
>
<button
className={classNames(styles['button-sort'], 'px-0')}
>
<span className="font-weight-bolder">Time</span>
<span className="pl-2">
<Icon className={classNames(styles['arrow-down'])} icon="DropDown" color="#2E3440" size={25} />
<Icon className={classNames(styles['arrow-up'])} icon="DropUp" color="#2E3440" size={25} />
</span>
</button>
</th>
</tr>
</thead>
<tbody>
{
getLessonsList()
}
</tbody>
</table>
</WithLoading>
</div>
</div>
</div>
)
}
Example #6
Source File: student-lesson-details.js From what-front with MIT License | 4 votes |
StudentLessonDetails = () => {
const history = useHistory();
const { id } = useParams();
const [lessonData, setLessonData] = useState({});
const { currentUser } = useSelector(currentUserSelector, shallowEqual);
const {
data: lessons,
isLoading: lessonsIsLoading,
isLoaded: lessonsIsLoaded,
} = useSelector(studentLessonsSelector, shallowEqual);
const fetchStudentLessons = useActions(fetchLessonsByStudentId);
useEffect(() => {
if (!lessonsIsLoaded) fetchStudentLessons(currentUser.id);
}, [lessonsIsLoaded]);
useEffect(() => {
if (lessonsIsLoaded) {
if (lessons.length === 0 || !lessons) {
history.push(paths.NOT_FOUND);
} else {
const lesson = lessons.find((lesson) => lesson.id === Number(id));
const { date, time } = commonHelpers.transformDateTime({ dateTime: lesson.lessonDate });
const lessonData = {
lessonShortDate: date,
lessonTime: time,
...lesson,
};
setLessonData(lessonData);
}
}
}, [lessons, lessonsIsLoaded]);
const handleCancel = useCallback(() => history.push(paths.LESSONS), [history]);
return (
<div className="container pt-5" data-testid='studLessonDetails'>
<div className={classNames(styles.page, 'col-12')}>
<h3>Lesson details</h3>
<hr />
<div className="col-12 d-flex flex-lg-row flex-md-column flex-sm-column">
<WithLoading
isLoading={lessonsIsLoading || !lessonsIsLoaded || lessons.length === 0 || !lessons}
className={styles['loader-centered']}
>
<div className="col-lg-5 col-md-8 col-sm-12 mb-2" >
<div className="mt-3 mb-4 row">
<div className="col-sm-5 font-weight-bolder"><span>Lesson Theme: </span></div>
<div className="col-sm-7"><span data-testid='theme'>{lessonData.themeName}</span></div>
</div>
<div className="row mb-4">
<div className="col-sm-5 font-weight-bolder">
<span>Lesson Date: </span>
</div>
<div className="col-sm-7" data-testid='date'>
{lessonData.lessonShortDate}
</div>
</div>
<div className="row mb-4">
<div className="col-sm-5 font-weight-bolder">
<span>Lesson Time: </span>
</div>
<div className="col-sm-7" data-testid='time'>
{lessonData.lessonTime}
</div>
</div>
</div>
<div className="col-lg-7 col-md-12 col-sm-12">
<table className="table table-bordered table-hover">
<thead>
<tr>
<th scope="col" className="text-center col-sm-3">Presence</th>
<th scope="col" className="text-center col-sm-3">Mark</th>
<th scope="col" className="text-center col-sm-6">Comment</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="col" className="text-center font-weight-bolder">
<span data-testid='presence'>{lessonData.presence
? <Icon icon="Present" /> : <Icon icon="Absent" />}
</span>
</td>
<td scope="col" className="text-center" data-testid='mark'>{lessonData.mark}</td>
<td scope="col" className="text-center" data-testid='comment'>{lessonData.comment}</td>
</tr>
</tbody>
</table>
</div>
</WithLoading>
</div>
<div className="col-12 mt-3">
<button
data-testid='backButton'
type="button"
className="btn btn-secondary btn-lg"
onClick={handleCancel}
>Cancel
</button>
</div>
</div>
</div>
);
}
Example #7
Source File: student-lesson-details.test.js From what-front with MIT License | 4 votes |
describe('Render of student lesson details', () => {
beforeEach(() => {
const mockedLessonsSelector = {
data: lessonsData,
isLoading: false,
isLoaded: true,
};
const mockedCurrentUserSelector = {
currentUser: {
email: '[email protected]',
first_name: 'student',
id: 1,
last_name: 'student',
role: 1,
},
};
const useActionsFns = {
fetchStudentLessons: jest.fn(),
};
useSelector
.mockReturnValueOnce(mockedCurrentUserSelector)
.mockReturnValue(mockedLessonsSelector);
useActions.mockReturnValue(useActionsFns.fetchStudentLessons);
const useStateLessonData = {
lessonData: '',
setLessonData: jest.fn(),
};
React.useState = jest.fn()
.mockReturnValueOnce([useStateLessonData.lessonData, useStateLessonData.setLessonData]);
commonHelpers.transformDateTime = jest.fn().mockReturnValue({ date: '15.06.2021', time: '11:38' });
});
it('should the component be rendered', () => {
const { getByTestId } = render(<StudentLessonDetails />);
expect(getByTestId('studLessonDetails'))
.toBeInTheDocument();
});
it('should loader appear when studentLessonsSelector.isLoading is true', () => {
const mockedLessonsSelector = {
data: lessonsData,
isLoading: true,
isLoaded: false,
};
useSelector.mockReturnValue(mockedLessonsSelector);
const { container } = render(<StudentLessonDetails />);
const loader = container.querySelector('.spinner-border');
expect(loader).toBeInTheDocument();
});
it('should display correct lesson-data in proper places', async () => {
const mockedLessonsSelector = {
data: lessonsData,
isLoaded: true,
};
useSelector.mockReturnValue(mockedLessonsSelector);
const { getByTestId } = await render(<StudentLessonDetails />);
const theme = getByTestId('theme');
const date = getByTestId('date');
const time = getByTestId('time');
const presence = getByTestId('presence');
const mark = getByTestId('mark');
const comment = getByTestId('comment');
await expect(theme.innerText).toBe(mockedLessonsSelector.themeName);
await expect(date.innerText).toBe(commonHelpers.transformDateTime.date);
await expect(time.innerText).toBe(commonHelpers.transformDateTime.time);
await expect(presence.innerText).toBe(mockedLessonsSelector.presence);
await expect(mark.innerText).toBe(mockedLessonsSelector.mark);
await expect(comment.innerText).toBe(mockedLessonsSelector.comment);
});
it('should loader appear when studentLessonsSelector.isLoaded is false', () => {
const mockedLessonsSelector = {
isLoaded: false,
};
useSelector.mockReturnValue(mockedLessonsSelector);
const { container } = render(<StudentLessonDetails />);
const loader = container.querySelector('.spinner-border');
expect(loader).toBeInTheDocument();
});
it('should loader appear when data in studentLessonsSelector is empty', () => {
const mockedLessonsSelector = {
data: [],
isLoaded: false,
};
useSelector.mockReturnValue(mockedLessonsSelector);
const { container } = render(<StudentLessonDetails />);
const loader = container.querySelector('.spinner-border');
expect(loader).toBeInTheDocument();
});
it('should the button Back redirect to correct URL', async () => {
const historyMock = { push: jest.fn(), location: {}, listen: jest.fn() };
const { getByTestId } = render(<Router history={historyMock}><StudentLessonDetails /></Router>);
const backButton = getByTestId('backButton');
fireEvent.click(backButton);
expect(historyMock.push.mock.calls[0][0])
.toEqual(paths.LESSONS);
});
it('should redirect to Not-Found if !lessons', async () => {
const mockedLessonsSelector = {
data: [],
isLoaded: true,
};
await useSelector.mockReturnValue(mockedLessonsSelector);
const historyMock = { push: jest.fn(), location: {}, listen: jest.fn() };
await render(<Router history={historyMock}><StudentLessonDetails /></Router>);
await expect(historyMock.push.mock.calls[0][0])
.toEqual(paths.NOT_FOUND);
});
});
Example #8
Source File: list-of-lessons.js From what-front with MIT License | 4 votes |
ListOfLessons = () => {
const history = useHistory();
const getAllLessons = useActions(fetchLessons);
const getMentorsLessons = useActions(fetchMentorLessons);
const allLessons = useSelector(lessonsSelector, shallowEqual);
const mentorsLessons = useSelector(mentorLessonsSelector, shallowEqual);
const { currentUser } = useSelector(currentUserSelector, shallowEqual);
const { data, isLoading } =
currentUser.role === 2 ? mentorsLessons : allLessons;
const [searchLessonsThemeValue, setSearchLessonsThemeValue] = useState('');
const [rawLessonsList, setRawLessonsList] = useState([]);
const [filteredLessonsList, setFilteredLessonsList] = useState([]);
const [visibleLessonsList, setVisibleLessonsList] = useState([]);
const INITIAL_CATEGORIES = [
{
id: 0,
name: 'themeName',
sortedByAscending: false,
tableHead: 'Theme Name',
},
{
id: 1,
name: 'lessonDate',
sortedByAscending: false,
tableHead: 'Lesson Date',
},
{
id: 2,
name: 'lessonTime',
sortedByAscending: false,
tableHead: 'Lesson Time',
},
];
const [showBlocks, setShowBlocks] = useState(false);
const [sortingCategories, setSortingCategories] = useState(
INITIAL_CATEGORIES
);
useEffect(() => {
if (currentUser.role === 2) {
getMentorsLessons(currentUser.id);
} else {
getAllLessons();
}
}, [currentUser, getAllLessons, getMentorsLessons]);
useEffect(() => {
if (data.length !== 0) {
const lessonsData = data.map((lesson) => {
const { date, time } = commonHelpers.transformDateTime({
dateTime: lesson.lessonDate,
});
return {
lessonShortDate: date,
lessonTime: time,
...lesson,
};
});
setRawLessonsList(lessonsData);
setFilteredLessonsList(lessonsData);
}
}, [data]);
useEffect(() => {
const lessons = rawLessonsList.filter((lesson) =>
lesson.themeName
.toUpperCase()
.includes(searchLessonsThemeValue.toUpperCase())
);
setFilteredLessonsList(lessons);
}, [searchLessonsThemeValue]);
const handleSearchTheme = (inputValue) => {
setSearchLessonsThemeValue(inputValue);
};
const addLesson = useCallback(() => history.push(paths.LESSON_ADD), [
history,
]);
const downloadThemes = useCallback(
() => history.push(paths.THEMES_DOWNLOAD),
[history]
);
const handleDetails = useCallback(
(id) => history.push(`${paths.LESSON_DETAILS}/${id}`),
[history]
);
const handleEdit = useCallback(
(event, id) => {
event.stopPropagation();
history.push(`${paths.LESSON_EDIT}/${id}`);
},
[history]
);
const changeActiveCategory = (categories, activeCategoryName) =>
categories.map((category) => {
if (category.name === activeCategoryName) {
return { ...category, sortedByAscending: !category.sortedByAscending };
}
return { ...category, sortedByAscending: false };
});
const handleSortByParam = (data, categoryParams) => {
const sortedLessons = data;
setSortingCategories(
changeActiveCategory(sortingCategories, categoryParams.sortingParam)
);
setFilteredLessonsList(sortedLessons);
};
const listProps = {
data: visibleLessonsList,
handleDetails,
handleEdit,
errors: [
{
message: 'Lesson is not found',
check: [
(!visibleLessonsList.length && !visibleLessonsList.length) ||
(!visibleLessonsList.length && !!searchLessonsThemeValue),
],
},
],
access: currentUser.role !== 8,
fieldsToShow: ['themeName', 'lessonShortDate', 'lessonTime', 'edit'],
};
return (
<div className="container pt-5">
<div className="row justify-content-between align-items-center mb-3">
<h2 className="col-6">Lessons</h2>
{!isLoading ? (
<div className="col-2 text-right">
{visibleLessonsList.length} of {filteredLessonsList.length} lessons
</div>
) : null}
</div>
<div className="row mr-0">
<div className="col-12 card shadow p-3 mb-5 bg-white ml-2 mr-2">
<div className="row align-items-center justify-content-between mt-2 mb-3">
<div className="col-3">
<div className="btn-group">
<button
type="button"
className="btn btn-secondary"
disabled={!showBlocks}
onClick={() => setShowBlocks(false)}
>
<Icon icon="List" color="#2E3440" size={25} />
</button>
<button
type="button"
className="btn btn-secondary"
disabled={showBlocks}
onClick={() => setShowBlocks(true)}
>
<Icon icon="Card" color="#2E3440" size={25} />
</button>
</div>
</div>
<div className="col-3">
<Search
onSearch={handleSearchTheme}
className={classNames(styles.text)}
placeholder="Theme's name"
/>
</div>
{currentUser.role !== 8 && (
<div className="col-4 text-right">
<Button
onClick={downloadThemes}
type="button"
className={classNames(
'btn btn-warning mr-3',
styles['left-add-btn']
)}
>
Add theme('s)
</Button>
<Button onClick={addLesson} type="button">
<span>Add a lesson</span>
</Button>
</div>
)}
</div>
<div className="row align-items-center justify-content-end mb-3">
<div className="col-6 offset-4">
{
<DoubleDateFilter
rawItemsList={rawLessonsList}
setFilteredItemsList={setFilteredLessonsList}
component={'lessons'}
/>
}
</div>
</div>
<WithLoading isLoading={isLoading} className="d-block mx-auto mt-3">
{showBlocks ? (
<div className="container d-flex flex-wrap">
<List listType={'block'} props={listProps} />
</div>
) : (
<Table
sortingCategories={sortingCategories}
currentUser={currentUser}
onClick={handleSortByParam}
data={filteredLessonsList}
access={{ unruledUser: [8], unassigned: '' }}
>
<List props={listProps} listType={'list'} />
</Table>
)}
</WithLoading>
</div>
<div
className={classNames(
'row justify-content-between align-items-center mb-3',
styles.paginate
)}
>
<Pagination
items={filteredLessonsList}
setVisibleItems={setVisibleLessonsList}
/>
</div>
</div>
</div>
);
}
Example #9
Source File: lesson-details.js From what-front with MIT License | 4 votes |
LessonDetails = () => {
const history = useHistory();
const { id } = useParams();
const [studentsGroup, setStudentsGroup] = useState({});
const [lesson, setLesson] = useState({});
const [formData, setFormData] = useState([]);
const [mentor, setMentor] = useState({});
const [
loadLessons,
loadMentors,
loadGroups,
fetchStudents,
] = useActions([fetchLessons, fetchMentors, globalLoadStudentGroups, loadStudents]);
const {
data: lessons,
isLoading: lessonsIsLoading,
isLoaded: lessonsIsLoaded,
} = useSelector(lessonsSelector, shallowEqual);
const {
data: mentors,
isLoading: mentorsIsLoading,
isLoaded: mentorsIsLoaded,
} = useSelector(mentorsSelector, shallowEqual);
const {
data: groups,
isLoading: groupsIsLoading,
isLoaded: groupsIsLoaded,
} = useSelector(loadStudentGroupsSelector, shallowEqual);
const {
data: students,
isLoading: studentsIsLoading,
isLoaded: studentsIsLoaded,
} = useSelector(studentsSelector, shallowEqual);
useEffect(() => {
if(!lessonsIsLoaded) loadLessons();
if(!studentsIsLoaded) fetchStudents();
if(!groupsIsLoaded) loadGroups();
if(!mentorsIsLoaded) loadMentors();
}, [loadLessons, fetchStudents, loadGroups, loadMentors]);
useEffect(() => {
if (lessonsIsLoaded) {
const lesson = lessons.find((lesson) => lesson.id === Number(id));
if (!lesson) {
history.push(paths.NOT_FOUND);
} else {
const {date, time} = commonHelpers.transformDateTime({ dateTime: lesson.lessonDate });
const lessonsData = {
lessonShortDate: date,
lessonTime: time,
...lesson,
};
setLesson(lessonsData);
}
}
}, [lessons]);
const getFormData = () => {
const uniqueIds = [...new Set(studentsGroup.studentIds)];
const studentD = uniqueIds.map((id) => students.find((student) => student.id === id));
const studentsData = studentD.map((student) => (
{
studentId: student.id,
studentName: `${student.firstName} ${student.lastName}`,
}
));
const resultLessonVisits = studentsData.sort((a, b) => {
if (a.studentName < b.studentName) {
return -1;
}
if (a.studentName > b.studentName) {
return 1;
}
})
.map((student, index) => ({
...lesson.lessonVisits[index],
...student,
}));
setFormData(resultLessonVisits);
};
useEffect(() => {
if (lesson && groups.length) {
const group = groups?.find((group) => group.id === lesson.studentGroupId);
if (group && studentsIsLoaded && !studentsIsLoading) {
setStudentsGroup(group);
if (studentsGroup && students) {
getFormData();
}
}
}
}, [groups, students, studentsIsLoaded, studentsIsLoading, lesson, studentsGroup]);
useEffect(() => {
if (lesson && mentorsIsLoaded && !mentorsIsLoading) {
const mentor = mentors?.find((mentor) => mentor.id === lesson.mentorId);
if (mentor) {
setMentor(mentor);
}
}
}, [lesson, mentorsIsLoaded, !mentorsIsLoading]);
useEffect(() => {
if (!lessons && lessonsIsLoaded) {
history.push(paths.NOT_FOUND);
}
}, [lessons, history, lessonsIsLoaded]);
const openStudentDetails = useCallback((studentId) => {
history.push(`${paths.STUDENTS_DETAILS}/${studentId}`);
}, [history]);
const handleCancel = useCallback(() => {
history.push(paths.LESSONS);
}, [history]);
return (
<div className="container pt-5">
<div className={classNames(styles.page, 'mx-auto', 'col-12')}>
<div className="d-flex flex-row">
<WithLoading
isLoading={lessonsIsLoading || mentorsIsLoading || groupsIsLoading
|| studentsIsLoading || !lesson || !formData.length}
className={styles['loader-centered']}
>
<div className="col-6">
<h3>Lesson details</h3>
<hr />
<div className="d-flex flex-row w-100">
<div className="col-12">
<div className="mt-3 mb-4 row">
<div className="col-sm-6 font-weight-bolder"><span>Lesson Theme: </span></div>
<div className="col-sm-6"><span>{lesson?.themeName}</span></div>
</div>
<div className="row mb-4">
<div className="col-sm-6 font-weight-bolder d-flex align-items-center">
<span>Mentor name: </span>
</div>
<div className="col-sm-6 lead">
<Badge pill className={styles.bg_colour}>
<Link
to={`${paths.MENTORS_DETAILS}/${mentor.id}`}
className="text-decoration-none text-white"
>{`${mentor.firstName} ${mentor.lastName}`}
</Link>
</Badge>
</div>
</div>
<div className="row mb-4">
<div className="col-sm-6 font-weight-bolder d-flex align-items-center">
<span>Group name: </span>
</div>
<div className="col-sm-6 lead">
<Badge pill className={styles.bg_colour}>
<Link
to={`${paths.GROUPS_DETAILS}/${studentsGroup?.id}`}
className="text-decoration-none text-white"
>{studentsGroup?.name}
</Link>
</Badge>
</div>
</div>
<div className="row mb-4">
<div className="col-sm-6 font-weight-bolder">
<span>Lesson Date: </span>
</div>
<div className="col-sm-6">
{lesson?.lessonShortDate}
</div>
</div>
<div className="row">
<div className="col-sm-6 font-weight-bolder">
<span>Lesson Time: </span>
</div>
<div className="col-sm-6">
{lesson?.lessonTime}
</div>
</div>
</div>
<div className="col-lg-12">
<table className="table table-bordered table-hover">
<thead>
<tr>
<th scope="col" aria-label="first_col" />
<th scope="col">Full Student`s Name</th>
<th scope="col" className="text-center">Mark</th>
<th scope="col" className="text-center">Presence</th>
</tr>
</thead>
<tbody>
{formData.map((lessonVisit, index) => (
<tr key={lessonVisit.studentId}>
<th scope="row">{ index + 1 }</th>
<td>
<p
className={classNames(styles.link)}
onClick={() => openStudentDetails(lessonVisit.studentId)}
>
{ lessonVisit.studentName }
</p>
</td>
<td className="text-center align-content-center">
<div>{lessonVisit.presence && lessonVisit.studentMark}</div>
</td>
<td className="text-center font-weight-bolder">
<span>{lessonVisit.presence ? <Icon icon="Present" /> : <Icon icon="Absent" />}</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</WithLoading>
</div>
<div className="col-12 mt-3">
<button
form="form"
type="button"
className="btn btn-secondary btn-lg"
onClick={handleCancel}
>Cancel
</button>
</div>
</div>
</div>
);
}
Example #10
Source File: edit-lesson.js From what-front with MIT License | 4 votes |
EditLesson = () => {
const history = useHistory();
const { id } = useParams();
const [lessonOnEdit, setLessonOnEdit] = useState({});
const [
loadLessons, // not getById, cos of mistake in fetch response (lesson.theme === null)
getGroup,
getStudents,
updateLesson,
dispatchAddAlert,
] = useActions([fetchLessons, loadStudentGroupById, loadStudents, editLesson, addAlert]);
const {
data: lessons,
isLoading: lessonsIsLoading,
isLoaded: lessonsIsLoaded,
error: lessonsError,
} = useSelector(lessonsSelector, shallowEqual);
const {
data: students,
isLoading: studentsIsLoading,
isLoaded: studentsIsLoaded,
error: studentsError,
} = useSelector(studentsSelector, shallowEqual);
const {
data: group,
isLoading: groupIsLoading,
isLoaded: groupIsLoaded,
error: groupError,
} = useSelector(loadStudentGroupByIdSelector, shallowEqual);
const {
isLoaded: editIsLoaded,
error: editError,
} = useSelector(editLessonSelector, shallowEqual);
useEffect(() => {
getStudents();
loadLessons();
}, []);
useEffect(() => {
if (lessonsIsLoaded && studentsIsLoaded) {
const lesson = lessons.find((lesson) => lesson.id === Number(id));
if (lesson !== undefined) {
const lessonToEdit = cloneDeep(lesson);
getGroup(lesson.studentGroupId);
setLessonOnEdit(lessonToEdit);
} else {
history.push(paths.NOT_FOUND);
}
}
}, [lessonsIsLoaded, studentsIsLoaded]);
useEffect(() => {
if (groupIsLoaded) {
const studentsData = group.studentIds.reduce((acc, student) => {
const studentObj = students.find((stud) => stud.id === student);
if (studentObj.comment === undefined) {
studentObj.comment = '';
}
if (studentObj.studentMark === undefined) {
studentObj.studentMark = '';
}
studentObj.studentName = `${studentObj.firstName} ${studentObj.lastName}`;
acc.push(studentObj);
return acc;
}, []);
studentsData.sort((a, b) => a.studentName.localeCompare(b.studentName));
const newlessonOnEdit = { ...lessonOnEdit, lessonVisits: studentsData };
setLessonOnEdit(newlessonOnEdit);
}
}, [groupIsLoaded, students]);
useEffect(() => {
if (!editError && editIsLoaded) {
history.push(paths.LESSONS);
dispatchAddAlert('The lesson has been edited successfully', 'success');
}
if (editError && !editIsLoaded) {
dispatchAddAlert(editError);
}
}, [dispatchAddAlert, editError, editIsLoaded, history]);
const openStudentDetails = useCallback((studentId) => {
history.push(`${paths.STUDENTS_DETAILS}/${studentId}`);
}, [history]);
const handleCancel = useCallback(() => {
history.push(paths.LESSONS);
}, [history]);
const onSubmit = (values) => {
const { lessonDate, themeName } = values;
let lessonVisits = group.studentIds.map((item, ind) => {
const studentMark = values?.formData[ind]?.studentMark ? values.formData[ind].studentMark : null;
const presence = values?.formData[ind]?.presence ? values.formData[ind].presence : 'false';
return (
{
comment: '',
presence,
studentId: item,
studentMark,
}
);
}).sort((a, b) => a.studentId - b.studentId);
const theme = commonHelpers.capitalizeTheme(!themeName ? 'text' : themeName);
const formalizedDate = commonHelpers.transformDateTime({ isRequest:true, dateTime: lessonDate }).formDateTimeForRequest;
const lessonObject = {
themeName: theme,
lessonDate: formalizedDate,
lessonVisits,
};
if (lessonObject) {
updateLesson(lessonObject, id);
}
};
const handlePresenceChange = (ev) => {
const arrIndex = ev.target.dataset.id;
const lessonOnEditChange = {...lessonOnEdit};
lessonOnEditChange.lessonVisits[arrIndex].presence = !lessonOnEdit.lessonVisits[arrIndex].presence;
lessonOnEditChange.lessonVisits[arrIndex].studentMark = '';
setLessonOnEdit(lessonOnEditChange);
};
const handleMarkChange = (ev) => {
const arrIndex = ev.target.dataset.id;
let mark = Number(ev.target.value);
if (mark < 0 || mark > 12) {
mark = 0;
}
const lessonOnEditChange = {...lessonOnEdit};
lessonOnEditChange.lessonVisits[arrIndex].studentMark = mark;
setLessonOnEdit(lessonOnEditChange);
};
return (
<div className="container" data-testid='editLessonRenderForm'>
<div className={classNames(styles.page, 'mx-auto', 'col-12')}>
<div className="d-flex flex-row">
{groupError && lessonsError && editError && studentsError && (
<div className="col-12 alert-danger">
Server Problems
</div>
)}
<div className="col-12">
<h3>Edit a Lesson</h3>
<hr />
<WithLoading
isLoading={
lessonsIsLoading
|| studentsIsLoading
|| groupIsLoading
|| !lessons
}
className={classNames(styles['loader-centered'])}
>
<Formik
data-testid='formik'
initialValues={{
themeName: lessonOnEdit.themeName,
groupName: group.name,
lessonDate: commonHelpers.transformDateTime({ dateTime: lessonOnEdit.lessonDate }).formInitialValue,
formData: lessonOnEdit.lessonVisits,
}}
onSubmit={onSubmit}
validationSchema={editLessonValidation}
>
{({ errors, isSubmitting }) => (
<Form id="form" className={classNames(styles.size)} data-testid='editForm'>
<div className="d-flex flex-sm-column flex-lg-row">
<div className="col-lg-6">
<div className="mt-3 form-group row">
<label
htmlFor="inputLessonTheme"
className="col-sm-4 col-form-label"
>Lesson Theme:
</label>
<div className="col-sm-8">
<Field
data-testid='themeName'
type="text"
className={classNames('form-control', { 'border-danger': errors.themeName })}
name="themeName"
id="inputLessonTheme"
/>
{ errors.themeName ? <div className={styles.error}>{errors.themeName}</div> : null }
</div>
</div>
<div className="form-group row">
<label htmlFor="inputGroupName" className="col-md-4 col-form-label">Group
Name:
</label>
<div className="col-sm-8 input-group">
<Field
data-testid='groupName'
name="groupName"
id="inputGroupName"
type="text"
className="form-control group-input"
value={group.name}
disabled
/>
</div>
</div>
<div className="form-group row">
<label
className="col-md-4 col-form-label"
htmlFor="choose-date/time"
>Lesson Date/Time:
</label>
<div className="col-md-8">
<Field
data-testid='lessonDate'
className="form-control"
type="datetime-local"
name="lessonDate"
id="choose-date/time"
max={commonHelpers.transformDateTime({}).formInitialValue }
required
/>
</div>
</div>
</div>
<div className="col-lg-6 mt-2" >
<FieldArray name="formData">
{() => (
<div className={classNames(styles.list, 'col-lg-12')}>
<table className="table table-bordered table-hover">
<thead>
<tr>
<th scope="col" aria-label="first_col" />
<th scope="col">Full Student`s Name</th>
<th scope="col" className="text-center">Mark</th>
<th scope="col" className="text-center">Presence
</th>
</tr>
</thead>
<tbody data-testid='formData'>
{lessonOnEdit.lessonVisits && lessonOnEdit.lessonVisits.length > 0 && (
lessonOnEdit.lessonVisits.map((lessonVisit, index) => (
<tr key={lessonVisit.studentId}>
<th scope="row">{index + 1}</th>
<td>
<p
data-testid={lessonVisit.studentId}
className={classNames(styles.link)}
onClick={() => openStudentDetails(lessonVisit.studentId)}
>
{lessonVisit.studentName}
</p>
</td>
<td>
<Field
key = {`mark-${index}`}
data-testid={`formData[${index}].studentMark`}
name={`formData[${index}].studentMark`}
type="number"
className={classNames(
'form-control',
styles.mode,
)}
max="12"
min="1"
data-id={index}
disabled={!lessonOnEdit.lessonVisits[index].presence}
onBlur={handleMarkChange}
/>
</td>
<td>
<Field
key = {`presence-${index}`}
data-testid={`formData[${index}].presence`}
name={`formData[${index}].presence`}
className={styles.mode}
type="checkbox"
onClick={handlePresenceChange}
data-id={index}
checked = {lessonOnEdit.lessonVisits[index].presence}
/>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
)}
</FieldArray>
</div>
</div>
<div className={classNames(styles.placement, 'col-12 ')}>
<button
data-testid='cancelBtn'
form="form"
type="button"
className="btn btn-secondary btn-lg"
onClick={handleCancel}
>Cancel
</button>
<button
data-testid='submitBtn'
form="form"
type="submit"
className="btn btn-success btn-lg"
disabled={isSubmitting}
>Save
</button>
</div>
</Form>
)}
</Formik>
</WithLoading>
</div>
</div>
</div>
</div>
);
}
Example #11
Source File: edit-lesson.test.js From what-front with MIT License | 4 votes |
describe('Tests EditLesson', () => {
describe('Render & form of EditLesson', () => {
let historyMock;
beforeEach(() => {
useSelector
.mockReturnValueOnce(lessonsSelectorsMock.allLessons)
.mockReturnValue(mockStudentsSelector)
.mockReturnValue(mockGroupSelector)
.mockReturnValueOnce(lessonsSelectorsMock.editLesson);
commonHelpers.transformDateTime = jest.fn().mockReturnValue({ formInitialValue: '2021-06-17T02:47' });
historyMock = { push: jest.fn(), location: {}, listen: jest.fn() };
});
afterEach(cleanup);
it('should loader appear when studentLessonsSelector.isLoading is true', () => {
const mockedLessonsSelector = {
data: lessonsSelectorsMock.allLessons.data,
isLoading: true,
};
useSelector.mockReturnValue(mockedLessonsSelector);
const { container } = render(<Router history={historyMock}><EditLesson /></Router>);
const loader = container.querySelector('.spinner-border');
expect(loader).toBeInTheDocument();
});
describe('redirect to path.NOT_FOUND', () => {
let historyMock;
let mockedNoLessonsSelector;
beforeAll(() => {
mockedNoLessonsSelector = {
data: noLessonData,
isLoading: false,
isLoaded: true,
error: '',
};
useSelector
.mockReturnValueOnce(mockedNoLessonsSelector)
.mockReturnValueOnce(mockStudentsSelector)
.mockReturnValueOnce(mockGroupSelector)
.mockReturnValueOnce(lessonsSelectorsMock.editLesson);
commonHelpers.transformDateTime = jest.fn().mockReturnValue({ formInitialValue: '2021-06-17T02:47' });
historyMock = { push: jest.fn(), location: {}, listen: jest.fn() };
render(<Router history={historyMock}><EditLesson /></Router>);
});
it('should redirect to URL NOT_FOUND if !lesson ', () => {
expect(historyMock.push.mock.calls[0][0]).toEqual(paths.NOT_FOUND);
});
});
it('should the component be rendered', () => {
React.useState = jest.fn()
.mockReturnValue([mockLessonOnEdit, useStates.lessonOnEdit.setLessonOnEdit]);
const { getByTestId } = render(<Router history={historyMock}><EditLesson /></Router>);
expect(getByTestId('editLessonRenderForm')).toBeInTheDocument();
expect(getByTestId('formData').children.length).not.toEqual(0);
});
it('should the Form be rendered correctly (name+group+date)', () => {
React.useState = jest.fn()
.mockReturnValue([mockLessonOnEdit, useStates.lessonOnEdit.setLessonOnEdit]);
const { getByTestId, getByText } = render(<Router history={historyMock}><EditLesson /></Router>);
const themeName = getByTestId('themeName');
const student1 = getByText('StudenT StudenT');
const groupName = getByTestId('groupName');
const lessonDate = getByTestId('lessonDate');
expect(getByTestId('editForm')).toBeInTheDocument();
expect(themeName.value).toBe('API testing');
expect(groupName.value).toBe('122-18-3');
expect(lessonDate.value).toBe('2021-06-17T02:47');
expect(groupName).toBeDisabled();
expect(student1).toBeInTheDocument();
});
it('FormData should be rendered correctly', () => {
React.useState = jest.fn()
.mockReturnValue([mockLessonOnEdit, useStates.lessonOnEdit.setLessonOnEdit]);
const { getByTestId, container } = render(<Router history={historyMock}><EditLesson /></Router>);
const user1Presence = getByTestId('formData[0].presence');
const user1Mark = getByTestId('formData[0].studentMark');
const user2Presence = getByTestId('formData[1].presence');
const user2Mark = getByTestId('formData[1].studentMark');
expect(container.querySelectorAll('tr').length - 1).toBe(2);
expect(user1Presence.value).toBe('true');
expect(user1Mark.value).toBe('10');
expect(user2Presence.value).toBe('true');
expect(user2Mark.value).toBe('11');
});
it('should change state LessonOnEdit ', async () => {
React.useState = jest.fn()
.mockReturnValue([mockLessonOnEdit, useStates.lessonOnEdit.setLessonOnEdit]);
const { getByTestId } = render(<Router history={historyMock}><EditLesson /></Router>);
const handlePresenceChange = jest.fn();
const user1Presence = getByTestId('formData[0].presence');
await expect(user1Presence.value).toBe('true');
await waitFor(() => fireEvent.click(user1Presence));
await expect(user1Presence.value).toBe('false');
await handlePresenceChange(user1Presence.value);
await expect(useStates.lessonOnEdit.setLessonOnEdit).toHaveBeenCalledWith(newPresenceStatusLessonOnEdit);
await expect(useStates.lessonOnEdit.setLessonOnEdit).toHaveBeenCalledTimes(1);
});
it('should change student\'s mark', () => {
React.useState = jest.fn()
.mockReturnValue([mockLessonOnEdit, useStates.lessonOnEdit.setLessonOnEdit]);
const { getByTestId } = render(<Router history={historyMock}><EditLesson /></Router>);
const handleMarkChange = jest.fn();
const userMark = getByTestId('formData[1].studentMark');
fireEvent.blur(userMark, { target: { value: '12' } });
expect(userMark.value).toBe('12');
handleMarkChange(userMark.value);
expect(useStates.lessonOnEdit.setLessonOnEdit).toHaveBeenCalledWith(newMarkLessonOnEdit);
});
it('should change student\'s mark > 12', () => {
React.useState = jest.fn()
.mockReturnValue([mockLessonOnEdit, useStates.lessonOnEdit.setLessonOnEdit]);
const { getByTestId } = render(<Router history={historyMock}><EditLesson /></Router>);
const handleMarkChange = jest.fn();
const userMark = getByTestId('formData[1].studentMark');
fireEvent.blur(userMark, { target: { value: '13' } });
expect(userMark.value).toBe('13');
handleMarkChange(userMark.value);
expect(useStates.lessonOnEdit.setLessonOnEdit).toHaveBeenCalledWith(newMarkMore12LessonOnEdit);
});
it('should redirect to URL STUDENTS_DETAILS ', () => {
React.useState = jest.fn()
.mockReturnValue([mockLessonOnEdit, useStates.lessonOnEdit.setLessonOnEdit]);
const { getByTestId } = render(<Router history={historyMock}><EditLesson /></Router>);
const studentId = getByTestId('1');
fireEvent.click(studentId);
expect(historyMock.push.mock.calls[1][0]).toEqual(`${paths.STUDENTS_DETAILS}/1`);
});
it('should redirect to correct URL if click cancelButton', () => {
React.useState = jest.fn()
.mockReturnValue([mockLessonOnEdit, useStates.lessonOnEdit.setLessonOnEdit]);
const { getByTestId } = render(<Router history={historyMock}><EditLesson /></Router>);
const cancelButton = getByTestId('cancelBtn');
fireEvent.click(cancelButton);
expect(historyMock.push.mock.calls[0][0]).toEqual(paths.LESSONS);
});
it('submit Form', async () => {
React.useState = jest.fn()
.mockReturnValueOnce([submitValues, useStates.lessonOnEdit.setLessonOnEdit]);
commonHelpers.transformDateTime = jest.fn().mockReturnValue({
formDateTimeForRequest: '2021-06-17T01:47:00.000Z',
formInitialValue: '2021-06-17T02:47',
});
const { getByTestId } = render(<Router history={historyMock}><EditLesson /></Router>);
const onSubmit = jest.fn();
const submitBtn = getByTestId('submitBtn');
commonHelpers.capitalizeTheme = jest.fn().mockReturnValue(submitValues.themeName);
await waitFor(() => {
fireEvent.click(submitBtn);
onSubmit();
});
// expect(onSubmit).toHaveBeenCalledWith(submitValues);
expect(onSubmit).toHaveBeenCalledTimes(1);
expect(useActionsFns.updateLesson).toHaveBeenCalledTimes(1);
});
});
describe('should create first state lessonOnEdit', () => {
let historyMock;
beforeAll(() => {
useSelector
.mockReturnValueOnce(lessonsSelectorsMock.allLessons)
.mockReturnValueOnce(mockStudentsSelector)
.mockReturnValueOnce(mockGroupSelector)
.mockReturnValueOnce(lessonsSelectorsMock.editLesson);
commonHelpers.transformDateTime = jest.fn().mockReturnValue({ formInitialValue: '2021-06-17T02:47' });
historyMock = { push: jest.fn(), location: {}, listen: jest.fn() };
});
it('should state lessonOnEdit be changed before first rendering', async () => {
React.useState = jest.fn()
.mockReturnValue([{}, useStates.lessonOnEdit.setLessonOnEdit]);
const { getByTestId } = render(<Router history={historyMock}><EditLesson /></Router>);
expect(getByTestId('editLessonRenderForm')).toBeInTheDocument();
const cloneDeep = jest.fn();
await waitFor(() => cloneDeep());
expect(cloneDeep).toHaveBeenCalledTimes(1);
expect(useStates.lessonOnEdit.setLessonOnEdit).toHaveBeenCalledWith(mockLessonOnEdit);
});
});
});
Example #12
Source File: add-lesson.js From what-front with MIT License | 4 votes |
AddLesson = () => {
const history = useHistory();
const [markError, setMarkError] = React.useState(false);
const [mentorError, setMentorError] = React.useState(false);
const [groupError, setGroupError] = React.useState(false);
const [studentsGroup, setStudentsGroup] = React.useState(null);
const [mentorInput, setMentorInput] = React.useState('');
const [btnSave, setBtnSave] = React.useState(false);
const [classRegister, setClassRegister] = React.useState(false);
const [formData, setFormData] = React.useState([]);
const {
data: mentors,
isLoading: mentorsIsLoading,
isLoaded: mentorsIsLoaded,
error: mentorsError,
} = useSelector(mentorsActiveSelector, shallowEqual);
const {
data: groups,
isLoading: groupsIsLoading,
isLoaded: groupsIsLoaded,
error: groupsError,
} = useSelector(loadStudentGroupsSelector, shallowEqual);
const {
data: students,
isLoading: studentsIsLoading,
isLoaded: studentsIsLoaded,
error: studentsError,
} = useSelector(studentsSelector, shallowEqual);
const {
isLoaded: addIsLoaded,
isLoading: lessonIsLoading,
error: addError,
} = useSelector(addLessonSelector, shallowEqual);
const [
getMentors,
getGroups,
getStudents,
createLesson,
dispatchAddAlert,
] = useActions([fetchActiveMentors, globalLoadStudentGroups, loadStudents, addLesson, addAlert]);
useEffect(() => {
getMentors();
getGroups();
getStudents();
}, []);
useEffect(() => {
if (!addError && addIsLoaded) {
history.push(paths.LESSONS);
dispatchAddAlert('The lesson has been added successfully!', 'success');
}
if (addError && !addIsLoaded) {
dispatchAddAlert(addError);
}
}, [addError, addIsLoaded, dispatchAddAlert, history]);
const openStudentDetails = useCallback((id) => {
history.push(`${paths.STUDENTS_DETAILS}/${id}`);
}, [history]);
const handleCancel = useCallback(() => {
history.push(paths.LESSONS);
}, [history]);
const onSubmit = (values) => {
const { lessonDate, themeName } = values;
const lessonVisits = formData.map((lessonVisit) => {
const {
presence, studentId, studentMark,
} = lessonVisit;
return (
{
comment: '',
presence,
studentId,
studentMark: studentMark || null,
}
);
});
const mentorData = mentors.find((mentor) => mentor.email === mentorInput);
const theme = commonHelpers.capitalizeTheme(themeName);
const formalizedDate = commonHelpers.transformDateTime({ isRequest: true, dateTime: lessonDate }).formDateTimeForRequest;
const lessonObject = {
lessonDate: formalizedDate,
themeName: theme,
lessonVisits,
studentGroupId: studentsGroup.id,
mentorId: mentorData.id,
};
if (!mentorsError && lessonObject) {
createLesson(lessonObject);
}
};
const getFormData = (studentsGroup, students) => {
const uniqueIds = [...new Set(studentsGroup.studentIds)];
const studentD = uniqueIds.map(
(id) => students.find((student) => student.id === id),
);
const activeStudents = studentD.filter((student) => student !== undefined);
const studentsData = activeStudents.map((student) => (
{
studentId: student.id,
studentName: `${student.firstName} ${student.lastName}`,
}
));
return studentsData.map((student) => ({
...student,
studentMark: 0,
presence: false,
comment: '',
}));
};
const openClassRegister = useCallback(() => {
if (studentsGroup) {
setFormData(getFormData(studentsGroup, students));
setBtnSave(true);
setClassRegister(true);
setGroupError(false);
}
!studentsGroup && setCorrectError('#inputGroupName', setGroupError, 'group name');
!mentorInput && setCorrectError('#mentorEmail', setMentorError, 'mentor email');
}, [studentsGroup, students, mentorInput, setGroupError, setMentorError, setBtnSave, setClassRegister, setGroupError]);
const setCorrectError = (inputSelector, setError, fieldName) => {
const {value} = document.querySelector(inputSelector);
value ? setError(`Invalid ${fieldName}`) : setError('This field is required');
};
const handleMentorChange = (ev) => {
setMentorInput(ev.target.value);
const mentorData = mentors.find((mentor) => mentor.email === ev.target.value);
mentorData ? setMentorError(false)
: setCorrectError('#mentorEmail', setMentorError, 'mentor email');
};
const handleGroupChange = (ev) => {
const resultGroup = groups.find((group) => group.name.toUpperCase() === ev.target.value.toUpperCase());
if (resultGroup) {
setStudentsGroup(resultGroup);
setGroupError(false);
setBtnSave(false);
setClassRegister(false);
} else {
setCorrectError('#inputGroupName', setGroupError, 'group name');
}
};
const handlePresenceChange = (ev) => {
const arrIndex = ev.target.dataset.id;
const newFormData = cloneDeep(formData);
newFormData[arrIndex].presence = !newFormData[arrIndex].presence;
newFormData[arrIndex].studentMark = 0;
setFormData(newFormData);
};
const handleMarkChange = (ev) => {
const arrIndex = ev.target.dataset.id;
const mark = Number(ev.target.value);
if (mark > 0 && mark < 13) {
const newFormData = cloneDeep(formData);
newFormData[arrIndex].studentMark = mark;
setFormData(newFormData);
setMarkError(false);
} else {
setMarkError(true);
ev.target.value = '';
}
};
return (
<div className="container">
<div className={classNames(styles.page, 'mx-auto', `${classRegister ? 'col-12' : 'col-8'}`)}>
<div className="d-flex flex-row">
{groupsError && mentorsError && studentsError && (
<div className="col-12 alert-danger">
Server Problems
</div>
)}
<div className='col-12'>
<h3>Add a Lesson</h3>
<hr />
<WithLoading
isLoading={
mentorsIsLoading
|| studentsIsLoading
|| groupsIsLoading
|| lessonIsLoading
}
className={classNames(styles['loader-centered'])}
>
<Formik
initialValues={{
themeName: '',
groupName: '',
lessonDate: '',
mentorEmail: '',
formData,
}}
onSubmit={onSubmit}
validationSchema={addLessonValidation}
>
{({ errors, touched, setFieldTouched }) => (
<Form id="form" className={classNames(styles.size)}>
<div className='d-flex flex-sm-column flex-lg-row' data-testid='addForm'>
<div className={classRegister ? 'col-lg-6' : 'col-lg-12'}>
<div className="mt-3 form-group row">
<label htmlFor="inputLessonTheme" className="col-md-4 col-form-label">Lesson Theme:</label>
<div className="col-md-8">
<Field
type="text"
className={classNames('form-control',
{ 'border-danger': !!(errors.themeName && touched.themeName) })}
name="themeName"
id="inputLessonTheme"
placeholder="Lesson Theme"
required
/>
{
errors.themeName
&& <div className={styles.error}>{errors.themeName}</div>
}
</div>
</div>
<div className="form-group row">
<label htmlFor="inputGroupName" className="col-md-4 col-form-label">Group Name:</label>
<div className="col-md-8 input-group">
<input
name="groupName"
id="inputGroupName"
type="text"
className={classNames('form-control group-input', { 'border-danger': !!groupError })}
placeholder="Group Name"
onChange={handleGroupChange}
list="group-list"
disabled={groupsIsLoading}
required
/>
<datalist id="group-list">
{groups.map(({ id, name }) => (
<option key={id}>{name}</option>
))}
</datalist>
</div>
{
groupError
? <div id='group-error' className={classNames('col-8 offset-4', styles.error)}>{groupError}</div>
: null
}
</div>
<div className="form-group row">
<label className="col-md-4 col-form-label" htmlFor="choose-date/time">Lesson Date/Time:</label>
<div className="col-md-8">
<Field
className="form-control"
type="datetime-local"
name="lessonDate"
id="choose-date-time"
max={ commonHelpers.transformDateTime({}).formInitialValue }
required
/>
</div>
</div>
<div className="form-group row">
<label className="col-md-4 col-form-label" htmlFor="mentorEmail">Mentor Email:</label>
<div className="col-md-8 input-group">
<input
className={classNames('form-control group-input', { 'border-danger': !!mentorError })}
type="text"
name="mentorEmail"
id="mentorEmail"
list="mentor-list"
placeholder="Mentor Email"
onChange={handleMentorChange}
disabled={mentorsIsLoading}
required
/>
<datalist id="mentor-list">
{mentors.map(({ id, firstName, lastName, email }) => (
<option key={id} value={email}>
{`${firstName} ${lastName}`}
</option>
))}
</datalist>
</div>
{
mentorError
? <div id='mentor-error' className={classNames('col-8 offset-4', styles.error)}>{mentorError}</div>
: null
}
</div>
</div>
{classRegister && formData && (
<div className={classRegister ? 'col-lg-6' : 'col-lg-12'}>
<FieldArray name="formData">
{() => (
<div className={classNames(styles.list, 'col-lg-12 pt-2')}>
<table className="table table-bordered table-hover" data-testid='students-form'>
<thead>
<tr>
<th scope="col" aria-label="first_col" />
<th scope="col">Full Student`s Name</th>
<th scope="col" className="text-center">Mark</th>
<th scope="col" className="text-center">Presence</th>
</tr>
</thead>
<tbody data-testid='students-formData-table'>
{formData && formData.length > 0 && (
formData.map((lessonVisit, index) => (
<tr key={lessonVisit.studentId}>
<th scope="row">{ index + 1 }</th>
<td>
<p
data-testid={`openStudentDetails-${lessonVisit.studentId}`}
className={classNames(styles.link)}
onClick={() => openStudentDetails(lessonVisit.studentId)}
>
{ lessonVisit.studentName }
</p>
</td>
<td>
<Field
data-testid={`formData[${index}].studentMark`}
name={`formData[${index}].studentMark`}
className={classNames(
'form-control',
{ 'border-danger': markError },
styles.mode,
)}
type="number"
max="12"
min="0"
placeholder=""
onChange={handleMarkChange}
data-id={index}
disabled={!formData[index].presence}
/>
</td>
<td>
<Field
data-testid={`formData[${index}].presence`}
name={`formData[${index}].presence`}
className={styles.mode}
type="checkbox"
onClick={handlePresenceChange}
data-id={index}
checked={formData[index].presence}
/>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
)}
</FieldArray>
</div>
)}
</div>
<div className='col-12 d-flex justify-content-between'>
<button form="form" data-testid='cancelBtn' type="button" className="btn btn-secondary btn-lg" onClick={handleCancel}>Cancel</button>
{btnSave
? <button id='submit' form="form" type="submit" className="btn btn-success btn-lg">Save</button>
: (
<Button
id='class-register-btn'
className="btn btn-success btn-lg"
onClick={(event) => {
event.preventDefault();
setFieldTouched();
openClassRegister();
}}
>
Class Register
</Button>
)}
</div>
</Form>
)}
</Formik>
</WithLoading>
</div>
</div>
</div>
</div>
);
}
Example #13
Source File: add-lesson.test.js From what-front with MIT License | 4 votes |
describe('Render of AddLesson', () => {
let historyMock;
let mockStudentsSelector;
let mockGroupsSelector;
let mockMentorsSelector;
let mockAddLessonSelector;
let useActionsFns;
beforeEach(() => {
mockStudentsSelector = {
data: studentsMock,
isLoading: false,
isLoaded: true,
error: '',
};
mockGroupsSelector = {
data: groupsMock,
isLoading: false,
isLoaded: true,
error: '',
};
mockMentorsSelector = {
data: mentorsMock,
isLoading: false,
isLoaded: true,
error: '',
};
mockAddLessonSelector = {
isLoaded: false,
isLoading: false,
error: '',
};
useSelector
.mockReturnValueOnce(mockMentorsSelector)
.mockReturnValueOnce(mockGroupsSelector)
.mockReturnValueOnce(mockStudentsSelector)
.mockReturnValue(mockAddLessonSelector);
useActionsFns = {
getMentors: jest.fn(),
getGroups: jest.fn(),
getStudents: jest.fn(),
createLesson: jest.fn(),
dispatchAddAlert: jest.fn(),
};
useActions.mockReturnValue([useActionsFns.getMentors, useActionsFns.getGroups, useActionsFns.getStudents, useActionsFns.createLesson, useActionsFns.dispatchAddAlert]);
commonHelpers.transformDateTime = jest.fn().mockReturnValue({ formDateTimeForRequest: '2021-06-17T01:47:00.000Z', formInitialValue: '2021-06-17T02:47' });
historyMock = { push: jest.fn(), location: {}, listen: jest.fn() };
});
it('should loader appear when mentorsIsLoading is false', () => {
mockMentorsSelector = {
isLoading: true,
};
useSelector.mockReturnValue(mockMentorsSelector);
const { container } = render(<Router history={historyMock}><AddLesson /></Router>);
const loader = container.querySelector('.spinner-border');
expect(loader).toBeInTheDocument();
});
it('should the component be rendered', () => {
const { getByTestId } = render(<Router history={historyMock}><AddLesson /></Router>);
expect(getByTestId('addForm')).toBeInTheDocument();
});
it('should be redirected to path LESSONS when cancelBtn is clicked', () => {
const { getByTestId } = render(<Router history={historyMock}><AddLesson /></Router>);
const cancelBtn = getByTestId('cancelBtn');
fireEvent.click(cancelBtn);
expect(historyMock.push.mock.calls[0][0]).toEqual(paths.LESSONS);
});
it('should redirect to URL LESSONS when !addError && addIsLoaded', () => {
mockAddLessonSelector = {
isLoaded: true,
isLoading: false,
error: '',
};
useSelector.mockReturnValueOnce(mockAddLessonSelector);
render(<Router history={historyMock}><AddLesson /></Router>);
expect(historyMock.push.mock.calls[0][0]).toEqual(paths.LESSONS);
});
it('should datalist of groups = 2', () => {
const { container } = render(<Router history={historyMock}><AddLesson /></Router>);
const groupList = container.querySelector('#group-list');
expect(groupList.children.length).toEqual(2);
});
it('should datalist of mentors emails = 1', () => {
const { container } = render(<Router history={historyMock}><AddLesson /></Router>);
const mentorList = container.querySelector('#mentor-list');
expect(mentorList.children.length).toEqual(1);
});
it('should mentor email be chosen', () => {
React.useState = useStateMock.default;
const { container } = render(<Router history={historyMock}><AddLesson /></Router>);
const mentorEmail = container.querySelector('#mentorEmail');
fireEvent.change(mentorEmail, { target: { value: mentorsMock[0].email } });
const mentorError = container.querySelector('#mentor-error');
expect(mentorError).not.toBeInTheDocument(); // mentorError === false
});
it('should set State formData', async () => {
React.useState = useStateMock.setFormData;
const { container } = render(<Router history={historyMock}><AddLesson /></Router>);
const openClassRegister = jest.fn();
const getFormData = jest.fn();
const inputLessonTheme = container.querySelector('#inputLessonTheme');
const inputDateTime = container.querySelector('#choose-date-time');
const inputGroupName = container.querySelector('#inputGroupName');
const mentorEmail = container.querySelector('#mentorEmail');
await waitFor(() => {
fireEvent.change(inputLessonTheme, { target: { value: 'New lesson' } });
fireEvent.change(inputGroupName, { target: { value: groupsMock[0].name } });
fireEvent.change(inputDateTime, { target: { value: '2021-06-21T12:41' } });
fireEvent.change(mentorEmail, { target: { value: mentorsMock[0].email } });
});
const classRegBtn = container.querySelector('#class-register-btn');
await waitFor(() => fireEvent.click(classRegBtn));
await openClassRegister();
await getFormData();
expect(inputLessonTheme.value).toBe('New lesson');
expect(inputGroupName.value).toBe(groupsMock[0].name);
expect(inputDateTime.value).toBe('2021-06-21T12:41');
expect(mentorEmail.value).toBe(mentorsMock[0].email);
expect(useStates.formData.setFormData).toHaveBeenCalledWith(formData.default);
expect(useStates.classRegister.setClassRegister).toHaveBeenCalledWith(true);
});
it('should change present state in formData && redirect to path STUDENTS_DETAILS', async () => {
React.useState = useStateMock.renderFormData;
const { getByTestId } = render(<Router history={historyMock}><AddLesson /></Router>);
const handlePresenceChange = jest.fn();
// Change present state in formData
const user1Presence = getByTestId('formData[0].presence');
await expect(user1Presence.value).toBe('false');
await waitFor(() => fireEvent.click(user1Presence));
const formDataMock = [
{ studentId: 1, studentMark: 0, presence: true, comment: '', studentName: 'Student Student' },
{ studentId: 3, studentMark: 0, presence: false, comment: '', studentName: 'StudenT StudenT' }];
await handlePresenceChange(user1Presence.value);
await expect(user1Presence.value).toBe('true');
await expect(useStates.formData.setFormData).toHaveBeenCalledWith(formDataMock);
// redirect to path STUDENTS_DETAILS
const studentForm = getByTestId('students-formData-table');
const studentName = getByTestId('openStudentDetails-1');
expect(studentForm).toBeInTheDocument();
expect(studentForm.children.length).toBe(2); // amount of students
await waitFor(() => fireEvent.click(studentName));
expect(historyMock.push.mock.calls[0][0]).toEqual(`${paths.STUDENTS_DETAILS}/1`);
});
it('should change mark state in formData ', async () => {
React.useState = useStateMock.changeMark;
const { getByTestId } = render(<Router history={historyMock}><AddLesson /></Router>);
const handleMarkChange = jest.fn();
const user1Mark = getByTestId('formData[0].studentMark');
const user2Mark = getByTestId('formData[1].studentMark');
await expect(user2Mark.value).toBe('0');
await expect(user1Mark.value).toBe('0');
await waitFor(() => {
fireEvent.change(user1Mark, { target: { value: 10 } });
fireEvent.change(user2Mark, { target: { value: 13 } });
});
const formDataMock = [
{ studentId: 1, studentMark: 10, presence: true, comment: '', studentName: 'Student Student' },
{ studentId: 3, studentMark: 0, presence: true, comment: '', studentName: 'StudenT StudenT' }];
await handleMarkChange(user1Mark.value);
await handleMarkChange(user2Mark.value);
await expect(user1Mark.value).toBe(user1Mark.value);
await expect(user2Mark.value).toBe('0');
await expect(useStates.formData.setFormData).toHaveBeenCalledWith(formDataMock);
await expect(useStates.markError.setMarkError).toHaveBeenCalledWith(true);
});
it('submit Form', async () => {
React.useState = useStateMock.submit;
commonHelpers.transformDateTime = jest.fn().mockReturnValue({ formDateTimeForRequest: '2021-06-21T19:53:00.000Z' });
const { container } = render(<Router history={historyMock}><AddLesson /></Router>);
const inputLessonTheme = container.querySelector('#inputLessonTheme');
const inputDateTime = container.querySelector('#choose-date-time');
const inputGroupName = container.querySelector('#inputGroupName');
const mentorEmail = container.querySelector('#mentorEmail');
const submitBtn = container.querySelector('#submit');
const onSubmit = jest.fn();
const formValues = {
themeName: 'New lesson',
lessonDate: '2021-06-21T22:53',
};
expect(submitBtn).toBeInTheDocument();
await waitFor(() => {
fireEvent.change(inputLessonTheme, { target: { value: formValues.themeName } });
fireEvent.change(inputGroupName, { target: { value: groupsMock[0].name } });
fireEvent.change(inputDateTime, { target: { value: formValues.lessonDate } });
fireEvent.change(mentorEmail, { target: { value: mentorsMock[0].email } });
});
await waitFor(() => {
fireEvent.click(submitBtn);
onSubmit();
});
expect(onSubmit).toHaveBeenCalled();
expect(useActionsFns.createLesson).toHaveBeenCalledTimes(1);
});
});
Example #14
Source File: homework-details.js From what-front with MIT License | 4 votes |
HomeworkDetails = ({ id }) => {
const history = useHistory();
const { data, isLoading, loaded } = useSelector(homeworkSelector, shallowEqual);
const {
data: dataAttachment,
isLoading: isLoadingAttachment,
isLoaded: isLoadedAttachment,
} = useSelector(attachmentByIdSelector, shallowEqual);
const [
getHomeworks,
] = useActions([getHomework]);
const [
getAttachment,
] = useActions([fetchAttachmentById]);
useEffect(() => {
if (!data && loaded) {
history.push(paths.NOT_FOUND);
}
}, [data, loaded, history]);
useEffect(() => {
getHomeworks(id);
}, [getHomeworks, id]);
useEffect(() => {
getAttachment(1);
}, [getAttachment]);
const changeText = (text) => {
if(loaded) {
return text.split('<br>');
}
}
const arr = new File([dataAttachment], "hello")
console.log(dataAttachment);
console.log(data);
return (
<div className="container">
<div className="row justify-content-center">
<div className={classNames('col-sm-12 card shadow')}>
<div className="px-2 py-4">
<h3>Homework Details</h3>
<div className="col-3 offset-1 text-right" />
<hr />
<WithLoading
isLoading={isLoading || !loaded}
className="d-block mx-auto m-0"
>
<div className="row">
<span className="col-12 col-md-6 font-weight-bolder">Lesson:</span>
<span className="col-12 col-md-6">{data?.lessonId}</span>
</div>
<hr />
<div className="row">
<span className="col-12 col-md-6 font-weight-bolder">Task:</span>
<span className="col-12 col-md-6 ">{changeText(data?.taskText)?.map((item) => <p key={item.length}>{item}</p>)}</span>
</div>
<hr />
<div className="row">
<span className="col-12 col-md-6 font-weight-bolder">Deadline date:</span>
<span className="col-12 col-md-6">{commonHelpers.transformDateTime({ dateTime: data?.dueDate }).date}</span>
</div>
<hr />
<div className="row">
<span className="col-12 col-md-6 font-weight-bolder">Deadline time</span>
<span className="col-12 col-md-6">{commonHelpers.transformDateTime({ dateTime: data?.dueDate }).time}</span>
</div>
<hr />
<div className="row">
<div className="col-12 col-md-6 font-weight-bolder"><span>Attachment('s): </span></div>
<a className="col-12 col-md-6 d-flex flex-wrap lead" href={dataAttachment} download="file"> ffff</a>
</div>
<hr />
</WithLoading>
</div>
</div>
</div>
</div>
);
}
Example #15
Source File: group-details.js From what-front with MIT License | 4 votes |
GroupDetails = ({
studentGroupData, studentsData, mentorsData, coursesData,
}) => {
const {
data: group,
isLoading: isGroupLoading,
isLoaded: isGroupLoaded,
} = studentGroupData;
const {
data: students,
isLoading: areStudentsLoading,
isLoaded: areStudentsLoaded,
} = studentsData;
const {
data: mentors,
isLoading: areMentorsLoading,
isLoaded: areMentorsLoaded,
} = mentorsData;
const {
data: courses,
isLoading: areCoursesLoading,
loaded: areCoursesLoaded,
} = coursesData;
const history = useHistory();
return (
<div className="container pt-5">
<div className="row justify-content-center">
<div className="w-100 card shadow p-4">
<WithLoading
isLoading={isGroupLoading || !isGroupLoaded || areMentorsLoading || !areMentorsLoaded
|| areCoursesLoading || !areCoursesLoaded}
className={styles['loader-centered']}
>
<div className="d-flex flex-row text-left justify-content-between">
<div className="d-flex flex-column">
<h2>
Group: {group.name}
</h2>
<p className="m-0">
{ commonHelpers.transformDateTime({ isDayTime:false, dateTime: group.startDate }).date}
-
{ commonHelpers.transformDateTime({ isDayTime:false, dateTime: group.finishDate }).date}
</p>
</div>
<div className="pt-3">
<Link to={`${paths.SCHEDULE_BY_GROUP_ID}/${group.id}`}>
<span className={styles['schedule-link']}>View schedule</span>
</Link>
</div>
</div>
<hr className="p-0" />
<div className="d-flex mb-2">
<h4 className="pr-2 mb-2">
Mentors:
</h4>
<div className="d-flex flex-wrap">
{ mentors
.filter((mentor) => group.mentorIds?.includes(mentor.id))
.map((mentor) => (
<div className="pr-2 lead" key={mentor.id}>
<Badge pill variant="info">
<Link
to={`${paths.MENTORS_DETAILS}/${mentor.id}`}
className="text-decoration-none text-light"
>
{mentor.firstName} {mentor.lastName}
</Link>
</Badge>
</div>
)) }
</div>
</div>
<div className="d-flex align-items-center mb-2 lead">
<h4 className="mb-2 pr-4">Course:</h4>
<Badge pill variant="info">
<Link
to={`${paths.COURSE_DETAILS}/${group.courseId}`}
className="text-decoration-none text-white"
>
{courses.find((course) => course.id === group.courseId)?.name }
</Link>
</Badge>
</div>
<h4 className="h4 my-2">
Students:
</h4>
<WithLoading isLoading={areStudentsLoading || !areStudentsLoaded}>
<Table bordered hover responsive>
<thead>
<tr>
<th>№</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
{ students
.filter((student) => group.studentIds?.includes(student.id))
.map((student, index) => (
<tr
key={student.id}
onClick={() => history.push(`${paths.STUDENTS_DETAILS}/${student.id}`)}
className={styles['table-row']}
>
<td>{index + 1}</td>
<td>{student.firstName} {student.lastName}</td>
<td>{student.email}</td>
</tr>
)) }
</tbody>
</Table>
</WithLoading>
</WithLoading>
</div>
</div>
</div>
);
}
Example #16
Source File: group-details.test.js From what-front with MIT License | 4 votes |
describe('Render of GroupDetails', () => {
let studentGroupSelector;
let mentorsSelector;
let coursesSelector;
let studentsSelector;
let historyMock;
let id;
beforeEach(() => {
studentGroupSelector = groupDetailsMock.studentGroupData;
mentorsSelector = groupDetailsMock.mentorsData;
coursesSelector = groupDetailsMock.coursesData;
studentsSelector = groupDetailsMock.studentsData;
id = groupDetailsMock.id;
historyMock = { push: jest.fn(), location: {}, listen: jest.fn(), createHref: jest.fn() };
commonHelpers.transformDateTime = jest.fn().mockReturnValue({ formInitialValue: '2022-06-16' });
useSelector
.mockReturnValueOnce(studentGroupSelector)
.mockReturnValueOnce(mentorsSelector)
.mockReturnValueOnce(coursesSelector)
.mockReturnValueOnce(studentsSelector);
});
afterEach(cleanup);
it('Should the component be rendered.', () => {
const { container } = render(
<Router history={historyMock}>
<GroupDetails id={id}
studentGroupData={studentGroupSelector}
studentsData={studentsSelector}
mentorsData={mentorsSelector}
coursesData={coursesSelector} />
</Router>
);
const groupDetailsContainer = container.getElementsByClassName('container');
expect(groupDetailsContainer).toMatchSnapshot();
});
it('should loader appear when data is false', () => {
studentGroupSelector = {
data: groupDetailsMock.studentGroupData.data,
isLoading: true,
isLoaded: false,
error: ''
};
useSelector.mockReturnValue(studentGroupSelector);
const { container } = render(
<Router history={historyMock}>
<GroupDetails id={id}
studentGroupData={studentGroupSelector}
studentsData={studentsSelector}
mentorsData={mentorsSelector}
coursesData={coursesSelector} />
</Router>);
const loader = container.querySelector('.spinner-border');
expect(loader).toBeInTheDocument();
});
it('should the h2 element contain "Group: Soft Skills for Lecturers - 2021/2"', () => {
const { container } = render(
<Router history={historyMock}>
<GroupDetails id={id}
studentGroupData={studentGroupSelector}
studentsData={studentsSelector}
mentorsData={mentorsSelector}
coursesData={coursesSelector} />
</Router>);
const header = container.querySelector('h2');
expect(header.innerHTML).toBe('Group: Soft Skills for Lecturers - 2021/2');
});
it('should the table content match', () => {
const { container } = render(
<Router history={historyMock}>
<GroupDetails id={id}
studentGroupData={studentGroupSelector}
studentsData={studentsSelector}
mentorsData={mentorsSelector}
coursesData={coursesSelector} />
</Router>);
const table = container.querySelector('tbody');
expect(table.innerHTML).toBe('<tr><td>1</td><td>Richard Thomas</td><td>[email protected]</td></tr><tr><td>2</td><td>Joseph Evans</td><td>[email protected]</td></tr><tr><td>3</td><td>Thomas Roberts</td><td>[email protected]</td></tr><tr><td>4</td><td>Barbara Harris</td><td>[email protected]</td></tr><tr><td>5</td><td>Susan Clark</td><td>[email protected]</td></tr><tr><td>6</td><td>Jessica Cooper</td><td>[email protected]</td></tr>');
});
it('should url change to "/students/2"', () => {
const { container } = render(
<Router history={historyMock}>
<GroupDetails id={id}
studentGroupData={studentGroupSelector}
studentsData={studentsSelector}
mentorsData={mentorsSelector}
coursesData={coursesSelector} />
</Router>);
userEvent.click(screen.getByText('Richard Thomas'));
window.history.pushState({}, 'Richard Thomas page', '/students/2');
expect(global.window.location.pathname).toEqual('/students/2');
});
it('Should redirect to 404 page in case of error in course details data.', () => {
const history = createMemoryHistory();
useSelector.mockReturnValue({
...studentGroupSelector,
error: 'Something went wrong',
loaded: false,
});
render(
<Router history={history}>
<GroupDetails id={id}
studentGroupData={studentGroupSelector}
studentsData={studentsSelector}
mentorsData={mentorsSelector}
coursesData={coursesSelector} />
</Router>
);
history.push(paths.NOT_FOUND);
expect(history.location.pathname).toBe(paths.NOT_FOUND);
});
});
Example #17
Source File: edit-group.js From what-front with MIT License | 4 votes |
EditGroup = ({
id: groupId, studentGroupData, studentsData, mentorsData, coursesData,
}) => {
const {
isLoading: isEditing,
isLoaded: isEdited,
error: editingError,
} = useSelector(editStudentGroupSelector, shallowEqual);
const [dispatchEditGroup, dispatchAddAlert] = useActions([editStudentGroup, addAlert]);
const history = useHistory();
const {
data: group,
isLoading: isGroupLoading,
isLoaded: isGroupLoaded,
} = studentGroupData;
const {
data: students,
isLoading: areStudentsLoading,
isLoaded: areStudentsLoaded,
} = studentsData;
const {
data: mentors,
isLoading: areMentorsLoading,
isLoaded: areMentorsLoaded,
} = mentorsData;
const {
data: courses,
isLoading: areCoursesLoading,
loaded: areCoursesLoaded,
} = coursesData;
const [groupMentors, setGroupMentors] = useState([]);
const [mentorInputError, setMentorInputError] = useState('');
const [groupStudents, setGroupStudents] = useState([]);
const [studentInputError, setStudentInputError] = useState('');
const prevGroupMentors = usePrevious(groupMentors);
const prevGroupStudents = usePrevious(groupStudents);
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
useEffect(() => {
if (!isEditing && isEdited && !editingError) {
history.push(paths.GROUPS);
dispatchAddAlert('The group has been successfully edited!', 'success');
}
if (!isEditing && !isEdited && editingError) {
dispatchAddAlert(editingError);
}
}, [isEdited, editingError, isEditing, history, dispatchAddAlert]);
useEffect(() => {
if (mentors.length) {
setGroupMentors(mentors.filter(({ id }) => group.mentorIds?.includes(id)));
}
}, [group.mentorIds, mentors]);
useEffect(() => {
if (students.length) {
setGroupStudents(students.filter(({ id }) => group.studentIds?.includes(id)));
}
}, [group.studentIds, students]);
const addMentor = (mentorFullInfo, clearField) => {
const [selectedMentorEmail] = mentorFullInfo.split(' ').reverse();
const mentor = mentors.find(({ email }) => email === selectedMentorEmail);
if (mentor) {
clearField();
setMentorInputError('');
setGroupMentors((prevState) => [...prevState, mentor]);
} else {
setMentorInputError('Mentor not found');
}
};
const addStudent = (studentFullInfo, clearField) => {
const [selectedStudentEmail] = studentFullInfo.split(' ').reverse();
const student = students.find(({ email }) => email === selectedStudentEmail);
if (student) {
clearField();
setStudentInputError('');
setGroupStudents((prevState) => [...prevState, student]);
} else {
setStudentInputError('Student not found');
}
};
const removeMentor = useCallback((mentorId) => {
setGroupMentors(groupMentors.filter(({ id }) => id !== mentorId));
}, [groupMentors]);
const removeStudent = useCallback((studentId) => {
setGroupStudents(groupStudents.filter(({ id }) => id !== studentId));
}, [groupStudents]);
const handleSubmit = ({
name, startDate, finishDate, courseId,
}) => {
const newGroupData = {
id: groupId,
name,
courseId,
startDate: commonHelpers.transformDateTime({ isDayTime: false, dateTime: startDate }).formInitialValue,
finishDate: commonHelpers.transformDateTime({ isDayTime: false, dateTime: finishDate }).formInitialValue,
studentIds: [...new Set(groupStudents.map(({ id }) => id))],
mentorIds: [...new Set(groupMentors.map(({ id }) => id))],
};
dispatchEditGroup(newGroupData);
};
const handleReset = () => {
setGroupStudents(students.filter(({ id }) => group.studentIds.includes(id)));
setGroupMentors(mentors.filter(({ id }) => group.mentorIds.includes(id)));
setStudentInputError('');
setMentorInputError('');
};
return (
<div className="w-100">
<div className="justify-content-center">
<div className="w-100 card shadow p-4">
<WithLoading
isLoading={isGroupLoading ||
areMentorsLoading ||
areCoursesLoading ||
areStudentsLoading}
className={styles['loader-centered']}
>
<Formik
initialValues={{
name: group.name,
startDate: commonHelpers.transformDateTime({ isDayTime: false, dateTime: group.startDate }).formInitialValue,
finishDate: commonHelpers.transformDateTime({ isDayTime: false, dateTime: group.finishDate }).formInitialValue,
courseId: group.courseId,
mentor: '',
student: '',
}}
onSubmit={handleSubmit}
validationSchema={editGroupValidation}
validateOnMount={false}
>
{({ values, errors, setFieldValue, isValid, dirty }) => (
<Form className="px-2 py-4" name="start-group" data-testid="editGroup">
<h3>Group Editing</h3>
<hr />
<div className="row mb-3 align-items-start">
<div className="col d-flex align-items-center">
<label className="mt-2" htmlFor="name">Group name:
</label>
</div>
<div className="col-md-8">
<Field
className={classNames('form-control', { 'border-danger': errors.name })}
type="text"
name="name"
id="group-name"
placeholder="group name"
/>
{errors.name && <p className="w-100 text-danger mb-0">{errors.name}</p>}
</div>
</div>
<div className="row mb-3">
<div className="col d-flex align-items-center">
<label className="mb-0" htmlFor="course">Course:</label>
</div>
<div className="col-md-8">
<Field
id="course-name"
as="select"
className={classNames('custom-select')}
name="courseId"
>
<option value={group.courseId} key={group.courseId}>
{ courses.find((course) => course.id === group.courseId)?.name }
</option>
{
courses
.filter((course) => course.id !== group.courseId)
.map((course) => (
<option value={course.id} key={course.id}>{course.name}</option>
))
}
</Field>
</div>
</div>
<div className="row mb-3">
<div className="col d-flex align-items-center">
<label className="mb-0" htmlFor="start-date">Start date:</label>
</div>
<div className="col-md-8">
<Field
className={classNames('form-control', { 'border-danger': errors.startDate })}
type="date"
name="startDate"
id="start-date"
/>
{errors.startDate && <p className="text-danger mb-0">{errors.startDate}</p>}
</div>
</div>
<div className="row mb-3">
<div className="col d-flex align-items-start">
<label className="mt-2" htmlFor="finish-date">Finish date:</label>
</div>
<div className="col-md-8">
<Field
className={classNames('form-control', { 'border-danger': errors.finishDate })}
type="date"
name="finishDate"
id="finish-date"
/>
{errors.finishDate && <p className="text-danger mb-0">{errors.finishDate}</p>}
</div>
</div>
<div className="row mb-3">
<div className="col d-flex align-items-start">
<label className="mt-2" htmlFor="mentor">Mentors:</label>
</div>
<div className="col-md-8">
<div className="d-flex" data-testid="mentor-field-wrapper">
<Field
className="form-control f"
type="text"
name="mentor"
list="mentors-list"
/>
<datalist id="mentors-list">
{
mentors
.filter(({ id }) => !groupMentors.find((mentor) => mentor.id === id))
.map(({
id, firstName, lastName, email,
}) => <option key={id} value={`${firstName} ${lastName} ${email}`} />)
}
</datalist>
<Button
id="add-mentor-btn"
variant="info"
onClick={() => addMentor(values.mentor, () => setFieldValue('mentor', ''))}
disabled={!dirty}
>
+
</Button>
</div>
{mentorInputError && <p className="text-danger mb-0">{mentorInputError}</p>}
<div className="w-100">
<ul className="col-md-12 d-flex flex-wrap justify-content-between p-0">
{
groupMentors.map(({ id, firstName, lastName }) => (
<li
key={id}
id="chosenMentorName"
className={classNames(
'd-flex bg-light border border-outline-secondary rounded',
styles['datalist-item'],
)}
>
{firstName} {lastName}
<button
type="button"
className={classNames('btn p-0 ml-auto mr-2 font-weight-bold', styles.cross)}
onClick={() => removeMentor(id)}
>
X
</button>
</li>
))
}
</ul>
</div>
</div>
</div>
<div className="row mb-3">
<div className="col d-flex align-items-start">
<label className="mt-2" htmlFor="finish-date">Students:</label>
</div>
<div className="col-md-8">
<div className="d-flex" data-testid="students-field-wrapper">
<Field
className="form-control f"
type="text"
name="student"
list="students-list"
/>
<datalist id="students-list">
{
students
.filter(({ id }) => !groupStudents.find((mentor) => mentor.id === id))
.map(({
id, firstName, lastName, email,
}) => <option key={id} value={`${firstName} ${lastName} ${email}`} />)
}
</datalist>
<Button
id="add-student-btn"
variant="info"
onClick={() => addStudent(values.student, () => setFieldValue('student', ''))}
disabled={!dirty}
>
+
</Button>
</div>
{studentInputError && <p className="text-danger mb-0">{studentInputError}</p>}
<div className="w-100">
<ul className="col-12 d-flex flex-wrap justify-content-between p-0">
{
groupStudents.map(({ id, firstName, lastName }) => (
<li
key={id}
className={classNames(
'd-flex bg-light border border-outline-secondary rounded',
styles['datalist-item'],
)}
>
{firstName} {lastName}
<button
type="button"
className={classNames('btn p-0 ml-auto mr-2 font-weight-bold', styles.cross)}
onClick={() => removeStudent(id)}
>
X
</button>
</li>
))
}
</ul>
</div>
</div>
</div>
<div className="row justify-content-around mt-4">
<Button type="reset" variant="secondary" className={classNames('w-25', styles['clear-button'])} disabled={ (!dirty && prevGroupMentors !== groupMentors && prevGroupStudents !== groupStudents) || isEditing} onClick={handleReset}>
Reset
</Button>
<Button id="submit" type="submit" className="btn btn-secondary w-25 buttonConfirm" disabled={!isValid || (!dirty && prevGroupMentors !== groupMentors && prevGroupStudents !== groupStudents) || isEditing}>
Confirm
</Button>
</div>
</Form>
)}
</Formik>
</WithLoading>
</div>
</div>
</div>
);
}
Example #18
Source File: edit-group.test.js From what-front with MIT License | 4 votes |
describe('Edit group', () => {
let mockedState;
let historyMock;
beforeEach(() => {
mockedState = {
isLoading: false,
isLoaded: true,
error: '',
};
useSelector.mockReturnValue(mockedState);
const useActionsFns = {
dispatchEditGroup: jest.fn(),
dispatchAddAlert: jest.fn()
};
useActions.mockReturnValue([
useActionsFns.dispatchEditGroup,
useActionsFns.dispatchAddAlert
]);
commonHelpers.transformDateTime = jest.fn().mockReturnValue({
formInitialValue: '2022-06-16'
});
historyMock = { push: jest.fn(), location: {}, listen: jest.fn() };
});
afterEach(cleanup);
it('should render edit-group component', () => {
const { getByTestId, debug } = render(
<Router history={historyMock}>
<EditGroup
id={id}
initialValues={initialValues}
coursesData={coursesData}
mentorsData={mentorsData}
studentsData={studentsData}
studentGroupData={studentGroupData}
/>
</Router>
);
expect(getByTestId('editGroup')).toBeInTheDocument();
});
it('should loader appear when studentGroup Loading is true', () => {
const { container } = render(
<Router history={historyMock}>
<EditGroup
id={id}
initialValues={initialValues}
coursesData={coursesData}
mentorsData={mentorsData}
studentsData={studentsData}
studentGroupData={studentGroupDataLoading}
/>
</Router>
);
const loader = container.querySelector('.spinner-border');
expect(loader).toBeInTheDocument();
});
it('should datalist of mentors = 2', () => {
const { container } = render(
<Router history={historyMock}>
<EditGroup
id={id}
initialValues={initialValues}
coursesData={coursesData}
mentorsData={mentorsData}
studentsData={studentsData}
studentGroupData={studentGroupData}
/>
</Router>
);
const mentorsList = container.querySelector('#mentors-list');
expect(mentorsList.children.length).toEqual(2);
});
it('should datalist of students = 2', () => {
const { container } = render(
<Router history={historyMock}>
<EditGroup
id={id}
initialValues={initialValues}
coursesData={coursesData}
mentorsData={mentorsData}
studentsData={studentsData}
studentGroupData={studentGroupData}
/>
</Router>
);
const studentsList = container.querySelector('#students-list');
expect(studentsList.children.length).toEqual(2);
});
it('should call addMentor and addStudent 1 time', async () => {
const { container, getByTestId } = render(
<Router history={historyMock}>
<EditGroup
id={id}
initialValues={initialValues}
coursesData={coursesData}
mentorsData={mentorsData}
studentsData={studentsData}
studentGroupData={studentGroupData}
/>
</Router>
);
const addMentor = jest.fn();
const addStudent = jest.fn();
const addMentorBtn = container.querySelector('#add-mentor-btn');
const addStudentBtn = container.querySelector('#add-student-btn');
await waitFor(() => {
fireEvent.click(addMentorBtn);
addMentor();
});
expect(addMentor).toHaveBeenCalledTimes(1);
await waitFor(() => {
fireEvent.click(addStudentBtn);
addStudent();
});
expect(addStudent).toHaveBeenCalledTimes(1);
});
it('should submit Form', async () => {
const { container } = render(
<Router history={historyMock}>
<EditGroup
id={id}
initialValues={initialValues}
coursesData={coursesData}
mentorsData={mentorsData}
studentsData={studentsData}
studentGroupData={studentGroupData}
/>
</Router>
);
const inputGroupName = container.querySelector('#group-name');
const inputCourseName = container.querySelector('#course-name');
const inputStartDate = container.querySelector('#start-date');
const inputFinishDate = container.querySelector('#finish-date');
const submitBtn = container.querySelector('#submit');
const handleSubmit = jest.fn();
expect(submitBtn).toBeInTheDocument();
await waitFor(() => {
fireEvent.change(inputGroupName, { target: { value: formValues.groupName } });
fireEvent.change(inputCourseName, { target: { value: coursesData.data[1] } });
fireEvent.change(inputStartDate, { target: { value: formValues.startDate } });
fireEvent.change(inputFinishDate, { target: { value: formValues.finishDate } });
});
React.useState = useStateMock.submit;
await waitFor(() => {
fireEvent.click(submitBtn);
handleSubmit();
});
expect(handleSubmit).toHaveBeenCalledTimes(1);
});
});
Example #19
Source File: rows.js From what-front with MIT License | 4 votes |
Rows = (
{ data, handleDetails, handleEdit, access = true, fieldsToShow },
custom
) =>
data.map(
({
id,
index,
firstName,
lastName,
email,
name,
themeName,
lessonShortDate,
lessonTime,
studentIds,
startDate,
finishDate,
}) => {
return (
<tr
key={id}
onClick={handleDetails ? () => handleDetails(id) : null}
className={styles['table-row']}
>
{fieldsToShow.includes('index') && index && (
<td className="text-center">{index + 1}</td>
)}
{fieldsToShow.includes('firstName') && firstName && (
<td>{firstName}</td>
)}
{fieldsToShow.includes('name') && name && <td>{name}</td>}
{fieldsToShow.includes('studentIds') && studentIds && (
<td>{studentIds.length}</td>
)}
{fieldsToShow.includes('startDate') && startDate && (
<td>
{
commonHelpers.transformDateTime({
isDayTime: false,
dateTime: startDate,
}).date
}
</td>
)}
{fieldsToShow.includes('finishDate') && finishDate && (
<td>
{
commonHelpers.transformDateTime({
isDayTime: false,
dateTime: finishDate,
}).date
}
</td>
)}
{fieldsToShow.includes('themeName') && themeName && (
<td>{themeName}</td>
)}
{fieldsToShow.includes('lessonShortDate') && lessonShortDate && (
<td>{lessonShortDate}</td>
)}
{fieldsToShow.includes('lessonTime') && lessonTime && (
<td>{lessonTime}</td>
)}
{fieldsToShow.includes('lastName') && lastName && <td>{lastName}</td>}
{fieldsToShow.includes('email') && email && <td>{email}</td>}
{fieldsToShow.includes('edit') && access && (
<td
className="text-center"
onClick={handleEdit ? (event) => handleEdit(event, id) : null}
>
<Icon
icon="Edit"
className={styles.scale}
color="#2E3440"
size={30}
/>
</td>
)}
{fieldsToShow.includes('custom') && custom && <td>{custom}</td>}
</tr>
);
}
)