Skip to content

Commit

Permalink
Merge pull request #678 from RockefellerArchiveCenter/development
Browse files Browse the repository at this point in the history
Adds configurable Reading Room date picker
  • Loading branch information
helrond authored Feb 27, 2024
2 parents 68d9803 + 781adf0 commit 67781b5
Show file tree
Hide file tree
Showing 20 changed files with 1,582 additions and 1,490 deletions.
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ REACT_APP_REQUEST_BROKER_BASEURL=http://rac-vch.ad.rockarchive.org:8001/api
REACT_APP_LOCALSTORAGE_KEY=DIMESMyList
REACT_APP_MINIMAP_KEY=DIMESMinimapIntro
REACT_APP_S3_BASEURL=https://iiif.rockarch.org
REACT_APP_CAPTCHA_SITE_KEY=6LdQiSkTAAA
REACT_APP_CAPTCHA_SITE_KEY=6LdQiSkTAAA
REACT_APP_ENABLE_READING_ROOM_SELECT=true
3 changes: 2 additions & 1 deletion .env.deploy
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ REACT_APP_REQUEST_BROKER_BASEURL=${REQUEST_BROKER_BASEURL}
REACT_APP_LOCALSTORAGE_KEY=DIMESMyList
REACT_APP_MINIMAP_KEY=DIMESMinimapIntro
REACT_APP_S3_BASEURL=${AWS_BUCKET_BASEURL}
REACT_APP_CAPTCHA_SITE_KEY=${CAPTCHA_SITE_KEY}
REACT_APP_CAPTCHA_SITE_KEY=${CAPTCHA_SITE_KEY}
REACT_APP_ENABLE_READING_ROOM_SELECT=${ENABLE_READING_ROOM_SELECT}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ DIMES uses a [macro implementation](https://lingui.dev/guides/message-extraction
to present translated strings.
5. Commit updated code to the GitHub repository.

### Aeon Reading Room Integration

Available dates for reading rooms can be pulled from Aeon via the request broker by setting the `REACT_APP_ENABLE_READING_ROOM_SELECT` environment variable. Not setting this environment variable or leaving it blank will disable this feature. Setting this environment variable to any string will activate it.

## License

This code is released under an [MIT License](LICENSE).
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@testing-library/user-event": "^14.2.0",
"axios": "^0.27.2",
"classnames": "^2.2.6",
"date-fns": "^2.30.0",
"downshift": "^6.1.7",
"formik": "^2.1.5",
"mirador": "^3.3.0",
Expand All @@ -20,6 +21,7 @@
"react": "^16.13.1",
"react-accessible-dropdown-menu-hook": "^3.2.0",
"react-aria-live": "^2.0.5",
"react-datepicker": "^4.20.0",
"react-dom": "^16.13.1",
"react-google-recaptcha": "^2.1.0",
"react-helmet": "^6.1.0",
Expand Down
4 changes: 2 additions & 2 deletions src/components/Inputs/__tests__/Inputs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ it('renders checkbox props correctly', () => {

it('renders date props correctly', () => {
act(() => {
render(<I18nApp ReactComponent={<DateInput label='Select a date' id='1' />} />, container)
render(<I18nApp ReactComponent={<DateInput label='Select a date' id='1' handleChange={jest.fn()} />} />, container)
})

const label = document.querySelector('label')
expect(label.textContent).toBe('Select a date')

const input = document.querySelector('.dp__input')
const input = document.querySelector('.dp__wrapper')
expect(input.id).toBe('1')
})

Expand Down
66 changes: 21 additions & 45 deletions src/components/Inputs/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import React from 'react'
import React, {useEffect, useState} from 'react'
import PropTypes from 'prop-types'
import {
DatePicker,
DatePickerInput,
DatePickerMonth,
DatePickerTable,
DatePickerButton,
DatePickerCalendar} from '@reecelucas/react-datepicker'
import DatePicker from 'react-datepicker'
import {useSelect} from 'downshift'
import MaterialIcon from '../MaterialIcon'
import classnames from 'classnames'
Expand Down Expand Up @@ -62,54 +56,36 @@ CheckBoxInput.defaultProps = {
checked: true,
}

export const DateInput = props => (
export const DateInput = ({className, defaultDate, handleChange, helpText, id, label, ...props}) => {
const [startDate, setStartDate] = useState(defaultDate || new Date())

useEffect(() => {
handleChange(startDate)
}, [startDate, setStartDate])

return (
<>
<label htmlFor={id}>{label}</label>
<DatePicker
className='mb-2'
initialDate={new Date()}
minDate={new Date()}
onSelect={date => props.handleChange(date)}>
<label htmlFor={props.id}>{props.label}</label>
<DatePickerInput
className='dp__input'
dateFormat={'MM/dd/yyyy'}
id={props.id}
name={props.name} />
<DatePickerCalendar className='dp__calendar py-20 px-20'>
<div className='dp__top-bar mb-12'>
<DatePickerButton
className='dp__button py-2 px-6'
aria-label={t({
comment: 'Aria label for Calendar button',
message: 'Switch to the previous month.'
})}
updateMonth={({ prev }) => prev()} >
<MaterialIcon icon='west' />
</DatePickerButton>
<DatePickerMonth className='dp__month px-16 py-0' />
<DatePickerButton
className='dp__button py-2 px-6'
aria-label={t({
comment: 'Aria label for Calendar button',
message: 'Switch to the next month.'
})}
updateMonth={({ next }) => next()} >
<MaterialIcon icon='east' />
</DatePickerButton>
</div>
<DatePickerTable className='dp__table' />
</DatePickerCalendar>
className={className || 'dp__wrapper'}
selected={startDate}
showTimeSelect='true'
onChange={date => setStartDate(date)}
dateFormat="yyyy-MM-dd h:mm aa"
id={id}
{...props}>
</DatePicker>
{props.helpText && <p className='input__help-text' aria-describedby={`desc-${props.id}`}>{props.helpText}</p>}
{helpText && <p className='input__help-text' aria-describedby={`desc-${id}`}>{helpText}</p>}
</>
)
)}

DateInput.propTypes = {
className: PropTypes.string,
handleChange: PropTypes.func,
helpText: PropTypes.string,
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
defaultDate: PropTypes.instanceOf(Date),
};


Expand Down
1 change: 1 addition & 0 deletions src/components/ModalMyList/__tests__/ModalMyList.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ beforeEach(() => {
return Promise.reject(new Error('not found'))
}
})
axios.get.mockImplementation((url) => Promise.resolve({data:[]}))
})

