Ajoute les explications des mécanismes sur la documentation publicodes

pull/1056/head
Johan Girod 2020-05-26 12:45:47 +02:00
parent 7b704445da
commit 612ea77cfc
22 changed files with 325 additions and 125 deletions

View File

@ -30,7 +30,6 @@
"classnames": "^2.2.5",
"color-convert": "^1.9.2",
"core-js": "^3.2.1",
"focus-trap-react": "^3.1.2",
"fuse.js": "5.2.1",
"iframe-resizer": "^4.1.1",
"js-yaml": "^3.13.1",

View File

@ -1,32 +0,0 @@
#overlayWrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
overflow: auto;
z-index: 1;
}
#overlayContent {
position: absolute;
padding-bottom: 1rem;
min-height: 6em;
}
#overlayCloseButton {
position: absolute;
top: 0rem;
text-decoration: none;
font-size: 200%;
color: rgba(51, 51, 80, 0.8);
right: 0.5rem;
}
@media (min-width: 600px) {
#overlayContent {
transform: translateX(-50%);
top: 100px;
left: 50%;
width: 80%;
max-width: 40em;
}
}

View File

@ -2,7 +2,7 @@ import * as animate from 'Components/ui/animate'
import { LinkButton } from 'Components/ui/Button'
import FocusTrap from 'focus-trap-react'
import React, { useEffect } from 'react'
import './Overlay.css'
import styled from 'styled-components'
type OverlayProps = React.HTMLAttributes<HTMLDivElement> & {
onClose?: () => void
@ -12,6 +12,7 @@ type OverlayProps = React.HTMLAttributes<HTMLDivElement> & {
export default function Overlay({
onClose,
children,
className,
...otherProps
}: OverlayProps) {
useEffect(() => {
@ -22,7 +23,7 @@ export default function Overlay({
}
}, [])
return (
<div id="overlayWrapper">
<StyledOverlayWrapper>
<animate.fromBottom>
<FocusTrap
focusTrapOptions={{
@ -32,16 +33,15 @@ export default function Overlay({
>
<div
aria-modal="true"
id="overlayContent"
className={'ui__ card overlayContent ' + className ?? ''}
{...otherProps}
className={'ui__ card ' + otherProps?.className}
>
{children}
{onClose && (
<LinkButton
aria-label="close"
onClick={onClose}
id="overlayCloseButton"
className="overlayCloseButton"
>
×
</LinkButton>
@ -49,6 +49,39 @@ export default function Overlay({
</div>
</FocusTrap>
</animate.fromBottom>
</div>
</StyledOverlayWrapper>
)
}
const StyledOverlayWrapper = styled.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
overflow: auto;
z-index: 1;
.overlayContent {
position: absolute;
padding-bottom: 1rem;
min-height: 6em;
}
.overlayCloseButton {
position: absolute;
top: 0rem;
text-decoration: none;
font-size: 200%;
color: rgba(51, 51, 80, 0.8);
right: 0.5rem;
}
@media (min-width: 600px) {
.overlayContent {
transform: translateX(-50%);
top: 100px;
left: 50%;
width: 80%;
max-width: 40em;
}
}
`

View File

@ -1,22 +0,0 @@
import { LinkRenderer } from 'Components/utils/markdown'
import React from 'react'
import emoji from 'react-easy-emoji'
import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter'
import yaml from 'react-syntax-highlighter/dist/esm/languages/prism/yaml'
import style from 'react-syntax-highlighter/dist/esm/styles/prism/atom-dark'
SyntaxHighlighter.registerLanguage('yaml', yaml)
export default ({ source }: { source: string }) => (
<div css="position: relative; margin-bottom: 1rem">
<SyntaxHighlighter language="yaml" style={style}>
{source}
</SyntaxHighlighter>
<LinkRenderer
href={`https://publi.codes/studio?code=${encodeURIComponent(source)}`}
css="position: absolute; bottom: 5px; right: 10px; color: white !important;"
>
{emoji('⚡')} Lancer le calcul
</LinkRenderer>
</div>
)

View File

@ -76,13 +76,8 @@ button {
}
p,
a,
ul {
line-height: 1.7rem;
}
p,
ul {
margin: 0 0 0.6rem;
}

View File

@ -1,4 +1,4 @@
import PublicodeHighlighter from 'Components/ui/PublicodeHighlighter'
import PublicodeHighlighter from '../../../../publicodes/source/components/PublicodeHighlighter'
import React, { useContext } from 'react'
import emoji from 'react-easy-emoji'
import ReactMarkdown, { ReactMarkdownProps } from 'react-markdown'

View File

@ -1,36 +1,11 @@
import { Markdown } from 'Components/utils/markdown'
import { ScrollToTop } from 'Components/utils/Scroll'
import mecanisms from '../../../../publicodes/source/mecanisms.yaml'
import React, { useEffect } from 'react'
import { useLocation } from 'react-router-dom'
import { HashLink as Link } from 'react-router-hash-link'
import { capitalise0 } from '../../utils'
import mecanisms from '../../../../publicodes/docs/mecanisms.yaml'
import MecanismExplanation from '../../../../publicodes/source/components/mecanisms/Explanation'
import { Header } from './Header'
type MecanismProp = {
exemples: { base: string }
description: string
name: string
}
const Mecanism = ({ name, description, exemples }: MecanismProp) => (
<React.Fragment key={name}>
<h2 id={name}>
<pre>{name}</pre>
</h2>
<Markdown source={description} />
{exemples && (
<>
{Object.entries(exemples).map(([name, exemple]) => (
<React.Fragment key={name}>
<h3>{name === 'base' ? 'Exemple' : capitalise0(name)}</h3>
<Markdown source={`\`\`\`yaml\n${exemple}\n\`\`\``} />
</React.Fragment>
))}{' '}
</>
)}
<Link to={useLocation().pathname + '#top'}>Retour à la liste</Link>
</React.Fragment>
)
export default function Landing() {
const { pathname } = useLocation()
useEffect(() => {
@ -59,7 +34,10 @@ export default function Landing() {
))}
</ul>
{Object.entries(mecanisms).map(([name, data]) => (
<Mecanism {...(data as any)} name={name} key={name} />
<React.Fragment key={name}>
<MecanismExplanation {...(data as any)} name={name} />
<Link to={pathname + '#top'}>Retour à la liste</Link>
</React.Fragment>
))}
</div>
)

View File

@ -15,5 +15,9 @@
},
"noEmit": true
},
"include": ["types", "source"]
"include": [
"types",
"source",
"../publicodes/source/components/PublicodeHighlighter.tsx"
]
}

View File

@ -1,9 +1,9 @@
/* eslint-env node */
const HTMLPlugin = require('html-webpack-plugin')
const CopyPlugin = require('copy-webpack-plugin')
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
const { EnvironmentPlugin } = require('webpack')
const path = require('path')
const { EnvironmentPlugin } = require('webpack')
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
module.exports.default = {
resolve: {

View File

@ -78,7 +78,6 @@
"mocha": "^5.0.4",
"mocha-webpack": "^2.0.0-beta.0",
"mock-local-storage": "^1.0.5",
"monaco-editor-webpack-plugin": "^1.9.0",
"nearley-loader": "^2.0.0",
"postcss-loader": "^2.1.2",
"prettier": "^1.19.1",

View File

@ -27,6 +27,8 @@
"react-easy-emoji": "^1.4.0",
"react-i18next": "^11.4.0",
"react-markdown": "^4.3.1",
"focus-trap-react": "^3.1.2",
"react-syntax-highlighter": "^12.2.1",
"styled-components": "^5.1.0",
"yaml": "^1.9.2"
},
@ -38,7 +40,7 @@
"scripts": {
"prepublishOnly": "yarn test && yarn run build",
"clean": "rimraf dist node_modules",
"prepare": "yarn run clean && yarn run build",
"prepare": "rimraf dist && yarn run build",
"build": "tsc && yarn run webpack --config webpack.config.js",
"test:file": "yarn mocha-webpack --webpack-config ./webpack.test.js --include test/setupIntl.js",
"test": "yarn test:file \"./{,!(node_modules)/**/}!(webpack).test.js\""

View File

@ -4,12 +4,7 @@ import ReactMarkdown, { ReactMarkdownProps } from 'react-markdown'
import { HashLink as Link } from 'react-router-hash-link'
import { EngineContext } from './contexts'
import { RuleLinkWithContext } from './RuleLink'
const internalURLs = {
'mon-entreprise.fr': 'mon-entreprise',
'mycompanyinfrance.fr': 'infrance',
'publi.codes': 'publicodes'
} as const
import PublicodeHighlighter from './PublicodeHighlighter'
export function LinkRenderer({
href,
@ -44,6 +39,14 @@ export function LinkRenderer({
</a>
)
}
const CodeBlock = ({ value, language }: { value: string; language: string }) =>
language === 'yaml' ? (
<PublicodeHighlighter source={value} />
) : (
<code>{value}</code>
)
const TextRenderer = ({ children }: { children: string }) => (
<>{emoji(children)}</>
)
@ -65,6 +68,7 @@ export const Markdown = ({
renderers={{
link: LinkRenderer,
text: TextRenderer,
code: CodeBlock,
...renderers
}}
{...otherProps}

View File

@ -0,0 +1,97 @@
import FocusTrap from 'focus-trap-react'
import React, { useEffect } from 'react'
import styled from 'styled-components'
type ModalProps = React.HTMLAttributes<HTMLDivElement> & {
onClose?: () => void
children: React.ReactNode
}
export default function Modal({
onClose,
children,
className,
...otherProps
}: ModalProps) {
useEffect(() => {
const body = document.getElementsByTagName('body')[0]
body.classList.add('no-scroll')
return () => {
body.classList.remove('no-scroll')
}
}, [])
return (
<StyledModalWrapper>
<FocusTrap
focusTrapOptions={{
onDeactivate: onClose,
clickOutsideDeactivates: !!onClose
}}
>
<div
aria-modal="true"
className={'ui__ card overlayContent ' + className ?? ''}
{...otherProps}
>
{children}
{onClose && (
<button
aria-label="close"
onClick={onClose}
className="overlayCloseButton"
>
×
</button>
)}
</div>
</FocusTrap>
</StyledModalWrapper>
)
}
const StyledModalWrapper = styled.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
overflow: auto;
z-index: 1;
.overlayContent {
position: absolute;
padding-bottom: 1rem;
min-height: 6em;
}
.overlayContent > * {
animation: fromBottom 0.4s;
}
.overlayCloseButton {
position: absolute;
top: 0rem;
text-decoration: none;
font-size: 200%;
color: rgba(51, 51, 80, 0.8);
right: 0.5rem;
}
@media (min-width: 600px) {
.overlayContent {
transform: translateX(-50%);
top: 100px;
left: 50%;
width: 80%;
max-width: 40em;
}
}
@keyframes fromBottom {
from {
transform: translateY(-10px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
`

View File

@ -0,0 +1,24 @@
import React from 'react'
import emoji from 'react-easy-emoji'
import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter'
import yaml from 'react-syntax-highlighter/dist/esm/languages/prism/yaml'
import style from 'react-syntax-highlighter/dist/esm/styles/prism/atom-dark'
SyntaxHighlighter.registerLanguage('yaml', yaml)
export default function PublicodeHighlighter({ source }: { source: string }) {
return (
<div css="position: relative; margin-bottom: 1rem">
<SyntaxHighlighter language="yaml" style={style}>
{source}
</SyntaxHighlighter>
<a
href={`https://publi.codes/studio?code=${encodeURIComponent(source)}`}
target="_blank"
css="position: absolute; bottom: 5px; right: 10px; color: white !important;"
>
{emoji('⚡')} Lancer le calcul
</a>
</div>
)
}

View File

@ -1,14 +1,17 @@
import { ParsedRule } from 'publicodes'
import yaml from 'yaml'
import React from 'react'
import rules from 'Rules'
import PublicodeHighlighter from './ui/PublicodeHighlighter'
type RuleSourceProps = Pick<ParsedRule, 'dottedName'>
export default function RuleSource({ dottedName }: RuleSourceProps) {
const source = rules[dottedName]
import Engine from '../index'
import PublicodeHighlighter from './PublicodeHighlighter'
type Props<Rules extends string> = { dottedName: Rules; engine: Engine<Rules> }
export default function RuleSource<Rules extends string>({
engine,
dottedName
}: Props<Rules>) {
const source = engine.getRules()[dottedName]
if (!source) {
return
}
return (
<section>
<h3>Source publicode</h3>

View File

@ -0,0 +1,36 @@
import React from 'react'
import { capitalise0 } from '../../utils'
import { Markdown } from '../Markdown'
type MecanismProp = {
exemples: { base: string }
description: string
name: string
}
export default function MecanismExplanation({
name,
description,
exemples
}: MecanismProp) {
return (
<>
{!!name && (
<h2 id={name}>
<pre>{name}</pre>
</h2>
)}
<Markdown source={description} />
{exemples && (
<>
{Object.entries(exemples).map(([name, exemple]) => (
<React.Fragment key={name}>
<h3>{name === 'base' ? 'Exemple' : capitalise0(name)}</h3>
<Markdown source={`\`\`\`yaml\n${exemple}\n\`\`\``} />
</React.Fragment>
))}{' '}
</>
)}
</>
)
}

View File

@ -86,7 +86,7 @@ const StyledRow = styled.div`
display: flex;
align-items: center;
flex-flow: row nowrap;
line-height: 1.7rem;
:nth-child(2n) {
background-color: var(--lightestColor);
}

View File

@ -1,5 +1,5 @@
import classnames from 'classnames'
import React from 'react'
import React, { useState, useEffect } from 'react'
import { Trans } from 'react-i18next'
import styled from 'styled-components'
import { formatValue } from '../../format'
@ -7,8 +7,10 @@ import { Evaluation, ParsedRule, Types, Unit, EvaluatedNode } from '../../types'
import { capitalise0 } from '../../utils'
import { RuleLinkWithContext } from '../RuleLink'
import mecanismColors from './colors'
import Modal from '../Modal'
import { makeJsx } from '../../evaluation'
import MecanismExplanation from './Explanation'
import mecanismsDoc from '../../../docs/mecanisms.yaml'
type NodeValuePointerProps = {
data: Evaluation<Types>
unit: Unit
@ -65,9 +67,9 @@ export function Mecanism({
return (
<StyledMecanism name={name}>
{displayName && (
<StyledMecanismName name={name}>
<MecanismName name={name}>
<Trans>{name}</Trans>
</StyledMecanismName>
</MecanismName>
)}
<>
{children}
@ -100,6 +102,7 @@ export const InfixMecanism = ({
<div
className="infix-mecanism"
css={`
line-height: 1.7rem;
border: 1px solid var(--darkColor);
padding: 1rem;
border-radius: 0.3rem;
@ -119,13 +122,44 @@ export const InfixMecanism = ({
}
export const InlineMecanismName = ({ name }: { name: string }) => {
return (
<StyledMecanismName inline name={name}>
<MecanismName inline name={name}>
<Trans>{name}</Trans>
</StyledMecanismName>
</MecanismName>
)
}
const MecanismName = ({
name,
inline = false,
children
}: {
name: string
inline?: boolean
children: React.ReactNode
}) => {
const [showExplanation, setShowExplanation] = useState(false)
return (
<>
<StyledMecanismName
name={name}
inline={inline}
onClick={() => setShowExplanation(true)}
>
{children}
</StyledMecanismName>
{showExplanation && (
<Modal onClose={() => setShowExplanation(false)}>
<MecanismExplanation name={name} {...mecanismsDoc[name]} />
</Modal>
)}
</>
)
}
const StyledOperation = styled.span`
line-height: 1.7rem;
::before {
content: '(';
}

View File

@ -24,7 +24,7 @@ export default function Rule({
const { description, question } = rule
return (
<div id="rule">
<div id="documentationRuleRoot">
<RuleHeader dottedName={dottedName} />
<section>
<Markdown source={description || question} />

View File

@ -16,7 +16,7 @@ function MecanismEncadrement({ nodeValue, explanation }) {
<>
<p
style={
nodeValue === explanation.plancher.nodeValue
nodeValue && nodeValue === explanation.plancher.nodeValue
? { background: 'var(--lighterColor)', fontWeight: 'bold' }
: {}
}
@ -30,7 +30,7 @@ function MecanismEncadrement({ nodeValue, explanation }) {
<>
<p
style={
nodeValue === explanation.plancher.nodeValue
nodeValue && nodeValue === explanation.plancher.nodeValue
? { background: 'var(--lighterColor)', fontWeight: 'bold' }
: {}
}

View File

@ -4690,6 +4690,11 @@ executable@4.1.1:
dependencies:
pify "^2.2.0"
exenv@^1.2.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=
exit-hook@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
@ -5624,6 +5629,11 @@ highlight.js@~9.13.0:
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e"
integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A==
highlight.js@~9.15.0, highlight.js@~9.15.1:
version "9.15.10"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2"
integrity sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw==
history@^4.9.0:
version "4.10.1"
resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
@ -7452,6 +7462,14 @@ lower-case@^1.1.1:
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
lowlight@1.12.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.12.1.tgz#014acf8dd73a370e02ff1cc61debcde3bb1681eb"
integrity sha512-OqaVxMGIESnawn+TU/QMV5BJLbUghUfjDWPAtFqDYDmDtr4FnB+op8xM+pR7nKlauHNUHXGt0VgWatFB8voS5w==
dependencies:
fault "^1.0.2"
highlight.js "~9.15.0"
lowlight@~1.11.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.11.0.tgz#1304d83005126d4e8b1dc0f07981e9b689ec2efc"
@ -9443,7 +9461,7 @@ react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-i
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-lifecycles-compat@^3.0.4:
react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
@ -9469,6 +9487,16 @@ react-markdown@^4.1.0, react-markdown@^4.3.1:
unist-util-visit "^1.3.0"
xtend "^4.0.1"
react-modal@^3.11.2:
version "3.11.2"
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.11.2.tgz#bad911976d4add31aa30dba8a41d11e21c4ac8a4"
integrity sha512-o8gvvCOFaG1T7W6JUvsYjRjMVToLZgLIsi5kdhFIQCtHxDkA47LznX62j+l6YQkpXDbvQegsDyxe/+JJsFQN7w==
dependencies:
exenv "^1.2.0"
prop-types "^15.5.10"
react-lifecycles-compat "^3.0.0"
warning "^4.0.3"
react-monaco-editor@^0.36.0:
version "0.36.0"
resolved "https://registry.yarnpkg.com/react-monaco-editor/-/react-monaco-editor-0.36.0.tgz#ac085c14f25fb072514c925596f6a06a711ee078"
@ -9578,6 +9606,17 @@ react-syntax-highlighter@^10.1.1:
prismjs "^1.8.4"
refractor "^2.4.1"
react-syntax-highlighter@^12.2.1:
version "12.2.1"
resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-12.2.1.tgz#14d78352da1c1c3f93c6698b70ec7c706b83493e"
integrity sha512-CTsp0ZWijwKRYFg9xhkWD4DSpQqE4vb2NKVMdPAkomnILSmsNBHE0n5GuI5zB+PU3ySVvXvdt9jo+ViD9XibCA==
dependencies:
"@babel/runtime" "^7.3.1"
highlight.js "~9.15.1"
lowlight "1.12.1"
prismjs "^1.8.4"
refractor "^2.4.1"
react-test-renderer@^16.0.0-0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.1.tgz#de25ea358d9012606de51e012d9742e7f0deabc1"
@ -11849,6 +11888,13 @@ walker@^1.0.7, walker@~1.0.5:
dependencies:
makeerror "1.0.x"
warning@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
dependencies:
loose-envify "^1.0.0"
watchpack@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.1.tgz#280da0a8718592174010c078c7585a74cd8cd0e2"