diff --git a/README.md b/README.md index 0b74316..4012b47 100755 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ npm run deploy Goto https://app.keycdn.com/zones/purgeurl/87880 and enter: ``` -/s3/republik-assets/dynamic-components/questionnaire-dn/index.js +/s3/republik-assets/dynamic-components/questionnaire/index.js ``` If you change asset files be sure to purge those too. diff --git a/package.json b/package.json index 9fe93ec..0469fab 100755 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "questionnaire-dn", + "name": "questionnaire", "version": "0.0.1", "description": "use a questionnaire inline", "main": "index.js", @@ -11,18 +11,18 @@ "now-build": "NODE_ENV=production rollup -c && cd test && next build", "heroku-postbuild": "NODE_ENV=production rollup -c && cd test && next build", "start": "NODE_ENV=production cd test && node server.js", - "deploy": "s3cmd sync --exclude '.DS_Store' --verbose --acl-public build/ s3://republik-assets/dynamic-components/questionnaire-dn/" + "deploy": "s3cmd sync --exclude '.DS_Store' --verbose --acl-public build/ s3://republik-assets/dynamic-components/questionnaire/" }, "repository": { "type": "git", - "url": "git+https://github.com/orbiting/questionnaire-dn.git" + "url": "git+https://github.com/orbiting/questionnaire.git" }, "author": "", "license": "BSD-3-Clause", "bugs": { - "url": "https://github.com/orbiting/questionnaire-dn/issues" + "url": "https://github.com/orbiting/questionnaire/issues" }, - "homepage": "https://github.com/orbiting/questionnaire-dn#readme", + "homepage": "https://github.com/orbiting/questionnaire#readme", "devDependencies": { "@babel/core": "^7.0.0-beta.51", "@babel/preset-env": "^7.0.0-beta.51", diff --git a/src/components/Chart.js b/src/components/Chart.js index 67a0f36..947e3c1 100644 --- a/src/components/Chart.js +++ b/src/components/Chart.js @@ -69,7 +69,7 @@ const styles = { class Chart extends Component { render () { - const { question } = this.props + const { question, colors = [] } = this.props if (!question || !question.results) { return } @@ -87,6 +87,9 @@ class Chart extends Component { const truePercent = getPercentage(trueResult) const falsePercent = getPercentage(falseResult) + const trueColor = colors.find(c => c.value) + const falseColor = colors.find(c => !c.value) + return (
@@ -97,7 +100,7 @@ class Chart extends Component {
@@ -113,7 +116,7 @@ class Chart extends Component {
diff --git a/src/components/Question.js b/src/components/Question.js index 324312b..4599122 100644 --- a/src/components/Question.js +++ b/src/components/Question.js @@ -16,7 +16,7 @@ import Chart from './Chart' const styles = { container: css({ - margin: '50px 0 10px 0' + paddingBottom: 30 }), question: css({ margin: '0px 0 10px 0', @@ -51,7 +51,14 @@ class ChoiceQuestion extends Component { render () { this.handleChange = (value) => { - const { onChange, questionnaire, question: { userAnswer, cardinality } } = this.props + const { + onChange, + questionnaire, + question: { + userAnswer, + cardinality + } + } = this.props const nextValue = new Set(userAnswer ? userAnswer.payload.value : []) if (cardinality === 0 || cardinality > 1) { @@ -70,20 +77,32 @@ class ChoiceQuestion extends Component { onChange(answerId, Array.from(nextValue)) } - const { questionnaire, question: { id, text, userAnswer, options, results } } = this.props - const { question } = this.props - const { userHasSubmitted } = questionnaire + const { + questionnaire = {}, + question: { + id, + text, + userAnswer, + options, + results + }, + forceShowResults = false + } = this.props + const { question, colors } = this.props + const { userHasSubmitted = false } = questionnaire + + const showResults = userAnswer || userHasSubmitted || forceShowResults return (

{text}

- { (userAnswer || userHasSubmitted) && + { showResults &&
- +
} - { (!userAnswer && !userHasSubmitted) && + { !showResults &&
{ options.map(option =>
diff --git a/src/components/Questionnaire.js b/src/components/Questionnaire.js index 28c533d..d805727 100644 --- a/src/components/Questionnaire.js +++ b/src/components/Questionnaire.js @@ -22,7 +22,8 @@ import { OverlayToolbar, OverlayToolbarClose, OverlayBody, - Button + Button, + Center } from '@project-r/styleguide' import Question from './Question' @@ -30,10 +31,15 @@ import Question from './Question' const { P } = Interaction const { Note } = Editorial +const HEADER_HEIGHT = 60 +const HEADER_HEIGHT_MOBILE = 45 + const styles = { + container: css({ + margin: '50px 0 10px 0' + }), count: css({ background: '#fff', - zIndex: 10, borderTop: `0.5px solid ${colors.divider}`, minHeight: 25 }), @@ -51,18 +57,27 @@ const styles = { minHeight: 30 }), signIn: css({ - textAlign: 'center', - margin: '40px 0' - - }) - + marginTop: 60 + }), + group: css({ + background: '#fff', + borderBottom: `0.5px solid ${colors.divider}`, + marginBottom: 10, + zIndex: 10, + position: 'sticky', + top: HEADER_HEIGHT - 1, + [mediaQueries.onlyS]: { + top: HEADER_HEIGHT_MOBILE - 1 + }, + }), } class Page extends Component { constructor (props) { super(props) this.state = { - showOverlay: false + showOverlay: false, + forceShowResults: false } } @@ -94,93 +109,162 @@ class Page extends Component { ) } - const { data, me, t, meta } = this.props + this.onClickForceShowResults = event => { + event.preventDefault() + this.setState({ + forceShowResults: true + }) + } + + const { data, me, t, meta, colors, questionIndex } = this.props + const singleQuestion = questionIndex !== undefined && questionIndex !== null + const Wrapper = singleQuestion ? Center : React.Fragment + + const pdfRendering = typeof window !== 'undefined' && window.location.search && window.location.search.indexOf('extract') + const forceShowResults = this.state.forceShowResults || pdfRendering return ( - { - const now = new Date() + + { + const now = new Date() - // handle not found or not started - if (!data.questionnaire || new Date(data.questionnaire.beginDate) > now) { - return ( -

- Der Fragebogen konnte nicht gefunden werden. -

- ) - } + // handle not found or not started + if (!data.questionnaire || new Date(data.questionnaire.beginDate) > now) { + return ( +

+ Der Fragebogen konnte nicht gefunden werden. +

+ ) + } - if (!me || !me.id) { - return ( -

Damit wir Ihnen zeigen können wo Sie im Vergleich zu allen anderen stehen müssen Sie sich anmelden. Sie benötigen keine Mitgliedschaft. Um Ihre Privatsphäre müssen Sie sich keine Sorgen machen: Sie können Ihre Antworten jederzeit wieder anonymisieren.

- ) - } + // handle questions + const { questionnaire } = data + const { questions, userHasSubmitted } = questionnaire - // handle questions - const { questionnaire } = data - const { questions, userHasSubmitted } = questionnaire + const { error, submitting, updating, showOverlay } = this.state + const questionCount = questions.filter(Boolean).length + const userAnswerCount = questions.map(q => q.userAnswer).filter(Boolean).length - const { error, submitting, updating, showOverlay } = this.state - const questionCount = questions.filter(Boolean).length - const userAnswerCount = questions.map(q => q.userAnswer).filter(Boolean).length + const elementForQuestion = q => + React.createElement( + Question, + { + onChange: this.createHandleChange(q), + questionnaire, + question: q, + key: q.id, + colors, + forceShowResults + } + ) - return ( -
- { - questions - //.slice(0, userHasSubmitted ? questionCount : userAnswerCount + 1) - .map(q => - React.createElement( - Question, - { - onChange: this.createHandleChange(q), - questionnaire, - question: q, - key: q.id - } - ) - ) - } -
- { error && -

{errorToString(error)}

+ // rolls questions into groups. The last question of a group is left outside + // for nicer sticky behaviour + const items = [] + let group + questions.forEach( (q, index) => { + const groupText = q.metadata && q.metadata.group + const nextQuestion = questions[index+1] + const nextHasGroup = nextQuestion && nextQuestion.metadata && nextQuestion.metadata.group + const isLast = index === questions.length - 1 + if (groupText) { + group = { + text: groupText, + questions: [], + __typename: 'QuestionGroup' } -
- { userHasSubmitted - ?

Sie haben Ihre Antworten anonymisiert und können daher nicht noch einmal teilnehmen.

- :

{t('questionnaire/header', { questionCount, userAnswerCount })}

- } - { (updating || submitting) && -
- } -
- { !userHasSubmitted && -

{e.preventDefault(); this.setState({ showOverlay: true })}}>Möchten Sie Ihre Antworten anonymisieren?

+ items.push(group) + } + if (questionIndex && index !== questionIndex) { + return + } + if (group && !nextHasGroup && !isLast) { + group.questions.push(q) + } else { + items.push(q) + } + }) + + return ( +
+ { !pdfRendering && !me && +
+

Melden Sie sich an, um an der Umfrage teilzunehmen und live zu sehen, wo Sie im Vergleich zu allen anderen stehen. Sie können Ihre Antworten jederzeit anonymisieren.

+

Das erste Mal hier? Starten Sie am besten gleich ein kostenloses Probeabo für 14 Tage.

+
+ { !forceShowResults && +

Jetzt gerade nicht? Resultate anzeigen.

+ } +
} - { showOverlay && - {this.setState({ showOverlay: false })}}> - - {this.setState({ showOverlay: false })}} /> - + { (me || forceShowResults) && + <> + { + items + .filter( i => i && (!i.questions || (i.questions && i.questions.length))) + .map((i, index) => { + if (i.__typename === 'QuestionGroup') { + return ( +
+

{i.text}

+ { + i.questions.map(q => elementForQuestion(q)) + } +
+ ) + } else { + return elementForQuestion(i) + } + }) + } + { me && !singleQuestion && +
+ { error && +

{errorToString(error)}

+ } +
+ { userHasSubmitted + ?

Sie haben Ihre Antworten anonymisiert und können daher nicht noch einmal teilnehmen.

+ :

{t('questionnaire/header', { questionCount, userAnswerCount })}

+ } + { (updating || submitting) && +
+ } +
+ { !userHasSubmitted && +

+ {e.preventDefault(); this.setState({ showOverlay: true })}}>Möchten Sie Ihre Antworten anonymisieren? +

+ } + { showOverlay && + {this.setState({ showOverlay: false })}}> + + {this.setState({ showOverlay: false })}} /> + - -

Wenn Sie möchten, können Sie Ihre Antworten anonymisieren. Diese bleiben zwar in unserer Datenbank erhalten, aber wir vergessen, dass sie von Ihnen stammen. Wir können Ihnen danach nicht mehr anzeigen, was Sie geantwortet haben, und Sie können keine Antworten mehr abgeben.

- -
-
+ +

Wenn Sie möchten, können Sie Ihre Antworten anonymisieren. Diese bleiben zwar in unserer Datenbank erhalten, aber wir vergessen, dass sie von Ihnen stammen. Wir können Ihnen danach nicht mehr anzeigen, was Sie geantwortet haben, und Sie können keine Antworten mehr abgeben.

+ +
+ + } +
+ } + }
-
- ) - }} /> + ) + }} /> + ) } } @@ -241,10 +325,12 @@ query getQuestionnaire($slug: String!) { id order text + metadata userAnswer { id payload } + __typename } ... on QuestionTypeChoice { cardinality diff --git a/test/article.md b/test/article.md index 8f3e8cd..5fce270 100755 --- a/test/article.md +++ b/test/article.md @@ -22,9 +22,10 @@ Was möchten Sie Fragen? { "autoHtml": false, "props": { - "slug": "mss-vaterschaftsurlaub" + "slug": "mss-demokratie", + "colors": [{ "value": true, "color": "#6FB977" }, { "value": false, "color": "#AD8BBD" }] }, - "src": "https://cdn.republik.space/s3/republik-assets/dynamic-components/questionnaire-dn/index.js" + "src": "https://cdn.republik.space/s3/republik-assets/dynamic-components/questionnaire/index.js" } ```