Permet de passer un composant pour l'affichage des références

Notre logique d'affichage des référence était assez spécifique à
mon-entreprise. D'autres sont possible comme l'affichage de “cartes”
https://futur.eco/documentation/transport/avion/impact

On passe maintenant par un “renderer” qui permet de personnalisé le
composant affiché.
pull/1817/head
Maxime Quandalle 2021-11-04 15:23:26 +01:00
parent e54bc92874
commit e2149ff4e6
7 changed files with 142 additions and 133 deletions

View File

@ -1,13 +1,13 @@
import { explainVariable } from 'Actions/actions'
import Overlay from 'Components/Overlay'
import { EngineContext } from 'Components/utils/EngineContext'
import { Markdown } from 'Components/utils/markdown'
import { useContext } from 'react'
import { Trans } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
import { RootState } from 'Reducers/rootReducer'
import { References } from 'publicodes-react'
import { Trans } from 'react-i18next'
import { References } from '../../pages/Documentation'
import './Aide.css'
import { EngineContext } from 'Components/utils/EngineContext'
export default function Aide() {
const explained = useSelector((state: RootState) => state.explainedVariable)
@ -36,7 +36,7 @@ export default function Aide() {
<h3>
<Trans>En savoir plus</Trans>
</h3>
<References refs={refs} />
<References references={refs} />
</>
)}
</div>

View File

@ -4,7 +4,6 @@ import Emoji from 'Components/utils/Emoji'
import { Markdown } from 'Components/utils/markdown'
import { DottedName } from 'modele-social'
import { EvaluatedNode, Rule, RuleNode, serializeEvaluation } from 'publicodes'
import { References } from 'publicodes-react'
import {
createContext,
useCallback,
@ -13,6 +12,7 @@ import {
useState,
} from 'react'
import { Trans } from 'react-i18next'
import { References } from '../../pages/Documentation'
import { Explicable } from './Explicable'
import { binaryQuestion, InputProps } from './RuleInput'
@ -211,7 +211,7 @@ export const RadioLabel = (props: RadioLabelProps) => (
<h3>
<Trans>En savoir plus</Trans>
</h3>
<References refs={props.références} />
<References references={props.références} />
</>
)}
</Explicable>

View File

