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

Suppression de redux-form #671

Merged
merged 7 commits into from Sep 23, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions .eslintrc.yaml
@@ -1,7 +1,4 @@
rules:
linebreak-style:
- 2
- unix
quotes:
- 1 # While https://github.com/eslint/eslint/issues/9662#issuecomment-353958854 we don't enforce this
- single
Expand All @@ -14,10 +11,13 @@ rules:
react/jsx-no-target-blank: 0
react/no-unescaped-entities: 0
react/display-name: 1
react-hooks/rules-of-hooks: error
react-hooks/exhaustive-deps: warn
parser: babel-eslint

plugins:
- react
- react-hooks
- flowtype
env:
browser: true
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -123,6 +123,7 @@
"eslint-config-prettier": "^4.0.0",
"eslint-plugin-flowtype": "^3.2.1",
"eslint-plugin-react": "^7.12.4",
"eslint-plugin-react-hooks": "^2.0.1",
"express": "^4.16.3",
"file-loader": "^1.1.11",
"flow-bin": "^0.92.0",
Expand Down
1 change: 1 addition & 0 deletions source/components/AttachDictionary.js
Expand Up @@ -8,6 +8,7 @@ import Overlay from './Overlay'
// Il suffit à la section d'appeler une fonction fournie en lui donnant du JSX
export let AttachDictionary = dictionary => Decorated =>
function withDictionary(props) {
// eslint-disable-next-line react-hooks/rules-of-hooks
const [{ explanation, term }, setState] = useState({
term: null,
explanation: null
Expand Down
36 changes: 19 additions & 17 deletions source/components/CurrencyInput/CurrencyInput.js
@@ -1,5 +1,5 @@
import classnames from 'classnames'
import React, { useEffect, useRef, useState } from 'react'
import React, { useRef, useState } from 'react'
import NumberFormat from 'react-number-format'
import { debounce } from '../../utils'
import './CurrencyInput.css'
Expand All @@ -22,23 +22,27 @@ let currencyFormat = language => ({
})

export default function CurrencyInput({
value: valueArg,
value: valueProp = '',
debounce: debounceTimeout,
onChange,
language,
className,
...forwardedProps
}) {
const [currentValue, setCurrentValue] = useState(valueArg)
const [initialValue] = useState(valueArg)
// When the component is rendered with a new "value" argument, we update our local state
useEffect(() => {
setCurrentValue(valueArg)
}, [valueArg])
const nextValue = useRef(null)
const [initialValue, setInitialValue] = useState(valueProp)
const [currentValue, setCurrentValue] = useState(valueProp)
const onChangeDebounced = useRef(
debounceTimeout ? debounce(debounceTimeout, onChange) : onChange
)
// We need some mutable reference because the <NumberFormat /> component doesn't provide
// the DOM `event` in its custom `onValueChange` handler
const nextValue = useRef(null)

// When the component is rendered with a new "value" prop, we reset our local state
if (valueProp !== initialValue) {
setCurrentValue(valueProp)
setInitialValue(valueProp)
}

const handleChange = event => {
// Only trigger the `onChange` event if the value has changed -- and not
Expand All @@ -61,19 +65,17 @@ export default function CurrencyInput({
thousandSeparator,
decimalSeparator
} = currencyFormat(language)

// We display negative numbers iff this was the provided value (but we allow the user to enter them)
// We display negative numbers iff this was the provided value (but we disallow the user to enter them)
const valueHasChanged = currentValue !== initialValue

// Autogrow the input
const valueLength = (currentValue || '').toString().length
const valueLength = currentValue.toString().length
const width = `${5 + (valueLength - 5) * 0.75}em`

return (
<div
className={classnames(className, 'currencyInput__container')}
{...(valueLength > 5
? { style: { width: `${5 + (valueLength - 5) * 0.75}em` } }
: {})}>
{...(valueLength > 5 ? { style: { width } } : {})}>
{isCurrencyPrefixed && '€'}
<NumberFormat
{...forwardedProps}
Expand All @@ -84,10 +86,10 @@ export default function CurrencyInput({
inputMode="numeric"
onValueChange={({ value }) => {
setCurrentValue(value)
nextValue.current = value.toString().replace(/^\-/, '')
nextValue.current = value.toString().replace(/^-/, '')
}}
onChange={handleChange}
value={(currentValue || '').toString().replace('.', decimalSeparator)}
value={currentValue.toString().replace('.', decimalSeparator)}
/>
{!isCurrencyPrefixed && <>&nbsp;€</>}
</div>
Expand Down
3 changes: 2 additions & 1 deletion source/components/PercentageField.js
Expand Up @@ -4,7 +4,8 @@ import './PercentageField.css'
export default function PercentageField({ onChange, value, debounce }) {
const [localValue, setLocalValue] = useState(value)
const debouncedOnChange = useCallback(
debounce ? debounce(debounce, onChange) : onChange
debounce ? debounce(debounce, onChange) : onChange,
[debounce, onChange]
)

return (
Expand Down
23 changes: 13 additions & 10 deletions source/components/PeriodSwitch.js
@@ -1,5 +1,5 @@
import { findRuleByDottedName } from 'Engine/rules'
import React, { useEffect } from 'react'
import React, { useCallback, useEffect } from 'react'
import { Trans } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import {
Expand All @@ -15,17 +15,20 @@ export default function PeriodSwitch() {
const initialPeriod = useSelector(
state => state.simulation?.config?.situation?.période
)
const currentPeriod = situation.période
useEffect(() => {
!currentPeriod && updatePeriod(initialPeriod || 'année')
}, [])
const currentPeriod = situation.période
const updatePeriod = toPeriod => {
const needConversion = Object.keys(situation).filter(dottedName => {
const rule = findRuleByDottedName(rules, dottedName)
return rule?.période === 'flexible'
})
dispatch({ type: 'UPDATE_PERIOD', toPeriod, needConversion })
}
}, [currentPeriod, initialPeriod, updatePeriod])
const updatePeriod = useCallback(
toPeriod => {
const needConversion = Object.keys(situation).filter(dottedName => {
const rule = findRuleByDottedName(rules, dottedName)
return rule?.période === 'flexible'
})
dispatch({ type: 'UPDATE_PERIOD', toPeriod, needConversion })
},
[dispatch, rules, situation]
)
const periods = ['mois', 'année']

return (
Expand Down
43 changes: 19 additions & 24 deletions source/components/SearchBar.js
Expand Up @@ -2,7 +2,7 @@ import withSitePaths from 'Components/utils/withSitePaths'
import { encodeRuleName } from 'Engine/rules.js'
import Fuse from 'fuse.js'
import { compose, pick, sortBy } from 'ramda'
import React, { useRef, useState } from 'react'
import React, { useMemo, useRef, useState } from 'react'
import Highlighter from 'react-highlight-words'
import { useTranslation } from 'react-i18next'
import { Link, Redirect } from 'react-router-dom'
Expand All @@ -19,28 +19,23 @@ function SearchBar({
const [inputValue, setInputValue] = useState(null)
const [selectedOption, setSelectedOption] = useState(null)
const inputElementRef = useRef()
const fuse = useRef()
const { i18n } = useTranslation()

const options = {
keys: [
{ name: 'name', weight: 0.3 },
{ name: 'title', weight: 0.3 },
{ name: 'espace', weight: 0.2 },
{ name: 'description', weight: 0.2 }
]
}
if (!fuse.current) {
// This operation is expensive, we don't want to do it everytime we re-render, so we cache its result in a reference
fuse.current = new Fuse(
rules.map(pick(['title', 'espace', 'description', 'name', 'dottedName'])),
options
// This operation is expensive, we don't want to do it everytime we re-render, so we cache its result
const fuse = useMemo(() => {
mquandalle marked this conversation as resolved.
Show resolved Hide resolved
const list = rules.map(
pick(['title', 'espace', 'description', 'name', 'dottedName'])
)
}
const options = {
keys: [
{ name: 'name', weight: 0.3 },
{ name: 'title', weight: 0.3 },
{ name: 'espace', weight: 0.2 },
{ name: 'description', weight: 0.2 }
]
}
return new Fuse(list, options)
}, [rules])
const { i18n } = useTranslation()

const handleChange = selectedOption => {
setSelectedOption(selectedOption)
}
const renderOption = ({ title, dottedName }) => (
<span>
<Highlighter searchWords={[inputValue]} textToHighlight={title} />
Expand All @@ -49,7 +44,7 @@ function SearchBar({
</span>
</span>
)
const filterOptions = (options, filter) => fuse.current.search(filter)
const filterOptions = (options, filter) => fuse.search(filter)

if (selectedOption != null) {
finallyCallback && finallyCallback()
Expand All @@ -68,8 +63,8 @@ function SearchBar({
<>
<Select
value={selectedOption && selectedOption.dottedName}
onChange={handleChange}
onInputChange={inputValue => setInputValue(inputValue)}
onChange={setSelectedOption}
onInputChange={setInputValue}
valueKey="dottedName"
labelKey="title"
options={rules}
Expand Down
15 changes: 7 additions & 8 deletions source/components/SearchButton.js
Expand Up @@ -14,21 +14,20 @@ export default compose(
)(function SearchButton({ flatRules, invisibleButton }) {
const [visible, setVisible] = useState(false)
useEffect(() => {
const handleKeyDown = e => {
if (!(e.ctrlKey && e.key === 'k')) return
setVisible(true)
e.preventDefault()
e.stopPropagation()
return false
}
window.addEventListener('keydown', handleKeyDown)

return () => {
window.removeEventListener('keydown', handleKeyDown)
}
}, [])

const handleKeyDown = e => {
if (!(e.ctrlKey && e.key === 'k')) return
setVisible(true)
e.preventDefault()
e.stopPropagation()
return false
}

const close = () => setVisible(false)

return visible ? (
Expand Down
24 changes: 9 additions & 15 deletions source/components/TargetSelection.js
Expand Up @@ -41,8 +41,14 @@ export default compose(
const situation = useSituation()
const dispatch = useDispatch()

const targets =
analysis?.targets.filter(
t =>
!secondaryObjectives.includes(t.dottedName) &&
t.dottedName !== 'contrat salarié . aides employeur'
) || []

useEffect(() => {
let targets = getTargets()
// Initialize defaultValue for target that can't be computed
targets
.filter(
Expand All @@ -64,22 +70,10 @@ export default compose(
)
})

if (initialRender) {
setInitialRender(false)
}
setInitialRender(false)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

const getTargets = () => {
if (!analysis) return []
return analysis.targets.filter(
t =>
!secondaryObjectives.includes(t.dottedName) &&
t.dottedName !== 'contrat salarié . aides employeur'
)
}

let targets = getTargets()

return (
<div id="targetSelection">
{(typeof objectifs[0] === 'string' ? [{ objectifs }] : objectifs).map(
Expand Down
11 changes: 7 additions & 4 deletions source/components/conversation/Question.js
Expand Up @@ -38,10 +38,13 @@ export default compose(
value: currentValue
}) {
const [touched, setTouched] = useState(false)
const onChange = useCallback(value => {
setFormValue(value)
setTouched(true)
})
const onChange = useCallback(
value => {
setFormValue(value)
setTouched(true)
},
[setFormValue]
)

const renderBinaryQuestion = () => {
return (
Expand Down
23 changes: 12 additions & 11 deletions source/components/conversation/SendButton.js
@@ -1,28 +1,29 @@
import React, { useEffect } from 'react'
import React, { useCallback, useEffect } from 'react'
import { Trans } from 'react-i18next'

export default function SendButton({ disabled, submit }) {
const getAction = useCallback(cause => (!disabled ? submit(cause) : null), [
disabled,
submit
])
useEffect(() => {
const handleKeyDown = ({ key }) => {
if (key !== 'Enter') return
getAction('enter')
}

window.addEventListener('keydown', handleKeyDown)
return () => {
window.removeEventListener('keydown', handleKeyDown)
}
}, [])

const getAction = () => {
return cause => (!disabled ? submit(cause) : null)
}
}, [getAction])

const handleKeyDown = ({ key }) => {
if (key !== 'Enter') return
getAction()('enter')
}
return (
<button
className="ui__ button plain"
css="margin-left: 1.2rem"
disabled={disabled}
onClick={() => getAction()('accept')}>
onClick={() => getAction('accept')}>
<span className="text">
<Trans>Suivant</Trans> →
</span>
Expand Down
2 changes: 1 addition & 1 deletion source/components/ui/AnimatedTargetValue.js
Expand Up @@ -17,7 +17,7 @@ export default function AnimatedTargetValue({ value }: Props) {
}
setDifference((value || 0) - (previousValue || 0))
setPreviousValue(value)
}, [value])
}, [previousValue, value])
const { i18n } = useTranslation()

const format = value => {
Expand Down
3 changes: 3 additions & 0 deletions source/components/ui/animate.js
Expand Up @@ -131,7 +131,10 @@ export function appear({
delay = 0,
style
}) {
// TODO: We should rename this function Appear
// eslint-disable-next-line react-hooks/rules-of-hooks
const [show, setShow] = useState(unless)
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
window.setTimeout(() => setShow(true), 0)
}, [])
Expand Down
2 changes: 1 addition & 1 deletion source/engine/uniroot.js
Expand Up @@ -79,7 +79,7 @@ export default function uniroot(

if (
p < 0.75 * cb * q - Math.abs(tol_act * q) / 2 &&
p < Math.abs(prev_step * q / 2)
p < Math.abs((prev_step * q) / 2)
mquandalle marked this conversation as resolved.
Show resolved Hide resolved
) {
// If (b + p / q) falls in [b,c] and isn't too large it is accepted
new_step = p / q
Expand Down