├── .eslintignore ├── static └── images │ └── logo.png ├── gatsby-ssr.js ├── src ├── styles │ ├── index.css │ ├── constants.js │ └── typography.js ├── pages │ ├── 404.js │ ├── draft.js │ ├── results.js │ ├── index.js │ ├── thankyou.js │ └── survey.js ├── components │ ├── ui │ │ ├── Image.js │ │ ├── Heading.js │ │ ├── Tooltip.js │ │ ├── Card.js │ │ ├── Link.js │ │ ├── CodeBlock.js │ │ ├── Text.js │ │ ├── PageLayout.js │ │ ├── Blockquote.js │ │ ├── Progress.js │ │ ├── Container.js │ │ ├── Header.js │ │ ├── Markdown.js │ │ ├── MarkdownImage.js │ │ ├── List.js │ │ ├── Slider.js │ │ ├── TextArea.js │ │ ├── RatingStar.js │ │ ├── Button.js │ │ ├── ProgressItem.js │ │ ├── CheckboxRadioInput.js │ │ └── PageSpinner.js │ ├── survey │ │ ├── answer │ │ │ ├── AnswerLayout.js │ │ │ ├── RadioAnswer.js │ │ │ ├── SliderAnswer.js │ │ │ ├── CheckboxAnswer.js │ │ │ ├── RatingAnswer.js │ │ │ ├── ButtonAnswer.js │ │ │ └── SurveyAnswer.js │ │ ├── SurveyContent.js │ │ ├── SurveyPageLayout.js │ │ ├── SurveyQuestion.js │ │ └── SurveyProgress.js │ ├── draft │ │ └── DraftContent.js │ ├── results │ │ └── ResultsContent.js │ ├── SurveyHeader.js │ └── Root.js ├── store │ ├── survey │ │ ├── index.js │ │ ├── responses.js │ │ ├── questions.js │ │ └── session.js │ ├── results │ │ └── index.js │ ├── draft │ │ └── index.js │ ├── utils.js │ ├── configureStore.js │ └── surveySelectors.js ├── hooks │ └── useHotKeys.js └── enums.js ├── .gitignore ├── survey ├── responses.json ├── questions │ ├── 06_rating_surveyless_experience.md │ ├── 01_excited_about_surveyless.md │ ├── 08_single_choice_best_layout.md │ ├── 15_feedback.md │ ├── 13_TODO_ranking_survey_features.md │ ├── 11_additional_comments_overview.md │ ├── 07_likert_best_layout.md │ ├── 12_slider_fun.md │ ├── 09_multiple_choice_web_stack.md │ ├── 04_likert_overview.md │ ├── 10_multiple_choice_web_stack_checkbox.md │ ├── 14_TODO_matrix_feature_importance_rating.md │ ├── 02_ready_to_explore_markdown.md │ ├── 05_likert_radio_vs_buttons.md │ └── 03_ready_to_explore_question_types.md ├── thankyou.md └── theme.json ├── .editorconfig ├── gatsby-browser.js ├── .eslintrc ├── gatsby-config.js ├── LICENSE ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | # Third party 2 | **/node_modules 3 | 4 | # Build products 5 | public 6 | -------------------------------------------------------------------------------- /static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisrzhou/surveyless/HEAD/static/images/logo.png -------------------------------------------------------------------------------- /gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | // HACK: gatsby build 2 | 3 | import Root from 'components/Root'; 4 | export const wrapRootElement = Root; 5 | -------------------------------------------------------------------------------- /src/styles/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | } 4 | body { 5 | margin: 0; 6 | overflow-x: hidden; 7 | padding: 0; 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # node 2 | node_modules 3 | 4 | # logs 5 | *.log* 6 | 7 | # files and IDE 8 | .DS_STORE 9 | *~ 10 | *.swp 11 | *.swo 12 | 13 | # project 14 | .cache 15 | public 16 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SurveyPageLayout from 'components/survey/SurveyPageLayout'; 3 | 4 | function NotFoundPage() { 5 | return ; 6 | } 7 | 8 | export default NotFoundPage; 9 | -------------------------------------------------------------------------------- /survey/responses.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "response_id": "a", 4 | "question_id": 1, 5 | "answer_id": 2 6 | }, 7 | { 8 | "response_id": "a", 9 | "question_id": 2, 10 | "answer_id": 3 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /src/components/ui/Image.js: -------------------------------------------------------------------------------- 1 | import {Box} from 'rebass'; 2 | import React from 'react'; 3 | 4 | function Image({src, width, ...otherProps}) { 5 | return ; 6 | } 7 | 8 | export default Image; 9 | -------------------------------------------------------------------------------- /src/store/survey/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux'; 2 | import questions from './questions'; 3 | import responses from './responses'; 4 | import session from './session'; 5 | 6 | export default combineReducers({ 7 | questions, 8 | responses, 9 | session, 10 | }); 11 | -------------------------------------------------------------------------------- /src/components/ui/Heading.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Text} from 'rebass'; 3 | 4 | function Heading({level, children, ...otherProps}) { 5 | return ( 6 | 7 | {children} 8 | 9 | ); 10 | } 11 | 12 | export default Heading; 13 | -------------------------------------------------------------------------------- /src/styles/constants.js: -------------------------------------------------------------------------------- 1 | export const SURVEYLESS_BRAND_COLOR = '#4b98e5'; 2 | export const SURVEYLESS_DARK_BRAND_COLOR = '#1d3b59'; 3 | export const SURVEYLESS_GRAY = '#a0afba'; 4 | export const SURVEYLESS_LIGHT_GRAY = '#F0F0F0'; 5 | export const DISABLED_OPACITY = 0.4; 6 | export const FOCUS_HOVER_OPACITY = 0.7; 7 | -------------------------------------------------------------------------------- /src/styles/typography.js: -------------------------------------------------------------------------------- 1 | import Typography from 'typography'; 2 | import theme from './../../survey/theme.json'; 3 | 4 | const typography = new Typography({ 5 | ...theme.typography, 6 | headerColor: theme.colors.headerText, 7 | bodyColor: theme.colors.primaryText, 8 | }); 9 | 10 | export default typography; 11 | -------------------------------------------------------------------------------- /src/components/ui/Tooltip.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactTooltip from 'react-tooltip-lite'; 3 | 4 | function Tooltip({children, content, otherProps}) { 5 | return ( 6 | 7 | {children} 8 | 9 | ); 10 | } 11 | 12 | export default Tooltip; 13 | -------------------------------------------------------------------------------- /survey/questions/06_rating_surveyless_experience.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: 6 3 | text: How would you rate the experience of surveyless so far? 4 | questionType: LIKERT 5 | choiceType: RATING 6 | choices: [Very Bad, Bad, Neutral, Good, Very Good] 7 | additionalComments: false 8 | --- 9 | 10 | Rating scales are a concise and valid way to represent likert questions. 11 | -------------------------------------------------------------------------------- /survey/questions/01_excited_about_surveyless.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: 1 3 | text: Hiya! How excited are you about surveyless? 4 | questionType: LIKERT 5 | choiceType: VERTICAL_RADIO 6 | choices: 7 | [ 8 | Not Excited at All, 9 | Somewhat Unexcited, 10 | Neutral, 11 | Somewhat Excited, 12 | Very Excited, 13 | ] 14 | additionalComments: false 15 | --- 16 | -------------------------------------------------------------------------------- /src/pages/draft.js: -------------------------------------------------------------------------------- 1 | import DraftContent from 'components/draft/DraftContent'; 2 | import PageLayout from 'components/ui/PageLayout'; 3 | import React from 'react'; 4 | import SurveyHeader from 'components/SurveyHeader'; 5 | 6 | function DraftPage() { 7 | return } content={} />; 8 | } 9 | 10 | export default DraftPage; 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | max_line_length = 80 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | max_line_length = 0 15 | trim_trailing_whitespace = false 16 | 17 | [COMMIT_EDITMSG] 18 | max_line_length = 0 19 | -------------------------------------------------------------------------------- /src/pages/results.js: -------------------------------------------------------------------------------- 1 | import PageLayout from 'components/ui/PageLayout'; 2 | import React from 'react'; 3 | import ResultsContent from 'components/results/ResultsContent'; 4 | import SurveyHeader from 'components/SurveyHeader'; 5 | 6 | function ResultsPage() { 7 | return } content={} />; 8 | } 9 | 10 | export default ResultsPage; 11 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | // HACK: gatsby build 2 | 3 | import Root from 'components/Root'; 4 | import typography from 'gatsby-plugin-typography/.cache/typography.js'; 5 | 6 | export const wrapRootElement = Root; 7 | 8 | // HACK: Hot reload typography in development. 9 | export function onClientEntry() { 10 | if (process.env.NODE_ENV !== `production`) { 11 | typography.injectStyles(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/survey/answer/AnswerLayout.js: -------------------------------------------------------------------------------- 1 | import List from 'components/ui/List'; 2 | import React from 'react'; 3 | 4 | function AnswerLayout({children, isVertical}) { 5 | return ( 6 | 10 | {children} 11 | 12 | ); 13 | } 14 | 15 | export default AnswerLayout; 16 | -------------------------------------------------------------------------------- /src/components/ui/Card.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Card as RebassCard} from 'rebass'; 3 | 4 | function Card({children}) { 5 | return ( 6 | 12 | {children} 13 | 14 | ); 15 | } 16 | 17 | export default Card; 18 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["fbjs", "prettier"], 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "relay/graphql-syntax": "ignore", 6 | "prettier/prettier": [ 7 | "error", 8 | { 9 | "bracketSpacing": false, 10 | "jsxBracketSameLine": true, 11 | "singleQuote": true, 12 | "trailingComma": "all", 13 | "useTabs": false 14 | } 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/store/results/index.js: -------------------------------------------------------------------------------- 1 | import {createAction, createActionTypes, createReducer} from 'store/utils'; 2 | 3 | export const actionTypes = createActionTypes('results', ['INITIALIZE']); 4 | 5 | export const actions = { 6 | initialize: createAction(actionTypes.INITIALIZE), 7 | }; 8 | 9 | export default createReducer( 10 | {}, 11 | { 12 | [actionTypes.INITIALIZE]: (state, _action) => ({...state}), 13 | }, 14 | ); 15 | -------------------------------------------------------------------------------- /survey/questions/08_single_choice_best_layout.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: 8 3 | text: Which choice set layout do you like best for single-choice questions? 4 | questionType: SINGLE_CHOICE 5 | choiceType: HORIZONTAL_BUTTON 6 | choices: [Vertical Radio, Horizontal Radio, Vertical Button, Horizontal Button] 7 | additionalComments: false 8 | --- 9 | 10 | Similar to likert questions, `surveyless` provides various ways to render single-choice choice sets. 11 | -------------------------------------------------------------------------------- /src/components/survey/SurveyContent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SurveyPageLayout from './SurveyPageLayout'; 3 | import SurveyProgress from './SurveyProgress'; 4 | import SurveyQuestion from './SurveyQuestion'; 5 | 6 | function SurveyContent() { 7 | return ( 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default SurveyContent; 16 | -------------------------------------------------------------------------------- /src/components/ui/Link.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Link as RebassLink} from 'rebass'; 3 | 4 | function Link({children, href, isExternal}) { 5 | return ( 6 | 10 | {children} 11 | 12 | ); 13 | } 14 | 15 | Link.defaultProps = { 16 | isExternal: true, 17 | }; 18 | 19 | export default Link; 20 | -------------------------------------------------------------------------------- /src/components/ui/CodeBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter'; 3 | import {ghcolors} from 'react-syntax-highlighter/dist/styles/prism'; 4 | 5 | export default function({value, language}) { 6 | return ( 7 | 11 | {value} 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /src/hooks/useHotKeys.js: -------------------------------------------------------------------------------- 1 | import {useEffect} from 'react'; 2 | 3 | function useHotKeys(handlers) { 4 | function handleKeyDown(e) { 5 | if (e.keyCode in handlers) { 6 | handlers[e.keyCode](e); 7 | } 8 | } 9 | useEffect(() => { 10 | document.body.addEventListener('keydown', handleKeyDown); 11 | return () => { 12 | document.body.removeEventListener('keydown', handleKeyDown); 13 | }; 14 | }); 15 | } 16 | 17 | export default useHotKeys; 18 | -------------------------------------------------------------------------------- /survey/questions/15_feedback.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: 15 3 | text: Please provide any additional feedback on the surveyless project! 4 | questionType: COMMENT 5 | choiceType: null 6 | choices: [] 7 | additionalComments: false 8 | --- 9 | 10 | ## Reaching the end! 11 | 12 | **Good job** on reaching the last question of this live survey demo! 13 | 14 | We hope that you are able to better understand the features of `surveyless`. Let's wrap up the survey demo with the final **Comments** question type: 15 | -------------------------------------------------------------------------------- /src/store/draft/index.js: -------------------------------------------------------------------------------- 1 | import {createAction, createActionTypes, createReducer} from 'store/utils'; 2 | 3 | const actionTypes = createActionTypes('draft', ['SET_PREVIEW_MODE']); 4 | 5 | const initialState = { 6 | isPreviewMode: false, 7 | }; 8 | 9 | export const actions = { 10 | setPreviewMode: createAction(actionTypes.SET_PREVIEW_MODE), 11 | }; 12 | 13 | export default createReducer(initialState, { 14 | [actionTypes.SET_PREVIEW_MODE]: (state, {payload}) => ({ 15 | ...state, 16 | isPreviewMode: payload, 17 | }), 18 | }); 19 | -------------------------------------------------------------------------------- /src/components/draft/DraftContent.js: -------------------------------------------------------------------------------- 1 | import Button from 'components/ui/Button'; 2 | import Card from 'components/ui/Card'; 3 | import Container from 'components/ui/Container'; 4 | import {Link} from 'gatsby'; 5 | import React from 'react'; 6 | import {routes} from 'enums'; 7 | 8 | function DraftContent() { 9 | return ( 10 | 11 | TODO (DRAFT) 12 | 13 |