Repository: surveyjs/surveyjs_react_quickstart Branch: master Commit: 72ca6a78404a Files: 24 Total size: 29.9 KB Directory structure: gitextract_hh3y72qy/ ├── .gitignore ├── README.md ├── azure-pipelines/ │ └── master/ │ └── react-quickstart-main.yml ├── package.json ├── public/ │ ├── index.html │ └── manifest.json └── src/ ├── App.css ├── App.js ├── App.test.js ├── components/ │ ├── MyQuestion.js │ ├── SurveyAnalytics.js │ ├── SurveyAnalyticsTabulator.js │ └── SurveyCreator.js ├── data/ │ ├── analytics_data.js │ └── survey_json.js ├── index.css ├── index.js ├── pages/ │ ├── Analytics.js │ ├── AnalyticsTabulator.js │ ├── Creator.js │ ├── Export.js │ ├── Home.js │ └── Survey.js └── registerServiceWorker.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # See https://help.github.com/ignore-files/ for more about ignoring files. # dependencies /node_modules # testing /coverage # production /build # misc .DS_Store .env.local .env.development.local .env.test.local .env.production.local npm-debug.log* yarn-debug.log* yarn-error.log* ================================================ FILE: README.md ================================================ # (Obsolete) SurveyJS + React Quickstart Template > This template is built using [Create React App](https://github.com/facebook/create-react-app), which is now deprecated. Use the [SurveyJS + Next.js quickstart template](https://github.com/surveyjs/surveyjs-nextjs) instead. SurveyJS is a set of JavaScript components that allow you and your users to build surveys / forms, store them in your database, and visualize survey results for data analysis. This quick start template is bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app) and uses the following SurveyJS components: - [SurveyJS Form Library](https://surveyjs.io/form-library/documentation/overview) - [Survey Creator / Form Builder](https://surveyjs.io/survey-creator/documentation/overview) - [SurveyJS PDF Generator](https://surveyjs.io/pdf-generator/documentation/overview) - [SurveyJS Dashboard](https://surveyjs.io/dashboard/documentation/overview) ## Run the application ```bash git clone https://github.com/surveyjs/surveyjs_react_quickstart.git cd surveyjs_react_quickstart npm i npm run start ``` Open http://localhost:3000/ in your web browser. ## Template structure This template covers most basic use cases. You can find code examples for them in the following files: - Create a standalone survey - [src/data/survey_json.js](src/data/survey_json.js) - [src/pages/Survey.js](src/pages/Survey.js) - Add Survey Creator to a page - [src/components/SurveyCreator.js](src/components/SurveyCreator.js) - [src/pages/Creator.js](src/pages/Creator.js) - Export a survey to a PDF document - [src/pages/Export.js](src/pages/Export.js) - Visualize survey results - As charts - [src/data/analytics_data.js](src/data/analytics_data.js) - [src/components/SurveyAnalytics.js](src/components/SurveyAnalytics.js) - [src/pages/Analytics.js](src/pages/Analytics.js) - As a table - [src/data/analytics_data.js](src/data/analytics_data.js) - [src/components/SurveyAnalyticsTabulator.js](src/components/SurveyAnalyticsTabulator.js) - [src/pages/AnalyticsTabulator.js](src/pages/AnalyticsTabulator.js) - Create a custom question type - [src/components/MyQuestion.js](src/components/MyQuestion.js) - Register third-party components - [src/App.js](src/App.js#L37) ================================================ FILE: azure-pipelines/master/react-quickstart-main.yml ================================================ resources: pipelines: - pipeline: ReleaseVersion source: Release Version trigger: true variables: npm_config_cache: $(Pipeline.Workspace)/.npm CurrentBranch: $(Build.SourceBranchName) ProjectId: 'd79f2855-7b94-4261-9daf-4cace0a06c03' trigger: batch: true branches: include: - master pr: none pool: vmImage: ubuntu-latest steps: - task: NodeTool@0 inputs: versionSpec: "24.x" displayName: 'Install Node.js' - script: | npm install npm run build displayName: 'npm install and build' - script: | npm run test -- --watchAll=false displayName: 'run unit tests' ================================================ FILE: package.json ================================================ { "name": "surveyjs_react_quickstart", "version": "0.2.0", "private": true, "dependencies": { "bootstrap": "^3.3.7", "jspdf": "1.5.3", "jspdf-autotable": "3.0.10", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router-dom": "^5.2.0", "react-scripts": "^5.0.0", "survey-analytics": "^1.12.26", "survey-core": "^1.12.26", "survey-creator-core": "^1.12.26", "survey-creator-react": "^1.12.26", "survey-pdf": "^1.12.26", "survey-react-ui": "^1.12.26", "xlsx": "^0.18.5" }, "scripts": { "start": "react-scripts start", "build": "react-scripts --max_old_space_size=4096 build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" }, "browserslist": [ ">0.2%", "not dead", "not ie <= 11", "not op_mini all" ], "devDependencies": { "jest-canvas-mock": "^2.3.1", "jest-watch-typeahead": "^0.6.5" } } ================================================ FILE: public/index.html ================================================ SurveyJS + React App
================================================ FILE: public/manifest.json ================================================ { "short_name": "React App", "name": "Create React App Sample", "icons": [ { "src": "favicon.ico", "sizes": "64x64 32x32 24x24 16x16", "type": "image/x-icon" } ], "start_url": "./index.html", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" } ================================================ FILE: src/App.css ================================================ .App-logo { animation: App-logo-spin infinite 20s linear; } .app-content { height: calc(100vh - 80px); overflow-y: auto; } h1, h1+span { padding: 0 20px; } .jumbotron ul { font-size: 21px; } @keyframes App-logo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } ================================================ FILE: src/App.js ================================================ import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom"; import "./App.css"; import { HomePage } from "./pages/Home"; import { CreatorPage } from "./pages/Creator"; import { SurveyPage } from "./pages/Survey"; import { ExportToPDFPage } from "./pages/Export"; import { AnalyticsPage } from "./pages/Analytics"; import { AnalyticsTabulatorPage } from "./pages/AnalyticsTabulator"; import "bootstrap/dist/css/bootstrap.css"; export { MyQuestion } from "./components/MyQuestion"; export default function SurveyJSReactApplication() { return (
); } ================================================ FILE: src/App.test.js ================================================ import React from 'react'; import ReactDOM from 'react-dom'; import "jest-canvas-mock"; jest.mock('plotly.js-dist-min', () => ({ Map: () => ({}), })); import App from './App'; it('renders without crashing', () => { const div = document.createElement('div'); ReactDOM.render(, div); ReactDOM.unmountComponentAtNode(div); }); ================================================ FILE: src/components/MyQuestion.js ================================================ import { Question, Serializer, ElementFactory } from "survey-core"; import { SurveyElementBase, ReactQuestionFactory } from "survey-react-ui"; const QUESTION_TYPE = "myquestion"; export function registerMyQuestion() { ElementFactory.Instance.registerElement(QUESTION_TYPE, (name) => { return new MyQuestionModel(name); }); } export class MyQuestionModel extends Question { getType() { return QUESTION_TYPE; } get text() { return this.getPropertyValue("text", ""); } set text(newValue) { this.setPropertyValue("text", newValue); } } export class MyQuestion extends SurveyElementBase { get question() { return this.props.question; } render() { if (!this.question) return null; const cssClasses = this.question.cssClasses; return (
My Text Value: {this.question.text}
); } } Serializer.addClass( QUESTION_TYPE, [ { name: "text" } ], function () { return new MyQuestionModel(""); }, "question" ); ReactQuestionFactory.Instance.registerQuestion(QUESTION_TYPE, props => { return React.createElement(MyQuestion, props); }); ================================================ FILE: src/components/SurveyAnalytics.js ================================================ import { Component } from "react"; import { data, json } from "../data/analytics_data"; import { VisualizationPanel } from "survey-analytics"; import "survey-analytics/survey.analytics.css"; import { Model } from "survey-core"; export default class SurveyAnalytics extends Component { visPanel; componentDidMount() { const survey = new Model(json); this.visPanel = new VisualizationPanel(survey.getAllQuestions(), data); this.visPanel.render(document.getElementById("summaryContainer")); } render() { return
; } } ================================================ FILE: src/components/SurveyAnalyticsTabulator.js ================================================ import { Component } from "react"; import { data, json } from "../data/analytics_data"; import jsPDF from "jspdf"; import * as XLSX from "xlsx"; import "jspdf-autotable"; import { Tabulator } from "survey-analytics/survey.analytics.tabulator.js"; import { Model } from "survey-core"; import "survey-analytics/survey.analytics.tabulator.css"; import "tabulator-tables/dist/css/tabulator.min.css"; window.jsPDF = jsPDF; window.XLSX = XLSX; export default class SurveyAnalyticsTabulator extends Component { visPanel; componentDidMount() { const survey = new Model(json); this.visPanel = new Tabulator(survey, data); this.visPanel.render(document.getElementById("summaryContainer")); } render() { return
; } } ================================================ FILE: src/components/SurveyCreator.js ================================================ import { useState } from "react"; import { SurveyCreatorComponent, SurveyCreator } from "survey-creator-react"; import { registerMyQuestion } from "./MyQuestion"; import "survey-core/defaultV2.css"; import "survey-creator-core/survey-creator-core.css"; registerMyQuestion(); export default function SurveyCreatorWidget(props) { let [creator, setCreator] = useState(); if (creator === undefined) { let options = { showLogicTab: true, showTranslationTab: true }; creator = new SurveyCreator(options); creator.saveSurveyFunc = (no, callback) => { console.log(JSON.stringify(creator.JSON)); callback(no, true); }; // creator.tabs().push({ // name: "survey-templates", // title: "My Custom Tab", // template: "custom-tab-survey-templates", // action: () => { // this.creator.makeNewViewActive("survey-templates"); // }, // data: {}, // }); setCreator(creator); } creator.JSON = props.json; return (
{/* */}
); } ================================================ FILE: src/data/analytics_data.js ================================================ export const json = { completedHtml: "

Thank you for completing the survey! (please wait for analytics to load ...)

", pages: [{ name: "page_info", elements: [{ type: "radiogroup", name: "organization_type", title: "Which of the following best describes you or your organization?", hasOther: true, choices: [{ value: "ISV", text: "ISV (building commercial/shrink-wrapped software)" }, { value: "Consulting", text: "Software consulting firm (providing development services to other organizations)" }, { value: "Custom", text: "Custom software development (as a freelancer/contractor)" }, { value: "In-house", text: "In-house software development" }, { value: "Hobbyist", text: "Hobbyist (developing apps for personal use)" }], colCount: 2 }, { type: "radiogroup", name: "developer_count", visibleIf: "{organization_type} != 'Hobbyist'", title: "How many software developers are in your organization?", choices: ["1", "2", "3-5", "6-10", "> 10"] }, { type: "radiogroup", name: "vertical_market", visibleIf: "{organization_type} != 'Hobbyist'", title: "Which vertical market does your product serve?", hasOther: true, choices: [ "Automotive", "Banking", "Consumer", "Education", "Engineering", "Energy", "Fast-moving consumer goods", "Financial", "FinTech", "Food and beverage", "Government (federal, state, local)", "Healthcare", "Insurance", "Legal", "Manufacturing", "Media", "Online", "Raw materials", "Real estate", "Religion", "Retail", "Jewelry", "Technology", "Telecommunications", "Transportation (Travel)", "Electronics", "Not-for-profit" ], colCount: 4 }, { type: "radiogroup", name: "product_discovering", title: "How did you discover our product?", hasOther: true, choices: [ "Search engine", "GitHub", "Friend or colleague", "Reddit", "Medium", "Twitter", "Facebook" ], otherText: "Other", colCount: 3 }] }, { name: "page_libraries_usage", elements: [{ type: "checkbox", name: "javascript_frameworks", title: "Which JavaScript frameworks do you use?", hasOther: true, choices: [ "React", "Angular", "jQuery", "Vue", "Meteor", "Ember", "Backbone", "Knockout", "Aurelia", "Polymer", "Mithril", ], choicesOrder: "asc", otherText: "Other", colCount: 3 }, { type: "checkbox", name: "backend_language", title: "Which web backend programming languages do you use?", hasOther: true, choices: [ "Java", "Python", "Node.js", "Go", "Django", "C#", "Ruby", ], choicesOrder: "asc", otherText: "Other", colCount: 3 }], }, { name: "page_product_usage", elements: [{ type: "radiogroup", name: "useproduct", title: "Do you currently use our components?", isRequired: true, choices: ["Yes", "No"], }, { type: "checkbox", name: "usecomponents", visibleIf: '{useproduct} = "Yes"', title: "Which of our components do you use?", isRequired: true, choices: ["Survey Library (Runner)", "Survey Creator", "Export to PDF", "SurveyJS Analytics"] }, { type: "checkbox", name: "supported_devices", title: "Which device types do you need to support?", isRequired: true, choices: [ "Desktop", "Tablet", "Mobile" ], }, { type: "radiogroup", name: "native_mobile_support", visibleIf: '{supported_devices} contains "Mobile"', title: "How important for you is native mobile support?", isRequired: true, choices: [{ value: 1, text: "I am happy with adaptive HTML rendering" }, { value: 2, text: "Somewhat important, but adaptive HTML rendering is fine" }, { value: 3, text: "Very important" }, { value: 4, text: "Cannot use your components without it" }] }, { type: "radiogroup", name: "native_framework", visibleIf: "{native_mobile_support} >= 3", title: "Which framework are you using or going to use for native mobile developlment?", hasOther: true, choices: [ "React Native", "NativeScript", "Ionic", "Xamarin", "Native iOS and Android apps", ], otherText: "Other", colCount: 2 }], }, { name: "page_alternative", elements: [{ type: "radiogroup", name: "product_alternative", title: "What would you use as an alternative if our product didn't exist?", isRequired: true, hasOther: true, choices: ["Popular cloud-based platforms", "Self-developed solution"], otherText: "Other" }, { type: "text", name: "survey_cloud_platform", visibleIf: '{product_alternative} = "Popular cloud-based platforms"', title: "What cloud-based platform would you choose?" }, { type: "radiogroup", name: "product_recommend", title: "Have you recommended our product to anyone?", choices: ["Yes", "No"] }] }, { name: "page_recommend", elements: [{ type: "rating", name: "nps_score", title: "How likely are you to recommend our product to a friend or colleague?", isRequired: true, rateMin: 0, rateMax: 10, minRateDescription: "Most unlikely", maxRateDescription: "Most likely" }, { type: "comment", name: "favorite_functionality", title: "What feature do you find most useful in our product?" }, { type: "comment", name: "product_improvement", title: "How could our components be improved to meet your needs better?" }] } ] }; // Survey results const firstResult = { organization_type: "In-house", developer_count: "1", vertical_market: "Education", product_discovering: "GitHub", javascript_frameworks: ["jQuery"], backend_language: ["Ruby"], useproduct: "Yes", usecomponents: ["Survey Library (Runner)"], supported_devices: ["Desktop", "Tablet", "Mobile"], native_mobile_support: 2, product_alternative: "Self-developed solution", product_recommend: "Yes", nps_score: 6, product_improvement: "The lack of accessibility is a huge disadvantage. That's one reason why I cannot use it in all my projects.", native_framework: "", survey_cloud_platform: "", favorite_functionality: "", }; const secondResult = { organization_type: "Consulting", developer_count: "3-5", vertical_market: "Government (federal, state, local)", product_discovering: "Search engine", javascript_frameworks: ["Vue", "jQuery", "other"], backend_language: ["Python", "Node.js"], useproduct: "Yes", usecomponents: ["Survey Library (Runner)"], supported_devices: ["Desktop"], product_alternative: "Develop ourselves", product_recommend: "Yes", nps_score: 8, native_mobile_support: "", native_framework: "", survey_cloud_platform: "", favorite_functionality: "", product_improvement: "", }; export const data = [firstResult, secondResult]; ================================================ FILE: src/data/survey_json.js ================================================ export const json = { completedHtml: "

Thank you for your feedback

Your thoughts and ideas will help us improve our product.
", completedHtmlOnCondition: [{ expression: "{nps_score} > 8", html: "

Thank you for your feedback

We are glad that you love our product. Your ideas and suggestions will help us make it even better.
" }, { expression: "{nps_score} < 7", html: "

Thank you for your feedback

We are glad that you shared your ideas with us. They will help us make our product better.
" }], pages: [{ name: "page1", elements: [{ type: "rating", name: "nps_score", title: "On a scale of zero to ten, how likely are you to recommend our product to a friend or colleague?", isRequired: true, rateMin: 0, rateMax: 10, minRateDescription: "(Most unlikely)", maxRateDescription: "(Most likely)" }, { type: "checkbox", name: "promoter_features", visibleIf: "{nps_score} >= 9", title: "Which of the following features do you value the most?", isRequired: true, validators: [{ type: "answercount", text: "Please select two features maximum.", maxCount: 2 }], hasOther: true, choices: [ "Performance", "Stability", "User Interface", "Complete Functionality" ], otherText: "Other features:", colCount: 2 }, { type: "comment", name: "passive_experience", visibleIf: "{nps_score} > 6 and {nps_score} < 9", title: "What is the primary reason for your score?" }, { type: "comment", name: "disappointed_experience", visibleIf: "{nps_score} notempty", title: "What do you miss and find disappointing in your experience with our product?" }] }], showQuestionNumbers: "off" }; ================================================ FILE: src/index.css ================================================ body { margin: 0; padding: 0; font-family: sans-serif; } ================================================ FILE: src/index.js ================================================ import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; ReactDOM.render(, document.getElementById('root')); registerServiceWorker(); ================================================ FILE: src/pages/Analytics.js ================================================ import SurveyAnalytics from "../components/SurveyAnalytics"; export function AnalyticsPage() { return (

Survey Analytics

); } ================================================ FILE: src/pages/AnalyticsTabulator.js ================================================ import SurveyAnalyticsTabulator from "../components/SurveyAnalyticsTabulator"; export function AnalyticsTabulatorPage() { return (

Survey Analytics - Table View

Uses Tabulator supported only by modern browsers.
); } ================================================ FILE: src/pages/Creator.js ================================================ import SurveyCreator from "../components/SurveyCreator"; import { json } from "../data/survey_json.js"; export function CreatorPage() { return ( <>

Survey Creator / Form Builder

); } ================================================ FILE: src/pages/Export.js ================================================ import { Model } from "survey-core"; import { SurveyPDF } from "survey-pdf"; import { json } from "../data/survey_json.js"; function savePDF(model) { const surveyPDF = new SurveyPDF(json); surveyPDF.data = model.data; surveyPDF.save(); }; export function ExportToPDFPage() { const model = new Model(json); return (

SurveyJS PDF Export

SurveyJS PDF Export is a client-side extension over the SurveyJS Library that enables users to save surveys as PDF documents.

NOTE: Dynamic elements and characteristics (visibility, validation, navigation buttons) are not supported.

Click the button below to export survey to a PDF document.

); } ================================================ FILE: src/pages/Home.js ================================================ import logo from "../logo.svg"; export function HomePage() { return (
ReactJS

SurveyJS + React Quickstart Template

SurveyJS is a set of JavaScript components that allow you and your users to build surveys / forms, store them in your database, and visualize survey results for data analysis. This quick start template uses the following SurveyJS components:

); } ================================================ FILE: src/pages/Survey.js ================================================ import React from "react"; import { Model, StylesManager } from "survey-core"; import { Survey } from "survey-react-ui"; import "survey-core/defaultV2.css"; import { json } from "../data/survey_json.js"; StylesManager.applyTheme("defaultV2"); function onValueChanged(_, options) { console.log("New value: " + options.value); } function onComplete(survey) { console.log("Survey complete! Results: " + JSON.stringify(survey.data)); } export function SurveyPage() { const model = new Model(json); return (

SurveyJS Library / Runner

); } ================================================ FILE: src/registerServiceWorker.js ================================================ // In production, we register a service worker to serve assets from local cache. // This lets the app load faster on subsequent visits in production, and gives // it offline capabilities. However, it also means that developers (and users) // will only see deployed updates on the "N+1" visit to a page, since previously // cached resources are updated in the background. // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. // This link also includes instructions on opting out of this behavior. const isLocalhost = Boolean( window.location.hostname === 'localhost' || // [::1] is the IPv6 localhost address. window.location.hostname === '[::1]' || // 127.0.0.1/8 is considered localhost for IPv4. window.location.hostname.match( /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ ) ); export default function register() { if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { // The URL constructor is available in all browsers that support SW. const publicUrl = new URL(process.env.PUBLIC_URL, window.location); if (publicUrl.origin !== window.location.origin) { // Our service worker won't work if PUBLIC_URL is on a different origin // from what our page is served on. This might happen if a CDN is used to // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 return; } window.addEventListener('load', () => { const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; if (isLocalhost) { // This is running on localhost. Lets check if a service worker still exists or not. checkValidServiceWorker(swUrl); // Add some additional logging to localhost, pointing developers to the // service worker/PWA documentation. navigator.serviceWorker.ready.then(() => { console.log( 'This web app is being served cache-first by a service ' + 'worker. To learn more, visit https://goo.gl/SC7cgQ' ); }); } else { // Is not local host. Just register service worker registerValidSW(swUrl); } }); } } function registerValidSW(swUrl) { navigator.serviceWorker .register(swUrl) .then(registration => { registration.onupdatefound = () => { const installingWorker = registration.installing; installingWorker.onstatechange = () => { if (installingWorker.state === 'installed') { if (navigator.serviceWorker.controller) { // At this point, the old content will have been purged and // the fresh content will have been added to the cache. // It's the perfect time to display a "New content is // available; please refresh." message in your web app. console.log('New content is available; please refresh.'); } else { // At this point, everything has been precached. // It's the perfect time to display a // "Content is cached for offline use." message. console.log('Content is cached for offline use.'); } } }; }; }) .catch(error => { console.error('Error during service worker registration:', error); }); } function checkValidServiceWorker(swUrl) { // Check if the service worker can be found. If it can't reload the page. fetch(swUrl) .then(response => { // Ensure service worker exists, and that we really are getting a JS file. if ( response.status === 404 || response.headers.get('content-type').indexOf('javascript') === -1 ) { // No service worker found. Probably a different app. Reload the page. navigator.serviceWorker.ready.then(registration => { registration.unregister().then(() => { window.location.reload(); }); }); } else { // Service worker found. Proceed as normal. registerValidSW(swUrl); } }) .catch(() => { console.log( 'No internet connection found. App is running in offline mode.' ); }); } export function unregister() { if ('serviceWorker' in navigator) { navigator.serviceWorker.ready.then(registration => { registration.unregister(); }); } }