diff --git a/.circleci/config.yml b/.circleci/config.yml index e7454f2..265d628 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,48 +1,28 @@ version: 2 jobs: - "node": + "python-3.6": &test-template docker: - - image: circleci/node:8.11.3 - - steps: - - checkout - - - restore_cache: - key: deps1-{{ .Branch }}-{{ checksum "package.json" }} - - - run: - name: Install package.json - command: npm i - - - save_cache: - key: deps1-{{ .Branch }}-{{ checksum "package.json" }} - paths: - - node_modules - - - run: - name: Run eslint - command: ./node_modules/.bin/eslint src - when: always - - - "python-3.6": - docker: - - image: circleci/python:3.6-stretch-browsers + - image: circleci/python:3.6-stretch-node-browsers environment: - PERCY_ENABLED: False + PERCY_ENABLE: 0 steps: - checkout + - run: + name: Write job name. + command: echo $CIRCLE_JOB > circlejob.txt + - restore_cache: - key: deps1-{{ .Branch }}-{{ checksum "tests/requirements.txt" }} + key: deps1-{{ .Branch }}-{{ checksum "circlejob.txt" }}-{{ checksum "tests/requirements.txt" }}-{{ checksum ".circleci/config.yml" }} - run: name: Create virtualenv command: | - python3 -m venv venv + sudo pip install virtualenv + python -m venv venv || virtualenv venv - run: name: Install requirements @@ -51,30 +31,21 @@ jobs: pip install -r tests/requirements.txt --quiet - save_cache: - key: deps1-{{ .Branch }}-{{ checksum "tests/requirements.txt" }} + key: deps1-{{ .Branch }}-{{ checksum "circlejob.txt" }}-{{ checksum "tests/requirements.txt" }}-{{ checksum ".circleci/config.yml" }} paths: - "venv" - run: - name: Run pylint - command: | - . venv/bin/activate - pylint usage.py tests - when: always - - - run: - name: Run flake8 + name: Generations tests command: | . venv/bin/activate - flake8 usage.py tests + pytest tests --driver Chrome when: always - - run: - name: Integration Tests - command: | - . venv/bin/activate - python -m unittest tests.test_render - when: always + "python-2.7": + <<: *test-template + docker: + - image: circleci/python:2.7-stretch-node-browsers workflows: @@ -82,4 +53,4 @@ workflows: build: jobs: - "python-3.6" - - "node" + - "python-2.7" diff --git a/.gitignore b/.gitignore index 2c426dc..53c7e67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,37 +1,274 @@ -# See https://help.github.com/ignore-files/ for more about ignoring files. +# Created by .ignore support plugin (hsz.mobi) +### VisualStudioCode template +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 -# dependencies -/node_modules +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf -# production -/build -/demo +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml -# testing -/coverage +# Gradle +.idea/**/gradle.xml +.idea/**/libraries -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests +### Node template +# Logs +logs +*.log npm-debug.log* yarn-debug.log* yarn-error.log* -# virtualenv -vv -venv +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next -# python -*.pyc +# nuxt.js build output +.nuxt -# builds -my_dash_component.egg-info -dist -*__pycache__* +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless +### Python template +# Byte-compiled / optimized / DLL files __pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +### SublimeText template +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache -*.pyc +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 3f83343..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include my_dash_component/bundle.js -include my_dash_component/metadata.json -include my_dash_component/package.json -include README.md diff --git a/README.md b/README.md index 5480451..6b2c4f6 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,45 @@ # Dash Component Boilerplate -This repository contains the minimal set of code required to create your own custom Dash component. +This repository contains a [Cookiecutter](https://github.com/audreyr/cookiecutter) template to create your own Dash components. -To create your own Dash component: -1. Fork this repo -2. Find-and-replace: - 1. `my_dash_component` with your component library name. - 2. `my-dash-component` with your component library name. - 3. `my-name` with your name and `my-email` with your email address - 4. `my-license` with your license (e.g. `MIT`) - 5. Rename the folder `my_dash_component/` with your component library name -3. Install the dependencies: -``` -npm install -``` -4. Open up the JavaScript demo environment: -``` -npm run start -``` -5. Write your component code in `src/lib/components`. There is a sample component called `ExampleComponent.react.js` that you can use for inspiration. The demo app is in `src/demo` and you will import your example component code into your demo app. -6. Test your code in a Python environment: - 1. Build your code - ``` - npm run build:js-dev - npm run build:py - ``` - 2. Run and modify the `usage.py` sample dash app: - ``` - python usage.py - ``` -7. Create a production build and publish: - 1. Build your code: - ``` - npm run build:js - npm run build:py - ``` - 2. Create a Python tarball - ``` - python setup.py sdist - ``` - This distribution tarball will get generated in the `dist/` folder +## Usage - 3. Test your tarball by copying it into a new environment and installing it locally: - ``` - pip install my_dash_component-0.0.1.tar.gz - ``` +To use this boilerplate: - 4. If it works, then you can publish the component to NPM and PyPI: - ``` - npm run publish - ``` - ``` - twine upload dist/dash_component-0.0.1.tar.gz - ``` -8. Share your component with the community! https://community.plot.ly/c/dash +1. Install the requirements: + ``` + $ pip install cookiecutter + $ pip install virtualenv + ``` + [Node.js/npm is also required.](https://nodejs.org/en/) +2. Run cookiecutter on the boilerplate repo: + ``` + $ cookiecutter git@github.com:plotly/dash-component-boilerplate.git + ``` +3. Answer the questions about the project. + - `project_name`: This is the "human-readable" name of your project. For example, "Dash Core Components". + - `project_shortname`: is derived from the project name, it is the name of the "python library" for your project. By default, this is generated from your `project_name` by lowercasing the name and replacing spaces & `-` with underscores. For example, for "Dash Core Components" this would be "dash_core_components". + - `component_name`: This is the name of the initial component that is generated. The default takes the `project_name` and remove the whitespace and `-`. As a javascript class name it should be in PascalCase. + - `author info`: author_name and author_email for package.json metadata. + - `description`: the project description, included in package.json. + - `license`: License type for the component library. + - `publish_on_npm`: Set to false to only serve locally from the package data. + - `install_dependencies`: Set to false to only generate the project structure. +4. The project will be generated in the folder of `project_shortname`. +5. Follow the directions in the generated README to start developing your new Dash component. -# More details -- Include CSS files in your distribution folder (`my_dash_component`) and reference them in `MANIFEST.in` -- The `tests` folder contains a sample integration test. This will run a sample Dash app in a browser. Run this with: - ``` - python -m tests.test_render - ``` - The Dash team uses these types of integration tests extensively. Browse the Dash component code on GitHub for more examples of testing (e.g. https://github.com/plotly/dash-core-components) -- Publishing your component to NPM will make the JavaScript bundles available on the unpkg CDN. By default, Dash servers the component library's CSS and JS from the remote unpkg CDN, so if you haven't published the component package to NPM you'll need to set the `serve_locally` flags to `True`. We will eventually make `serve_locally=True` the default, [follow our progress in this issue](https://github.com/plotly/dash/issues/284). -- Watch the [component boilerplate repository](https://github.com/plotly/dash-component-boilerplate) to stay informed of changes to our components. +Installing the dependencies can take a long time. They will be installed in a +folder named `venv`, created by virtualenv. This ensures that dash is installed +to generate the components in the `build:py` script of the generated +`package.json`. -# More Resources +## More Resources + - Learn more about Dash: https://dash.plot.ly -- View the original component boilerplate: https://github.com/plotly/dash-component-boilerplate +- Questions about this project? Create an issue: https://github.com/plotly/dash-component-boilerplate/issues/new +- Watch the [component boilerplate repository](https://github.com/plotly/dash-component-boilerplate) to stay informed of changes to our components. +- [React guide for python developers](https://dash.plot.ly/react-for-python-developers) +- Need help with your component? View the Dash Community Forum: https://community.plot.ly/c/dash +- Examples of Dash component libraries include `dash-core-components`: https://github.com/plotly/dash-core-components` and `dash-html-components`: https://github.com/plotly/dash-html-components. +- To get a feel for what's involved in creating a component, read through the [README.MD file that this cookiecutter project generates](/cookie-cutter/%7B%7Bcookiecutter.project_shortname%7D%7D/README.md) diff --git a/cookiecutter.json b/cookiecutter.json new file mode 100644 index 0000000..8377f96 --- /dev/null +++ b/cookiecutter.json @@ -0,0 +1,18 @@ +{ + "project_name": "my dash component", + "project_shortname": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}", + "component_name": "{{ cookiecutter.project_name.replace(' ', '').replace('-', '') }}", + "author_name": "Enter your name (For package.json)", + "author_email": "Enter your email (For package.json)", + "description": "Project Description", + "license": [ + "MIT License", + "BSD License", + "ISC License", + "Apache Software License 2.0", + "GNU General Public License v3", + "Not open source" + ], + "publish_on_npm": true, + "install_dependencies": true +} \ No newline at end of file diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py new file mode 100644 index 0000000..d9b8130 --- /dev/null +++ b/hooks/post_gen_project.py @@ -0,0 +1,98 @@ +from __future__ import print_function + +import shlex +import sys +import os +import subprocess +import json +import collections + +install_deps = '{{cookiecutter.install_dependencies}}' +project_shortname = '{{cookiecutter.project_shortname}}' + + +is_windows = sys.platform == 'win32' + +if is_windows: + python_executable = os.path.join('venv', 'Scripts', 'python') +else: + python_executable = os.path.join('venv', 'bin', 'python') + + +def _execute_command(cmd): + line = shlex.split(cmd, posix=not is_windows) + + print('Executing: {}'.format(cmd)) + + # call instead of Popen to get realtime output + status = subprocess.call(line, shell=is_windows) + + if status != 0: + print('post_gen_project command failed: {}'.format(cmd), + file=sys.stderr) + sys.exit(status) + + return status + + +# Patch up the package.json to use the venv python for class generation. +# If install_dependencies is false, the venv must be created manually and +# the requirements installed. +print('Patching build command') + +with open('package.json', 'r+') as package_file: + package_json = json.load(package_file, + object_pairs_hook=collections.OrderedDict) + + package_json['scripts']['build:py'] \ + = package_json['scripts']['build:py'].replace( + '%(python_path)', python_executable) + + package_file.seek(0) + package_file.truncate(0) + + json.dump(package_json, package_file, indent=4) + + +if install_deps != 'True': + print('`install_dependencies` is false!!', file=sys.stderr) + print('Please create a venv in your project root' + ' and install the dependencies in requirements.txt', + file=sys.stderr) + sys.exit(0) + +# Create a virtual env +if sys.version.split(' ')[0] > '3.2': + venv = 'python -m venv venv' +else: + venv = 'virtualenv venv' + +# noinspection PyBroadException +try: + _execute_command(venv) +except BaseException: + print( + ''' + venv creation failed. + Make sure you have installed virtualenv on python 2. + ''', + file=sys.stderr + ) + raise + +print('\n\nInstalling dependencies\n', file=sys.stderr) + +# Install python requirements. +_execute_command( + r'{} -m pip install -r requirements.txt'.format(python_executable)) + +# Install node_modules +_execute_command('npm install --ignore-scripts') + +# Run the first build +print('Building initial bundles...') +_execute_command('npm run build:all') + +print('\n{} ready!\n'.format(project_shortname)) + +sys.exit(0) diff --git a/hooks/pre_gen_project.py b/hooks/pre_gen_project.py new file mode 100644 index 0000000..942cbd5 --- /dev/null +++ b/hooks/pre_gen_project.py @@ -0,0 +1,48 @@ +from __future__ import print_function +import sys + +full_name = '{{cookiecutter.author_name}}' +email = '{{cookiecutter.author_email}}' +project_shortname = '{{cookiecutter.project_shortname}}' + +invalid_package_message = 'Invalid {variable}: {value}' +project_shortname_message = ''' +({variable}={value}) should be a valid python package name. + +Only lowercase letters and `_` are allowed. +''' + + +def verify(check, variable_name, value, message): + if check(value): + print(message.format(variable=variable_name, value=value), + file=sys.stderr) + sys.exit(-1) + + +def package_check(s): + return '(For package.json)' in s + + +def _check_specials_characters(s): + i = ord(s) + if i == 95: + # Allow for `_` + return False + return not 96 < i < 123 + + +def check_specials_characters(s): + return any(_check_specials_characters(x) for x in s) + + +for values in ( + (package_check, 'author_name', full_name, invalid_package_message), + (package_check, 'author_email', email, invalid_package_message), + (check_specials_characters, 'project_shortname', + project_shortname, project_shortname_message) +): + verify(*values) + + +sys.exit(0) diff --git a/my_dash_component/bundle.js b/my_dash_component/bundle.js deleted file mode 100644 index fdd783e..0000000 --- a/my_dash_component/bundle.js +++ /dev/null @@ -1,184 +0,0 @@ -window["my_dash_component"] = -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./src/lib/index.js"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "./node_modules/object-assign/index.js": -/*!*********************************************!*\ - !*** ./node_modules/object-assign/index.js ***! - \*********************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("/*\nobject-assign\n(c) Sindre Sorhus\n@license MIT\n*/\n\n\n/* eslint-disable no-unused-vars */\nvar getOwnPropertySymbols = Object.getOwnPropertySymbols;\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\nvar propIsEnumerable = Object.prototype.propertyIsEnumerable;\n\nfunction toObject(val) {\n\tif (val === null || val === undefined) {\n\t\tthrow new TypeError('Object.assign cannot be called with null or undefined');\n\t}\n\n\treturn Object(val);\n}\n\nfunction shouldUseNative() {\n\ttry {\n\t\tif (!Object.assign) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Detect buggy property enumeration order in older V8 versions.\n\n\t\t// https://bugs.chromium.org/p/v8/issues/detail?id=4118\n\t\tvar test1 = new String('abc'); // eslint-disable-line no-new-wrappers\n\t\ttest1[5] = 'de';\n\t\tif (Object.getOwnPropertyNames(test1)[0] === '5') {\n\t\t\treturn false;\n\t\t}\n\n\t\t// https://bugs.chromium.org/p/v8/issues/detail?id=3056\n\t\tvar test2 = {};\n\t\tfor (var i = 0; i < 10; i++) {\n\t\t\ttest2['_' + String.fromCharCode(i)] = i;\n\t\t}\n\t\tvar order2 = Object.getOwnPropertyNames(test2).map(function (n) {\n\t\t\treturn test2[n];\n\t\t});\n\t\tif (order2.join('') !== '0123456789') {\n\t\t\treturn false;\n\t\t}\n\n\t\t// https://bugs.chromium.org/p/v8/issues/detail?id=3056\n\t\tvar test3 = {};\n\t\t'abcdefghijklmnopqrst'.split('').forEach(function (letter) {\n\t\t\ttest3[letter] = letter;\n\t\t});\n\t\tif (Object.keys(Object.assign({}, test3)).join('') !==\n\t\t\t\t'abcdefghijklmnopqrst') {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t} catch (err) {\n\t\t// We don't expect any of the above to throw, but better to be safe.\n\t\treturn false;\n\t}\n}\n\nmodule.exports = shouldUseNative() ? Object.assign : function (target, source) {\n\tvar from;\n\tvar to = toObject(target);\n\tvar symbols;\n\n\tfor (var s = 1; s < arguments.length; s++) {\n\t\tfrom = Object(arguments[s]);\n\n\t\tfor (var key in from) {\n\t\t\tif (hasOwnProperty.call(from, key)) {\n\t\t\t\tto[key] = from[key];\n\t\t\t}\n\t\t}\n\n\t\tif (getOwnPropertySymbols) {\n\t\t\tsymbols = getOwnPropertySymbols(from);\n\t\t\tfor (var i = 0; i < symbols.length; i++) {\n\t\t\t\tif (propIsEnumerable.call(from, symbols[i])) {\n\t\t\t\t\tto[symbols[i]] = from[symbols[i]];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn to;\n};\n\n\n//# sourceURL=webpack://my_dash_component/./node_modules/object-assign/index.js?"); - -/***/ }), - -/***/ "./node_modules/prop-types/checkPropTypes.js": -/*!***************************************************!*\ - !*** ./node_modules/prop-types/checkPropTypes.js ***! - \***************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\n\nvar printWarning = function() {};\n\nif (true) {\n var ReactPropTypesSecret = __webpack_require__(/*! ./lib/ReactPropTypesSecret */ \"./node_modules/prop-types/lib/ReactPropTypesSecret.js\");\n var loggedTypeFailures = {};\n\n printWarning = function(text) {\n var message = 'Warning: ' + text;\n if (typeof console !== 'undefined') {\n console.error(message);\n }\n try {\n // --- Welcome to debugging React ---\n // This error was thrown as a convenience so that you can use this stack\n // to find the callsite that caused this warning to fire.\n throw new Error(message);\n } catch (x) {}\n };\n}\n\n/**\n * Assert that the values match with the type specs.\n * Error messages are memorized and will only be shown once.\n *\n * @param {object} typeSpecs Map of name to a ReactPropType\n * @param {object} values Runtime values that need to be type-checked\n * @param {string} location e.g. \"prop\", \"context\", \"child context\"\n * @param {string} componentName Name of the component for error messages.\n * @param {?Function} getStack Returns the component stack.\n * @private\n */\nfunction checkPropTypes(typeSpecs, values, location, componentName, getStack) {\n if (true) {\n for (var typeSpecName in typeSpecs) {\n if (typeSpecs.hasOwnProperty(typeSpecName)) {\n var error;\n // Prop type validation may throw. In case they do, we don't want to\n // fail the render phase where it didn't fail before. So we log it.\n // After these have been cleaned up, we'll let them throw.\n try {\n // This is intentionally an invariant that gets caught. It's the same\n // behavior as without this statement except with a better message.\n if (typeof typeSpecs[typeSpecName] !== 'function') {\n var err = Error(\n (componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' +\n 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.'\n );\n err.name = 'Invariant Violation';\n throw err;\n }\n error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);\n } catch (ex) {\n error = ex;\n }\n if (error && !(error instanceof Error)) {\n printWarning(\n (componentName || 'React class') + ': type specification of ' +\n location + ' `' + typeSpecName + '` is invalid; the type checker ' +\n 'function must return `null` or an `Error` but returned a ' + typeof error + '. ' +\n 'You may have forgotten to pass an argument to the type checker ' +\n 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +\n 'shape all require an argument).'\n )\n\n }\n if (error instanceof Error && !(error.message in loggedTypeFailures)) {\n // Only monitor this failure once because there tends to be a lot of the\n // same error.\n loggedTypeFailures[error.message] = true;\n\n var stack = getStack ? getStack() : '';\n\n printWarning(\n 'Failed ' + location + ' type: ' + error.message + (stack != null ? stack : '')\n );\n }\n }\n }\n }\n}\n\nmodule.exports = checkPropTypes;\n\n\n//# sourceURL=webpack://my_dash_component/./node_modules/prop-types/checkPropTypes.js?"); - -/***/ }), - -/***/ "./node_modules/prop-types/factoryWithTypeCheckers.js": -/*!************************************************************!*\ - !*** ./node_modules/prop-types/factoryWithTypeCheckers.js ***! - \************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\n\nvar assign = __webpack_require__(/*! object-assign */ \"./node_modules/object-assign/index.js\");\n\nvar ReactPropTypesSecret = __webpack_require__(/*! ./lib/ReactPropTypesSecret */ \"./node_modules/prop-types/lib/ReactPropTypesSecret.js\");\nvar checkPropTypes = __webpack_require__(/*! ./checkPropTypes */ \"./node_modules/prop-types/checkPropTypes.js\");\n\nvar printWarning = function() {};\n\nif (true) {\n printWarning = function(text) {\n var message = 'Warning: ' + text;\n if (typeof console !== 'undefined') {\n console.error(message);\n }\n try {\n // --- Welcome to debugging React ---\n // This error was thrown as a convenience so that you can use this stack\n // to find the callsite that caused this warning to fire.\n throw new Error(message);\n } catch (x) {}\n };\n}\n\nfunction emptyFunctionThatReturnsNull() {\n return null;\n}\n\nmodule.exports = function(isValidElement, throwOnDirectAccess) {\n /* global Symbol */\n var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;\n var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.\n\n /**\n * Returns the iterator method function contained on the iterable object.\n *\n * Be sure to invoke the function with the iterable as context:\n *\n * var iteratorFn = getIteratorFn(myIterable);\n * if (iteratorFn) {\n * var iterator = iteratorFn.call(myIterable);\n * ...\n * }\n *\n * @param {?object} maybeIterable\n * @return {?function}\n */\n function getIteratorFn(maybeIterable) {\n var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]);\n if (typeof iteratorFn === 'function') {\n return iteratorFn;\n }\n }\n\n /**\n * Collection of methods that allow declaration and validation of props that are\n * supplied to React components. Example usage:\n *\n * var Props = require('ReactPropTypes');\n * var MyArticle = React.createClass({\n * propTypes: {\n * // An optional string prop named \"description\".\n * description: Props.string,\n *\n * // A required enum prop named \"category\".\n * category: Props.oneOf(['News','Photos']).isRequired,\n *\n * // A prop named \"dialog\" that requires an instance of Dialog.\n * dialog: Props.instanceOf(Dialog).isRequired\n * },\n * render: function() { ... }\n * });\n *\n * A more formal specification of how these methods are used:\n *\n * type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...)\n * decl := ReactPropTypes.{type}(.isRequired)?\n *\n * Each and every declaration produces a function with the same signature. This\n * allows the creation of custom validation functions. For example:\n *\n * var MyLink = React.createClass({\n * propTypes: {\n * // An optional string or URI prop named \"href\".\n * href: function(props, propName, componentName) {\n * var propValue = props[propName];\n * if (propValue != null && typeof propValue !== 'string' &&\n * !(propValue instanceof URI)) {\n * return new Error(\n * 'Expected a string or an URI for ' + propName + ' in ' +\n * componentName\n * );\n * }\n * }\n * },\n * render: function() {...}\n * });\n *\n * @internal\n */\n\n var ANONYMOUS = '<>';\n\n // Important!\n // Keep this list in sync with production version in `./factoryWithThrowingShims.js`.\n var ReactPropTypes = {\n array: createPrimitiveTypeChecker('array'),\n bool: createPrimitiveTypeChecker('boolean'),\n func: createPrimitiveTypeChecker('function'),\n number: createPrimitiveTypeChecker('number'),\n object: createPrimitiveTypeChecker('object'),\n string: createPrimitiveTypeChecker('string'),\n symbol: createPrimitiveTypeChecker('symbol'),\n\n any: createAnyTypeChecker(),\n arrayOf: createArrayOfTypeChecker,\n element: createElementTypeChecker(),\n instanceOf: createInstanceTypeChecker,\n node: createNodeChecker(),\n objectOf: createObjectOfTypeChecker,\n oneOf: createEnumTypeChecker,\n oneOfType: createUnionTypeChecker,\n shape: createShapeTypeChecker,\n exact: createStrictShapeTypeChecker,\n };\n\n /**\n * inlined Object.is polyfill to avoid requiring consumers ship their own\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is\n */\n /*eslint-disable no-self-compare*/\n function is(x, y) {\n // SameValue algorithm\n if (x === y) {\n // Steps 1-5, 7-10\n // Steps 6.b-6.e: +0 != -0\n return x !== 0 || 1 / x === 1 / y;\n } else {\n // Step 6.a: NaN == NaN\n return x !== x && y !== y;\n }\n }\n /*eslint-enable no-self-compare*/\n\n /**\n * We use an Error-like object for backward compatibility as people may call\n * PropTypes directly and inspect their output. However, we don't use real\n * Errors anymore. We don't inspect their stack anyway, and creating them\n * is prohibitively expensive if they are created too often, such as what\n * happens in oneOfType() for any type before the one that matched.\n */\n function PropTypeError(message) {\n this.message = message;\n this.stack = '';\n }\n // Make `instanceof Error` still work for returned errors.\n PropTypeError.prototype = Error.prototype;\n\n function createChainableTypeChecker(validate) {\n if (true) {\n var manualPropTypeCallCache = {};\n var manualPropTypeWarningCount = 0;\n }\n function checkType(isRequired, props, propName, componentName, location, propFullName, secret) {\n componentName = componentName || ANONYMOUS;\n propFullName = propFullName || propName;\n\n if (secret !== ReactPropTypesSecret) {\n if (throwOnDirectAccess) {\n // New behavior only for users of `prop-types` package\n var err = new Error(\n 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' +\n 'Use `PropTypes.checkPropTypes()` to call them. ' +\n 'Read more at http://fb.me/use-check-prop-types'\n );\n err.name = 'Invariant Violation';\n throw err;\n } else if (\"development\" !== 'production' && typeof console !== 'undefined') {\n // Old behavior for people using React.PropTypes\n var cacheKey = componentName + ':' + propName;\n if (\n !manualPropTypeCallCache[cacheKey] &&\n // Avoid spamming the console because they are often not actionable except for lib authors\n manualPropTypeWarningCount < 3\n ) {\n printWarning(\n 'You are manually calling a React.PropTypes validation ' +\n 'function for the `' + propFullName + '` prop on `' + componentName + '`. This is deprecated ' +\n 'and will throw in the standalone `prop-types` package. ' +\n 'You may be seeing this warning due to a third-party PropTypes ' +\n 'library. See https://fb.me/react-warning-dont-call-proptypes ' + 'for details.'\n );\n manualPropTypeCallCache[cacheKey] = true;\n manualPropTypeWarningCount++;\n }\n }\n }\n if (props[propName] == null) {\n if (isRequired) {\n if (props[propName] === null) {\n return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.'));\n }\n return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.'));\n }\n return null;\n } else {\n return validate(props, propName, componentName, location, propFullName);\n }\n }\n\n var chainedCheckType = checkType.bind(null, false);\n chainedCheckType.isRequired = checkType.bind(null, true);\n\n return chainedCheckType;\n }\n\n function createPrimitiveTypeChecker(expectedType) {\n function validate(props, propName, componentName, location, propFullName, secret) {\n var propValue = props[propName];\n var propType = getPropType(propValue);\n if (propType !== expectedType) {\n // `propValue` being instance of, say, date/regexp, pass the 'object'\n // check, but we can offer a more precise error message here rather than\n // 'of type `object`'.\n var preciseType = getPreciseType(propValue);\n\n return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + preciseType + '` supplied to `' + componentName + '`, expected ') + ('`' + expectedType + '`.'));\n }\n return null;\n }\n return createChainableTypeChecker(validate);\n }\n\n function createAnyTypeChecker() {\n return createChainableTypeChecker(emptyFunctionThatReturnsNull);\n }\n\n function createArrayOfTypeChecker(typeChecker) {\n function validate(props, propName, componentName, location, propFullName) {\n if (typeof typeChecker !== 'function') {\n return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside arrayOf.');\n }\n var propValue = props[propName];\n if (!Array.isArray(propValue)) {\n var propType = getPropType(propValue);\n return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an array.'));\n }\n for (var i = 0; i < propValue.length; i++) {\n var error = typeChecker(propValue, i, componentName, location, propFullName + '[' + i + ']', ReactPropTypesSecret);\n if (error instanceof Error) {\n return error;\n }\n }\n return null;\n }\n return createChainableTypeChecker(validate);\n }\n\n function createElementTypeChecker() {\n function validate(props, propName, componentName, location, propFullName) {\n var propValue = props[propName];\n if (!isValidElement(propValue)) {\n var propType = getPropType(propValue);\n return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement.'));\n }\n return null;\n }\n return createChainableTypeChecker(validate);\n }\n\n function createInstanceTypeChecker(expectedClass) {\n function validate(props, propName, componentName, location, propFullName) {\n if (!(props[propName] instanceof expectedClass)) {\n var expectedClassName = expectedClass.name || ANONYMOUS;\n var actualClassName = getClassName(props[propName]);\n return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + actualClassName + '` supplied to `' + componentName + '`, expected ') + ('instance of `' + expectedClassName + '`.'));\n }\n return null;\n }\n return createChainableTypeChecker(validate);\n }\n\n function createEnumTypeChecker(expectedValues) {\n if (!Array.isArray(expectedValues)) {\n true ? printWarning('Invalid argument supplied to oneOf, expected an instance of array.') : undefined;\n return emptyFunctionThatReturnsNull;\n }\n\n function validate(props, propName, componentName, location, propFullName) {\n var propValue = props[propName];\n for (var i = 0; i < expectedValues.length; i++) {\n if (is(propValue, expectedValues[i])) {\n return null;\n }\n }\n\n var valuesString = JSON.stringify(expectedValues);\n return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of value `' + propValue + '` ' + ('supplied to `' + componentName + '`, expected one of ' + valuesString + '.'));\n }\n return createChainableTypeChecker(validate);\n }\n\n function createObjectOfTypeChecker(typeChecker) {\n function validate(props, propName, componentName, location, propFullName) {\n if (typeof typeChecker !== 'function') {\n return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside objectOf.');\n }\n var propValue = props[propName];\n var propType = getPropType(propValue);\n if (propType !== 'object') {\n return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an object.'));\n }\n for (var key in propValue) {\n if (propValue.hasOwnProperty(key)) {\n var error = typeChecker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);\n if (error instanceof Error) {\n return error;\n }\n }\n }\n return null;\n }\n return createChainableTypeChecker(validate);\n }\n\n function createUnionTypeChecker(arrayOfTypeCheckers) {\n if (!Array.isArray(arrayOfTypeCheckers)) {\n true ? printWarning('Invalid argument supplied to oneOfType, expected an instance of array.') : undefined;\n return emptyFunctionThatReturnsNull;\n }\n\n for (var i = 0; i < arrayOfTypeCheckers.length; i++) {\n var checker = arrayOfTypeCheckers[i];\n if (typeof checker !== 'function') {\n printWarning(\n 'Invalid argument supplied to oneOfType. Expected an array of check functions, but ' +\n 'received ' + getPostfixForTypeWarning(checker) + ' at index ' + i + '.'\n );\n return emptyFunctionThatReturnsNull;\n }\n }\n\n function validate(props, propName, componentName, location, propFullName) {\n for (var i = 0; i < arrayOfTypeCheckers.length; i++) {\n var checker = arrayOfTypeCheckers[i];\n if (checker(props, propName, componentName, location, propFullName, ReactPropTypesSecret) == null) {\n return null;\n }\n }\n\n return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`.'));\n }\n return createChainableTypeChecker(validate);\n }\n\n function createNodeChecker() {\n function validate(props, propName, componentName, location, propFullName) {\n if (!isNode(props[propName])) {\n return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a ReactNode.'));\n }\n return null;\n }\n return createChainableTypeChecker(validate);\n }\n\n function createShapeTypeChecker(shapeTypes) {\n function validate(props, propName, componentName, location, propFullName) {\n var propValue = props[propName];\n var propType = getPropType(propValue);\n if (propType !== 'object') {\n return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));\n }\n for (var key in shapeTypes) {\n var checker = shapeTypes[key];\n if (!checker) {\n continue;\n }\n var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);\n if (error) {\n return error;\n }\n }\n return null;\n }\n return createChainableTypeChecker(validate);\n }\n\n function createStrictShapeTypeChecker(shapeTypes) {\n function validate(props, propName, componentName, location, propFullName) {\n var propValue = props[propName];\n var propType = getPropType(propValue);\n if (propType !== 'object') {\n return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));\n }\n // We need to check all keys in case some are required but missing from\n // props.\n var allKeys = assign({}, props[propName], shapeTypes);\n for (var key in allKeys) {\n var checker = shapeTypes[key];\n if (!checker) {\n return new PropTypeError(\n 'Invalid ' + location + ' `' + propFullName + '` key `' + key + '` supplied to `' + componentName + '`.' +\n '\\nBad object: ' + JSON.stringify(props[propName], null, ' ') +\n '\\nValid keys: ' + JSON.stringify(Object.keys(shapeTypes), null, ' ')\n );\n }\n var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);\n if (error) {\n return error;\n }\n }\n return null;\n }\n\n return createChainableTypeChecker(validate);\n }\n\n function isNode(propValue) {\n switch (typeof propValue) {\n case 'number':\n case 'string':\n case 'undefined':\n return true;\n case 'boolean':\n return !propValue;\n case 'object':\n if (Array.isArray(propValue)) {\n return propValue.every(isNode);\n }\n if (propValue === null || isValidElement(propValue)) {\n return true;\n }\n\n var iteratorFn = getIteratorFn(propValue);\n if (iteratorFn) {\n var iterator = iteratorFn.call(propValue);\n var step;\n if (iteratorFn !== propValue.entries) {\n while (!(step = iterator.next()).done) {\n if (!isNode(step.value)) {\n return false;\n }\n }\n } else {\n // Iterator will provide entry [k,v] tuples rather than values.\n while (!(step = iterator.next()).done) {\n var entry = step.value;\n if (entry) {\n if (!isNode(entry[1])) {\n return false;\n }\n }\n }\n }\n } else {\n return false;\n }\n\n return true;\n default:\n return false;\n }\n }\n\n function isSymbol(propType, propValue) {\n // Native Symbol.\n if (propType === 'symbol') {\n return true;\n }\n\n // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol'\n if (propValue['@@toStringTag'] === 'Symbol') {\n return true;\n }\n\n // Fallback for non-spec compliant Symbols which are polyfilled.\n if (typeof Symbol === 'function' && propValue instanceof Symbol) {\n return true;\n }\n\n return false;\n }\n\n // Equivalent of `typeof` but with special handling for array and regexp.\n function getPropType(propValue) {\n var propType = typeof propValue;\n if (Array.isArray(propValue)) {\n return 'array';\n }\n if (propValue instanceof RegExp) {\n // Old webkits (at least until Android 4.0) return 'function' rather than\n // 'object' for typeof a RegExp. We'll normalize this here so that /bla/\n // passes PropTypes.object.\n return 'object';\n }\n if (isSymbol(propType, propValue)) {\n return 'symbol';\n }\n return propType;\n }\n\n // This handles more types than `getPropType`. Only used for error messages.\n // See `createPrimitiveTypeChecker`.\n function getPreciseType(propValue) {\n if (typeof propValue === 'undefined' || propValue === null) {\n return '' + propValue;\n }\n var propType = getPropType(propValue);\n if (propType === 'object') {\n if (propValue instanceof Date) {\n return 'date';\n } else if (propValue instanceof RegExp) {\n return 'regexp';\n }\n }\n return propType;\n }\n\n // Returns a string that is postfixed to a warning about an invalid type.\n // For example, \"undefined\" or \"of type array\"\n function getPostfixForTypeWarning(value) {\n var type = getPreciseType(value);\n switch (type) {\n case 'array':\n case 'object':\n return 'an ' + type;\n case 'boolean':\n case 'date':\n case 'regexp':\n return 'a ' + type;\n default:\n return type;\n }\n }\n\n // Returns class name of the object, if any.\n function getClassName(propValue) {\n if (!propValue.constructor || !propValue.constructor.name) {\n return ANONYMOUS;\n }\n return propValue.constructor.name;\n }\n\n ReactPropTypes.checkPropTypes = checkPropTypes;\n ReactPropTypes.PropTypes = ReactPropTypes;\n\n return ReactPropTypes;\n};\n\n\n//# sourceURL=webpack://my_dash_component/./node_modules/prop-types/factoryWithTypeCheckers.js?"); - -/***/ }), - -/***/ "./node_modules/prop-types/index.js": -/*!******************************************!*\ - !*** ./node_modules/prop-types/index.js ***! - \******************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nif (true) {\n var REACT_ELEMENT_TYPE = (typeof Symbol === 'function' &&\n Symbol.for &&\n Symbol.for('react.element')) ||\n 0xeac7;\n\n var isValidElement = function(object) {\n return typeof object === 'object' &&\n object !== null &&\n object.$$typeof === REACT_ELEMENT_TYPE;\n };\n\n // By explicitly using `prop-types` you are opting into new development behavior.\n // http://fb.me/prop-types-in-prod\n var throwOnDirectAccess = true;\n module.exports = __webpack_require__(/*! ./factoryWithTypeCheckers */ \"./node_modules/prop-types/factoryWithTypeCheckers.js\")(isValidElement, throwOnDirectAccess);\n} else {}\n\n\n//# sourceURL=webpack://my_dash_component/./node_modules/prop-types/index.js?"); - -/***/ }), - -/***/ "./node_modules/prop-types/lib/ReactPropTypesSecret.js": -/*!*************************************************************!*\ - !*** ./node_modules/prop-types/lib/ReactPropTypesSecret.js ***! - \*************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\n\nvar ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';\n\nmodule.exports = ReactPropTypesSecret;\n\n\n//# sourceURL=webpack://my_dash_component/./node_modules/prop-types/lib/ReactPropTypesSecret.js?"); - -/***/ }), - -/***/ "./src/lib/components/ExampleComponent.react.js": -/*!******************************************************!*\ - !*** ./src/lib/components/ExampleComponent.react.js ***! - \******************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _react = __webpack_require__(/*! react */ \"react\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _propTypes = __webpack_require__(/*! prop-types */ \"./node_modules/prop-types/index.js\");\n\nvar _propTypes2 = _interopRequireDefault(_propTypes);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\n/**\n * ExampleComponent is an example component.\n * It takes a property, `label`, and\n * displays it.\n * It renders an input with the property `value`\n * which is editable by the user.\n */\nvar ExampleComponent = function (_Component) {\n _inherits(ExampleComponent, _Component);\n\n function ExampleComponent() {\n _classCallCheck(this, ExampleComponent);\n\n return _possibleConstructorReturn(this, (ExampleComponent.__proto__ || Object.getPrototypeOf(ExampleComponent)).apply(this, arguments));\n }\n\n _createClass(ExampleComponent, [{\n key: 'render',\n value: function render() {\n var _this2 = this;\n\n var _props = this.props,\n id = _props.id,\n label = _props.label,\n setProps = _props.setProps,\n value = _props.value;\n\n\n return _react2.default.createElement(\n 'div',\n { id: id },\n 'ExampleComponent: ',\n label,\n _react2.default.createElement('input', {\n value: value,\n onChange: function onChange(e) {\n /*\n * Send the new value to the parent component.\n # setProps is a prop that is automatically supplied\n * by dash's front-end (\"dash-renderer\").\n * In a Dash app, this will send the data back to the\n * Python Dash app server.\n * If the component properties are not \"subscribed\"\n * to by a Dash callback, then Dash dash-renderer\n * will not pass through `setProps` and it is expected\n * that the component manages its own state.\n */\n if (setProps) {\n setProps({\n value: e.target.value\n });\n } else {\n _this2.setState({\n value: e.target.value\n });\n }\n }\n })\n );\n }\n }]);\n\n return ExampleComponent;\n}(_react.Component);\n\nexports.default = ExampleComponent;\n\n\nExampleComponent.propTypes = {\n /**\n * The ID used to identify this compnent in Dash callbacks\n */\n id: _propTypes2.default.string,\n\n /**\n * A label that will be printed when this component is rendered.\n */\n label: _propTypes2.default.string.isRequired,\n\n /**\n * The value displayed in the input\n */\n value: _propTypes2.default.string,\n\n /**\n * Dash-assigned callback that should be called whenever any of the\n * properties change\n */\n setProps: _propTypes2.default.func\n};\n\n//# sourceURL=webpack://my_dash_component/./src/lib/components/ExampleComponent.react.js?"); - -/***/ }), - -/***/ "./src/lib/index.js": -/*!**************************!*\ - !*** ./src/lib/index.js ***! - \**************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.ExampleComponent = undefined;\n\nvar _ExampleComponent = __webpack_require__(/*! ./components/ExampleComponent.react */ \"./src/lib/components/ExampleComponent.react.js\");\n\nvar _ExampleComponent2 = _interopRequireDefault(_ExampleComponent);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nexports.ExampleComponent = _ExampleComponent2.default; /* eslint-disable import/prefer-default-export */\n\n//# sourceURL=webpack://my_dash_component/./src/lib/index.js?"); - -/***/ }), - -/***/ "react": -/*!************************!*\ - !*** external "React" ***! - \************************/ -/*! no static exports found */ -/***/ (function(module, exports) { - -eval("(function() { module.exports = window[\"React\"]; }());\n\n//# sourceURL=webpack://my_dash_component/external_%22React%22?"); - -/***/ }) - -/******/ }); \ No newline at end of file diff --git a/my_dash_component/metadata.json b/my_dash_component/metadata.json deleted file mode 100644 index 67e5ea5..0000000 --- a/my_dash_component/metadata.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "src/lib/components/ExampleComponent.react.js": { - "description": "ExampleComponent is an example component.\nIt takes a property, `label`, and\ndisplays it.\nIt renders an input with the property `value`\nwhich is editable by the user.", - "displayName": "ExampleComponent", - "methods": [], - "props": { - "id": { - "type": { - "name": "string" - }, - "required": false, - "description": "The ID used to identify this compnent in Dash callbacks" - }, - "label": { - "type": { - "name": "string" - }, - "required": true, - "description": "A label that will be printed when this component is rendered." - }, - "value": { - "type": { - "name": "string" - }, - "required": false, - "description": "The value displayed in the input" - }, - "setProps": { - "type": { - "name": "func" - }, - "required": false, - "description": "Dash-assigned callback that should be called whenever any of the\nproperties change" - } - } - } -} diff --git a/my_dash_component/package.json b/my_dash_component/package.json deleted file mode 100644 index 2f885fc..0000000 --- a/my_dash_component/package.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "my-dash-component", - "version": "0.0.1", - "description": "my-dash-component", - "main": "build/index.js", - "scripts": { - "start": "webpack-serve ./webpack.serve.config.js --open", - "build:js-dev": "webpack --mode development", - "build:js": "webpack --mode production", - "build:py": "./extract-meta src/lib/components > my_dash_component/metadata.json && cp package.json my_dash_component" - }, - "author": "my-name my-email", - "license": "my-license", - "dependencies": { - "ramda": "^0.25.0", - "react": "15.4.2", - "react-dom": "15.4.2" - }, - "devDependencies": { - "babel-core": "^6.26.3", - "babel-eslint": "^8.2.3", - "babel-loader": "^7.1.4", - "babel-preset-env": "^1.7.0", - "babel-preset-react": "^6.24.1", - "css-loader": "^0.28.11", - "eslint": "^4.19.1", - "eslint-config-prettier": "^2.9.0", - "eslint-plugin-import": "^2.12.0", - "eslint-plugin-react": "^7.9.1", - "npm": "^6.1.0", - "react-docgen": "^2.20.1", - "style-loader": "^0.21.0", - "webpack": "^4.8.3", - "webpack-cli": "^2.1.3", - "webpack-serve": "^1.0.2" - }, - "peerDependencies": { - "react": ">=0.14", - "react-dom": ">=0.14" - }, - "engines": { - "node": ">=8.11.0", - "npm": ">=6.1.0" - } -} diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..39baf33 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +testpaths = tests/ diff --git a/src/lib/index.js b/src/lib/index.js deleted file mode 100644 index e5530c1..0000000 --- a/src/lib/index.js +++ /dev/null @@ -1,6 +0,0 @@ -/* eslint-disable import/prefer-default-export */ -import ExampleComponent from './components/ExampleComponent.react'; - -export { - ExampleComponent -}; diff --git a/tests/IntegrationTests.py b/tests/IntegrationTests.py deleted file mode 100644 index 1487a39..0000000 --- a/tests/IntegrationTests.py +++ /dev/null @@ -1,108 +0,0 @@ -from __future__ import absolute_import - -import logging -import os -import multiprocessing -import sys -import time -import unittest -import percy -import threading -import platform -import flask -import requests - -from selenium import webdriver -from selenium.webdriver.chrome.options import Options - - -class IntegrationTests(unittest.TestCase): - def percy_snapshot(self, name=''): - if os.environ.get('PERCY_ENABLED', False): - snapshot_name = '{} - {}'.format(name, sys.version_info) - self.percy_runner.snapshot( - name=snapshot_name - ) - - @classmethod - def setUpClass(cls): - super(IntegrationTests, cls).setUpClass() - - options = Options() - if 'DASH_TEST_CHROMEPATH' in os.environ: - options.binary_location = os.environ['DASH_TEST_CHROMEPATH'] - - cls.driver = webdriver.Chrome(chrome_options=options) - - if os.environ.get('PERCY_ENABLED', False): - loader = percy.ResourceLoader( - webdriver=cls.driver - ) - cls.percy_runner = percy.Runner(loader=loader) - cls.percy_runner.initialize_build() - - @classmethod - def tearDownClass(cls): - super(IntegrationTests, cls).tearDownClass() - - cls.driver.quit() - if os.environ.get('PERCY_ENABLED', False): - cls.percy_runner.finalize_build() - - def setUp(self): - pass - - def tearDown(self): - time.sleep(3) - if platform.system() == 'Windows': - requests.get('http://localhost:8050/stop') - else: - self.server_process.terminate() - time.sleep(3) - - def startServer(self, app): - if 'DASH_TEST_PROCESSES' in os.environ: - processes = int(os.environ['DASH_TEST_PROCESSES']) - else: - processes = 4 - - def run(): - app.scripts.config.serve_locally = True - app.css.config.serve_locally = True - app.run_server( - port=8050, - debug=False, - processes=processes - ) - - def run_windows(): - app.scripts.config.serve_locally = True - app.css.config.serve_locally = True - - @app.server.route('/stop') - def _stop_server_windows(): - stopper = flask.request.environ['werkzeug.server.shutdown'] - stopper() - return 'stop' - - app.run_server( - port=8050, - debug=False, - threaded=True - ) - - # Run on a separate process so that it doesn't block - - system = platform.system() - if system == 'Windows': - self.server_thread = threading.Thread(target=run_windows) - self.server_thread.start() - else: - self.server_process = multiprocessing.Process(target=run) - self.server_process.start() - logging.getLogger('werkzeug').setLevel(logging.ERROR) - time.sleep(5) - - # Visit the dash page - self.driver.get('http://localhost:8050') - time.sleep(0.5) diff --git a/tests/requirements.txt b/tests/requirements.txt index c692ef6..becc970 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,13 +1,7 @@ -# Switch into a virtual environment -# pip install -r requirements.txt - -chromedriver-binary dash -dash-core-components dash-html-components -dash-renderer -ipdb -percy -selenium -flake8 -pylint +dash-core-components +pytest +pytest-cookies +pytest-selenium +pytest-dash diff --git a/tests/test_generate.py b/tests/test_generate.py new file mode 100644 index 0000000..fecf309 --- /dev/null +++ b/tests/test_generate.py @@ -0,0 +1,29 @@ +import json +import os +import sys + + +default_context = { + 'install_dependencies': False, + 'project_name': 'Test Component', + 'author_name': 'test', + 'author_email': 'test@example.com' +} + + +def test_package_json(cookies): + result = cookies.bake(extra_context=default_context) + + package_json = json.loads(result.project.join('package.json').read()) + + if sys.platform == 'win32': + python = os.path.join('venv', 'Scripts', 'python') + else: + python = os.path.join('venv', 'bin', 'python') + + assert package_json['name'] == 'test_component' + assert package_json['license'] == 'MIT' + assert python in package_json['scripts']['build:py'] + assert package_json['author'] == \ + '{} {}'.format(default_context['author_name'], + default_context['author_email']) diff --git a/tests/test_install.py b/tests/test_install.py new file mode 100644 index 0000000..f353b2d --- /dev/null +++ b/tests/test_install.py @@ -0,0 +1,41 @@ +import shutil +import time +import sys + + +def test_install(cookies, dash_app, selenium): + results = cookies.bake(extra_context={ + 'project_name': 'Test Component', + 'author_name': 'test', + 'author_email': 'test', + }) + + # Add the generated project to the path so it can be loaded from usage.py + # It lies somewhere in a temp directory created by pytest-cookies + sys.path.insert(0, str(results.project)) + + # Test that `usage.py` works after building the default component. + dash_app(str(results.project.join('usage.py'))) + + time.sleep(1) + + input_component = selenium.find_element_by_xpath( + '//div[@id="input"]/input') + input_component.clear() + input_component.send_keys('Hello dash component') + + time.sleep(2) + + output = selenium.find_element_by_id('output') + + assert output.text == 'You have entered Hello dash component' + + node_modules = str(results.project.join('node_modules')) + + if sys.platform == 'win32': + # Fix delete long names on windows. + # pytest-cookies have trouble deleting some file generated by webpack. + node_modules = '\\\\?\\' + node_modules + + shutil.rmtree(node_modules) + diff --git a/tests/test_render.py b/tests/test_render.py deleted file mode 100644 index b995f1e..0000000 --- a/tests/test_render.py +++ /dev/null @@ -1,25 +0,0 @@ -from .IntegrationTests import IntegrationTests -import dash -import dash_html_components as html -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC - -from my_dash_component import ExampleComponent # pylint: disable=no-name-in-module - - -class Tests(IntegrationTests): - def test_render_component(self): - app = dash.Dash(__name__) - app.layout = html.Div([ - html.Div(id='waitfor'), - ExampleComponent(label='Example Component Label') - ]) - - self.startServer(app) - - WebDriverWait(self.driver, 10).until( - EC.presence_of_element_located((By.ID, "waitfor")) - ) - - self.percy_snapshot('Simple Render') diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 6471471..0000000 --- a/webpack.config.js +++ /dev/null @@ -1,41 +0,0 @@ -const path = require('path'); -const packagejson = require('./package.json'); - -const dashLibraryName = packagejson.name.replace(/-/g, '_'); - -module.exports = { - entry: {main: './src/lib/index.js'}, - output: { - path: path.resolve(__dirname, dashLibraryName), - filename: 'bundle.js', - library: dashLibraryName, - libraryTarget: 'window', - }, - externals: { - react: 'React', - 'react-dom': 'ReactDOM', - 'plotly.js': 'Plotly', - }, - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: { - loader: 'babel-loader', - }, - }, - { - test: /\.css$/, - use: [ - { - loader: 'style-loader', - }, - { - loader: 'css-loader', - }, - ], - }, - ], - }, -}; diff --git a/.babelrc b/{{cookiecutter.project_shortname}}/.babelrc similarity index 100% rename from .babelrc rename to {{cookiecutter.project_shortname}}/.babelrc diff --git a/.eslintignore b/{{cookiecutter.project_shortname}}/.eslintignore similarity index 100% rename from .eslintignore rename to {{cookiecutter.project_shortname}}/.eslintignore diff --git a/.eslintrc b/{{cookiecutter.project_shortname}}/.eslintrc similarity index 100% rename from .eslintrc rename to {{cookiecutter.project_shortname}}/.eslintrc diff --git a/{{cookiecutter.project_shortname}}/.gitignore b/{{cookiecutter.project_shortname}}/.gitignore new file mode 100644 index 0000000..53c7e67 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/.gitignore @@ -0,0 +1,274 @@ +# Created by .ignore support plugin (hsz.mobi) +### VisualStudioCode template +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests +### Node template +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +### SublimeText template +# Cache files for Sublime Text +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache + +# Workspace files are user-specific +*.sublime-workspace + +# Project files should be checked into the repository, unless a significant +# proportion of contributors will probably not be using Sublime Text +# *.sublime-project + +# SFTP configuration file +sftp-config.json + +# Package control specific files +Package Control.last-run +Package Control.ca-list +Package Control.ca-bundle +Package Control.system-ca-bundle +Package Control.cache/ +Package Control.ca-certs/ +Package Control.merged-ca-bundle +Package Control.user-ca-bundle +oscrypto-ca-bundle.crt +bh_unicode_properties.cache + +# Sublime-github package stores a github token in this file +# https://packagecontrol.io/packages/sublime-github +GitHub.sublime-settings diff --git a/.npmignore b/{{cookiecutter.project_shortname}}/.npmignore similarity index 100% rename from .npmignore rename to {{cookiecutter.project_shortname}}/.npmignore diff --git a/.prettierrc b/{{cookiecutter.project_shortname}}/.prettierrc similarity index 100% rename from .prettierrc rename to {{cookiecutter.project_shortname}}/.prettierrc diff --git a/.pylintrc b/{{cookiecutter.project_shortname}}/.pylintrc similarity index 100% rename from .pylintrc rename to {{cookiecutter.project_shortname}}/.pylintrc diff --git a/{{cookiecutter.project_shortname}}/CONTRIBUTING.md b/{{cookiecutter.project_shortname}}/CONTRIBUTING.md new file mode 100644 index 0000000..fc39028 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/CONTRIBUTING.md @@ -0,0 +1,4 @@ +# CONTRIBUTING + +This project was generated by the [dash-component-boilerplate](https://github.com/plotly/dash-component-boilerplate) it contains the minimal set of code required to create your own custom Dash component. + diff --git a/{{cookiecutter.project_shortname}}/LICENSE b/{{cookiecutter.project_shortname}}/LICENSE new file mode 100644 index 0000000..edcfd65 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/LICENSE @@ -0,0 +1,111 @@ +{% if cookiecutter.license == 'MIT license' -%} +MIT License + +Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.author_name }} + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +{% elif cookiecutter.license == 'BSD license' %} + +BSD License + +Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.author_name }} +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. +{% elif cookiecutter.license == 'ISC license' -%} +ISC License + +Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.author_name }} + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +{% elif cookiecutter.license == 'Apache Software License 2.0' -%} +Apache Software License 2.0 + +Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.author_name }} + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +{% elif cookiecutter.license == 'GNU General Public License v3' -%} +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + {{ cookiecutter.project_short_description }} + Copyright (C) {% now 'local', '%Y' %} {{ cookiecutter.author_name }} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. +{% endif %} \ No newline at end of file diff --git a/{{cookiecutter.project_shortname}}/MANIFEST.in b/{{cookiecutter.project_shortname}}/MANIFEST.in new file mode 100644 index 0000000..35d3766 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/MANIFEST.in @@ -0,0 +1,5 @@ +include {{cookiecutter.project_shortname}}/{{cookiecutter.project_shortname}}.min.js +include {{cookiecutter.project_shortname}}/{{cookiecutter.project_shortname}}.dev.js +include {{cookiecutter.project_shortname}}/metadata.json +include {{cookiecutter.project_shortname}}/package.json +include README.md diff --git a/{{cookiecutter.project_shortname}}/README.md b/{{cookiecutter.project_shortname}}/README.md new file mode 100644 index 0000000..a33401f --- /dev/null +++ b/{{cookiecutter.project_shortname}}/README.md @@ -0,0 +1,94 @@ +# {{cookiecutter.project_name}} + +{{cookiecutter.project_name}} is a Dash component library. + +Get started with: +1. Install Dash and its dependencies: https://dash.plot.ly/installation +2. Run `python usage.py` +3. Visit http://localhost:8050 in your web browser + +## Contributing + +See [CONTRIBUTING.md](./CONTRIBUTING.md) + +### Install dependencies + +If you have selected install_dependencies during the prompt, you can skip this part. + +1. Install npm packages + ``` + $ npm install + ``` +2. Create a virtual env and activate. + ``` + $ virtualenv venv + $ venv/Scripts/activate + ``` + _Note: venv\Scripts\activate for windows_ + +3. Install python packages required to build components. + ``` + $ pip install -r requirements.txt + ``` +4. Install the python packages for testing (optional) + ``` + $ pip install -r tests/requirements.txt + ``` + +### Write your component code in `src/lib/components/{{cookiecutter.component_name}}.react.js`. + +- The demo app is in `src/demo` and you will import your example component code into your demo app. +- Test your code in a Python environment: + 1. Build your code + ``` + $ npm run build:all + ``` + 2. Run and modify the `usage.py` sample dash app: + ``` + $ python usage.py + ``` +- Write tests for your component. + - A sample test is available in `tests/test_usage.py`, it will load `usage.py` and you can then automate interactions with selenium. + - Run the tests with `$ pytest tests`. + - The Dash team uses these types of integration tests extensively. Browse the Dash component code on GitHub for more examples of testing (e.g. https://github.com/plotly/dash-core-components) +- Add custom styles to your component by putting your custom CSS files into your distribution folder (`{{cookiecutter.project_shortname}}`). + - Make sure that they are referenced in `MANIFEST.in` so that they get properly included when you're ready to publish your component. + - Make sure the stylesheets are added to the `_css_dist` dict in `{{cookiecutter.project_shortname}}/__init__.py` so dash will serve them automatically when the component suite is requested. +- [Review your code](./review_checklist.md) + +### Create a production build and publish: + +1. Build your code: + ``` + $ npm run build:all + ``` +2. Create a Python tarball + ``` + $ python setup.py sdist + ``` + This distribution tarball will get generated in the `dist/` folder + +3. Test your tarball by copying it into a new environment and installing it locally: + ``` + $ pip install {{cookiecutter.project_shortname}}-0.0.1.tar.gz + ``` + +4. If it works, then you can publish the component to NPM and PyPI: + 1. Cleanup the dist folder (optional) + ``` + $ rm -rf dist + ``` + 2. Publish on PyPI + ``` + $ twine upload dist/* + ``` + 3. Publish on NPM (Optional if chosen False in `publish_on_npm`) + ``` + $ npm publish + ``` + _Publishing your component to NPM will make the JavaScript bundles available on the unpkg CDN. By default, Dash servers the component library's CSS and JS from the remote unpkg CDN, so if you haven't published the component package to NPM you'll need to set the `serve_locally` flags to `True` (unless you choose `False` on `publish_on_npm`). We will eventually make `serve_locally=True` the default, [follow our progress in this issue](https://github.com/plotly/dash/issues/284)._ +5. Share your component with the community! https://community.plot.ly/c/dash + 1. Publish this repository to GitHub + 2. Tag your GitHub repository with the plotly-dash tag so that it appears here: https://github.com/topics/plotly-dash + 3. Create a post in the Dash community forum: https://community.plot.ly/c/dash + diff --git a/{{cookiecutter.project_shortname}}/_validate_init.py b/{{cookiecutter.project_shortname}}/_validate_init.py new file mode 100644 index 0000000..1a25d49 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/_validate_init.py @@ -0,0 +1,67 @@ +""" +DO NOT MODIFY +This file is used to validate your publish settings. +""" +from __future__ import print_function + +import os +import sys +import importlib + + +components_package = '{{cookiecutter.project_shortname}}' + +components_lib = importlib.import_module(components_package) + +missing_dist_msg = 'Warning {} was not found in `{}.__init__.{}`!!!' +missing_manifest_msg = ''' +Warning {} was not found in `MANIFEST.in`! +It will not be included in the build! +''' + +with open('MANIFEST.in', 'r') as f: + manifest = f.read() + + +def check_dist(dist, filename): + # Support the dev bundle. + if filename.endswith('dev.js'): + return True + + return any( + filename in x + for d in dist + for x in ( + [d.get('relative_package_path')] + if not isinstance(d.get('relative_package_path'), list) + else d.get('relative_package_path') + ) + ) + + +def check_manifest(filename): + return filename in manifest + + +def check_file(dist, filename): + if not check_dist(dist, filename): + print( + missing_dist_msg.format(filename, components_package, '_js_dist'), + file=sys.stderr + ) + if not check_manifest(filename): + print(missing_manifest_msg.format(filename), + file=sys.stderr) + + +for cur, _, files in os.walk(components_package): + for f in files: + + if f.endswith('js'): + # noinspection PyProtectedMember + check_file(components_lib._js_dist, f) + elif f.endswith('css'): + # noinspection PyProtectedMember + check_file(components_lib._css_dist, f) + elif not f.endswith('py'): + check_manifest(f) diff --git a/extract-meta b/{{cookiecutter.project_shortname}}/extract-meta.js old mode 100755 new mode 100644 similarity index 80% rename from extract-meta rename to {{cookiecutter.project_shortname}}/extract-meta.js index 33e1152..8394bcf --- a/extract-meta +++ b/{{cookiecutter.project_shortname}}/extract-meta.js @@ -35,6 +35,21 @@ function writeError(msg, filePath) { } } +function checkWarn(name, value) { + if (value.length < 1) { + process.stderr.write(`\nDescription for ${name} is missing!\n`) + } +} + +function docstringWarning(doc) { + checkWarn(doc.displayName, doc.description); + + Object.entries(doc.props).forEach( + ([name, p]) => checkWarn(`${doc.displayName}.${name}`, p.description) + ); +} + + function parseFile(filepath) { const urlpath = filepath.split(path.sep).join('/'); let src; @@ -45,7 +60,8 @@ function parseFile(filepath) { try { src = fs.readFileSync(filepath); - metadata[urlpath] = reactDocs.parse(src); + const doc = metadata[urlpath] = reactDocs.parse(src); + docstringWarning(doc); } catch (error) { writeError(error, filepath); } diff --git a/index.html b/{{cookiecutter.project_shortname}}/index.html similarity index 100% rename from index.html rename to {{cookiecutter.project_shortname}}/index.html diff --git a/{{cookiecutter.project_shortname}}/package.json b/{{cookiecutter.project_shortname}}/package.json new file mode 100644 index 0000000..9e18ef8 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/package.json @@ -0,0 +1,49 @@ +{ + "name": "{{ cookiecutter.project_shortname }}", + "version": "0.0.1", + "description": "{{ cookiecutter.description }}", + "main": "build/index.js", + "scripts": { + "start": "webpack-serve ./webpack.serve.config.js --open", + "validate-init": "python _validate_init.py", + "prepublish": "npm run validate-init", + "build:js-dev": "webpack --mode development", + "build:js": "webpack --mode production", + "build:py": "node ./extract-meta.js src/lib/components > {{cookiecutter.project_shortname}}/metadata.json && copyfiles package.json {{cookiecutter.project_shortname}} && %(python_path) -c \"import dash; dash.development.component_loader.generate_classes('{{cookiecutter.project_shortname}}', '{{cookiecutter.project_shortname}}/metadata.json')\"", + "build:all": "npm run build:js & npm run build:js-dev & npm run build:py" + }, + "author": "{{ cookiecutter.author_name }} {{ cookiecutter.author_email }}", + "license": "{% set _license_identifiers = {'MIT License': 'MIT','BSD License': 'BSD','ISC License': 'ISC','Apache Software License 2.0': 'Apache-2.0','GNU General Public License v3': 'GPL-3.0','Not open source': ''} %}{{ _license_identifiers[cookiecutter.license] }}", + "dependencies": { + "ramda": "^0.25.0", + "react": "15.4.2", + "react-dom": "15.4.2" + }, + "devDependencies": { + "babel-core": "^6.26.3", + "babel-eslint": "^8.2.3", + "babel-loader": "^7.1.4", + "copyfiles": "^2.0.0", + "babel-preset-env": "^1.7.0", + "babel-preset-react": "^6.24.1", + "css-loader": "^0.28.11", + "eslint": "^4.19.1", + "eslint-config-prettier": "^2.9.0", + "eslint-plugin-import": "^2.12.0", + "eslint-plugin-react": "^7.9.1", + "npm": "^6.1.0", + "react-docgen": "^2.20.1", + "style-loader": "^0.21.0", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.1", + "webpack-serve": "^1.0.2" + }, + "peerDependencies": { + "react": ">=0.14", + "react-dom": ">=0.14" + }, + "engines": { + "node": ">=8.11.0", + "npm": ">=6.1.0" + } +} diff --git a/{{cookiecutter.project_shortname}}/requirements.txt b/{{cookiecutter.project_shortname}}/requirements.txt new file mode 100644 index 0000000..0b7f2dd --- /dev/null +++ b/{{cookiecutter.project_shortname}}/requirements.txt @@ -0,0 +1,3 @@ +# dash is required to call `build:py` +dash +dash-html-components \ No newline at end of file diff --git a/{{cookiecutter.project_shortname}}/review_checklist.md b/{{cookiecutter.project_shortname}}/review_checklist.md new file mode 100644 index 0000000..5dc4663 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/review_checklist.md @@ -0,0 +1,46 @@ +# Code Review Checklist + +## Code quality & design + +- Is your code clear? If you had to go back to it in a month, would you be happy to? If someone else had to contribute to it, would they be able to? + + A few suggestions: + + - Make your variable names descriptive and use the same naming conventions throughout the code. + + - For more complex pieces of logic, consider putting a comment, and maybe an example. + + - In the comments, focus on describing _why_ the code does what it does, rather than describing _what_ it does. The reader can most likely read the code, but not necessarily understand why it was necessary. + + - Don't overdo it in the comments. The code should be clear enough to speak for itself. Stale comments that no longer reflect the intent of the code can hurt code comprehension. + +* Don't repeat yourself. Any time you see that the same piece of logic can be applied in multiple places, factor it out into a function, or variable, and reuse that code. +* Scan your code for expensive operations (large computations, DOM queries, React re-renders). Have you done your possible to limit their impact? If not, it is going to slow your app down. + +## Component API + +- Have you tested your component on the Python side by creating an app in `usage.py` ? + + Do all of your component's props work when set from the back-end? + + Should all of them be settable from the back-end or are some only relevant to user interactions in the front-end? + +- Have you provided some basic documentation about your component? The Dash community uses [react docstrings](https://github.com/plotly/dash-docs/blob/master/tutorial/plugins.py#L45) to provide basic information about dash components. Take a look at this [Checklist component example](https://github.com/plotly/dash-core-components/blob/master/src/components/Checklist.react.js) and others from the dash-core-components repository. + + At a minimum, you should describe what your component does, and describe its props and the features they enable. + + Be careful to use the correct formatting for your docstrings for them to be properly recognized. + +## Tests + +- The Dash team uses integration tests extensively, and we highly encourage you to write tests for the main functionality of your component. In the `tests` folder of the boilerplate, you can see a sample integration test. By launching it, you will run a sample Dash app in a browser. You can run the test with: + ``` + python -m tests.test_render + ``` + [Browse the Dash component code on GitHub for more examples of testing.](https://github.com/plotly/dash-core-components) + +## Ready to publish? Final scan + +- Take a last look at the external resources that your component is using. Are all the external resources used [referenced in `MANIFEST.in`](https://github.com/plotly/dash-docs/blob/0b2fd8f892db720a7f3dc1c404b4cff464b5f8d4/tutorial/plugins.py#L55)? + +- You're ready to publish! Continue to [step #9 here](README.md#publishing). \ No newline at end of file diff --git a/setup.py b/{{cookiecutter.project_shortname}}/setup.py similarity index 84% rename from setup.py rename to {{cookiecutter.project_shortname}}/setup.py index e2cf9ca..3b73f40 100644 --- a/setup.py +++ b/{{cookiecutter.project_shortname}}/setup.py @@ -3,7 +3,7 @@ from setuptools import setup -with open(os.path.join('my_dash_component', 'package.json')) as f: +with open(os.path.join('{{cookiecutter.project_shortname}}', 'package.json')) as f: package = json.load(f) package_name = package["name"].replace(" ", "_").replace("-", "_") diff --git a/src/demo/App.js b/{{cookiecutter.project_shortname}}/src/demo/App.js similarity index 77% rename from src/demo/App.js rename to {{cookiecutter.project_shortname}}/src/demo/App.js index fa19afa..fd83c64 100644 --- a/src/demo/App.js +++ b/{{cookiecutter.project_shortname}}/src/demo/App.js @@ -1,9 +1,7 @@ /* eslint no-magic-numbers: 0 */ import React, {Component} from 'react'; -import PropTypes from 'prop-types'; -import * as R from 'ramda'; -import {ExampleComponent} from '../lib'; +import {{cookiecutter.component_name}} from '../lib'; class App extends Component { @@ -11,7 +9,7 @@ class App extends Component { super(); this.state = { value: '' - } + }; this.setProps = this.setProps.bind(this); } @@ -22,7 +20,7 @@ class App extends Component { render() { return (
- diff --git a/src/demo/index.js b/{{cookiecutter.project_shortname}}/src/demo/index.js similarity index 100% rename from src/demo/index.js rename to {{cookiecutter.project_shortname}}/src/demo/index.js diff --git a/src/lib/components/ExampleComponent.react.js b/{{cookiecutter.project_shortname}}/src/lib/components/{{cookiecutter.component_name}}.react.js similarity index 90% rename from src/lib/components/ExampleComponent.react.js rename to {{cookiecutter.project_shortname}}/src/lib/components/{{cookiecutter.component_name}}.react.js index 2fd1652..bb0f989 100644 --- a/src/lib/components/ExampleComponent.react.js +++ b/{{cookiecutter.project_shortname}}/src/lib/components/{{cookiecutter.component_name}}.react.js @@ -8,13 +8,13 @@ import PropTypes from 'prop-types'; * It renders an input with the property `value` * which is editable by the user. */ -export default class ExampleComponent extends Component { +export default class {{cookiecutter.component_name}} extends Component { render() { const {id, label, setProps, value} = this.props; return (
- ExampleComponent: {label} + ExampleComponent: {label}  { @@ -45,7 +45,9 @@ export default class ExampleComponent extends Component { } } -ExampleComponent.propTypes = { +{{cookiecutter.component_name}}.defaultProps = {}; + +{{cookiecutter.component_name}}.propTypes = { /** * The ID used to identify this component in Dash callbacks */ diff --git a/{{cookiecutter.project_shortname}}/src/lib/index.js b/{{cookiecutter.project_shortname}}/src/lib/index.js new file mode 100644 index 0000000..1b318cc --- /dev/null +++ b/{{cookiecutter.project_shortname}}/src/lib/index.js @@ -0,0 +1,6 @@ +/* eslint-disable import/prefer-default-export */ +import {{cookiecutter.component_name}} from './components/{{cookiecutter.component_name}}.react'; + +export { + {{cookiecutter.component_name}} +}; diff --git a/tests/__init__.py b/{{cookiecutter.project_shortname}}/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to {{cookiecutter.project_shortname}}/tests/__init__.py diff --git a/{{cookiecutter.project_shortname}}/tests/requirements.txt b/{{cookiecutter.project_shortname}}/tests/requirements.txt new file mode 100644 index 0000000..1fcc228 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/tests/requirements.txt @@ -0,0 +1,15 @@ +# Packages needed to run the tests. +# Switch into a virtual environment +# pip install -r requirements.txt + +chromedriver-binary +dash +dash-core-components +dash-html-components +dash-renderer +ipdb +percy +selenium +flake8 +pylint +pytest-dash diff --git a/{{cookiecutter.project_shortname}}/tests/test_usage.py b/{{cookiecutter.project_shortname}}/tests/test_usage.py new file mode 100644 index 0000000..b84d650 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/tests/test_usage.py @@ -0,0 +1,10 @@ +# Basic test for the component rendering. +def test_render_component(dash_app, selenium): + # Start a dash app contained in `usage.py` + # dash_app is a fixture by pytest-dash + # It will load a py file containing a Dash instance named `app` + # and start it in a thread. + app = dash_app('usage.py') + + # Get the generated component with selenium + my_component = selenium.find_element_by_id('input') diff --git a/usage.py b/{{cookiecutter.project_shortname}}/usage.py similarity index 78% rename from usage.py rename to {{cookiecutter.project_shortname}}/usage.py index d7a9cfb..ec13476 100644 --- a/usage.py +++ b/{{cookiecutter.project_shortname}}/usage.py @@ -1,15 +1,15 @@ -import my_dash_component +import {{cookiecutter.project_shortname}} import dash from dash.dependencies import Input, Output import dash_html_components as html -app = dash.Dash('') +app = dash.Dash(__name__) app.scripts.config.serve_locally = True app.css.config.serve_locally = True app.layout = html.Div([ - my_dash_component.ExampleComponent( + {{cookiecutter.project_shortname}}.{{cookiecutter.component_name}}( id='input', value='my-value', label='my-label' diff --git a/{{cookiecutter.project_shortname}}/webpack.config.js b/{{cookiecutter.project_shortname}}/webpack.config.js new file mode 100644 index 0000000..cfee2ad --- /dev/null +++ b/{{cookiecutter.project_shortname}}/webpack.config.js @@ -0,0 +1,63 @@ +const path = require('path'); +const packagejson = require('./package.json'); + +const dashLibraryName = packagejson.name.replace(/-/g, '_'); + +module.exports = (env, argv) => { + + let mode; + + + // if user specified mode flag take that value + if (argv && argv.mode) { + mode = argv.mode; + } + + // else if configuration object is already set (module.exports) use that value + else if (module.exports && module.exports.mode) { + mode = module.exports = mode; + } + + // else take webpack default + else { + mode = 'production'; // webpack default + } + + return { + entry: {main: './src/lib/index.js'}, + output: { + path: path.resolve(__dirname, dashLibraryName), + filename: `${dashLibraryName}.${mode === 'development' ? 'dev' : 'min'}.js`, + library: dashLibraryName, + libraryTarget: 'window', + }, + externals: { + react: 'React', + 'react-dom': 'ReactDOM', + 'plotly.js': 'Plotly', + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + }, + }, + { + test: /\.css$/, + use: [ + { + loader: 'style-loader', + }, + { + loader: 'css-loader', + }, + ], + }, + ], + }, + devtool: mode === 'development' ? "eval-source-map" : 'none' + } +}; diff --git a/webpack.serve.config.js b/{{cookiecutter.project_shortname}}/webpack.serve.config.js similarity index 100% rename from webpack.serve.config.js rename to {{cookiecutter.project_shortname}}/webpack.serve.config.js diff --git a/my_dash_component/__init__.py b/{{cookiecutter.project_shortname}}/{{cookiecutter.project_shortname}}/__init__.py similarity index 57% rename from my_dash_component/__init__.py rename to {{cookiecutter.project_shortname}}/{{cookiecutter.project_shortname}}/__init__.py index 493683c..4328b22 100644 --- a/my_dash_component/__init__.py +++ b/{{cookiecutter.project_shortname}}/{{cookiecutter.project_shortname}}/__init__.py @@ -6,6 +6,10 @@ import dash as _dash +# noinspection PyUnresolvedReferences +from ._imports_ import * +from ._imports_ import __all__ + if not hasattr(_dash, 'development'): print('Dash was not successfully imported. ' 'Make sure you don\'t have a file ' @@ -21,21 +25,18 @@ __version__ = package['version'] _current_path = _os.path.dirname(_os.path.abspath(__file__)) -_components = _dash.development.component_loader.load_components( - _os.path.join(_current_path, 'metadata.json'), - package_name -) _this_module = _sys.modules[__name__] _js_dist = [ { - 'relative_package_path': 'bundle.js', - 'external_url': ( - 'https://unpkg.com/my_dash_component' - '/' + package_name + '/bundle.js' - ).format(__version__), + 'relative_package_path': '{{cookiecutter.project_shortname}}.min.js', + 'dev_package_path': '{{cookiecutter.project_shortname}}.dev.js', + {% if cookiecutter.publish_on_npm == 'True' -%} + 'external_url': 'https://unpkg.com/{0}@{1}/{0}/{0}.min.js'.format( + __name__, __version__), + {%- endif %} 'namespace': package_name } ] @@ -43,7 +44,6 @@ _css_dist = [] -for _component in _components: - setattr(_this_module, _component.__name__, _component) - setattr(_component, '_js_dist', _js_dist) - setattr(_component, '_css_dist', _css_dist) +for _component in __all__: + setattr(locals()[_component], '_js_dist', _js_dist) + setattr(locals()[_component], '_css_dist', _css_dist) diff --git a/{{cookiecutter.project_shortname}}/{{cookiecutter.project_shortname}}/_imports_.py b/{{cookiecutter.project_shortname}}/{{cookiecutter.project_shortname}}/_imports_.py new file mode 100644 index 0000000..93e70a3 --- /dev/null +++ b/{{cookiecutter.project_shortname}}/{{cookiecutter.project_shortname}}/_imports_.py @@ -0,0 +1,5 @@ +from .{{cookiecutter.component_name}} import {{cookiecutter.component_name}} + +__all__ = [ + "{{cookiecutter.component_name}}" +] diff --git a/package.json b/{{cookiecutter.project_shortname}}/{{cookiecutter.project_shortname}}/package.json similarity index 58% rename from package.json rename to {{cookiecutter.project_shortname}}/{{cookiecutter.project_shortname}}/package.json index 4acbbfc..977d2e8 100644 --- a/package.json +++ b/{{cookiecutter.project_shortname}}/{{cookiecutter.project_shortname}}/package.json @@ -1,18 +1,17 @@ { - "name": "my-dash-component", + "name": "{{ cookiecutter.project_shortname }}", "version": "0.0.1", - "description": "my-dash-component", + "description": "{{ cookiecutter.description }}", "main": "build/index.js", "scripts": { "start": "webpack-serve ./webpack.serve.config.js --open", "build:js-dev": "webpack --mode development", "build:js": "webpack --mode production", - "build:py": "node ./extract-meta src/lib/components > my_dash_component/metadata.json && copyfiles package.json my_dash_component && python -c \"import dash; dash.development.component_loader.generate_classes('my_dash_component', 'my_dash_component/metadata.json')\"", - "build:all": "npm run build:js & npm run build:py", - "build:all-dev": "npm run build:js-dev & npm run build:py" + "build:py": "node ./extract-meta src/lib/components > {{cookiecutter.project_shortname}}/metadata.json && copyfiles package.json {{cookiecutter.project_shortname}} && python -c \"import dash; dash.development.component_loader.generate_classes('{{cookiecutter.project_shortname}}', '{{cookiecutter.project_shortname}}/metadata.json')\"", + "build:all": "npm run build:js & npm run build:js-dev & npm run build:py" }, - "author": "my-name my-email", - "license": "my-license", + "author": "{{ cookiecutter.author_name }} {{ cookiecutter.author_email }}", + "license": "{{ cookiecutter.license }}", "dependencies": { "ramda": "^0.25.0", "react": "15.4.2", @@ -33,8 +32,8 @@ "npm": "^6.1.0", "react-docgen": "^2.20.1", "style-loader": "^0.21.0", - "webpack": "^4.8.3", - "webpack-cli": "^2.1.3", + "webpack": "^4.20.2", + "webpack-cli": "^3.1.1", "webpack-serve": "^1.0.2" }, "peerDependencies": {