Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EDSC-4125: Recurring temporal slider doesn't work #1841

Merged
merged 23 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5807349
EDSC-4125 GranuleFilter DatePicker improvements
daniel-zamora Nov 21, 2024
cb0ae66
EDSC-4125 prevents InputRange request spam
daniel-zamora Nov 26, 2024
eecde65
EDSC-4125 some fixes to datepicker
daniel-zamora Nov 26, 2024
934be2f
EDSC-4125 some fixes to recurring granule filter
daniel-zamora Nov 26, 2024
a7e2cd3
EDSC-4125 adds test cases for updated date picker logic
daniel-zamora Dec 3, 2024
adf2891
EDSC-4125 some test fixes
daniel-zamora Dec 3, 2024
b09d3cd
EDSC-4125 test fixes
daniel-zamora Dec 3, 2024
99b1c0c
EDSC-4125 cleanup
daniel-zamora Dec 4, 2024
9b5018d
EDSC-4125 fixes collection temporal pickers
daniel-zamora Dec 16, 2024
2790abc
EDSC-4076 slight refactor
daniel-zamora Dec 16, 2024
aa9ca87
EDSC-4076 slight refactor
daniel-zamora Dec 16, 2024
7e4bdb8
EDSC-4125 adds temporal dropdown test for recurring
daniel-zamora Dec 17, 2024
12c3e6b
EDSC-4125 test fixes
daniel-zamora Dec 18, 2024
0e4b652
EDSC-4125 pr comments
daniel-zamora Dec 18, 2024
24fa898
EDSC-4125 adds mockdate to datepicker test
daniel-zamora Jan 8, 2025
c5256df
EDSC-4125 handles unset date cases
daniel-zamora Jan 8, 2025
409974f
EDSC-4125 test fixes
daniel-zamora Jan 8, 2025
a18c063
EDSC-4125 utc time zone test fixes
daniel-zamora Jan 9, 2025
281886f
EDSC-4125 test fix test
daniel-zamora Jan 13, 2025
105ef55
EDSC-4125 changes display value in datepicker, updates tests
daniel-zamora Jan 24, 2025
118854f
EDSC-4125 granule filter fix
daniel-zamora Jan 24, 2025
9101855
EDSC-4125 granule filters form fixes
daniel-zamora Jan 27, 2025
486700a
EDSC-4125 pr comments
daniel-zamora Jan 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 154 additions & 50 deletions static/src/js/components/Datepicker/Datepicker.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { PureComponent } from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import Datetime from 'react-datetime'

