🐛 répare le style des questions des simulateurs

pull/816/head
Johan Girod 2020-01-23 14:55:49 +01:00
parent cc51cd9b03
commit 1f87e1b90f
12 changed files with 80 additions and 326 deletions

View File

@ -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>

View File

@ -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 && (

View File

@ -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>

View File

@ -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} />

View File

@ -1,5 +0,0 @@
.binaryQuestionList {
display: flex;
align-items: center;
justify-content: flex-end;
}

View File

@ -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

View File

@ -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')}

View File

@ -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;
}
}

View File

@ -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
})
}

View File

@ -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(() => {

View File

@ -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

View File

@ -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