afterEach(() => {
Expand Down
127 changes: 101 additions & 26 deletions src/components/ModalMyList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { ModalSavedItemList } from '../ModalSavedItem'
import { getFormattedDate } from '../Helpers'
import './styles.scss'
import { Plural, Trans, select, t } from '@lingui/macro'
import axios from 'axios'
import { addBusinessDays, parse, parseISO, startOfDay, isWithinInterval } from 'date-fns'


const SubmitListInput = ({ submitList }) => {
Expand Down Expand Up @@ -354,7 +356,97 @@ EmailModal.propTypes = {
toggleModal: PropTypes.func.isRequired,
}

export const ReadingRoomRequestModal = props => (
const ReadingRoomSelect = ({ readingRooms }) => {
const { setFieldValue } = useFormikContext();
const [site, setSite] = useState('');

const ReadingRoomLocations = readingRooms.map(readingRoom => ({
value: readingRoom.sites[0],
label: readingRoom.name,
}));
ReadingRoomLocations.unshift({
value: "",
label: t({message: "Please select a reading room"}),
});

useEffect(() => {
setFieldValue('site', site);
}, [site, setFieldValue]);

return (
<div className='form-group'>
<SelectInput
className='select__modal'
id='site'
label={t({message: 'Select Reading Room Location'})}
name='site'
onChange={({ selectedItem }) => setSite(selectedItem.value)}
options={ReadingRoomLocations}
required={true}
selectedItem={site || ''} />
<ErrorMessage
id='site-error'
name='site'
component='div'
className='modal-form__error' />
</div>
)
}

const ReadingRoomDateInput = ({ readingRoom }) => {
const { setFieldValue } = useFormikContext();

return (
<div className='form-group'>
<Field
component={DateInput}
handleChange={date => setFieldValue('scheduledDate', date)}
helpText={t({
comment: 'Helptext for scheduling date.',
message: 'Enter the date of your research visit (mm/dd/yyyy)'
})}
id='scheduledDate'
label={t({
comment: 'Label for scheduling date',
message: 'Scheduled Date *'
})}
type='date'
defaultDate={addBusinessDays(new Date(), readingRoom?.policies[0]?.appointmentMinLeadDays || 1)}
minDate={addBusinessDays(new Date(), readingRoom?.policies[0]?.appointmentMinLeadDays || 1)}
filterDate={!!process.env.REACT_APP_ENABLE_READING_ROOM_SELECT ? date => readingRoom?.openHours.some(x => x.dayOfWeek === date.getDay()) : null}
filterTime={!!process.env.REACT_APP_ENABLE_READING_ROOM_SELECT ? date => {
if (readingRoom === undefined) return false;
const hours = readingRoom.openHours.find(x => x.dayOfWeek === date.getDay());
return isWithinInterval(date, {
start: parse(hours.openTime, "HH:mm:ss", date),
end: parse(hours.closeTime, "HH:mm:ss", date),
});
} : null}
excludeDateIntervals={readingRoom?.closures.map(closure => ({
start: startOfDay(parseISO(closure.startDate)),
end: startOfDay(parseISO(closure.endDate)),
}))} />
<ErrorMessage
id='scheduledDate-error'
name='scheduledDate'
component='div'
className='modal-form__error' />
</div>
)
}

export const ReadingRoomRequestModal = props => {
const [aeonReadingRooms, setAeonReadingRooms] = useState([]);

useEffect(() => {
if (!!process.env.REACT_APP_ENABLE_READING_ROOM_SELECT) {
axios.get(`${process.env.REACT_APP_REQUEST_BROKER_BASEURL}/reading-rooms`).then(response => {
setAeonReadingRooms(response.data);
});
}
}, []); // empty deps array means it runs once

return (
<ModalMyList
appElement={props.appElement}
title='Request in Reading Room'
Expand All @@ -372,6 +464,9 @@ export const ReadingRoomRequestModal = props => (
comment: 'Missing Scheduled Date error',
message: 'Please provide the date of your research visit.'
});
if (!!process.env.REACT_APP_ENABLE_READING_ROOM_SELECT && !values.site) errors.site = t({
message: 'Please select a location of a reading room.'
})
if (!values.recaptcha) errors.recaptcha = t({
message: 'Please complete this field.'
});
Expand All @@ -390,7 +485,7 @@ export const ReadingRoomRequestModal = props => (
setSubmitting(false);
}}
>
{({ errors, isSubmitting, setFieldValue, touched }) => (
{({ errors, isSubmitting, setFieldValue, touched, values }) => (
<Form>
<SubmitListInput submitList={props.submitList} />
<ErrorMessage
Expand All @@ -400,29 +495,9 @@ export const ReadingRoomRequestModal = props => (
})}
component='div'
className='input__error' />
<div className='form-group mx-0'>
<Field
component={DateInput}
handleChange={date => setFieldValue('scheduledDate', date)}
helpText={t({
comment: 'Helptext for scheduling date.',
message: 'Enter the date of your research visit (mm/dd/yyyy)'
})}
id='scheduledDate'
label={t({
comment: 'Label for scheduling date',
message: 'Scheduled Date *'
})}
type='date' />
<ErrorMessage
id='scheduledDate-error'
name={t({
comment: 'Name for scheduling date error',
message: 'scheduledDate'
})}
component='div'
className='input__error' />
</div>
{!!process.env.REACT_APP_ENABLE_READING_ROOM_SELECT && <ReadingRoomSelect readingRooms={aeonReadingRooms} />}
<ReadingRoomDateInput
readingRoom={aeonReadingRooms.find(room => room.sites[0] === values.site)} />
<FormGroup
label={t({
comment: 'Label for RAC staff message Form',
Expand Down Expand Up @@ -472,7 +547,7 @@ export const ReadingRoomRequestModal = props => (
</Formik>
}
/>
)
)}

ReadingRoomRequestModal.propTypes = {
appElement: PropTypes.object,
Expand Down
2 changes: 2 additions & 0 deletions src/components/PageMyList/__tests__/PageMyList.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ it('renders props correctly', async () => {
axios.get.mockImplementation((url) => {
if (url.includes('status')) {
return Promise.resolve({ data: { pong: true } })
} else if (url.includes('reading-rooms')) {
return Promise.resolve({data:[]})
} else {
return Promise.reject(new Error('not found'))
}
Expand Down
Loading

0 comments on commit 67781b5

Please sign in to comment.