🐛 répare le style des questions des simulateurs
parent
cc51cd9b03
commit
1f87e1b90f
|
@ -180,13 +180,15 @@ const Target = ({ target, initialRender }) => {
|
|||
</div>
|
||||
{isActiveInput && (
|
||||
<Animate.fromTop>
|
||||
<InputSuggestions
|
||||
suggestions={target.suggestions}
|
||||
onFirstClick={value => {
|
||||
dispatch(updateSituation(target.dottedName, value))
|
||||
}}
|
||||
unit={target.defaultUnit}
|
||||
/>
|
||||
<div css="display: flex; justify-content: flex-end">
|
||||
<InputSuggestions
|
||||
suggestions={target.suggestions}
|
||||
onFirstClick={value => {
|
||||
dispatch(updateSituation(target.dottedName, value))
|
||||
}}
|
||||
unit={target.defaultUnit}
|
||||
/>
|
||||
</div>
|
||||
</Animate.fromTop>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -7,7 +7,11 @@ import emoji from 'react-easy-emoji'
|
|||
import { Trans } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { RootState } from 'Reducers/rootReducer'
|
||||
import { currentQuestionSelector, flatRulesSelector, nextStepsSelector } from 'Selectors/analyseSelectors'
|
||||
import {
|
||||
currentQuestionSelector,
|
||||
flatRulesSelector,
|
||||
nextStepsSelector
|
||||
} from 'Selectors/analyseSelectors'
|
||||
import * as Animate from 'Ui/animate'
|
||||
import Aide from './Aide'
|
||||
import './conversation.css'
|
||||
|
@ -41,17 +45,15 @@ export default function Conversation({ customEndMessages }: ConversationProps) {
|
|||
}
|
||||
}
|
||||
const DecoratedInputComponent = FormDecorator(InputComponent)
|
||||
return nextSteps.length ? (
|
||||
|
||||
return flatRules && nextSteps.length ? (
|
||||
<>
|
||||
<Aide />
|
||||
<div tabIndex={0} style={{ outline: 'none' }} onKeyDown={handleKeyDown}>
|
||||
{currentQuestion && (
|
||||
<React.Fragment key={currentQuestion}>
|
||||
<Animate.fadeIn>
|
||||
<DecoratedInputComponent
|
||||
rules={flatRules}
|
||||
dottedName={currentQuestion}
|
||||
/>
|
||||
<DecoratedInputComponent dottedName={currentQuestion} />
|
||||
</Animate.fadeIn>
|
||||
<div className="ui__ answer-group">
|
||||
{previousAnswers.length > 0 && (
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import { updateSituation } from 'Actions/actions'
|
||||
import Explicable from 'Components/conversation/Explicable'
|
||||
import { serializeUnit } from 'Engine/units'
|
||||
import { findRuleByDottedName } from 'Engine/rules'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { situationSelector } from 'Selectors/analyseSelectors'
|
||||
import {
|
||||
flatRulesSelector,
|
||||
situationSelector
|
||||
} from 'Selectors/analyseSelectors'
|
||||
|
||||
/*
|
||||
This higher order component wraps "Form" components (e.g. Question.js), that represent user inputs,
|
||||
|
@ -15,43 +18,37 @@ to understand those precious higher order components.
|
|||
*/
|
||||
|
||||
export default function FormDecorator(RenderField) {
|
||||
return function FormStep({
|
||||
fieldName,
|
||||
question,
|
||||
inversion,
|
||||
unit,
|
||||
...otherProps
|
||||
}) {
|
||||
return function FormStep({ dottedName }) {
|
||||
const dispatch = useDispatch()
|
||||
const situation = useSelector(situationSelector)
|
||||
const flatRules = useSelector(flatRulesSelector)
|
||||
|
||||
const language = useTranslation().i18n.language
|
||||
const submit = source =>
|
||||
dispatch({
|
||||
type: 'STEP_ACTION',
|
||||
name: 'fold',
|
||||
step: fieldName,
|
||||
step: dottedName,
|
||||
source
|
||||
})
|
||||
const setFormValue = value => {
|
||||
dispatch(updateSituation(fieldName, value))
|
||||
dispatch(updateSituation(dottedName, value))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="step">
|
||||
<div className="unfoldedHeader">
|
||||
<h3>
|
||||
{question} {!inversion && <Explicable dottedName={fieldName} />}
|
||||
</h3>
|
||||
</div>
|
||||
<h3>
|
||||
{findRuleByDottedName(flatRules, dottedName).question}{' '}
|
||||
<Explicable dottedName={dottedName} />
|
||||
</h3>
|
||||
|
||||
<fieldset>
|
||||
<RenderField
|
||||
{...otherProps}
|
||||
name={fieldName}
|
||||
value={situation[fieldName]}
|
||||
dottedName={dottedName}
|
||||
value={situation[dottedName]}
|
||||
onChange={setFormValue}
|
||||
onSubmit={submit}
|
||||
unit={serializeUnit(unit, situation[fieldName], language)}
|
||||
rules={flatRules}
|
||||
/>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { ThemeColorsContext } from 'Components/utils/colors'
|
||||
import { currencyFormat } from 'Engine/format'
|
||||
import { serializeUnit } from 'Engine/units'
|
||||
import React, { useCallback, useContext } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import NumberFormat from 'react-number-format'
|
||||
|
@ -49,7 +50,7 @@ export default function Input({
|
|||
autoComplete="off"
|
||||
/>
|
||||
<label className="suffix" htmlFor={'step-' + dottedName}>
|
||||
{unit}
|
||||
{serializeUnit(unit, value, language)}
|
||||
</label>
|
||||
{onSubmit && (
|
||||
<SendButton disabled={value === undefined} onSubmit={onSubmit} />
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
.binaryQuestionList {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
import classnames from 'classnames'
|
||||
import { ThemeColorsContext } from 'Components/utils/colors'
|
||||
import { is } from 'ramda'
|
||||
import React, { useCallback, useContext, useState } from 'react'
|
||||
import React, { useCallback, useContext } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import Explicable from './Explicable'
|
||||
import './Question.css'
|
||||
import SendButton from './SendButton'
|
||||
|
||||
/* Ceci est une saisie de type "radio" : l'utilisateur choisit une réponse dans une liste, ou une liste de listes.
|
||||
|
@ -23,8 +22,6 @@ import SendButton from './SendButton'
|
|||
|
||||
*/
|
||||
|
||||
// FormDecorator permet de factoriser du code partagé par les différents types de saisie,
|
||||
// dont Question est un example
|
||||
export default function Question({
|
||||
choices,
|
||||
onSubmit,
|
||||
|
@ -33,34 +30,28 @@ export default function Question({
|
|||
value: currentValue
|
||||
}) {
|
||||
const colors = useContext(ThemeColorsContext)
|
||||
const [touched, setTouched] = useState(false)
|
||||
const handleChange = useCallback(
|
||||
value => {
|
||||
onChange(value)
|
||||
setTouched(true)
|
||||
},
|
||||
[onChange]
|
||||
)
|
||||
|
||||
const renderBinaryQuestion = () => {
|
||||
return (
|
||||
<div className="binaryQuestionList">
|
||||
{choices.map(({ value, label }) => (
|
||||
<RadioLabel
|
||||
key={value}
|
||||
{...{
|
||||
value,
|
||||
css: 'margin-right: 0.6rem',
|
||||
label,
|
||||
currentValue,
|
||||
onSubmit,
|
||||
colors,
|
||||
onChange: handleChange
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
return choices.map(({ value, label }) => (
|
||||
<RadioLabel
|
||||
key={value}
|
||||
{...{
|
||||
value,
|
||||
css: 'margin-right: 0.6rem',
|
||||
label,
|
||||
currentValue,
|
||||
onSubmit,
|
||||
colors,
|
||||
onChange: handleChange
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
const renderChildren = choices => {
|
||||
// seront stockées ainsi dans le state :
|
||||
|
@ -122,7 +113,7 @@ export default function Question({
|
|||
css="margin-top: 0.6rem; display: flex; align-items: center; flex-wrap: wrap;"
|
||||
>
|
||||
{choiceElements}
|
||||
{onSubmit && <SendButton disabled={!touched} onSubmit={onSubmit} />}
|
||||
{onSubmit && <SendButton disabled={!currentValue} onSubmit={onSubmit} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -154,7 +145,9 @@ function RadioLabelContent({
|
|||
key={value}
|
||||
style={labelStyle}
|
||||
css={css}
|
||||
className={classnames('radio', 'userAnswerButton', { selected })}
|
||||
className={classnames('radio', 'userAnswerButton', 'ui__', 'button', {
|
||||
selected
|
||||
})}
|
||||
>
|
||||
<Trans>{label}</Trans>
|
||||
<input
|
||||
|
|
|
@ -25,7 +25,7 @@ export default function SendButton({ disabled, onSubmit }: SendButtonProps) {
|
|||
|
||||
return (
|
||||
<button
|
||||
className="ui__ button plain"
|
||||
className="ui__ plain button "
|
||||
css="margin-left: 1.2rem"
|
||||
disabled={disabled}
|
||||
onClick={() => getAction('accept')}
|
||||
|
|
|
@ -1,86 +1,3 @@
|
|||
.scrollIndication {
|
||||
margin: 0.6em 0;
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
.scrollIndication.down {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#resultsScrollElement,
|
||||
#myScrollToElement {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#foldedSteps {
|
||||
padding: 1em 0;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#foldedSteps .header button {
|
||||
display: block;
|
||||
margin: 0 auto 1em;
|
||||
}
|
||||
#foldedSteps button i {
|
||||
margin-right: 0.3em;
|
||||
font-size: 110%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#foldedSteps button {
|
||||
border: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#myScrollToElement {
|
||||
padding-top: 0.3em;
|
||||
}
|
||||
|
||||
.step {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.step:first-child {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.unfoldedHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.step.completed .edit:hover {
|
||||
background: #333350;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Our little help icon */
|
||||
.help-button {
|
||||
display: inline-block;
|
||||
margin-top: 0.5em;
|
||||
line-height: 1.1em;
|
||||
border-radius: 1em;
|
||||
font-size: 90%;
|
||||
color: #777;
|
||||
border: 1px solid;
|
||||
background: none;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
color: #aaa;
|
||||
text-transform: uppercase;
|
||||
font-size: 60%;
|
||||
padding: 0.25em 0.6em;
|
||||
}
|
||||
.help-button:hover {
|
||||
color: #333;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.step fieldset {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
@ -92,8 +9,9 @@
|
|||
list-style-type: none;
|
||||
}
|
||||
|
||||
.step fieldset > ul:not(.binaryQuestionList) {
|
||||
width: 100%;
|
||||
.step fieldset .step.question .variantLeaf,
|
||||
.step fieldset .step.question {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.step.question .variant {
|
||||
|
@ -141,17 +59,6 @@
|
|||
font-size: 120%;
|
||||
}
|
||||
|
||||
.resume {
|
||||
transition: 1s display;
|
||||
}
|
||||
|
||||
.answer-ignored {
|
||||
font-size: 80%;
|
||||
opacity: 0.8;
|
||||
margin-left: 0.4em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.step.question input[type='radio'] {
|
||||
display: none;
|
||||
}
|
||||
|
@ -203,175 +110,25 @@
|
|||
outline: none;
|
||||
}
|
||||
|
||||
.help-box {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.help-box p {
|
||||
padding: 1em;
|
||||
font-size: 90%;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.close-help {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
font-size: 105%;
|
||||
}
|
||||
.close-text {
|
||||
text-transform: uppercase;
|
||||
font-size: 60%;
|
||||
}
|
||||
|
||||
.close-text .icon {
|
||||
font-size: 150%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.step .send {
|
||||
padding: 0.1em 0.4em 0em 1em;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
border: 1px solid;
|
||||
border-radius: 0.4em;
|
||||
line-height: 0em;
|
||||
}
|
||||
|
||||
.step .send:disabled {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.step .send i {
|
||||
margin: 0 0.3em;
|
||||
font-size: 160%;
|
||||
}
|
||||
|
||||
.step .send .text {
|
||||
text-transform: uppercase;
|
||||
font-size: 135%;
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
.answer {
|
||||
margin-top: 0.6rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.foldedQuestion .answer {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.step-input-error {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: -1.5em;
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
color: #c0392b;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.step textarea {
|
||||
vertical-align: middle;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
#share-link {
|
||||
color: white;
|
||||
padding: 0.3em 0.3em;
|
||||
display: inline-block;
|
||||
margin-top: 0.3em;
|
||||
border-radius: 0.25em;
|
||||
}
|
||||
#share-icon {
|
||||
font-size: 200%;
|
||||
vertical-align: middle;
|
||||
line-height: 0em;
|
||||
margin-left: 0.3em;
|
||||
}
|
||||
|
||||
.info-zone {
|
||||
font-size: 65%;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
color: #666;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
|
||||
.input-tip {
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
.input-tip p {
|
||||
margin: 0.1em;
|
||||
}
|
||||
|
||||
#show-advanced {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Positioning the animated elements absolutely + transition-delay will make it possible
|
||||
for the appearing element to appear without stacking up below the first one */
|
||||
|
||||
.input-tip {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.foldedQuestion {
|
||||
margin-left: 0em;
|
||||
}
|
||||
|
||||
.foldedQuestion .edit {
|
||||
vertical-align: middle;
|
||||
margin-left: 0.5em;
|
||||
border: none;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.foldedQuestion .borderWrapper {
|
||||
padding: 0.1em 0;
|
||||
display: inline-block;
|
||||
width: calc(100% - 8em);
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.foldedQuestion:last-of-type .borderWrapper {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.conversationContainer {
|
||||
flex: 1;
|
||||
padding-bottom: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.step label.userAnswerButton {
|
||||
border: 1px solid var(--color);
|
||||
border: 1px solid var(--color) !important;
|
||||
text-transform: none !important;
|
||||
background-color: white;
|
||||
color: var(--textColorOnWhite);
|
||||
}
|
||||
.step label.userAnswerButton.selected {
|
||||
background: var(--color);
|
||||
border: 1px solid var(--color);
|
||||
color: var(--textColor);
|
||||
}
|
||||
@media (hover) {
|
||||
.step label.userAnswerButton:hover {
|
||||
.step label.userAnswerButton:hover:not(.selected) {
|
||||
background: var(--color);
|
||||
border: 1px solid var(--color);
|
||||
color: var(--textColor);
|
||||
transition: all 0.05s;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ async function searchCommunes(input) {
|
|||
return json
|
||||
}
|
||||
|
||||
export default function Select({ setFormValue, submit }) {
|
||||
export default function Select({ onChange, onSubmit }) {
|
||||
const [searchResults, setSearchResults] = useState()
|
||||
const [isLoading, setLoadingState] = useState(false)
|
||||
|
||||
|
@ -47,7 +47,7 @@ export default function Select({ setFormValue, submit }) {
|
|||
tauxVersementTransport(option.code)
|
||||
.then(({ taux }) => {
|
||||
// serialize to not mix our data schema and the API response's
|
||||
setFormValue(
|
||||
onChange(
|
||||
JSON.stringify({
|
||||
...option,
|
||||
...(taux != undefined
|
||||
|
@ -57,15 +57,15 @@ export default function Select({ setFormValue, submit }) {
|
|||
: {})
|
||||
})
|
||||
)
|
||||
submit()
|
||||
onSubmit()
|
||||
})
|
||||
.catch(error => {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log(
|
||||
'Erreur dans la récupération du taux de versement transport à partir du code commune',
|
||||
error
|
||||
) || setFormValue(JSON.stringify({ option }))
|
||||
submit() // eslint-disable-line no-console
|
||||
) || onChange(JSON.stringify({ option }))
|
||||
onSubmit() // eslint-disable-line no-console
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,12 @@ import { Trans, useTranslation } from 'react-i18next'
|
|||
import Worker from 'worker-loader!./SelectTauxRisque.worker.js'
|
||||
const worker = new Worker()
|
||||
|
||||
function SelectComponent({ setFormValue, submit, options }) {
|
||||
function SelectComponent({ onChange, onSubmit, options }) {
|
||||
const [searchResults, setSearchResults] = useState()
|
||||
let submitOnChange = option => {
|
||||
option.text = +option['Taux net'].replace(',', '.')
|
||||
setFormValue(option.text)
|
||||
submit()
|
||||
onChange(option.text)
|
||||
onSubmit()
|
||||
}
|
||||
const { t } = useTranslation()
|
||||
useEffect(() => {
|
||||
|
|
|
@ -14,8 +14,13 @@ export const binaryOptionChoices = [
|
|||
// This function takes the unknown rule and finds which React component should be displayed to get a user input through successive if statements
|
||||
// That's not great, but we won't invest more time until we have more diverse input components and a better type system.
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
export default ({ rules, dottedName, onChange, value }) => {
|
||||
export default function InputComponent({
|
||||
rules,
|
||||
dottedName,
|
||||
onChange,
|
||||
onSubmit,
|
||||
value
|
||||
}) {
|
||||
let rule = findRuleByDottedName(rules, dottedName)
|
||||
|
||||
let commonProps = {
|
||||
|
@ -23,6 +28,7 @@ export default ({ rules, dottedName, onChange, value }) => {
|
|||
fieldName: dottedName,
|
||||
value,
|
||||
onChange,
|
||||
onSubmit,
|
||||
...pick(
|
||||
['dottedName', 'title', 'question', 'defaultValue', 'suggestions'],
|
||||
rule
|
||||
|
|
|
@ -678,6 +678,7 @@ path:
|
|||
sécuritéSociale: /social-security
|
||||
simulateurs:
|
||||
index: /simulators
|
||||
dnrti: /dnrti
|
||||
assimilé-salarié: /assimile-salarie
|
||||
indépendant: /independant
|
||||
auto-entrepreneur: /auto-entrepreneur
|
||||
|
|
Loading…
Reference in New Issue