Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import React from 'react'
import React, { useRef } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import { useDebouncedCallback } from 'use-debounce'
import { isEmpty, isNil } from 'lodash'

import useAdditionalInputs from '../../../hooks/useAdditionalInputs'
import useAdjustLabel from '../../../hooks/useAdjustLabel'
import useIdle from '../../../hooks/useIdle'

import AdditionalTextInput from './common/AdditionalTextInput'
import AdditionalTextareaInput from './common/AdditionalTextareaInput'
import OptionHelp from './common/OptionHelp'
import OptionText from './common/OptionText'

const CheckboxInput = ({ question, value, option, optionIndex, disabled, onCreate, onUpdate, onDelete }) => {

const ref = useRef(null)
const [getAdditionalInput, setAdditionalInput] = useAdditionalInputs(value, [option])
const [idle, setIdle] = useIdle([value])

useAdjustLabel(ref)

const checked = !isNil(value)

const classnames = classNames('checkbox', {
Expand All @@ -34,14 +44,20 @@ const CheckboxInput = ({ question, value, option, optionIndex, disabled, onCreat
}

const handleChange = () => {
if (checked) {
onDelete(value)
} else {
handleCreate(option, optionIndex)
if (idle) {
setIdle(false)
handleAdditionalInputChange.cancel()

if (checked) {
onDelete(value)
setAdditionalInput(option, '')
} else {
handleCreate(option, optionIndex, getAdditionalInput(option))
}
}
}

const handleAdditionalValueChange = useDebouncedCallback((value, option, additionalInput) => {
const handleAdditionalInputChange = useDebouncedCallback((value, option, additionalInput) => {
if (checked) {
if (option.has_provider) {
onUpdate(value, {
Expand All @@ -64,7 +80,7 @@ const CheckboxInput = ({ question, value, option, optionIndex, disabled, onCreat
}, 500)

return (
<div className={classnames}>
<div ref={ref} className={classnames}>
<label>
<input
type="checkbox"
Expand All @@ -83,7 +99,15 @@ const CheckboxInput = ({ question, value, option, optionIndex, disabled, onCreat
option.additional_input == 'text' && (
<>
<span>:</span>
<AdditionalTextInput className="ml-10" value={value} option={option} disabled={disabled} onChange={handleAdditionalValueChange} />
<AdditionalTextInput
className="ml-10"
inputValue={getAdditionalInput(option)}
disabled={disabled}
onChange={(additionalInput) => {
setAdditionalInput(option, additionalInput)
handleAdditionalInputChange(value, option, additionalInput)
}}
/>
<OptionHelp className="ml-10" option={option} />
</>
)
Expand All @@ -92,7 +116,14 @@ const CheckboxInput = ({ question, value, option, optionIndex, disabled, onCreat
option.additional_input == 'textarea' && (
<>
<span>:</span>
<AdditionalTextareaInput value={value} option={option} disabled={disabled} onChange={handleAdditionalValueChange} />
<AdditionalTextareaInput
inputValue={getAdditionalInput(option)}
disabled={disabled}
onChange={(additionalInput) => {
setAdditionalInput(option, additionalInput)
handleAdditionalInputChange(value, option, additionalInput)
}}
/>
<div>
<OptionHelp option={option} />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react'
import PropTypes from 'prop-types'


import { getQuestionTextId, getQuestionHelpId } from '../../../utils/question'
import { gatherOptions } from '../../../utils/options'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react'
import React, { useRef } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import { useDebouncedCallback } from 'use-debounce'
Expand All @@ -7,13 +7,24 @@ import { isEmpty } from 'lodash'
import { getQuestionTextId, getQuestionHelpId } from '../../../utils/question'
import { isDefaultValue } from '../../../utils/value'

import useAdditionalInputs from '../../../hooks/useAdditionalInputs'
import useAdjustLabel from '../../../hooks/useAdjustLabel'

import AdditionalTextInput from './common/AdditionalTextInput'
import AdditionalTextareaInput from './common/AdditionalTextareaInput'
import OptionHelp from './common/OptionHelp'
import OptionText from './common/OptionText'

const RadioInput = ({ question, value, options, disabled, updateValue, buttons }) => {

const ref = useRef(null)
const [getAdditionalInput, setAdditionalInput] = useAdditionalInputs(value, options)

useAdjustLabel(ref)

const handleChange = (option) => {
handleAdditionalInputChange.cancel()

if (option.has_provider) {
updateValue(value, {
text: option.text,
Expand All @@ -23,15 +34,15 @@ const RadioInput = ({ question, value, options, disabled, updateValue, buttons }
})
} else {
updateValue(value, {
text: option.default_text || '',
text: getAdditionalInput(option) || option.default_text || '',
option: option.id,
unit: question.unit,
value_type: question.value_type
})
}
}

const handleAdditionalValueChange = useDebouncedCallback((value, option, additionalInput) => {
const handleAdditionalInputChange = useDebouncedCallback((value, option, additionalInput) => {
updateValue(value, {
option: option.id,
text: additionalInput,
Expand All @@ -46,7 +57,7 @@ const RadioInput = ({ question, value, options, disabled, updateValue, buttons }
})

return (
<div className="interview-input radio-input">
<div ref={ref} className="interview-input radio-input">
<div className="buttons-wrapper">
{buttons}
<fieldset className={classnames}
Expand Down Expand Up @@ -78,10 +89,12 @@ const RadioInput = ({ question, value, options, disabled, updateValue, buttons }
<span>:</span>
<AdditionalTextInput
className="ml-10"
value={value}
option={option}
inputValue={getAdditionalInput(option)}
disabled={disabled}
onChange={handleAdditionalValueChange}
onChange={(additionalInput) => {
setAdditionalInput(option, additionalInput)
handleAdditionalInputChange(value, option, additionalInput)
}}
/>
<OptionHelp className="ml-10" option={option} />
</>
Expand All @@ -92,10 +105,12 @@ const RadioInput = ({ question, value, options, disabled, updateValue, buttons }
<>
<span>:</span>
<AdditionalTextareaInput
value={value}
option={option}
inputValue={getAdditionalInput(option)}
disabled={disabled}
onChange={handleAdditionalValueChange}
onChange={(additionalInput) => {
setAdditionalInput(option, additionalInput)
handleAdditionalInputChange(value, option, additionalInput)
}}
/>
<div>
<OptionHelp option={option} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,23 @@
import React, { useState, useEffect } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { get, isNil } from 'lodash'

const AdditionalTextInput = ({ className, value, option, disabled, onChange }) => {
const [inputValue, setInputValue] = useState('')

useEffect(() => {
if (isNil(value)) {
setInputValue('')
} else {
setInputValue(value.option == option.id ? value.text : '')
}
}, [get(value, 'id'), get(value, 'text'), get(value, 'option'), get(value, 'external_id')])
import classNames from 'classnames'

const AdditionalTextInput = ({ className, inputValue, disabled, onChange }) => {
return (
<span className={className}>
<input
type="text"
className="form-control input-sm"
disabled={disabled}
aria-label={gettext('Additional input')}
value={inputValue}
onChange={(event) => {
setInputValue(event.target.value)
onChange(value, option, event.target.value)
}}
/>
</span>
<input
type="text"
className={classNames('form-control input-sm', className)}
disabled={disabled}
aria-label={gettext('Additional input')}
value={inputValue}
onChange={(event) => onChange(event.target.value)}
/>
)
}

AdditionalTextInput.propTypes = {
className: PropTypes.string,
value: PropTypes.object,
option: PropTypes.object.isRequired,
inputValue: PropTypes.string,
disabled: PropTypes.bool,
onChange: PropTypes.func.isRequired
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,23 @@
import React, { useState, useEffect } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { get, isNil } from 'lodash'

const AdditionalTextareaInput = ({ value, option, disabled, onChange }) => {
const [inputValue, setInputValue] = useState('')

useEffect(() => {
if (isNil(value)) {
setInputValue('')
} else {
setInputValue(value.option == option.id ? value.text : '')
}
}, [get(value, 'id'), get(value, 'text'), get(value, 'option'), get(value, 'external_id')])
import classNames from 'classnames'

const AdditionalTextareaInput = ({ className, inputValue, disabled, onChange }) => {
return (
<textarea
rows={4}
className="form-control input-sm"
className={classNames('form-control input-sm', className)}
disabled={disabled}
aria-label={gettext('Additional input')}
value={inputValue}
onChange={(event) => {
setInputValue(event.target.value)
onChange(value, option, event.target.value)
}}
onChange={(event) => onChange(event.target.value)}
/>
)
}

AdditionalTextareaInput.propTypes = {
value: PropTypes.object,
option: PropTypes.object.isRequired,
className: PropTypes.string,
inputValue: PropTypes.string,
disabled: PropTypes.bool,
onChange: PropTypes.func.isRequired
}
Expand Down
28 changes: 28 additions & 0 deletions rdmo/projects/assets/js/interview/hooks/useAdditionalInputs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useState, useEffect } from 'react'
import { get, isNil } from 'lodash'

const useAdditionalInputs = (value, options) => {
const [additionalInputs, setAdditionalInputs] = useState({})

useEffect(() => {
if (isNil(value)) {
setAdditionalInputs({})
} else {
setAdditionalInputs(options.reduce((additionalInputs, option) => {
if (value.option == option.id) {
additionalInputs[option.id] = value.text
}
return additionalInputs
}, {}))
}
}, [get(value, 'id'), get(value, 'text'), get(value, 'option'), get(value, 'external_id')])

return [
(option) => additionalInputs[option.id] || '',
(option, additionalInput) => setAdditionalInputs({
...additionalInputs, [option.id]: additionalInput
})
]
}

export default useAdditionalInputs
23 changes: 23 additions & 0 deletions rdmo/projects/assets/js/interview/hooks/useAdjustLabel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useEffect } from 'react'

const useAdjustLabel = (ref) => {
useEffect(() => {
if (ref.current) {
// find the .buttons node and get it's width
const buttonsWidth = ref.current.classList.contains('checkbox') ? (
// for the checkbox widget, the parent of the parent is the buttons-wrapper
ref.current.parentElement.parentElement.querySelector('.buttons').offsetWidth
) : (
ref.current.querySelector('.buttons').offsetWidth
)

// find the first label for a radio or checkbox and adjust the right padding
const label = ref.current.querySelector('.radio:first-child label, .checkbox:first-child label')
if (label) {
label.style.paddingRight = `${label.style.paddingRight + buttonsWidth}px`
}
}
}, [ref.current])
}

export default useAdjustLabel
11 changes: 11 additions & 0 deletions rdmo/projects/assets/js/interview/hooks/useIdle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useState, useEffect } from 'react'

const useIdle = (dependencies) => {
const [idle, setIdle] = useState(true)

useEffect(() => setIdle(true), dependencies)

return [idle, setIdle]
}

export default useIdle
Loading