Expand All @@ -23,14 +22,20 @@ import './Datepicker.scss'
* @param {String} props.viewMode - The default view mode for the picker
*/
class Datepicker extends PureComponent {
constructor(props) {
super(props)
this.containerRef = React.createRef()
}

componentDidMount() {
const {
onTodayClick,
onClearClick
} = this.props

// Add a custom set of "Today" and "Clear" buttons and insert them into the picker
const container = ReactDOM.findDOMNode(this).querySelector('.rdtPicker') // eslint-disable-line
const container = this.containerRef.current?.querySelector('.rdtPicker')
if (!container) return

// Container to hold custom buttons
const buttonContainer = document.createElement('div')
Expand All @@ -55,23 +60,24 @@ class Datepicker extends PureComponent {

// Adds the new button container to the DOM
container.appendChild(buttonContainer)

// Set up navigation button click handlers
this.setupNavigationHandlers(container)
}

componentDidUpdate(prevProps) {
const {
viewMode: previousViewMode
} = prevProps

const {
viewMode,
picker
} = this.props

// If the viewMode has changed, navigate to the new viewMode
if (previousViewMode !== viewMode) picker.current.navigate(viewMode)
if (prevProps.viewMode !== viewMode) {
picker.current.navigate(viewMode)
}
}

onInputChange(event) {
onInputChange = (event) => {
const caret = event.target.selectionStart
const element = event.target

Expand All @@ -82,6 +88,97 @@ class Datepicker extends PureComponent {
})
}

setupNavigationHandlers = (container) => {
container.addEventListener('click', (event) => {
// Handle month selection
if (event.target.classList.contains('rdtMonth')) {
requestAnimationFrame(() => this.updateNavigationArrows())
}

// Handle navigation arrows
if (event.target.classList.contains('rdtPrev')
|| event.target.parentElement.classList.contains('rdtPrev')
|| event.target.classList.contains('rdtNext')
|| event.target.parentElement.classList.contains('rdtNext')) {
requestAnimationFrame(() => this.updateNavigationArrows())
}

// Handle going back to months view
if (event.target.classList.contains('rdtSwitch')) {
// Reset arrow visibility when going back to months view
const prevButton = container.querySelector('.rdtPrev')
const nextButton = container.querySelector('.rdtNext')
if (prevButton) {
prevButton.innerHTML = '<span>‹</span>'
prevButton.style.pointerEvents = ''
prevButton.style.visibility = 'visible'
}

if (nextButton) {
nextButton.innerHTML = '<span>›</span>'
nextButton.style.pointerEvents = ''
nextButton.style.visibility = 'visible'
}
}
})
}

setupCalendar = () => {
const container = this.containerRef.current?.querySelector('.rdtPicker')
if (container) {
this.setupNavigationHandlers(container)
this.updateNavigationArrows()
}
}

updateNavigationArrows = () => {
const { viewMode } = this.props
if (viewMode !== 'months') return

const container = this.containerRef.current?.querySelector('.rdtPicker')
if (!container) return

const isDayView = container.querySelector('.rdtDays') !== null
if (!isDayView) return

const prevButton = container.querySelector('.rdtPrev')
const nextButton = container.querySelector('.rdtNext')
const monthDisplay = container.querySelector('.rdtSwitch')

if (!monthDisplay) return

// Modify month display to remove year
const [monthStr] = monthDisplay.textContent.split(' ')
monthDisplay.textContent = monthStr

const currentMonth = new Date(`${monthStr} 1, 2000`).getMonth()

// Check if navigation would cross year boundary
if (prevButton) {
if (currentMonth === 0) {
prevButton.innerHTML = ''
prevButton.style.pointerEvents = 'none'
prevButton.style.visibility = 'hidden'
} else {
prevButton.innerHTML = '<span>‹</span>'
prevButton.style.pointerEvents = ''
prevButton.style.visibility = 'visible'
}
}

if (nextButton) {
if (currentMonth === 11) {
nextButton.innerHTML = ''
nextButton.style.pointerEvents = 'none'
nextButton.style.visibility = 'hidden'
} else {
nextButton.innerHTML = '<span>›</span>'
nextButton.style.pointerEvents = ''
nextButton.style.visibility = 'visible'
}
}
}

render() {
const {
filterType,
Expand All @@ -92,60 +189,67 @@ class Datepicker extends PureComponent {
size,
value,
onInputBlur,
onInputFocus
onInputFocus,
format,
id,
viewMode
} = this.props
const { format, id, viewMode } = this.props
const conditionalInputProps = {}

// React-datetime does not clear out the input field when a empty string is received. When
// the value is an empty string, the value is manually set on the input via `inputProps`.
if (!value) {
conditionalInputProps.value = ''
}
const conditionalInputProps = !value ? { value: '' } : {}

const onKeyDown = (event) => {
// If the user presses `Enter`, the field should behave the same as bluring the input
if (event.key === 'Enter') onInputBlur(event)
}

return (
<Datetime
className="datetime"
closeOnClickOutside
closeOnSelect
closeOnTab
dateFormat={format}
initialViewMode={viewMode}
inputProps={
{
id,
placeholder: format,
autoComplete: 'off',
className: `form-control ${size === 'sm' ? 'form-control-sm' : ''}`,
'aria-label': label,
onChange: (event) => {
this.onInputChange(event)
// eslint-disable-next-line no-underscore-dangle
picker.current._closeCalendar()
if (filterType === 'collection') {
onChange(event.target.value, false, 'Typed')
}
},
onBlur: onInputBlur,
onFocus: onInputFocus,
onKeyDown,
...conditionalInputProps
<div ref={this.containerRef}>
<Datetime
className="datetime"
closeOnClickOutside
closeOnSelect
closeOnTab
dateFormat={format}
initialViewMode={viewMode}
inputProps={
{
id,
placeholder: format,
autoComplete: 'off',
className: `form-control ${size === 'sm' ? 'form-control-sm' : ''}`,
'aria-label': label,
onChange: (event) => {
this.onInputChange(event)
// eslint-disable-next-line no-underscore-dangle
picker.current._closeCalendar()
if (filterType === 'collection') {
onChange(event.target.value, false, 'Typed')
}
},
onBlur: onInputBlur,
onFocus: onInputFocus,

onKeyDown,
...conditionalInputProps
}
}
}
isValidDate={isValidDate}
onChange={onChange}
ref={picker}
strictParsing
timeFormat={false}
utc
value={value}
viewMode={viewMode}
/>
isValidDate={isValidDate}
onChange={onChange}
onOpen={
() => {
requestAnimationFrame(() => this.setupCalendar())
}
}
ref={picker}
strictParsing
timeFormat={false}
utc
value={value}
viewMode={viewMode}
/>
</div>
)
}
}
Expand Down
Loading
Loading