Source

component/calendar/CalendarSelect.jsx

import React from "react"
import PropTypes from "prop-types"
import { CalendarBox, CalendarOption, DateSelect} from "../../style"
import { datePickerParams } from "../../utils/datePickerParams"


/**
 * Provides span option for CalendarBox list
 * @param {object} item 
 * @param {string} baseId - date picker input id
 * @returns {object} span html element
 */
function getCalendarOption(item, baseId){
    return(
        <CalendarOption 
            key={`${baseId}-opt-${item.name}`} 
            id={datePickerParams.id[baseId][getSelectItemId(item.name)]} 
            data-testid={`${item.name}-test`}
            $name={item.name} 
            $type={item.type} 
        >
            {item.value && item.value}
        </CalendarOption>
    )
}

/**
 * Provides span option for Calendar Select list
 * @param {string | number} textContent 
 * @param {string} id 
 * @param {boolean} isSelected 
 * @param {function} spanOnClickFunction 
 * @returns {object} span html element
 */
const getSpan = (textContent, id, isSelected = false, spanOnClickFunction = false) => <span 
                                                            key={`opt-${id}-${textContent}`} 
                                                            id={id} 
                                                            className={isSelected ? "selected-option" : null} 
                                                            onClick={spanOnClickFunction ? spanOnClickFunction : null}
                                                        >{textContent}</span> 
/**
 * Corrects the value passed in parameter if it does not respect the minimum and maximum values
 * @param {number} value 
 * @param {number} maxValue 
 * @param {number} minValue 
 * @returns {number}
 */ 
function getSpanValue(value, maxValue, minValue){
    return value > maxValue ? value - (maxValue+1) 
        : value < minValue ? value + maxValue + 1 
        : value
}

/**
 * Provide the id of the html select tag
 * @param {string} name 
 * @returns {string}
 */
function getSelectItemId(name){
    switch(name){
        case "home": return "todayBtn" 
        case "month": return "selectedMonth" 
        case "next-month": return "nextMonthBtn" 
        case "previous-month": return "prevMonthBtn" 
        case "year": return "selectedYear" 
        default: return false                   
    }
}

/**
 * Scrolls through list values - onClick
 * @param {string} moreOrLess - if receive "more" increment the value of the list else do a decrement
 * @param {string} id 
 * @param {array} list 
 * @param {number} maxValue 
 * @param {number} minValue 
 */
function moveDateSelectList(moreOrLess, id, list, maxValue, minValue = 0){
    updateDateSelectList(id, list, maxValue, minValue, 
        moreOrLess === "more" 
        ? parseInt(document.querySelector(`div#${id} .selected-option`).textContent) - (parseInt(list.length / 2)-1) 
        :  parseInt(document.querySelector(`div#${id} .selected-option`).textContent) - (parseInt(list.length / 2)+1)
    )
}

/**
 * Scrolls through list values - onWheel
 * @param {object} e - event
 * @param {string} id 
 * @param {array} list 
 * @param {number} maxValue 
 * @param {number} minValue 
 */
function onWheelFunction(e, id, list, maxValue, minValue = 0){
    const selectedValue = parseInt(document.querySelector(`div#${id} .selected-option`).textContent)
    const startValue = getSpanValue(
        e.deltaY > 0 ? (selectedValue + 1) - parseInt(list.length / 2) : (selectedValue - 1) - parseInt(list.length / 2), 
        maxValue, 
        minValue
    )
    updateDateSelectList(id, list, maxValue, minValue, startValue)
}

/**
 * modify span value of CalendarSelect
 * @param {string} id 
 * @param {array} list 
 * @param {number} maxValue 
 * @param {number} minValue 
 * @param {number} startValue 
 */
function updateDateSelectList(id, list, maxValue, minValue, startValue){
    list.map((item, index) => {
        document.getElementById(`${id}-option-${index}`).textContent =  getSpanValue(startValue + index, maxValue, minValue)
    })
}


/**
 * Display Calendar select
 * @component
 * @param {object} props 
 * @param {string} props.baseId - date picker input id
 * @param {string} props.className 
 * @param {array} props.list - list of values ​​or objects used to create the select
 * @param {number} props.maxValue - maximum value that the select can display
 * @param {number} props.minValue - minimum value that the select can display
 * @param {string} props.name
 * @param {object} props.onClickFunction - function apply to select 
 * @param {number} props.selectedValue - value display as selected
 * @param {function} props.spanOnClickFunction - function apply to option select    
 * @returns {object} 
 */ 
function CalendarSelect(props){

    const { baseId, className, list, maxValue, minValue, name, onClickFunction, selectedValue, spanOnClickFunction } = props
    const elementId = datePickerParams.id[baseId][`${name}Select`]
    
    /**
     * change selected value of select
     * @param {object} e - event
     */
    const updateDateSelectOnClickSpan = (e) => {
        e.stopPropagation()
        updateDateSelectList(elementId, list, maxValue, minValue, parseInt(e.target.textContent) - parseInt(list.length / 2))
    }

    return typeof list[0] === "object" ? (
        <CalendarBox $name="option" onClick={onClickFunction} data-testid="calendar-options-menu" >
                { list.map((item) => getCalendarOption(item, baseId)) }
        </CalendarBox>
    ) : (
        <DateSelect 
            $name={name} 
            id={elementId} 
            onClick={onClickFunction} 
            onWheel={ (e) => maxValue && onWheelFunction(e, elementId, list, maxValue, minValue)} 
            className={className}
        >
            { spanOnClickFunction && ( 
                <CalendarOption 
                    id={`${elementId}-less-btn`} 
                    data-testid={`${elementId}-less-btn-test`} 
                    $name={`less`} 
                    $type={"move-icon"} 
                    onClick={() => moveDateSelectList("less", elementId, list, maxValue, minValue)}
                />
            )}
            { list.map((item, index) => getSpan(
                getSpanValue(Number.isInteger(item) ? item + index : item, maxValue, minValue), 
                `${elementId}-option-${index}`, 
                selectedValue === item + index && true, 
                spanOnClickFunction && updateDateSelectOnClickSpan
            )) }
            { spanOnClickFunction && ( 
                <CalendarOption 
                    id={`${elementId}-more-btn`} 
                    data-testid={`${elementId}-more-btn-test`} 
                    $name={`more`} 
                    $type={"move-icon"} 
                    onClick={() => moveDateSelectList("more", elementId, list, maxValue, minValue)}
                /> 
            )}
        </DateSelect>
    )
}

CalendarSelect.defaultProp = {
    className: null,
    maxValue: false, 
    minValue: 0,
    selectedValue: false, 
    spanOnClickFunction: false
}

CalendarSelect.propTypes = {
    baseId: PropTypes.string,
    className: PropTypes.string,
    list : PropTypes.array.isRequired, 
    maxValue: PropTypes.number, 
    minValue: PropTypes.number,
    name: PropTypes.string.isRequired, 
    onClickFunction: PropTypes.func.isRequired,
    selectedValue: PropTypes.number, 
    spanOnClickFunction: PropTypes.bool
}

export default CalendarSelect