@ -2,20 +2,27 @@ import SearchRules from 'Components/search/SearchRules'
import { FromBottom } from 'Components/ui/animate'
import { ThemeColorsProvider } from 'Components/utils/colors'
import { useEngine } from 'Components/utils/EngineContext'
import Meta from 'Components/utils/Meta'
import { ScrollToTop } from 'Components/utils/Scroll'
import { SitePathsContext } from 'Components/utils/SitePathsContext'
import { RulePage, getDocumentationSiteMap } from 'publicodes-react'
import rules, { DottedName } from 'modele-social'
import { getDocumentationSiteMap, RulePage } from 'publicodes-react'
import { useCallback, useContext, useMemo } from 'react'
import { Helmet } from 'react-helmet-async'
import { Trans, useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { Redirect, useHistory, useLocation, Link } from 'react-router-dom'
import {
Link,
Redirect,
Route,
useHistory,
useLocation,
} from 'react-router-dom'
import { RootState } from 'Reducers/rootReducer'
import styled from 'styled-components'
import { TrackPage } from '../ATInternetTracking'
import rules, { DottedName } from 'modele-social'
import RuleLink from '../components/RuleLink'
import Meta from 'Components/utils/Meta'
import { Route } from 'react-router-dom'
import { Helmet } from 'react-helmet-async'
import { capitalise0 } from '../utils'
export default function MonEntrepriseRulePage() {
const currentSimulation = useSelector(
@ -70,10 +77,10 @@ export default function MonEntrepriseRulePage() {
rulePath={match.params.name}
engine={engine}
documentationPath={documentationPath}
referenceImages={referencesImages}
renderers={{
Head: Helmet,
Link,
References,
}}
/>
)}
@ -88,7 +95,7 @@ function BackToSimulation() {
const history = useHistory()
const handleClick = useCallback(() => {
url && history.push(url)
}, [])
}, [history, url])
return (
<button
className="ui__ simple small push-left button"
@ -132,15 +139,86 @@ function DocumentationRulesList() {
}
const referencesImages = {
'service-public.fr': 'images/références/marianne.png',
'urssaf.fr': 'images/références/Urssaf.svg',
'secu-independants.fr': 'images/références/Urssaf.svg',
'gouv.fr': 'images/références/marianne.png',
'agirc-arrco.fr': 'images/références/agirc-arrco.png',
'pole-emploi.fr': 'images/références/pole-emploi.png',
'service-public.fr': '/images/références/marianne.png',
'legifrance.gouv.fr': '/images/références/marianne.png',
'urssaf.fr': '/images/références/Urssaf.svg',
'secu-independants.fr': '/images/références/Urssaf.svg',
'gouv.fr': '/images/références/marianne.png',
'agirc-arrco.fr': '/images/références/agirc-arrco.png',
'pole-emploi.fr': '/images/références/pole-emploi.png',
'ladocumentationfrançaise.fr':
'images/références/ladocumentationfrançaise.png',
'senat.fr': 'images/références/senat.png',
'ameli.fr': 'images/références/ameli.png',
'bpifrance-creation': 'images/références/bpi-création.png',
'/images/références/ladocumentationfrançaise.png',
'senat.fr': '/images/références/senat.png',
'ameli.fr': '/images/références/ameli.png',
'bpifrance-creation.fr': '/images/références/bpi-création.png',
}
type ReferencesProps = React.ComponentProps<
NonNullable<React.ComponentProps<typeof RulePage>['renderers']['References']>
>
export function References({ references }: ReferencesProps) {
const cleanDomain = (link: string) =>
(link.includes('://') ? link.split('/')[2] : link.split('/')[0]).replace(
'www.',
''
)
return (
<StyledReferences>
{Object.entries(references).map(([name, link]) => {
const domain = cleanDomain(link)
return (
<li key={name}>
<span className="imageWrapper">
{Object.keys(referencesImages).includes(domain) && (
<img
src={
referencesImages[domain as keyof typeof referencesImages]
}
alt={`logo de ${domain}`}
/>
)}
</span>
<a href={link} target="_blank">
{capitalise0(name)}
</a>
<span className="ui__ label">{domain}</span>
</li>
)
})}
</StyledReferences>
)
}
const StyledReferences = styled.ul`
list-style: none;
padding: 0;
a {
flex: 1;
min-width: 45%;
text-decoration: underline;
margin-right: 1rem;
}
li {
margin-bottom: 0.6em;
width: 100%;
display: flex;
align-items: center;
}
.imageWrapper {
width: 4.5rem;
height: 3rem;
display: flex;
align-items: center;
justify-content: center;
margin-right: 1rem;
}
img {
max-height: 3rem;
vertical-align: sub;
max-width: 100%;
border-radius: 0.3em;
}
`

View File

@ -1,12 +1,15 @@
import Engine from 'publicodes'
import React, { createContext } from 'react'
import References from './rule/References'
export type SupportedRenderers = {
Head?: React.ComponentType
Link: React.ComponentType<{ to: string }>
Head?: React.ComponentType
References?: typeof References
}
export const BasepathContext = createContext<string>('/documentation')
export const EngineContext = createContext<Engine<string> | null>(null)
export const ReferencesImagesContext = createContext<Record<string, string>>({})
export const RenderersContext = createContext<Partial<SupportedRenderers>>({})
export const RenderersContext = createContext<Partial<SupportedRenderers>>({
References,
})

View File

@ -1,9 +1,7 @@
import { utils } from 'publicodes'
import References from './rule/References'
export { default as Explanation } from './Explanation'
export { default as RulePage } from './rule/RulePage'
export { RuleLink } from './RuleLink'
export { References }
export function getDocumentationSiteMap({ engine, documentationPath }) {
const parsedRules = engine.getParsedRules()

View File

@ -1,97 +1,32 @@
import { toPairs } from 'ramda'
import { capitalise0 } from 'publicodes'
import styled from 'styled-components'
import { useContext } from 'react'
import { ReferencesImagesContext } from '../contexts'
const cleanDomain = (link: string) =>
(link.includes('://') ? link.split('/')[2] : link.split('/')[0]).replace(
'www.',
''
)
type RefProps = {
name: string
link: string
}
// TODO: currently we only allow customizing the list of "references icons", but
// this migth be limited for more advanced usages. For instance futur.eco uses a
// different renderer for references:
// https://futur.eco/documentation/transport/avion/impact We will probably want
// to allow the user to provide its own components that can be inserted at
// certains positions in the generated documentation ("hooks").
function Ref({ name, link }: RefProps) {
const references = useContext(ReferencesImagesContext)
const refKey = Object.keys(references).find((r) => link.includes(r))
const domain = cleanDomain(link)
return (
<li
style={{
display: 'flex',
alignItems: 'center',
}}
key={name}
>
<span className="imageWrapper">
{refKey && <img src={references[refKey]} />}
</span>
<a
href={link}
target="_blank"
style={{
marginRight: '1rem',
}}
>
{capitalise0(name)}
</a>
<span className="ui__ label">{domain}</span>
</li>
)
}
type ReferencesProps = {
refs: { [name: string]: string }
references: Record<string, string>
}
export default function References({ refs }: ReferencesProps) {
const references = toPairs(refs)
export default function References({ references }: ReferencesProps) {
return (
<StyledComponent>
{references.map(([name, link]) => (
<Ref key={link} name={name} link={link} />
<ul>
{Object.entries(references).map(([name, link]) => (
<li
style={{
display: 'flex',
alignItems: 'center',
}}
key={name}
>
<a
href={link}
target="_blank"
style={{
marginRight: '1rem',
}}
>
{capitalise0(name)}
</a>
<span className="ui__ label">{link}</span>
</li>
))}
</StyledComponent>
</ul>
)
}
const StyledComponent = styled.ul`
list-style: none;
padding: 0;
a {
flex: 1;
min-width: 45%;
text-decoration: underline;
}
li {
margin-bottom: 0.6em;
width: 100%;
display: flex;
align-items: center;
}
.imageWrapper {
width: 4.5rem;
height: 3rem;
display: flex;
align-items: center;
justify-content: center;
margin-right: 1rem;
}
img {
max-height: 3rem;
vertical-align: sub;
max-width: 100%;
border-radius: 0.3em;
}
`

View File

@ -10,7 +10,6 @@ import { useContext } from 'react'
import {
BasepathContext,
EngineContext,
ReferencesImagesContext,
RenderersContext,
SupportedRenderers,
} from '../contexts'
@ -18,7 +17,6 @@ import Explanation from '../Explanation'
import { Markdown } from '../Markdown'
import { RuleLinkWithContext } from '../RuleLink'
import RuleHeader from './Header'
import References from './References'
import RuleSource from './RuleSource'
type RulePageProps = {
@ -26,7 +24,6 @@ type RulePageProps = {
rulePath: string
engine: Engine
language: 'fr' | 'en'
referenceImages?: Record<string, string>
renderers: SupportedRenderers
}
@ -36,7 +33,6 @@ export default function RulePage({
engine,
renderers,
language,
referenceImages = {},
}: RulePageProps) {
const currentEngineId = new URLSearchParams(window.location.search).get(
'currentEngineId'
@ -46,15 +42,13 @@ export default function RulePage({
<EngineContext.Provider value={engine}>
<BasepathContext.Provider value={documentationPath}>
<RenderersContext.Provider value={renderers}>
<ReferencesImagesContext.Provider value={referenceImages}>
<Rule
dottedName={decodeRuleName(rulePath)}
subEngineId={
currentEngineId ? parseInt(currentEngineId) : undefined
}
language={language}
/>
</ReferencesImagesContext.Provider>
<Rule
dottedName={decodeRuleName(rulePath)}
subEngineId={
currentEngineId ? parseInt(currentEngineId) : undefined
}
language={language}
/>
</RenderersContext.Provider>
</BasepathContext.Provider>
</EngineContext.Provider>
@ -69,6 +63,7 @@ type RuleProps = {
export function Rule({ dottedName, language, subEngineId }: RuleProps) {
const baseEngine = useContext(EngineContext)
const { References } = useContext(RenderersContext)
if (!baseEngine) {
throw new Error('Engine expected')
}
@ -192,10 +187,10 @@ export function Rule({ dottedName, language, subEngineId }: RuleProps) {
</div>
</>
)}
{rule.rawNode.références && (
{rule.rawNode.références && References && (
<>
<h2>Références</h2>
<References refs={rule.rawNode.références} />
<h3>Références</h3>
<References references={rule.rawNode.références} />
</>
)}
{/* <Examples