├── .eslintrc.js
├── .fossaignore
├── .gitignore
├── .gitlab-ci.yml
├── .nojekyll
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── babel.config.js
├── dist
├── manual_test.html
└── screenshot.html
├── docs
├── 404.md
├── Gemfile
├── Gemfile.lock
├── _config.yml
├── _includes
│ ├── examples.html
│ ├── preview-tool.html
│ ├── site-header.html
│ └── social-metatags.html
├── _layouts
│ ├── default.html
│ ├── home.html
│ ├── not_found.html
│ ├── page.html
│ ├── post.html
│ └── preview.html
├── _sass
│ ├── blog.scss
│ ├── blog
│ │ ├── _base.scss
│ │ └── _layout.scss
│ ├── jekyll-theme-cayman-blog.scss
│ ├── normalize.scss
│ ├── rouge-github.scss
│ └── variables.scss
├── assets
│ └── css
│ │ └── style.scss
├── components.md
├── examples
│ ├── 1_food_comparison.yml
│ ├── area_chart.md
│ ├── area_chart.yml
│ ├── bar_chart.md
│ ├── bar_chart.yml
│ ├── board.md
│ ├── board.yml
│ ├── bubble_chart.md
│ ├── bubble_chart.yml
│ ├── columns.md
│ ├── columns.yml
│ ├── custom_axes.md
│ ├── custom_axes.yml
│ ├── donut_chart.md
│ ├── donut_chart.yml
│ ├── dropdown.md
│ ├── dropdown.yml
│ ├── gauge_chart.md
│ ├── gauge_chart.yml
│ ├── h1_text.md
│ ├── h1_text.yml
│ ├── h2_text.md
│ ├── h2_text.yml
│ ├── h3_text.md
│ ├── h3_text.yml
│ ├── horizontal_bar_chart.md
│ ├── horizontal_bar_chart.yml
│ ├── horizontal_stacked_bar_chart.md
│ ├── horizontal_stacked_bar_chart.yml
│ ├── line_chart.md
│ ├── line_chart.yml
│ ├── p_text.md
│ ├── p_text.yml
│ ├── pie_chart.md
│ ├── pie_chart.yml
│ ├── rows.md
│ ├── rows.yml
│ ├── scatter_plot.md
│ ├── scatter_plot.yml
│ ├── spline_chart.md
│ ├── spline_chart.yml
│ ├── stacked_area_chart.md
│ ├── stacked_area_chart.yml
│ ├── stacked_bar_chart.md
│ ├── stacked_bar_chart.yml
│ ├── stacked_line_chart.md
│ ├── stacked_line_chart.yml
│ ├── stacked_spline_chart.md
│ ├── stacked_spline_chart.yml
│ ├── step_chart.md
│ └── step_chart.yml
├── gallery.json
├── index.md
├── jekyll-theme-cayman-blog.gemspec
├── page_screenshot.png
├── preview.md
├── screenshots
│ ├── area_chart.png
│ ├── bar_chart.png
│ ├── board.png
│ ├── bubble_chart.png
│ ├── columns.png
│ ├── custom_axes.png
│ ├── donut_chart.png
│ ├── dropdown.png
│ ├── gauge_chart.png
│ ├── h1_text.png
│ ├── h2_text.png
│ ├── h3_text.png
│ ├── horizontal_bar_chart.png
│ ├── horizontal_stacked_bar_chart.png
│ ├── line_chart.png
│ ├── p_text.png
│ ├── pie_chart.png
│ ├── rows.png
│ ├── scatter_plot.png
│ ├── spline_chart.png
│ ├── stacked_area_chart.png
│ ├── stacked_bar_chart.png
│ ├── stacked_line_chart.png
│ ├── stacked_spline_chart.png
│ └── step_chart.png
├── script
│ ├── bootstrap
│ ├── cibuild
│ ├── release
│ └── server
└── tutorial.md
├── jsdoc.conf.json
├── package.json
├── postcss.config.js
├── screenshot.png
├── screenshot_variables.png
├── scripts
└── compile_yaml_to_json.js
├── src
├── components
│ ├── base
│ │ ├── base_component.test.js
│ │ ├── bind.js
│ │ ├── error_handling.js
│ │ ├── external_data.js
│ │ ├── index.js
│ │ ├── loaders.js
│ │ ├── render.js
│ │ ├── spinner.js
│ │ └── state_handling.js
│ ├── board
│ │ ├── Board.js
│ │ ├── Board.scss
│ │ ├── Board.test.js
│ │ ├── gallery.json
│ │ └── index.js
│ ├── chart
│ │ ├── Chart.js
│ │ ├── Chart.scss
│ │ ├── Chart.test.js
│ │ ├── gallery.json
│ │ └── index.js
│ ├── columns
│ │ ├── Columns.js
│ │ ├── Columns.scss
│ │ ├── Columns.test.js
│ │ ├── gallery.json
│ │ └── index.js
│ ├── container_base.js
│ ├── container_base.test.js
│ ├── dropdown
│ │ ├── Dropdown.js
│ │ ├── Dropdown.test.js
│ │ ├── gallery.json
│ │ └── index.js
│ ├── root
│ │ ├── Root.js
│ │ ├── Root.test.js
│ │ └── index.js
│ ├── rows
│ │ ├── Rows.js
│ │ ├── Rows.scss
│ │ ├── Rows.test.js
│ │ ├── gallery.json
│ │ └── index.js
│ ├── text
│ │ ├── Text.js
│ │ ├── Text.scss
│ │ ├── Text.test.js
│ │ ├── gallery.json
│ │ └── index.js
│ ├── wrapped.js
│ └── wrapped.test.js
├── default_parser.js
├── e2e-test.js
├── index.js
├── interpolation.js
├── interpolation.test.js
├── jq-web.js
├── loader
│ ├── loader.js
│ └── loader.test.js
├── manual_test.js
├── parser
│ ├── parser.js
│ └── parser.test.js
├── screenshot.js
├── state_handler.js
├── state_handler.test.js
├── style.scss
├── test_parser.js
├── validators.js
├── validators.test.js
└── yaml-format
│ ├── parser.js
│ └── parser.test.js
├── webpack.config.js
├── webpack.screenshot.config.js
├── webpack.test.config.js
└── yarn.lock
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "node": true,
5 | "es6": true,
6 | "mocha": true
7 | },
8 | "parser": "babel-eslint",
9 | "extends": "eslint:recommended",
10 | "parserOptions": {
11 | "sourceType": "module",
12 | "allowImportExportEverywhere": true
13 | },
14 | "rules": {
15 | "max-len": [
16 | "warn"
17 | ],
18 | "indent": [
19 | "error",
20 | 2
21 | ],
22 | "linebreak-style": [
23 | "error",
24 | "unix"
25 | ],
26 | "quotes": [
27 | "error",
28 | "single"
29 | ],
30 | "semi": [
31 | "error",
32 | "never"
33 | ]
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/.fossaignore:
--------------------------------------------------------------------------------
1 | docs/
2 | docs/Gemfile
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist/bundle.js
2 | coverage/
3 | .nyc_output/
4 | out/
5 | dist/
6 | lib/
7 | /coverage.lcov
8 | /stats.json
9 | mochawesome-report/
10 | /component_gallery/gallery.csv
11 | /component_gallery/gallery.json
12 | /src/_temp.js
13 | docs/_site/
14 | docs/.sass-cache/
15 | docs/.jekyll-cache/
16 | docs/.jekyll-metadata
17 | node_modules/
18 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | image: ubuntu:18.04
2 |
3 | stages:
4 | - test
5 | - publish
6 |
7 | before_script:
8 | - apt-get update -y
9 | - apt-get install curl gnupg wget git -y
10 | - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
11 | - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
12 | - apt-get remove cmdtest
13 | - apt-get update -y
14 | - apt-get install firefox nodejs npm yarn -y
15 | - npm install -g node-gyp
16 | - npm install -g cross-env
17 | - yarn
18 | - wget https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-linux64.tar.gz
19 | - sh -c 'tar -x geckodriver -zf geckodriver-v0.23.0-linux64.tar.gz -O > /usr/bin/geckodriver'
20 | - chmod +x /usr/bin/geckodriver
21 | - rm geckodriver-v0.23.0-linux64.tar.gz
22 |
23 | test:
24 | stage: test
25 | script:
26 | - yarn test
27 |
28 | test-e2e:
29 | stage: test
30 | script:
31 | - yarn test:e2e
32 |
33 | test-lint:
34 | stage: test
35 | script:
36 | - yarn lint
37 |
38 | test-build:
39 | stage: test
40 | script:
41 | - yarn build
42 |
43 | semantic-release:
44 | stage: publish
45 | script:
46 | - yarn build
47 | - yarn semantic-release
48 | only:
49 | - master
50 |
51 |
--------------------------------------------------------------------------------
/.nojekyll:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at github@daniel-kantor.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # just-dashboard contribution guidelines
2 | Thank you for considering contributing to just-dashboard!
3 |
4 | As this project is still pretty new, I only state a few simple principles here.
5 |
6 | You are welcome to implement bug fixes, features, report issues and post feature requests of any kind.
7 |
8 | The preferred workflow for implementing new features or bug fixes is:
9 | - Write a test case that fails
10 | - Create an implementation that makes the test case pass
11 | - Refactor
12 | - Repeat
13 |
14 | ## Commit messages
15 | Please use [Angular Git Commit Guidelines](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines) to write your commit messages.
16 | This is important because just-dashboard uses [semantic-release](https://github.com/semantic-release/semantic-release) to generate releases.
17 |
18 | I recommend trying a tool a like [commitizen](https://github.com/commitizen/cz-cli) to help with that.
19 |
20 | ## Documentation
21 | just-dashboard uses jsdoc to generate documentation.
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Dániel Kántor
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | docs: docs/examples docs/components.md docs/screenshots
2 |
3 |
4 | docs/screenshots: docs/gallery.json
5 | cat $^ | tr "\n" "\0"| xargs -L1 -0 -n1 -I % bash -c "make docs\/screenshots\/\`echo '%' | jq '.[1]' -r | sed 's/ /_/g'\`.png"
6 |
7 | docs/screenshots/%.png: docs/examples/%.js
8 | rm -f src/_temp.js
9 | cp $< src/_temp.js
10 | bash -c "npx webpack src/_temp.js --output-filename screenshot_index.js --config webpack.screenshot.config.js"
11 | node ./src/screenshot.js $@
12 | rm -f src/_temp.js
13 |
14 | docs/_includes/examples.html: docs/examples
15 | ls $^ | \
16 | grep -e '\.yml$$' | \
17 | sed 's \.yml$$ ' | \
18 | sed 's \(.*\) ' \
19 | > $@
20 |
21 | docs/examples/%.md: ./docs/examples/%.yml
22 | echo "---" > $@
23 | echo "layout: default" >> $@
24 | echo "title: How to use $*" | sed 's/_/ /g' >> $@
25 | echo "---" >> $@
26 | echo "" >> $@
27 | echo "# How to use $*" | sed 's/_/ /g' >> $@
28 | echo "Here's an example code regarding the use of $*: " | sed 's/_/ /g' >> $@
29 | echo "" >> $@
30 | echo "\`\`\`yaml" >> $@
31 | cat $^ | sed '1,1d' | sed 's/^ //' >> $@
32 | echo "\`\`\`" >> $@
33 | echo "The code above will render a $* that looks like this:" | sed 's/_/ /g' >> $@
34 | echo "" >> $@
35 | echo "" >> $@
36 | echo "" >> $@
37 | echo "## JSON format" >> $@
38 | echo "The YAML above is equivalent to this JSON:" >> $@
39 | echo "\`\`\`json" >> $@
40 | cat $^ | npx babel-node scripts/compile_yaml_to_json >> $@
41 | echo "\`\`\`" >> $@
42 |
43 | docs/examples/%.js: ./docs/examples/%.yml
44 | echo " \
45 | import {json_parser, yaml_parser} from './index.js'; \
46 | import * as d3 from 'd3'; \
47 | 'so random'; \
48 | const render_dashboard = (data) => {; \
49 | const parserd_yaml = yaml_parser(data); \
50 | json_parser(parserd_yaml)(d3.selection()); \
51 | }; \
52 | render_dashboard(\``cat '$<'`\`); \
53 | d3.selectAll('path.domain').attr('fill', 'transparent'); \
54 | d3.selectAll('path.domain').attr('stroke', 'black'); \
55 | d3.selectAll('.bb-chart-line').attr('fill', 'transparent'); \
56 | " | sed 's/---/\n/' > $@
57 |
58 | docs/components.md: docs/gallery.json
59 | bash -c 'echo "# Components" > $@'
60 | cat $^ | jq --slurp " \
61 | group_by(.[0]) | \
62 | .[] | [ \
63 | \"## \" + .[0][0], \
64 | \"\", \
65 | \"
\", \
66 | \"\", \
67 | ([.[] | \
68 | \"\", \
69 | \"
\", \
70 | \"\", \
71 | \"### \" + .[1] + \
72 | \" ([Usage](examples/\" + (.[1] | split(\" \") | join(\"_\")) + \"))\", \
73 | \" | join(\"_\")) + \".png)\", \
74 | \"\", \
75 | \"
\", \
76 | \"\" \
77 | ] | join(\"\n\") ), \
78 | \"\", \
79 | \"
\", \
80 | \"\", \
81 | \"\"] \
82 | | join(\"\n\")" -r >> $@
83 |
84 | docs/examples: docs/gallery.json
85 | cat $^ | tr "\n" "\0"| xargs -L1 -0 -n1 -I % bash -c "echo '%' | jq '{\"dashboard \\\"Example\\\"\": [.[2]]}' | ./node_modules/json2yaml/cli.js > docs\/examples\/\`echo '%' | jq '.[1]' -r | sed 's/ /_/g'\`.yml"
86 | cat $^ | tr "\n" "\0"| xargs -L1 -0 -n1 -I % bash -c "make docs\/examples\/\`echo '%' | jq '.[1]' -r | sed 's/ /_/g'\`.md"
87 |
88 | docs/gallery.json: $(shell find src/components -name "gallery.json")
89 | cat $^ | jq -c " \
90 | .[] | {\"example\": .examples | to_entries| .[], \"data\": .data, \"category\": .category} | \
91 | {\"code\": {(.example.value): .data}, \"category\": .category, \"name\": .example.key} | \
92 | [.category, .name, .code] \
93 | " > $@
94 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | just-dashboard
2 |
3 |
4 |
5 | Documentation •
6 | Getting started •
7 | Chart types
8 |
9 |
10 | []() 
11 |
12 | just-dashboard turns this:
13 | ```yaml
14 | dashboard "Food":
15 | - h1 text: Food
16 | - h2 text: By caloric content
17 | - 3 columns:
18 | - rows:
19 | - h3 text: Bananas
20 | - pie chart: {
21 | "columns": [
22 | ["Protein", 5], ["Sugar", 10], ["Other carbs", 40], ["Fat", 1]
23 | ]
24 | }
25 | - rows:
26 | - h3 text: Tofu
27 | - pie chart: {
28 | "columns": [
29 | ["Protein", 30], ["Sugar", 0], ["Other carbs", 40], ["Fat", 3]
30 | ]
31 | }
32 | - rows:
33 | - h3 text: Peanut butter
34 | - pie chart: {
35 | "columns": [
36 | ["Protein", 20], ["Sugar", 2], ["Other carbs", 20], ["Fat", 50]
37 | ]
38 | }
39 | ```
40 |
41 | Into this:
42 |
43 | 
44 |
45 | To host your dashboard, you can roll your own backend, or:
46 |
47 | - Create a public GitHub gist with a file named dashboard.yml or dashboard.json (depending on your preferred format)
48 | - Access it as a shareable dashboard at: `http://bottoml.in/e/{Github username}/{Gist ID}`
49 |
50 | In fact, I've created a Gist with the example above: [https://gist.github.com/kantord/2973bdd4ad689642562018bb4091ffbd](https://gist.github.com/kantord/2973bdd4ad689642562018bb4091ffbd);
51 | thus it's accessible as a dashboard at: [http://bottoml.in/e/kantord/2973bdd4ad689642562018bb4091ffbd](http://bottoml.in/e/kantord/2973bdd4ad689642562018bb4091ffbd)
52 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "presets": [
3 | "@babel/preset-env",
4 | ],
5 | "plugins": [
6 | "@babel/syntax-dynamic-import",
7 | ],
8 | "env": {
9 | "build": {
10 | "plugins": [
11 | "@babel/syntax-dynamic-import"
12 | ]
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/dist/manual_test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/dist/screenshot.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/docs/404.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: not_found
3 | permalink: /404.html
4 | ---
5 |
6 |
--------------------------------------------------------------------------------
/docs/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 | gem 'github-pages', group: :jekyll_plugins
3 | gem "jekyll-theme-cayman-blog"
4 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | title: just-dashboard
2 | description: Create dashboards using YAML/JSON files
3 | theme: jekyll-theme-cayman
4 | google_analytics: UA-115481527-1
5 |
--------------------------------------------------------------------------------
/docs/_includes/examples.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/docs/_includes/preview-tool.html:
--------------------------------------------------------------------------------
1 |
44 |
45 |
46 |
47 |
48 |
49 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/docs/_includes/site-header.html:
--------------------------------------------------------------------------------
1 |
37 |
--------------------------------------------------------------------------------
/docs/_includes/social-metatags.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% if page.title %}
5 | {% assign page_title = page.title | escape %}
6 | {% else %}
7 | {% assign page_title = site.title | escape %}
8 | {% endif %}
9 |
10 |
11 | {% if page.author or page.facebook.publisher %}
12 | {% assign page_author = page.author | default: nil | escape %}
13 | {% assign facebook_publisher = page.facebook.publisher | default: nil | escape %}
14 | {% assign twitter_creator = page.twitter.username | default: site.twitter.site | default: nil | escape %}
15 | {% assign facebook_authors = page.facebook.authors | default: nil | escape %}
16 | {% else %}
17 | {% assign page_author = site.author | default: nil | escape %}
18 | {% assign facebook_publisher = site.facebook.publisher | default: nil | escape %}
19 | {% assign twitter_creator = site.twitter.username | default: nil | escape %}
20 | {% assign facebook_authors = site.facebook.authors | default: nil | escape %}
21 | {% endif %}
22 |
23 | {% assign page_description = page.excerpt | default: site.description | strip_html | normalize_whitespace | truncate: 152 | escape %}
24 |
25 | {%if page.image %}
26 | {% assign page_image = site.url | append: site.baseurl | append: page.image %}
27 | {%else if site.image %}
28 | {% assign page_image = site.url | append: site.baseurl | append: site.image %}
29 | {%endif %}
30 |
31 | {% assign canonical_url = page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url %}
32 |
33 |
34 | {%if page_author %}
35 |
36 | {%endif %}
37 |
38 | {%if page.tags.length > 0 %}
39 |
40 | {%endif %}
41 | {%if page.keywords %}
42 |
43 | {%endif %}
44 | {%if page.categories.length > 0 %}
45 |
46 | {%endif %}
47 | {%if page.category %}
48 |
49 | {%endif %}
50 |
51 |
52 |
53 |
54 | {%if page_image %}
55 |
56 | {%endif %}
57 |
58 |
59 |
60 |
61 | {%if site.twitter.site %}
62 |
63 | {%endif %}
64 |
65 |
66 |
67 |
68 | {%if twitter_creator %}
69 |
70 | {%endif %}
71 |
72 |
73 | {%if page_image %}
74 |
75 |
76 | {%endif %}
77 |
78 |
79 |
80 |
81 |
82 |
83 | {%if page_image %}
84 |
85 | {%endif %}
86 |
87 |
88 | {%if page.date %}
89 |
90 | {%endif %}
91 | {%if page.modified_date %}
92 |
93 | {%endif %}
94 |
95 | {%if site.facebook.admins %}
96 | {% for admin in site.facebook.admins %}
97 |
98 | {% endfor %}
99 | {%endif %}
100 |
101 | {%if site.facebook.app_id %}
102 |
103 | {%endif %}
104 |
105 | {%if site.facebook.profile_id %}
106 |
107 | {%endif %}
108 |
109 | {%if facebook_publisher %}
110 |
111 | {%endif %}
112 | {%if facebook_authors %}
113 | {% for author in facebook_authors %}
114 |
115 | {% endfor %}
116 | {%endif %}
117 |
118 |
119 | {%if page.tags %}
120 | {% for tag in page.tags %}
121 |
122 | {% endfor %}
123 | {%endif %}
124 |
125 | {%if page.keywords %}
126 | {% assign keywordsList = page.keywords | split:', ' %}
127 | {% for keyword in keywordsList %}
128 |
129 | {% endfor %}
130 | {%endif %}
131 |
132 | {%if page.categories %}
133 | {% for category in page.categories %}
134 |
135 | {% endfor %}
136 | {%endif %}
137 |
138 | {%if page.category %}
139 |
140 | {%endif %}
141 |
--------------------------------------------------------------------------------
/docs/_layouts/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {% if page.title %}
11 | {% assign page-title = page.title | escape %}
12 | {% else %}
13 | {% assign page-title = site.title | escape %}
14 | {% endif %}
15 |
16 | {{ page-title }}
17 |
18 | {% if site.gems contains "jekyll-seo-tag" %}
19 |
20 | {% else %}
21 | {% include social-metatags.html %}
22 | {% endif %}
23 |
24 |
25 |
26 | {% if site.gems contains "jekyll-feed" %}
27 | {% endif %}
28 |
29 |
30 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | {% include site-header.html %}
40 |
41 | {% if page.layout == 'home' %}
42 | {% assign page-tagline = site.description | default: site.github.project_tagline | escape %}
43 | {% endif %}
44 | {% if page.layout == 'not_found' %}
45 | {% assign page-title = '404 - Not Found' | escape %}
46 | {% endif %}
47 | {% if page.layout == 'page' %}
48 | {% assign page-tagline = page.tagline | escape %}
49 | {% endif %}
50 | {% if page.layout == 'post' %}
51 | {% assign page-tagline = page.tagline | escape %}
52 | {% endif %}
53 |
54 |
81 |
82 |
83 |
84 | {{ content }}
85 |
86 |
98 |
99 |
100 | {% if site.google_analytics %}
101 |
110 | {% endif %}
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/docs/_layouts/home.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 |
6 |
7 | {{ content }}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/_layouts/not_found.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 |
6 |
7 | {{ content }}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/_layouts/page.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 |
6 |
7 | {{ content }}
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/docs/_layouts/post.html:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 |
5 |
6 |
17 |
18 |
19 | {{ content }}
20 |
21 |
22 | {% if site.disqus.shortname %}
23 | {% include disqus_comments.html %}
24 | {% endif %}
25 |
26 |
--------------------------------------------------------------------------------
/docs/_layouts/preview.html:
--------------------------------------------------------------------------------
1 | Loading...
2 |
3 |
11 |
--------------------------------------------------------------------------------
/docs/_sass/blog.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | // Define defaults for each variable.
4 |
5 | $base-font-size: 16px !default;
6 | $base-font-weight: 400 !default;
7 | $small-font-size: $base-font-size * 0.875 !default;
8 | $base-line-height: 1.5 !default;
9 |
10 | $spacing-unit: 30px !default;
11 |
12 | $text-color: $section-headings-color !default;
13 | $background-color: #fdfdfd !default;
14 | $brand-color: #2a7ae2 !default;
15 |
16 | $grey-color: #828282 !default;
17 | $grey-color-light: lighten($grey-color, 40%) !default;
18 | $grey-color-dark: darken($grey-color, 25%) !default;
19 |
20 | // Width of the content area
21 | //$content-width: 800px !default;
22 | $navbar-width: 54em !default;
23 |
24 | $on-palm: 38em !default;
25 | $on-laptop: 54em !default;
26 |
27 | // Use media queries like this:
28 | // @include media-query($on-palm) {
29 | // .wrapper {
30 | // padding-right: $spacing-unit / 2;
31 | // padding-left: $spacing-unit / 2;
32 | // }
33 | // }
34 | @mixin media-query($device) {
35 | @media screen and (max-width: $device) {
36 | @content;
37 | }
38 | }
39 |
40 | @mixin relative-font-size($ratio) {
41 | font-size: $base-font-size * $ratio;
42 | }
43 |
44 | // Import partials.
45 | @import
46 | "blog/base",
47 | "blog/layout"
48 | ;
49 |
--------------------------------------------------------------------------------
/docs/_sass/blog/_base.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Wrapper
3 | */
4 | .wrapper {
5 | max-width: -webkit-calc(#{$navbar-width} - (#{$spacing-unit} * 2));
6 | max-width: calc(#{$navbar-width} - (#{$spacing-unit} * 2));
7 | margin-right: auto;
8 | margin-left: auto;
9 | padding-right: $spacing-unit;
10 | padding-left: $spacing-unit;
11 | @extend %clearfix;
12 |
13 | @include media-query($on-laptop) {
14 | max-width: -webkit-calc(#{$navbar-width} - (#{$spacing-unit}));
15 | max-width: calc(#{$navbar-width} - (#{$spacing-unit}));
16 | padding-right: $spacing-unit / 2;
17 | padding-left: $spacing-unit / 2;
18 | }
19 | }
20 |
21 |
22 |
23 | /**
24 | * Clearfix
25 | */
26 | %clearfix:after {
27 | content: "";
28 | display: table;
29 | clear: both;
30 | }
31 |
--------------------------------------------------------------------------------
/docs/_sass/blog/_layout.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Site header
3 | */
4 | .site-header {
5 | border-bottom: 1px solid $grey-color-light;
6 | min-height: $spacing-unit * 1.865;
7 |
8 | // Positioning context for the mobile navigation icon
9 | position: relative;
10 | }
11 |
12 | .site-title {
13 | @include relative-font-size(1.4);
14 | font-weight: 300;
15 | line-height: $base-line-height * $base-font-size * 2.25;
16 | letter-spacing: -1px;
17 | margin-bottom: 0;
18 | float: left;
19 | text-decoration: none;
20 |
21 | &,
22 | &:visited,
23 | &:hover {
24 | color: $text-color;
25 | text-decoration: none;
26 | }
27 | }
28 |
29 | .site-nav {
30 | float: right;
31 | line-height: $base-line-height * $base-font-size * 2.25;
32 |
33 | .nav-trigger {
34 | display: none;
35 | }
36 |
37 | .menu-icon {
38 | display: none;
39 | }
40 |
41 | .page-link {
42 | color: $text-color;
43 | line-height: $base-line-height;
44 |
45 | // Gaps between nav items, but not on the last one
46 | &:not(:last-child) {
47 | margin-right: 20px;
48 | }
49 | }
50 |
51 | @include media-query($on-palm) {
52 | position: absolute;
53 | top: 9px;
54 | right: $spacing-unit / 2;
55 | background-color: $background-color;
56 | border: 1px solid $grey-color-light;
57 | border-radius: 5px;
58 | text-align: right;
59 |
60 | label[for="nav-trigger"] {
61 | display: block;
62 | float: right;
63 | width: 36px;
64 | height: 36px;
65 | z-index: 2;
66 | cursor: pointer;
67 | }
68 |
69 | .menu-icon {
70 | display: block;
71 | float: right;
72 | width: 36px;
73 | height: 26px;
74 | line-height: 0;
75 | padding-top: 10px;
76 | text-align: center;
77 |
78 | > svg path {
79 | fill: $grey-color-dark;
80 | }
81 | }
82 |
83 | input ~ .trigger {
84 | clear: both;
85 | display: none;
86 | }
87 |
88 | input:checked ~ .trigger {
89 | display: block;
90 | padding-bottom: 5px;
91 | }
92 |
93 | .trigger {
94 | padding-top: 20px;
95 | }
96 | .page-link {
97 | display: block;
98 | padding: 10px 10px;
99 |
100 | &:last-child {
101 | margin-bottom: 10px;
102 | }
103 | margin-right: 10px;
104 | margin-left: 10px;
105 | }
106 |
107 | }
108 | }
109 |
110 | /**
111 | * Post list
112 | */
113 | .post-list {
114 | margin-left: 0;
115 | list-style: none;
116 |
117 | > li {
118 | margin-bottom: $spacing-unit * 1.5;
119 | }
120 |
121 | > li > h2 {
122 | margin-top: 0rem;
123 | margin-bottom: 0.4rem;
124 | }
125 | }
126 |
127 | @include media-query($on-palm) {
128 | .post-list {
129 | -webkit-margin-end: 10px;
130 | -webkit-padding-start: 10px;
131 | }
132 | }
133 |
134 | .post-meta {
135 | font-size: $small-font-size;
136 | color: $grey-color;
137 | }
138 |
139 | .post-link {
140 | display: block;
141 | @include relative-font-size(1.5);
142 | }
143 |
--------------------------------------------------------------------------------
/docs/_sass/rouge-github.scss:
--------------------------------------------------------------------------------
1 | .highlight table td { padding: 5px; }
2 | .highlight table pre { margin: 0; }
3 | .highlight .cm {
4 | color: #999988;
5 | font-style: italic;
6 | }
7 | .highlight .cp {
8 | color: #999999;
9 | font-weight: bold;
10 | }
11 | .highlight .c1 {
12 | color: #999988;
13 | font-style: italic;
14 | }
15 | .highlight .cs {
16 | color: #999999;
17 | font-weight: bold;
18 | font-style: italic;
19 | }
20 | .highlight .c, .highlight .cd {
21 | color: #999988;
22 | font-style: italic;
23 | }
24 | .highlight .err {
25 | color: #a61717;
26 | background-color: #e3d2d2;
27 | }
28 | .highlight .gd {
29 | color: #000000;
30 | background-color: #ffdddd;
31 | }
32 | .highlight .ge {
33 | color: #000000;
34 | font-style: italic;
35 | }
36 | .highlight .gr {
37 | color: #aa0000;
38 | }
39 | .highlight .gh {
40 | color: #999999;
41 | }
42 | .highlight .gi {
43 | color: #000000;
44 | background-color: #ddffdd;
45 | }
46 | .highlight .go {
47 | color: #888888;
48 | }
49 | .highlight .gp {
50 | color: #555555;
51 | }
52 | .highlight .gs {
53 | font-weight: bold;
54 | }
55 | .highlight .gu {
56 | color: #aaaaaa;
57 | }
58 | .highlight .gt {
59 | color: #aa0000;
60 | }
61 | .highlight .kc {
62 | color: #000000;
63 | font-weight: bold;
64 | }
65 | .highlight .kd {
66 | color: #000000;
67 | font-weight: bold;
68 | }
69 | .highlight .kn {
70 | color: #000000;
71 | font-weight: bold;
72 | }
73 | .highlight .kp {
74 | color: #000000;
75 | font-weight: bold;
76 | }
77 | .highlight .kr {
78 | color: #000000;
79 | font-weight: bold;
80 | }
81 | .highlight .kt {
82 | color: #445588;
83 | font-weight: bold;
84 | }
85 | .highlight .k, .highlight .kv {
86 | color: #000000;
87 | font-weight: bold;
88 | }
89 | .highlight .mf {
90 | color: #009999;
91 | }
92 | .highlight .mh {
93 | color: #009999;
94 | }
95 | .highlight .il {
96 | color: #009999;
97 | }
98 | .highlight .mi {
99 | color: #009999;
100 | }
101 | .highlight .mo {
102 | color: #009999;
103 | }
104 | .highlight .m, .highlight .mb, .highlight .mx {
105 | color: #009999;
106 | }
107 | .highlight .sb {
108 | color: #d14;
109 | }
110 | .highlight .sc {
111 | color: #d14;
112 | }
113 | .highlight .sd {
114 | color: #d14;
115 | }
116 | .highlight .s2 {
117 | color: #d14;
118 | }
119 | .highlight .se {
120 | color: #d14;
121 | }
122 | .highlight .sh {
123 | color: #d14;
124 | }
125 | .highlight .si {
126 | color: #d14;
127 | }
128 | .highlight .sx {
129 | color: #d14;
130 | }
131 | .highlight .sr {
132 | color: #009926;
133 | }
134 | .highlight .s1 {
135 | color: #d14;
136 | }
137 | .highlight .ss {
138 | color: #990073;
139 | }
140 | .highlight .s {
141 | color: #d14;
142 | }
143 | .highlight .na {
144 | color: #008080;
145 | }
146 | .highlight .bp {
147 | color: #999999;
148 | }
149 | .highlight .nb {
150 | color: #0086B3;
151 | }
152 | .highlight .nc {
153 | color: #445588;
154 | font-weight: bold;
155 | }
156 | .highlight .no {
157 | color: #008080;
158 | }
159 | .highlight .nd {
160 | color: #3c5d5d;
161 | font-weight: bold;
162 | }
163 | .highlight .ni {
164 | color: #800080;
165 | }
166 | .highlight .ne {
167 | color: #990000;
168 | font-weight: bold;
169 | }
170 | .highlight .nf {
171 | color: #990000;
172 | font-weight: bold;
173 | }
174 | .highlight .nl {
175 | color: #990000;
176 | font-weight: bold;
177 | }
178 | .highlight .nn {
179 | color: #555555;
180 | }
181 | .highlight .nt {
182 | color: #000080;
183 | }
184 | .highlight .vc {
185 | color: #008080;
186 | }
187 | .highlight .vg {
188 | color: #008080;
189 | }
190 | .highlight .vi {
191 | color: #008080;
192 | }
193 | .highlight .nv {
194 | color: #008080;
195 | }
196 | .highlight .ow {
197 | color: #000000;
198 | font-weight: bold;
199 | }
200 | .highlight .o {
201 | color: #000000;
202 | font-weight: bold;
203 | }
204 | .highlight .w {
205 | color: #bbbbbb;
206 | }
207 | .highlight {
208 | background-color: #f8f8f8;
209 | }
210 |
--------------------------------------------------------------------------------
/docs/_sass/variables.scss:
--------------------------------------------------------------------------------
1 | // Breakpoints
2 | $large-breakpoint: 64em !default;
3 | $medium-breakpoint: 42em !default;
4 |
5 | // Headers
6 | $header-heading-color: #fff !default;
7 | $header-bg-color: #263b65 !default;
8 | $header-bg-color-secondary: #008599 !default;
9 |
10 | // Text
11 | $section-headings-color: #606c71 !default;
12 | $body-text-color: #606c71 !default;
13 | $body-link-color: #1e6bb8 !default;
14 | $blockquote-text-color: #819198 !default;
15 |
16 | // Code
17 | $code-bg-color: #f3f6fa !default;
18 | $code-text-color: #567482 !default;
19 |
20 | // Borders
21 | $border-color: #dce6f0 !default;
22 | $table-border-color: #e9ebec !default;
23 | $hr-border-color: #eff0f1 !default;
24 |
--------------------------------------------------------------------------------
/docs/assets/css/style.scss:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | @import 'jekyll-theme-cayman-blog';
5 |
--------------------------------------------------------------------------------
/docs/components.md:
--------------------------------------------------------------------------------
1 | # Components
2 | ## Charts
3 |
4 |
5 |
6 |
7 |
8 |
9 | ### bar chart ([Usage](examples/bar_chart))
10 | 
11 |
12 |
13 |
14 |
15 |
16 |
17 | ### horizontal bar chart ([Usage](examples/horizontal_bar_chart))
18 | 
19 |
20 |
21 |
22 |
23 |
24 |
25 | ### donut chart ([Usage](examples/donut_chart))
26 | 
27 |
28 |
29 |
30 |
31 |
32 |
33 | ### pie chart ([Usage](examples/pie_chart))
34 | 
35 |
36 |
37 |
38 |
39 |
40 |
41 | ### stacked bar chart ([Usage](examples/stacked_bar_chart))
42 | 
43 |
44 |
45 |
46 |
47 |
48 |
49 | ### horizontal stacked bar chart ([Usage](examples/horizontal_stacked_bar_chart))
50 | 
51 |
52 |
53 |
54 |
55 |
56 |
57 | ### stacked line chart ([Usage](examples/stacked_line_chart))
58 | 
59 |
60 |
61 |
62 |
63 |
64 |
65 | ### stacked spline chart ([Usage](examples/stacked_spline_chart))
66 | 
67 |
68 |
69 |
70 |
71 |
72 |
73 | ### stacked area chart ([Usage](examples/stacked_area_chart))
74 | 
75 |
76 |
77 |
78 |
79 |
80 |
81 | ### gauge chart ([Usage](examples/gauge_chart))
82 | 
83 |
84 |
85 |
86 |
87 |
88 |
89 | ### scatter plot ([Usage](examples/scatter_plot))
90 | 
91 |
92 |
93 |
94 |
95 |
96 |
97 | ### line chart ([Usage](examples/line_chart))
98 | 
99 |
100 |
101 |
102 |
103 |
104 |
105 | ### bubble chart ([Usage](examples/bubble_chart))
106 | 
107 |
108 |
109 |
110 |
111 |
112 |
113 | ### spline chart ([Usage](examples/spline_chart))
114 | 
115 |
116 |
117 |
118 |
119 |
120 |
121 | ### custom axes ([Usage](examples/custom_axes))
122 | 
123 |
124 |
125 |
126 |
127 |
128 |
129 | ### area chart ([Usage](examples/area_chart))
130 | 
131 |
132 |
133 |
134 |
135 |
136 |
137 | ### step chart ([Usage](examples/step_chart))
138 | 
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | ## Controls
147 |
148 |
149 |
150 |
151 |
152 |
153 | ### dropdown ([Usage](examples/dropdown))
154 | 
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 | ## Grid
163 |
164 |
165 |
166 |
167 |
168 |
169 | ### rows ([Usage](examples/rows))
170 | 
171 |
172 |
173 |
174 |
175 |
176 |
177 | ### board ([Usage](examples/board))
178 | 
179 |
180 |
181 |
182 |
183 |
184 |
185 | ### columns ([Usage](examples/columns))
186 | 
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 | ## Text
195 |
196 |
197 |
198 |
199 |
200 |
201 | ### h1 text ([Usage](examples/h1_text))
202 | 
203 |
204 |
205 |
206 |
207 |
208 |
209 | ### h2 text ([Usage](examples/h2_text))
210 | 
211 |
212 |
213 |
214 |
215 |
216 |
217 | ### h3 text ([Usage](examples/h3_text))
218 | 
219 |
220 |
221 |
222 |
223 |
224 |
225 | ### p text ([Usage](examples/p_text))
226 | 
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
--------------------------------------------------------------------------------
/docs/examples/1_food_comparison.yml:
--------------------------------------------------------------------------------
1 | # EDIT THIS
2 | dashboard "Food":
3 | - h1 text: Food
4 | - h2 text: By caloric content
5 | - 3 columns:
6 | - rows:
7 | - h3 text: Bananas
8 | - pie chart: {
9 | "columns": [
10 | ["Protein", 5], ["Sugar", 10], ["Other carbs", 40], ["Fat", 1]
11 | ]
12 | }
13 | - rows:
14 | - h3 text: Tofu
15 | - pie chart: {
16 | "columns": [
17 | ["Protein", 30], ["Sugar", 0], ["Other carbs", 40], ["Fat", 3]
18 | ]
19 | }
20 | - rows:
21 | - h3 text: Peanut butter
22 | - pie chart: {
23 | "columns": [
24 | ["Protein", 20], ["Sugar", 2], ["Other carbs", 20], ["Fat", 50]
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/docs/examples/area_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use area chart
4 | ---
5 |
6 | # How to use area chart
7 | Here's an example code regarding the use of area chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | area chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 30
17 | - 29
18 | - 25
19 | - 26
20 | - 27
21 | - 10
22 | -
23 | - "Oranges"
24 | - 20
25 | - 21
26 | - 22
27 | - 20
28 | - 27
29 | - 19
30 |
31 | ```
32 | The code above will render a area chart that looks like this:
33 |
34 | 
35 |
36 | ## JSON format
37 | The YAML above is equivalent to this JSON:
38 | ```json
39 | {
40 | "component": "root",
41 | "args": {
42 | "title": "Example"
43 | },
44 | "data": [
45 | {
46 | "component": "chart",
47 | "args": {
48 | "type": "area",
49 | "stacked": false
50 | },
51 | "data": {
52 | "columns": [
53 | [
54 | "Apples",
55 | 30,
56 | 29,
57 | 25,
58 | 26,
59 | 27,
60 | 10
61 | ],
62 | [
63 | "Oranges",
64 | 20,
65 | 21,
66 | 22,
67 | 20,
68 | 27,
69 | 19
70 | ]
71 | ]
72 | }
73 | }
74 | ]
75 | }
76 | ```
77 |
--------------------------------------------------------------------------------
/docs/examples/area_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | area chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 30
9 | - 29
10 | - 25
11 | - 26
12 | - 27
13 | - 10
14 | -
15 | - "Oranges"
16 | - 20
17 | - 21
18 | - 22
19 | - 20
20 | - 27
21 | - 19
22 |
23 |
--------------------------------------------------------------------------------
/docs/examples/bar_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use bar chart
4 | ---
5 |
6 | # How to use bar chart
7 | Here's an example code regarding the use of bar chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | bar chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 3
17 | -
18 | - "Oranges"
19 | - 2
20 | -
21 | - "Pears"
22 | - 2
23 |
24 | ```
25 | The code above will render a bar chart that looks like this:
26 |
27 | 
28 |
29 | ## JSON format
30 | The YAML above is equivalent to this JSON:
31 | ```json
32 | {
33 | "component": "root",
34 | "args": {
35 | "title": "Example"
36 | },
37 | "data": [
38 | {
39 | "component": "chart",
40 | "args": {
41 | "type": "bar",
42 | "stacked": false
43 | },
44 | "data": {
45 | "columns": [
46 | [
47 | "Apples",
48 | 3
49 | ],
50 | [
51 | "Oranges",
52 | 2
53 | ],
54 | [
55 | "Pears",
56 | 2
57 | ]
58 | ]
59 | }
60 | }
61 | ]
62 | }
63 | ```
64 |
--------------------------------------------------------------------------------
/docs/examples/bar_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | bar chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 3
9 | -
10 | - "Oranges"
11 | - 2
12 | -
13 | - "Pears"
14 | - 2
15 |
16 |
--------------------------------------------------------------------------------
/docs/examples/board.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use board
4 | ---
5 |
6 | # How to use board
7 | Here's an example code regarding the use of board:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | board:
13 | -
14 | rows:
15 | -
16 | pie chart:
17 | columns:
18 | -
19 | - "Apples"
20 | - 3
21 | -
22 | - "Oranges"
23 | - 2
24 | -
25 | - "Pears"
26 | - 2
27 | -
28 | p text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."
29 |
30 | ```
31 | The code above will render a board that looks like this:
32 |
33 | 
34 |
35 | ## JSON format
36 | The YAML above is equivalent to this JSON:
37 | ```json
38 | {
39 | "component": "root",
40 | "args": {
41 | "title": "Example"
42 | },
43 | "data": [
44 | {
45 | "component": "board",
46 | "data": [
47 | {
48 | "component": "rows",
49 | "data": [
50 | {
51 | "component": "chart",
52 | "args": {
53 | "type": "pie",
54 | "stacked": false
55 | },
56 | "data": {
57 | "columns": [
58 | [
59 | "Apples",
60 | 3
61 | ],
62 | [
63 | "Oranges",
64 | 2
65 | ],
66 | [
67 | "Pears",
68 | 2
69 | ]
70 | ]
71 | }
72 | },
73 | {
74 | "component": "text",
75 | "args": {
76 | "tagName": "p"
77 | },
78 | "data": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."
79 | }
80 | ]
81 | }
82 | ]
83 | }
84 | ]
85 | }
86 | ```
87 |
--------------------------------------------------------------------------------
/docs/examples/board.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | board:
5 | -
6 | rows:
7 | -
8 | pie chart:
9 | columns:
10 | -
11 | - "Apples"
12 | - 3
13 | -
14 | - "Oranges"
15 | - 2
16 | -
17 | - "Pears"
18 | - 2
19 | -
20 | p text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."
21 |
22 |
--------------------------------------------------------------------------------
/docs/examples/bubble_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use bubble chart
4 | ---
5 |
6 | # How to use bubble chart
7 | Here's an example code regarding the use of bubble chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | bubble chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 30
17 | - 29
18 | - 25
19 | - 26
20 | - 27
21 | - 10
22 | -
23 | - "Oranges"
24 | - 20
25 | - 21
26 | - 22
27 | - 20
28 | - 27
29 | - 19
30 | -
31 | - "Pears"
32 | - 10
33 | - 9
34 | - 8
35 | - 8
36 | - 7
37 | - 8
38 |
39 | ```
40 | The code above will render a bubble chart that looks like this:
41 |
42 | 
43 |
44 | ## JSON format
45 | The YAML above is equivalent to this JSON:
46 | ```json
47 | {
48 | "component": "root",
49 | "args": {
50 | "title": "Example"
51 | },
52 | "data": [
53 | {
54 | "component": "chart",
55 | "args": {
56 | "type": "bubble",
57 | "stacked": false
58 | },
59 | "data": {
60 | "columns": [
61 | [
62 | "Apples",
63 | 30,
64 | 29,
65 | 25,
66 | 26,
67 | 27,
68 | 10
69 | ],
70 | [
71 | "Oranges",
72 | 20,
73 | 21,
74 | 22,
75 | 20,
76 | 27,
77 | 19
78 | ],
79 | [
80 | "Pears",
81 | 10,
82 | 9,
83 | 8,
84 | 8,
85 | 7,
86 | 8
87 | ]
88 | ]
89 | }
90 | }
91 | ]
92 | }
93 | ```
94 |
--------------------------------------------------------------------------------
/docs/examples/bubble_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | bubble chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 30
9 | - 29
10 | - 25
11 | - 26
12 | - 27
13 | - 10
14 | -
15 | - "Oranges"
16 | - 20
17 | - 21
18 | - 22
19 | - 20
20 | - 27
21 | - 19
22 | -
23 | - "Pears"
24 | - 10
25 | - 9
26 | - 8
27 | - 8
28 | - 7
29 | - 8
30 |
31 |
--------------------------------------------------------------------------------
/docs/examples/columns.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use columns
4 | ---
5 |
6 | # How to use columns
7 | Here's an example code regarding the use of columns:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | 2 columns:
13 | -
14 | pie chart:
15 | columns:
16 | -
17 | - "Apples"
18 | - 3
19 | -
20 | - "Oranges"
21 | - 2
22 | -
23 | - "Pears"
24 | - 2
25 | -
26 | p text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."
27 |
28 | ```
29 | The code above will render a columns that looks like this:
30 |
31 | 
32 |
33 | ## JSON format
34 | The YAML above is equivalent to this JSON:
35 | ```json
36 | {
37 | "component": "root",
38 | "args": {
39 | "title": "Example"
40 | },
41 | "data": [
42 | {
43 | "component": "columns",
44 | "args": {
45 | "columns": 2
46 | },
47 | "data": [
48 | {
49 | "component": "chart",
50 | "args": {
51 | "type": "pie",
52 | "stacked": false
53 | },
54 | "data": {
55 | "columns": [
56 | [
57 | "Apples",
58 | 3
59 | ],
60 | [
61 | "Oranges",
62 | 2
63 | ],
64 | [
65 | "Pears",
66 | 2
67 | ]
68 | ]
69 | }
70 | },
71 | {
72 | "component": "text",
73 | "args": {
74 | "tagName": "p"
75 | },
76 | "data": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."
77 | }
78 | ]
79 | }
80 | ]
81 | }
82 | ```
83 |
--------------------------------------------------------------------------------
/docs/examples/columns.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | 2 columns:
5 | -
6 | pie chart:
7 | columns:
8 | -
9 | - "Apples"
10 | - 3
11 | -
12 | - "Oranges"
13 | - 2
14 | -
15 | - "Pears"
16 | - 2
17 | -
18 | p text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."
19 |
20 |
--------------------------------------------------------------------------------
/docs/examples/custom_axes.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use custom axes
4 | ---
5 |
6 | # How to use custom axes
7 | Here's an example code regarding the use of custom axes:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | line chart:
13 | -
14 | attr:axis:
15 | x:
16 | label: "Year"
17 | type: "timeseries"
18 | tick:
19 | format: "%Y"
20 | y:
21 | label: "Amount consumed (tonnes)"
22 | -
23 | data:
24 | x: "x"
25 | xFormat: "%Y"
26 | columns:
27 | -
28 | - "x"
29 | - "1999"
30 | - "2001"
31 | - "2002"
32 | - "2004"
33 | - "2007"
34 | - "2008"
35 | -
36 | - "Apples"
37 | - 30
38 | - 29
39 | - 25
40 | - 26
41 | - 27
42 | - 10
43 | -
44 | - "Oranges"
45 | - 20
46 | - 21
47 | - 22
48 | - 20
49 | - 27
50 | - 19
51 | -
52 | - "Pears"
53 | - 10
54 | - 9
55 | - 8
56 | - 8
57 | - 7
58 | - 8
59 |
60 | ```
61 | The code above will render a custom axes that looks like this:
62 |
63 | 
64 |
65 | ## JSON format
66 | The YAML above is equivalent to this JSON:
67 | ```json
68 | {
69 | "component": "root",
70 | "args": {
71 | "title": "Example"
72 | },
73 | "data": [
74 | {
75 | "component": "chart",
76 | "args": {
77 | "axis": {
78 | "x": {
79 | "label": "Year",
80 | "type": "timeseries",
81 | "tick": {
82 | "format": "%Y"
83 | }
84 | },
85 | "y": {
86 | "label": "Amount consumed (tonnes)"
87 | }
88 | },
89 | "type": "line",
90 | "stacked": false
91 | },
92 | "data": {
93 | "x": "x",
94 | "xFormat": "%Y",
95 | "columns": [
96 | [
97 | "x",
98 | "1999",
99 | "2001",
100 | "2002",
101 | "2004",
102 | "2007",
103 | "2008"
104 | ],
105 | [
106 | "Apples",
107 | 30,
108 | 29,
109 | 25,
110 | 26,
111 | 27,
112 | 10
113 | ],
114 | [
115 | "Oranges",
116 | 20,
117 | 21,
118 | 22,
119 | 20,
120 | 27,
121 | 19
122 | ],
123 | [
124 | "Pears",
125 | 10,
126 | 9,
127 | 8,
128 | 8,
129 | 7,
130 | 8
131 | ]
132 | ]
133 | }
134 | }
135 | ]
136 | }
137 | ```
138 |
--------------------------------------------------------------------------------
/docs/examples/custom_axes.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | line chart:
5 | -
6 | attr:axis:
7 | x:
8 | label: "Year"
9 | type: "timeseries"
10 | tick:
11 | format: "%Y"
12 | y:
13 | label: "Amount consumed (tonnes)"
14 | -
15 | data:
16 | x: "x"
17 | xFormat: "%Y"
18 | columns:
19 | -
20 | - "x"
21 | - "1999"
22 | - "2001"
23 | - "2002"
24 | - "2004"
25 | - "2007"
26 | - "2008"
27 | -
28 | - "Apples"
29 | - 30
30 | - 29
31 | - 25
32 | - 26
33 | - 27
34 | - 10
35 | -
36 | - "Oranges"
37 | - 20
38 | - 21
39 | - 22
40 | - 20
41 | - 27
42 | - 19
43 | -
44 | - "Pears"
45 | - 10
46 | - 9
47 | - 8
48 | - 8
49 | - 7
50 | - 8
51 |
52 |
--------------------------------------------------------------------------------
/docs/examples/donut_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use donut chart
4 | ---
5 |
6 | # How to use donut chart
7 | Here's an example code regarding the use of donut chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | donut chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 3
17 | -
18 | - "Oranges"
19 | - 2
20 | -
21 | - "Pears"
22 | - 2
23 |
24 | ```
25 | The code above will render a donut chart that looks like this:
26 |
27 | 
28 |
29 | ## JSON format
30 | The YAML above is equivalent to this JSON:
31 | ```json
32 | {
33 | "component": "root",
34 | "args": {
35 | "title": "Example"
36 | },
37 | "data": [
38 | {
39 | "component": "chart",
40 | "args": {
41 | "type": "donut",
42 | "stacked": false
43 | },
44 | "data": {
45 | "columns": [
46 | [
47 | "Apples",
48 | 3
49 | ],
50 | [
51 | "Oranges",
52 | 2
53 | ],
54 | [
55 | "Pears",
56 | 2
57 | ]
58 | ]
59 | }
60 | }
61 | ]
62 | }
63 | ```
64 |
--------------------------------------------------------------------------------
/docs/examples/donut_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | donut chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 3
9 | -
10 | - "Oranges"
11 | - 2
12 | -
13 | - "Pears"
14 | - 2
15 |
16 |
--------------------------------------------------------------------------------
/docs/examples/dropdown.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use dropdown
4 | ---
5 |
6 | # How to use dropdown
7 | Here's an example code regarding the use of dropdown:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | dropdown gender=male:
13 | -
14 | value: "male"
15 | text: "Male"
16 | -
17 | value: "female"
18 | text: "Female"
19 |
20 | ```
21 | The code above will render a dropdown that looks like this:
22 |
23 | 
24 |
25 | ## JSON format
26 | The YAML above is equivalent to this JSON:
27 | ```json
28 | {
29 | "component": "root",
30 | "args": {
31 | "title": "Example"
32 | },
33 | "data": [
34 | {
35 | "component": "dropdown",
36 | "args": {
37 | "variable": "gender",
38 | "default": "male"
39 | },
40 | "data": [
41 | {
42 | "value": "male",
43 | "text": "Male"
44 | },
45 | {
46 | "value": "female",
47 | "text": "Female"
48 | }
49 | ]
50 | }
51 | ]
52 | }
53 | ```
54 |
--------------------------------------------------------------------------------
/docs/examples/dropdown.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | dropdown gender=male:
5 | -
6 | value: "male"
7 | text: "Male"
8 | -
9 | value: "female"
10 | text: "Female"
11 |
12 |
--------------------------------------------------------------------------------
/docs/examples/gauge_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use gauge chart
4 | ---
5 |
6 | # How to use gauge chart
7 | Here's an example code regarding the use of gauge chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | gauge chart:
13 | columns:
14 | -
15 | - "data"
16 | - 30
17 |
18 | ```
19 | The code above will render a gauge chart that looks like this:
20 |
21 | 
22 |
23 | ## JSON format
24 | The YAML above is equivalent to this JSON:
25 | ```json
26 | {
27 | "component": "root",
28 | "args": {
29 | "title": "Example"
30 | },
31 | "data": [
32 | {
33 | "component": "chart",
34 | "args": {
35 | "type": "gauge",
36 | "stacked": false
37 | },
38 | "data": {
39 | "columns": [
40 | [
41 | "data",
42 | 30
43 | ]
44 | ]
45 | }
46 | }
47 | ]
48 | }
49 | ```
50 |
--------------------------------------------------------------------------------
/docs/examples/gauge_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | gauge chart:
5 | columns:
6 | -
7 | - "data"
8 | - 30
9 |
10 |
--------------------------------------------------------------------------------
/docs/examples/h1_text.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use h1 text
4 | ---
5 |
6 | # How to use h1 text
7 | Here's an example code regarding the use of h1 text:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | h1 text: "Lorem ipsum dolor sit amet"
13 |
14 | ```
15 | The code above will render a h1 text that looks like this:
16 |
17 | 
18 |
19 | ## JSON format
20 | The YAML above is equivalent to this JSON:
21 | ```json
22 | {
23 | "component": "root",
24 | "args": {
25 | "title": "Example"
26 | },
27 | "data": [
28 | {
29 | "component": "text",
30 | "args": {
31 | "tagName": "h1"
32 | },
33 | "data": "Lorem ipsum dolor sit amet"
34 | }
35 | ]
36 | }
37 | ```
38 |
--------------------------------------------------------------------------------
/docs/examples/h1_text.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | h1 text: "Lorem ipsum dolor sit amet"
5 |
6 |
--------------------------------------------------------------------------------
/docs/examples/h2_text.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use h2 text
4 | ---
5 |
6 | # How to use h2 text
7 | Here's an example code regarding the use of h2 text:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | h2 text: "Lorem ipsum dolor sit amet"
13 |
14 | ```
15 | The code above will render a h2 text that looks like this:
16 |
17 | 
18 |
19 | ## JSON format
20 | The YAML above is equivalent to this JSON:
21 | ```json
22 | {
23 | "component": "root",
24 | "args": {
25 | "title": "Example"
26 | },
27 | "data": [
28 | {
29 | "component": "text",
30 | "args": {
31 | "tagName": "h2"
32 | },
33 | "data": "Lorem ipsum dolor sit amet"
34 | }
35 | ]
36 | }
37 | ```
38 |
--------------------------------------------------------------------------------
/docs/examples/h2_text.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | h2 text: "Lorem ipsum dolor sit amet"
5 |
6 |
--------------------------------------------------------------------------------
/docs/examples/h3_text.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use h3 text
4 | ---
5 |
6 | # How to use h3 text
7 | Here's an example code regarding the use of h3 text:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | h3 text: "Lorem ipsum dolor sit amet"
13 |
14 | ```
15 | The code above will render a h3 text that looks like this:
16 |
17 | 
18 |
19 | ## JSON format
20 | The YAML above is equivalent to this JSON:
21 | ```json
22 | {
23 | "component": "root",
24 | "args": {
25 | "title": "Example"
26 | },
27 | "data": [
28 | {
29 | "component": "text",
30 | "args": {
31 | "tagName": "h3"
32 | },
33 | "data": "Lorem ipsum dolor sit amet"
34 | }
35 | ]
36 | }
37 | ```
38 |
--------------------------------------------------------------------------------
/docs/examples/h3_text.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | h3 text: "Lorem ipsum dolor sit amet"
5 |
6 |
--------------------------------------------------------------------------------
/docs/examples/horizontal_bar_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use horizontal bar chart
4 | ---
5 |
6 | # How to use horizontal bar chart
7 | Here's an example code regarding the use of horizontal bar chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | horizontal bar chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 3
17 | -
18 | - "Oranges"
19 | - 2
20 | -
21 | - "Pears"
22 | - 2
23 |
24 | ```
25 | The code above will render a horizontal bar chart that looks like this:
26 |
27 | 
28 |
29 | ## JSON format
30 | The YAML above is equivalent to this JSON:
31 | ```json
32 | {
33 | "component": "root",
34 | "args": {
35 | "title": "Example"
36 | },
37 | "data": [
38 | {
39 | "component": "chart",
40 | "args": {
41 | "type": "bar",
42 | "stacked": false,
43 | "axis": {
44 | "rotated": true
45 | }
46 | },
47 | "data": {
48 | "columns": [
49 | [
50 | "Apples",
51 | 3
52 | ],
53 | [
54 | "Oranges",
55 | 2
56 | ],
57 | [
58 | "Pears",
59 | 2
60 | ]
61 | ]
62 | }
63 | }
64 | ]
65 | }
66 | ```
67 |
--------------------------------------------------------------------------------
/docs/examples/horizontal_bar_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | horizontal bar chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 3
9 | -
10 | - "Oranges"
11 | - 2
12 | -
13 | - "Pears"
14 | - 2
15 |
16 |
--------------------------------------------------------------------------------
/docs/examples/horizontal_stacked_bar_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use horizontal stacked bar chart
4 | ---
5 |
6 | # How to use horizontal stacked bar chart
7 | Here's an example code regarding the use of horizontal stacked bar chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | horizontal stacked bar chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 3
17 | - 2
18 | - 3
19 | - 4
20 | - 2
21 | -
22 | - "Oranges"
23 | - 2
24 | - 1
25 | - 0
26 | - 1
27 | - 1
28 | -
29 | - "Pears"
30 | - 2
31 | - 0
32 | - 0
33 | - 3
34 | - 4
35 |
36 | ```
37 | The code above will render a horizontal stacked bar chart that looks like this:
38 |
39 | 
40 |
41 | ## JSON format
42 | The YAML above is equivalent to this JSON:
43 | ```json
44 | {
45 | "component": "root",
46 | "args": {
47 | "title": "Example"
48 | },
49 | "data": [
50 | {
51 | "component": "chart",
52 | "args": {
53 | "type": "bar",
54 | "stacked": true,
55 | "axis": {
56 | "rotated": true
57 | }
58 | },
59 | "data": {
60 | "columns": [
61 | [
62 | "Apples",
63 | 3,
64 | 2,
65 | 3,
66 | 4,
67 | 2
68 | ],
69 | [
70 | "Oranges",
71 | 2,
72 | 1,
73 | 0,
74 | 1,
75 | 1
76 | ],
77 | [
78 | "Pears",
79 | 2,
80 | 0,
81 | 0,
82 | 3,
83 | 4
84 | ]
85 | ]
86 | }
87 | }
88 | ]
89 | }
90 | ```
91 |
--------------------------------------------------------------------------------
/docs/examples/horizontal_stacked_bar_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | horizontal stacked bar chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 3
9 | - 2
10 | - 3
11 | - 4
12 | - 2
13 | -
14 | - "Oranges"
15 | - 2
16 | - 1
17 | - 0
18 | - 1
19 | - 1
20 | -
21 | - "Pears"
22 | - 2
23 | - 0
24 | - 0
25 | - 3
26 | - 4
27 |
28 |
--------------------------------------------------------------------------------
/docs/examples/line_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use line chart
4 | ---
5 |
6 | # How to use line chart
7 | Here's an example code regarding the use of line chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | line chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 30
17 | - 29
18 | - 25
19 | - 26
20 | - 27
21 | - 10
22 | -
23 | - "Oranges"
24 | - 20
25 | - 21
26 | - 22
27 | - 20
28 | - 27
29 | - 19
30 | -
31 | - "Pears"
32 | - 10
33 | - 9
34 | - 8
35 | - 8
36 | - 7
37 | - 8
38 |
39 | ```
40 | The code above will render a line chart that looks like this:
41 |
42 | 
43 |
44 | ## JSON format
45 | The YAML above is equivalent to this JSON:
46 | ```json
47 | {
48 | "component": "root",
49 | "args": {
50 | "title": "Example"
51 | },
52 | "data": [
53 | {
54 | "component": "chart",
55 | "args": {
56 | "type": "line",
57 | "stacked": false
58 | },
59 | "data": {
60 | "columns": [
61 | [
62 | "Apples",
63 | 30,
64 | 29,
65 | 25,
66 | 26,
67 | 27,
68 | 10
69 | ],
70 | [
71 | "Oranges",
72 | 20,
73 | 21,
74 | 22,
75 | 20,
76 | 27,
77 | 19
78 | ],
79 | [
80 | "Pears",
81 | 10,
82 | 9,
83 | 8,
84 | 8,
85 | 7,
86 | 8
87 | ]
88 | ]
89 | }
90 | }
91 | ]
92 | }
93 | ```
94 |
--------------------------------------------------------------------------------
/docs/examples/line_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | line chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 30
9 | - 29
10 | - 25
11 | - 26
12 | - 27
13 | - 10
14 | -
15 | - "Oranges"
16 | - 20
17 | - 21
18 | - 22
19 | - 20
20 | - 27
21 | - 19
22 | -
23 | - "Pears"
24 | - 10
25 | - 9
26 | - 8
27 | - 8
28 | - 7
29 | - 8
30 |
31 |
--------------------------------------------------------------------------------
/docs/examples/p_text.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use p text
4 | ---
5 |
6 | # How to use p text
7 | Here's an example code regarding the use of p text:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | p text: "Lorem ipsum dolor sit amet"
13 |
14 | ```
15 | The code above will render a p text that looks like this:
16 |
17 | 
18 |
19 | ## JSON format
20 | The YAML above is equivalent to this JSON:
21 | ```json
22 | {
23 | "component": "root",
24 | "args": {
25 | "title": "Example"
26 | },
27 | "data": [
28 | {
29 | "component": "text",
30 | "args": {
31 | "tagName": "p"
32 | },
33 | "data": "Lorem ipsum dolor sit amet"
34 | }
35 | ]
36 | }
37 | ```
38 |
--------------------------------------------------------------------------------
/docs/examples/p_text.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | p text: "Lorem ipsum dolor sit amet"
5 |
6 |
--------------------------------------------------------------------------------
/docs/examples/pie_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use pie chart
4 | ---
5 |
6 | # How to use pie chart
7 | Here's an example code regarding the use of pie chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | pie chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 3
17 | -
18 | - "Oranges"
19 | - 2
20 | -
21 | - "Pears"
22 | - 2
23 |
24 | ```
25 | The code above will render a pie chart that looks like this:
26 |
27 | 
28 |
29 | ## JSON format
30 | The YAML above is equivalent to this JSON:
31 | ```json
32 | {
33 | "component": "root",
34 | "args": {
35 | "title": "Example"
36 | },
37 | "data": [
38 | {
39 | "component": "chart",
40 | "args": {
41 | "type": "pie",
42 | "stacked": false
43 | },
44 | "data": {
45 | "columns": [
46 | [
47 | "Apples",
48 | 3
49 | ],
50 | [
51 | "Oranges",
52 | 2
53 | ],
54 | [
55 | "Pears",
56 | 2
57 | ]
58 | ]
59 | }
60 | }
61 | ]
62 | }
63 | ```
64 |
--------------------------------------------------------------------------------
/docs/examples/pie_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | pie chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 3
9 | -
10 | - "Oranges"
11 | - 2
12 | -
13 | - "Pears"
14 | - 2
15 |
16 |
--------------------------------------------------------------------------------
/docs/examples/rows.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use rows
4 | ---
5 |
6 | # How to use rows
7 | Here's an example code regarding the use of rows:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | 2 columns:
13 | -
14 | rows:
15 | -
16 | h2 text: "Lorem ipsum dolor sit amet"
17 | -
18 | pie chart:
19 | columns:
20 | -
21 | - "Apples"
22 | - 3
23 | -
24 | - "Oranges"
25 | - 2
26 | -
27 | - "Pears"
28 | - 2
29 | -
30 | rows:
31 | -
32 | h2 text: "Lorem ipsum dolor sit amet"
33 | -
34 | p text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."
35 |
36 | ```
37 | The code above will render a rows that looks like this:
38 |
39 | 
40 |
41 | ## JSON format
42 | The YAML above is equivalent to this JSON:
43 | ```json
44 | {
45 | "component": "root",
46 | "args": {
47 | "title": "Example"
48 | },
49 | "data": [
50 | {
51 | "component": "columns",
52 | "args": {
53 | "columns": 2
54 | },
55 | "data": [
56 | {
57 | "component": "rows",
58 | "data": [
59 | {
60 | "component": "text",
61 | "args": {
62 | "tagName": "h2"
63 | },
64 | "data": "Lorem ipsum dolor sit amet"
65 | },
66 | {
67 | "component": "chart",
68 | "args": {
69 | "type": "pie",
70 | "stacked": false
71 | },
72 | "data": {
73 | "columns": [
74 | [
75 | "Apples",
76 | 3
77 | ],
78 | [
79 | "Oranges",
80 | 2
81 | ],
82 | [
83 | "Pears",
84 | 2
85 | ]
86 | ]
87 | }
88 | }
89 | ]
90 | },
91 | {
92 | "component": "rows",
93 | "data": [
94 | {
95 | "component": "text",
96 | "args": {
97 | "tagName": "h2"
98 | },
99 | "data": "Lorem ipsum dolor sit amet"
100 | },
101 | {
102 | "component": "text",
103 | "args": {
104 | "tagName": "p"
105 | },
106 | "data": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."
107 | }
108 | ]
109 | }
110 | ]
111 | }
112 | ]
113 | }
114 | ```
115 |
--------------------------------------------------------------------------------
/docs/examples/rows.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | 2 columns:
5 | -
6 | rows:
7 | -
8 | h2 text: "Lorem ipsum dolor sit amet"
9 | -
10 | pie chart:
11 | columns:
12 | -
13 | - "Apples"
14 | - 3
15 | -
16 | - "Oranges"
17 | - 2
18 | -
19 | - "Pears"
20 | - 2
21 | -
22 | rows:
23 | -
24 | h2 text: "Lorem ipsum dolor sit amet"
25 | -
26 | p text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."
27 |
28 |
--------------------------------------------------------------------------------
/docs/examples/scatter_plot.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | scatter plot:
5 | xs:
6 | setosa: "setosa_x"
7 | versicolor: "versicolor_x"
8 | columns:
9 | -
10 | - "setosa_x"
11 | - 3.5
12 | - 3
13 | - 3.2
14 | - 3.1
15 | - 3.6
16 | - 3.9
17 | - 3.4
18 | - 3.4
19 | - 2.9
20 | - 3.1
21 | - 3.7
22 | - 3.4
23 | - 3
24 | - 3
25 | - 4
26 | - 4.4
27 | - 3.9
28 | - 3.5
29 | - 3.8
30 | - 3.8
31 | - 3.4
32 | - 3.7
33 | - 3.6
34 | - 3.3
35 | - 3.4
36 | - 3
37 | - 3.4
38 | - 3.5
39 | - 3.4
40 | - 3.2
41 | - 3.1
42 | - 3.4
43 | - 4.1
44 | - 4.2
45 | - 3.1
46 | - 3.2
47 | - 3.5
48 | - 3.6
49 | - 3
50 | - 3.4
51 | - 3.5
52 | - 2.3
53 | - 3.2
54 | - 3.5
55 | - 3.8
56 | - 3
57 | - 3.8
58 | - 3.2
59 | - 3.7
60 | - 3.3
61 | -
62 | - "versicolor_x"
63 | - 3.2
64 | - 3.2
65 | - 3.1
66 | - 2.3
67 | - 2.8
68 | - 2.8
69 | - 3.3
70 | - 2.4
71 | - 2.9
72 | - 2.7
73 | - 2
74 | - 3
75 | - 2.2
76 | - 2.9
77 | - 2.9
78 | - 3.1
79 | - 3
80 | - 2.7
81 | - 2.2
82 | - 2.5
83 | - 3.2
84 | - 2.8
85 | - 2.5
86 | - 2.8
87 | - 2.9
88 | - 3
89 | - 2.8
90 | - 3
91 | - 2.9
92 | - 2.6
93 | - 2.4
94 | - 2.4
95 | - 2.7
96 | - 2.7
97 | - 3
98 | - 3.4
99 | - 3.1
100 | - 2.3
101 | - 3
102 | - 2.5
103 | - 2.6
104 | - 3
105 | - 2.6
106 | - 2.3
107 | - 2.7
108 | - 3
109 | - 2.9
110 | - 2.9
111 | - 2.5
112 | - 2.8
113 | -
114 | - "setosa"
115 | - 0.2
116 | - 0.2
117 | - 0.2
118 | - 0.2
119 | - 0.2
120 | - 0.4
121 | - 0.3
122 | - 0.2
123 | - 0.2
124 | - 0.1
125 | - 0.2
126 | - 0.2
127 | - 0.1
128 | - 0.1
129 | - 0.2
130 | - 0.4
131 | - 0.4
132 | - 0.3
133 | - 0.3
134 | - 0.3
135 | - 0.2
136 | - 0.4
137 | - 0.2
138 | - 0.5
139 | - 0.2
140 | - 0.2
141 | - 0.4
142 | - 0.2
143 | - 0.2
144 | - 0.2
145 | - 0.2
146 | - 0.4
147 | - 0.1
148 | - 0.2
149 | - 0.2
150 | - 0.2
151 | - 0.2
152 | - 0.1
153 | - 0.2
154 | - 0.2
155 | - 0.3
156 | - 0.3
157 | - 0.2
158 | - 0.6
159 | - 0.4
160 | - 0.3
161 | - 0.2
162 | - 0.2
163 | - 0.2
164 | - 0.2
165 | -
166 | - "versicolor"
167 | - 1.4
168 | - 1.5
169 | - 1.5
170 | - 1.3
171 | - 1.5
172 | - 1.3
173 | - 1.6
174 | - 1
175 | - 1.3
176 | - 1.4
177 | - 1
178 | - 1.5
179 | - 1
180 | - 1.4
181 | - 1.3
182 | - 1.4
183 | - 1.5
184 | - 1
185 | - 1.5
186 | - 1.1
187 | - 1.8
188 | - 1.3
189 | - 1.5
190 | - 1.2
191 | - 1.3
192 | - 1.4
193 | - 1.4
194 | - 1.7
195 | - 1.5
196 | - 1
197 | - 1.1
198 | - 1
199 | - 1.2
200 | - 1.6
201 | - 1.5
202 | - 1.6
203 | - 1.5
204 | - 1.3
205 | - 1.3
206 | - 1.3
207 | - 1.2
208 | - 1.4
209 | - 1.2
210 | - 1
211 | - 1.3
212 | - 1.2
213 | - 1.3
214 | - 1.3
215 | - 1.1
216 | - 1.3
217 |
218 |
--------------------------------------------------------------------------------
/docs/examples/spline_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use spline chart
4 | ---
5 |
6 | # How to use spline chart
7 | Here's an example code regarding the use of spline chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | spline chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 30
17 | - 29
18 | - 25
19 | - 26
20 | - 27
21 | - 10
22 | -
23 | - "Oranges"
24 | - 20
25 | - 21
26 | - 22
27 | - 20
28 | - 27
29 | - 19
30 | -
31 | - "Pears"
32 | - 10
33 | - 9
34 | - 8
35 | - 8
36 | - 7
37 | - 8
38 |
39 | ```
40 | The code above will render a spline chart that looks like this:
41 |
42 | 
43 |
44 | ## JSON format
45 | The YAML above is equivalent to this JSON:
46 | ```json
47 | {
48 | "component": "root",
49 | "args": {
50 | "title": "Example"
51 | },
52 | "data": [
53 | {
54 | "component": "chart",
55 | "args": {
56 | "type": "spline",
57 | "stacked": false
58 | },
59 | "data": {
60 | "columns": [
61 | [
62 | "Apples",
63 | 30,
64 | 29,
65 | 25,
66 | 26,
67 | 27,
68 | 10
69 | ],
70 | [
71 | "Oranges",
72 | 20,
73 | 21,
74 | 22,
75 | 20,
76 | 27,
77 | 19
78 | ],
79 | [
80 | "Pears",
81 | 10,
82 | 9,
83 | 8,
84 | 8,
85 | 7,
86 | 8
87 | ]
88 | ]
89 | }
90 | }
91 | ]
92 | }
93 | ```
94 |
--------------------------------------------------------------------------------
/docs/examples/spline_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | spline chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 30
9 | - 29
10 | - 25
11 | - 26
12 | - 27
13 | - 10
14 | -
15 | - "Oranges"
16 | - 20
17 | - 21
18 | - 22
19 | - 20
20 | - 27
21 | - 19
22 | -
23 | - "Pears"
24 | - 10
25 | - 9
26 | - 8
27 | - 8
28 | - 7
29 | - 8
30 |
31 |
--------------------------------------------------------------------------------
/docs/examples/stacked_area_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use stacked area chart
4 | ---
5 |
6 | # How to use stacked area chart
7 | Here's an example code regarding the use of stacked area chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | stacked area chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 3
17 | - 2
18 | - 3
19 | - 4
20 | - 2
21 | -
22 | - "Oranges"
23 | - 2
24 | - 1
25 | - 0
26 | - 1
27 | - 1
28 | -
29 | - "Pears"
30 | - 2
31 | - 0
32 | - 0
33 | - 3
34 | - 4
35 |
36 | ```
37 | The code above will render a stacked area chart that looks like this:
38 |
39 | 
40 |
41 | ## JSON format
42 | The YAML above is equivalent to this JSON:
43 | ```json
44 | {
45 | "component": "root",
46 | "args": {
47 | "title": "Example"
48 | },
49 | "data": [
50 | {
51 | "component": "chart",
52 | "args": {
53 | "type": "area",
54 | "stacked": true
55 | },
56 | "data": {
57 | "columns": [
58 | [
59 | "Apples",
60 | 3,
61 | 2,
62 | 3,
63 | 4,
64 | 2
65 | ],
66 | [
67 | "Oranges",
68 | 2,
69 | 1,
70 | 0,
71 | 1,
72 | 1
73 | ],
74 | [
75 | "Pears",
76 | 2,
77 | 0,
78 | 0,
79 | 3,
80 | 4
81 | ]
82 | ]
83 | }
84 | }
85 | ]
86 | }
87 | ```
88 |
--------------------------------------------------------------------------------
/docs/examples/stacked_area_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | stacked area chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 3
9 | - 2
10 | - 3
11 | - 4
12 | - 2
13 | -
14 | - "Oranges"
15 | - 2
16 | - 1
17 | - 0
18 | - 1
19 | - 1
20 | -
21 | - "Pears"
22 | - 2
23 | - 0
24 | - 0
25 | - 3
26 | - 4
27 |
28 |
--------------------------------------------------------------------------------
/docs/examples/stacked_bar_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use stacked bar chart
4 | ---
5 |
6 | # How to use stacked bar chart
7 | Here's an example code regarding the use of stacked bar chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | stacked bar chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 3
17 | - 2
18 | - 3
19 | - 4
20 | - 2
21 | -
22 | - "Oranges"
23 | - 2
24 | - 1
25 | - 0
26 | - 1
27 | - 1
28 | -
29 | - "Pears"
30 | - 2
31 | - 0
32 | - 0
33 | - 3
34 | - 4
35 |
36 | ```
37 | The code above will render a stacked bar chart that looks like this:
38 |
39 | 
40 |
41 | ## JSON format
42 | The YAML above is equivalent to this JSON:
43 | ```json
44 | {
45 | "component": "root",
46 | "args": {
47 | "title": "Example"
48 | },
49 | "data": [
50 | {
51 | "component": "chart",
52 | "args": {
53 | "type": "bar",
54 | "stacked": true
55 | },
56 | "data": {
57 | "columns": [
58 | [
59 | "Apples",
60 | 3,
61 | 2,
62 | 3,
63 | 4,
64 | 2
65 | ],
66 | [
67 | "Oranges",
68 | 2,
69 | 1,
70 | 0,
71 | 1,
72 | 1
73 | ],
74 | [
75 | "Pears",
76 | 2,
77 | 0,
78 | 0,
79 | 3,
80 | 4
81 | ]
82 | ]
83 | }
84 | }
85 | ]
86 | }
87 | ```
88 |
--------------------------------------------------------------------------------
/docs/examples/stacked_bar_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | stacked bar chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 3
9 | - 2
10 | - 3
11 | - 4
12 | - 2
13 | -
14 | - "Oranges"
15 | - 2
16 | - 1
17 | - 0
18 | - 1
19 | - 1
20 | -
21 | - "Pears"
22 | - 2
23 | - 0
24 | - 0
25 | - 3
26 | - 4
27 |
28 |
--------------------------------------------------------------------------------
/docs/examples/stacked_line_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use stacked line chart
4 | ---
5 |
6 | # How to use stacked line chart
7 | Here's an example code regarding the use of stacked line chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | stacked line chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 3
17 | - 2
18 | - 3
19 | - 4
20 | - 2
21 | -
22 | - "Oranges"
23 | - 2
24 | - 1
25 | - 0
26 | - 1
27 | - 1
28 | -
29 | - "Pears"
30 | - 2
31 | - 0
32 | - 0
33 | - 3
34 | - 4
35 |
36 | ```
37 | The code above will render a stacked line chart that looks like this:
38 |
39 | 
40 |
41 | ## JSON format
42 | The YAML above is equivalent to this JSON:
43 | ```json
44 | {
45 | "component": "root",
46 | "args": {
47 | "title": "Example"
48 | },
49 | "data": [
50 | {
51 | "component": "chart",
52 | "args": {
53 | "type": "line",
54 | "stacked": true
55 | },
56 | "data": {
57 | "columns": [
58 | [
59 | "Apples",
60 | 3,
61 | 2,
62 | 3,
63 | 4,
64 | 2
65 | ],
66 | [
67 | "Oranges",
68 | 2,
69 | 1,
70 | 0,
71 | 1,
72 | 1
73 | ],
74 | [
75 | "Pears",
76 | 2,
77 | 0,
78 | 0,
79 | 3,
80 | 4
81 | ]
82 | ]
83 | }
84 | }
85 | ]
86 | }
87 | ```
88 |
--------------------------------------------------------------------------------
/docs/examples/stacked_line_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | stacked line chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 3
9 | - 2
10 | - 3
11 | - 4
12 | - 2
13 | -
14 | - "Oranges"
15 | - 2
16 | - 1
17 | - 0
18 | - 1
19 | - 1
20 | -
21 | - "Pears"
22 | - 2
23 | - 0
24 | - 0
25 | - 3
26 | - 4
27 |
28 |
--------------------------------------------------------------------------------
/docs/examples/stacked_spline_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use stacked spline chart
4 | ---
5 |
6 | # How to use stacked spline chart
7 | Here's an example code regarding the use of stacked spline chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | stacked spline chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 3
17 | - 2
18 | - 3
19 | - 4
20 | - 2
21 | -
22 | - "Oranges"
23 | - 2
24 | - 1
25 | - 0
26 | - 1
27 | - 1
28 | -
29 | - "Pears"
30 | - 2
31 | - 0
32 | - 0
33 | - 3
34 | - 4
35 |
36 | ```
37 | The code above will render a stacked spline chart that looks like this:
38 |
39 | 
40 |
41 | ## JSON format
42 | The YAML above is equivalent to this JSON:
43 | ```json
44 | {
45 | "component": "root",
46 | "args": {
47 | "title": "Example"
48 | },
49 | "data": [
50 | {
51 | "component": "chart",
52 | "args": {
53 | "type": "spline",
54 | "stacked": true
55 | },
56 | "data": {
57 | "columns": [
58 | [
59 | "Apples",
60 | 3,
61 | 2,
62 | 3,
63 | 4,
64 | 2
65 | ],
66 | [
67 | "Oranges",
68 | 2,
69 | 1,
70 | 0,
71 | 1,
72 | 1
73 | ],
74 | [
75 | "Pears",
76 | 2,
77 | 0,
78 | 0,
79 | 3,
80 | 4
81 | ]
82 | ]
83 | }
84 | }
85 | ]
86 | }
87 | ```
88 |
--------------------------------------------------------------------------------
/docs/examples/stacked_spline_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | stacked spline chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 3
9 | - 2
10 | - 3
11 | - 4
12 | - 2
13 | -
14 | - "Oranges"
15 | - 2
16 | - 1
17 | - 0
18 | - 1
19 | - 1
20 | -
21 | - "Pears"
22 | - 2
23 | - 0
24 | - 0
25 | - 3
26 | - 4
27 |
28 |
--------------------------------------------------------------------------------
/docs/examples/step_chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | title: How to use step chart
4 | ---
5 |
6 | # How to use step chart
7 | Here's an example code regarding the use of step chart:
8 |
9 | ```yaml
10 | dashboard "Example":
11 | -
12 | step chart:
13 | columns:
14 | -
15 | - "Apples"
16 | - 30
17 | - 29
18 | - 25
19 | - 26
20 | - 27
21 | - 10
22 | -
23 | - "Oranges"
24 | - 20
25 | - 21
26 | - 22
27 | - 20
28 | - 27
29 | - 19
30 |
31 | ```
32 | The code above will render a step chart that looks like this:
33 |
34 | 
35 |
36 | ## JSON format
37 | The YAML above is equivalent to this JSON:
38 | ```json
39 | {
40 | "component": "root",
41 | "args": {
42 | "title": "Example"
43 | },
44 | "data": [
45 | {
46 | "component": "chart",
47 | "args": {
48 | "type": "step",
49 | "stacked": false
50 | },
51 | "data": {
52 | "columns": [
53 | [
54 | "Apples",
55 | 30,
56 | 29,
57 | 25,
58 | 26,
59 | 27,
60 | 10
61 | ],
62 | [
63 | "Oranges",
64 | 20,
65 | 21,
66 | 22,
67 | 20,
68 | 27,
69 | 19
70 | ]
71 | ]
72 | }
73 | }
74 | ]
75 | }
76 | ```
77 |
--------------------------------------------------------------------------------
/docs/examples/step_chart.yml:
--------------------------------------------------------------------------------
1 | ---
2 | dashboard "Example":
3 | -
4 | step chart:
5 | columns:
6 | -
7 | - "Apples"
8 | - 30
9 | - 29
10 | - 25
11 | - 26
12 | - 27
13 | - 10
14 | -
15 | - "Oranges"
16 | - 20
17 | - 21
18 | - 22
19 | - 20
20 | - 27
21 | - 19
22 |
23 |
--------------------------------------------------------------------------------
/docs/gallery.json:
--------------------------------------------------------------------------------
1 | ["Controls","dropdown",{"dropdown gender=male":[{"value":"male","text":"Male"},{"value":"female","text":"Female"}]}]
2 | ["Grid","rows",{"2 columns":[{"rows":[{"h2 text":"Lorem ipsum dolor sit amet"},{"pie chart":{"columns":[["Apples",3],["Oranges",2],["Pears",2]]}}]},{"rows":[{"h2 text":"Lorem ipsum dolor sit amet"},{"p text":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."}]}]}]
3 | ["Charts","bar chart",{"bar chart":{"columns":[["Apples",3],["Oranges",2],["Pears",2]]}}]
4 | ["Charts","horizontal bar chart",{"horizontal bar chart":{"columns":[["Apples",3],["Oranges",2],["Pears",2]]}}]
5 | ["Charts","donut chart",{"donut chart":{"columns":[["Apples",3],["Oranges",2],["Pears",2]]}}]
6 | ["Charts","pie chart",{"pie chart":{"columns":[["Apples",3],["Oranges",2],["Pears",2]]}}]
7 | ["Charts","stacked bar chart",{"stacked bar chart":{"columns":[["Apples",3,2,3,4,2],["Oranges",2,1,0,1,1],["Pears",2,0,0,3,4]]}}]
8 | ["Charts","horizontal stacked bar chart",{"horizontal stacked bar chart":{"columns":[["Apples",3,2,3,4,2],["Oranges",2,1,0,1,1],["Pears",2,0,0,3,4]]}}]
9 | ["Charts","stacked line chart",{"stacked line chart":{"columns":[["Apples",3,2,3,4,2],["Oranges",2,1,0,1,1],["Pears",2,0,0,3,4]]}}]
10 | ["Charts","stacked spline chart",{"stacked spline chart":{"columns":[["Apples",3,2,3,4,2],["Oranges",2,1,0,1,1],["Pears",2,0,0,3,4]]}}]
11 | ["Charts","stacked area chart",{"stacked area chart":{"columns":[["Apples",3,2,3,4,2],["Oranges",2,1,0,1,1],["Pears",2,0,0,3,4]]}}]
12 | ["Charts","gauge chart",{"gauge chart":{"columns":[["data",30]]}}]
13 | ["Charts","scatter plot",{"scatter plot":{"xs":{"setosa":"setosa_x","versicolor":"versicolor_x"},"columns":[["setosa_x",3.5,3,3.2,3.1,3.6,3.9,3.4,3.4,2.9,3.1,3.7,3.4,3,3,4,4.4,3.9,3.5,3.8,3.8,3.4,3.7,3.6,3.3,3.4,3,3.4,3.5,3.4,3.2,3.1,3.4,4.1,4.2,3.1,3.2,3.5,3.6,3,3.4,3.5,2.3,3.2,3.5,3.8,3,3.8,3.2,3.7,3.3],["versicolor_x",3.2,3.2,3.1,2.3,2.8,2.8,3.3,2.4,2.9,2.7,2,3,2.2,2.9,2.9,3.1,3,2.7,2.2,2.5,3.2,2.8,2.5,2.8,2.9,3,2.8,3,2.9,2.6,2.4,2.4,2.7,2.7,3,3.4,3.1,2.3,3,2.5,2.6,3,2.6,2.3,2.7,3,2.9,2.9,2.5,2.8],["setosa",0.2,0.2,0.2,0.2,0.2,0.4,0.3,0.2,0.2,0.1,0.2,0.2,0.1,0.1,0.2,0.4,0.4,0.3,0.3,0.3,0.2,0.4,0.2,0.5,0.2,0.2,0.4,0.2,0.2,0.2,0.2,0.4,0.1,0.2,0.2,0.2,0.2,0.1,0.2,0.2,0.3,0.3,0.2,0.6,0.4,0.3,0.2,0.2,0.2,0.2],["versicolor",1.4,1.5,1.5,1.3,1.5,1.3,1.6,1,1.3,1.4,1,1.5,1,1.4,1.3,1.4,1.5,1,1.5,1.1,1.8,1.3,1.5,1.2,1.3,1.4,1.4,1.7,1.5,1,1.1,1,1.2,1.6,1.5,1.6,1.5,1.3,1.3,1.3,1.2,1.4,1.2,1,1.3,1.2,1.3,1.3,1.1,1.3]]}}]
14 | ["Charts","line chart",{"line chart":{"columns":[["Apples",30,29,25,26,27,10],["Oranges",20,21,22,20,27,19],["Pears",10,9,8,8,7,8]]}}]
15 | ["Charts","bubble chart",{"bubble chart":{"columns":[["Apples",30,29,25,26,27,10],["Oranges",20,21,22,20,27,19],["Pears",10,9,8,8,7,8]]}}]
16 | ["Charts","spline chart",{"spline chart":{"columns":[["Apples",30,29,25,26,27,10],["Oranges",20,21,22,20,27,19],["Pears",10,9,8,8,7,8]]}}]
17 | ["Charts","custom axes",{"line chart":[{"attr:axis":{"x":{"label":"Year","type":"timeseries","tick":{"format":"%Y"}},"y":{"label":"Amount consumed (tonnes)"}}},{"data":{"x":"x","xFormat":"%Y","columns":[["x","1999","2001","2002","2004","2007","2008"],["Apples",30,29,25,26,27,10],["Oranges",20,21,22,20,27,19],["Pears",10,9,8,8,7,8]]}}]}]
18 | ["Charts","area chart",{"area chart":{"columns":[["Apples",30,29,25,26,27,10],["Oranges",20,21,22,20,27,19]]}}]
19 | ["Charts","step chart",{"step chart":{"columns":[["Apples",30,29,25,26,27,10],["Oranges",20,21,22,20,27,19]]}}]
20 | ["Grid","board",{"board":[{"rows":[{"pie chart":{"columns":[["Apples",3],["Oranges",2],["Pears",2]]}},{"p text":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."}]}]}]
21 | ["Text","h1 text",{"h1 text":"Lorem ipsum dolor sit amet"}]
22 | ["Text","h2 text",{"h2 text":"Lorem ipsum dolor sit amet"}]
23 | ["Text","h3 text",{"h3 text":"Lorem ipsum dolor sit amet"}]
24 | ["Text","p text",{"p text":"Lorem ipsum dolor sit amet"}]
25 | ["Grid","columns",{"2 columns":[{"pie chart":{"columns":[["Apples",3],["Oranges",2],["Pears",2]]}},{"p text":"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."}]}]
26 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 | image: /page_screenshot.png
4 | ---
5 |
--------------------------------------------------------------------------------
/docs/jekyll-theme-cayman-blog.gemspec:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 |
3 | Gem::Specification.new do |s|
4 | s.name = "jekyll-theme-cayman-blog"
5 | s.version = "0.0.6"
6 | s.license = "CC0-1.0"
7 | s.authors = ["Lorenzo Pirritano"]
8 | s.email = ["lorepirri@gmail.com"]
9 | s.homepage = "https://github.com/lorepirri/cayman-blog"
10 | s.summary = "Cayman Blog Theme is a clean, responsive blogging theme for Jekyll and GitHub Pages, with social/SEO features. Based on Cayman theme."
11 |
12 | s.files = `git ls-files -z`.split("\x0").select do |f|
13 | f.match(%r{^((_includes|_layouts|_sass|assets)/|(LICENSE|README|index|about|contact|404)((\.(txt|md|markdown)|$)))}i)
14 | end
15 |
16 | s.platform = Gem::Platform::RUBY
17 | s.add_runtime_dependency "jekyll", "~> 3.3"
18 | end
19 |
--------------------------------------------------------------------------------
/docs/page_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/page_screenshot.png
--------------------------------------------------------------------------------
/docs/preview.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: preview
3 | permalink: /_preview.html
4 | ---
5 |
--------------------------------------------------------------------------------
/docs/screenshots/area_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/area_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/bar_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/bar_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/board.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/board.png
--------------------------------------------------------------------------------
/docs/screenshots/bubble_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/bubble_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/columns.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/columns.png
--------------------------------------------------------------------------------
/docs/screenshots/custom_axes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/custom_axes.png
--------------------------------------------------------------------------------
/docs/screenshots/donut_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/donut_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/dropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/dropdown.png
--------------------------------------------------------------------------------
/docs/screenshots/gauge_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/gauge_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/h1_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/h1_text.png
--------------------------------------------------------------------------------
/docs/screenshots/h2_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/h2_text.png
--------------------------------------------------------------------------------
/docs/screenshots/h3_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/h3_text.png
--------------------------------------------------------------------------------
/docs/screenshots/horizontal_bar_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/horizontal_bar_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/horizontal_stacked_bar_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/horizontal_stacked_bar_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/line_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/line_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/p_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/p_text.png
--------------------------------------------------------------------------------
/docs/screenshots/pie_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/pie_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/rows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/rows.png
--------------------------------------------------------------------------------
/docs/screenshots/scatter_plot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/scatter_plot.png
--------------------------------------------------------------------------------
/docs/screenshots/spline_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/spline_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/stacked_area_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/stacked_area_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/stacked_bar_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/stacked_bar_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/stacked_line_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/stacked_line_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/stacked_spline_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/stacked_spline_chart.png
--------------------------------------------------------------------------------
/docs/screenshots/step_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/docs/screenshots/step_chart.png
--------------------------------------------------------------------------------
/docs/script/bootstrap:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | gem install bundler
6 | bundle install
7 |
--------------------------------------------------------------------------------
/docs/script/cibuild:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | bundle exec jekyll build
6 | gem build jekyll-theme-cayman-blog.gemspec
7 |
--------------------------------------------------------------------------------
/docs/script/release:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # Tag and push a release.
3 |
4 | set -e
5 |
6 | # Make sure we're in the project root.
7 |
8 | cd $(dirname "$0")/..
9 |
10 | # Make sure the darn thing works
11 |
12 | bundle update
13 |
14 | # Build a new gem archive.
15 |
16 | rm -rf jekyll-theme-cayman-blog-*.gem
17 | gem build -q jekyll-theme-cayman-blog.gemspec
18 |
19 | # Make sure we're on the master branch.
20 |
21 | (git branch | grep -q 'master') || {
22 | echo "Only release from the master branch."
23 | exit 1
24 | }
25 |
26 | # Figure out what version we're releasing.
27 |
28 | tag=v`ls jekyll-theme-cayman-blog-*.gem | sed 's/^jekyll-theme-cayman-blog-\(.*\)\.gem$/\1/'`
29 |
30 | # Make sure we haven't released this version before.
31 |
32 | git fetch -t origin
33 |
34 | (git tag -l | grep -q "$tag") && {
35 | echo "Whoops, there's already a '${tag}' tag."
36 | exit 1
37 | }
38 |
39 | # Tag it and bag it.
40 |
41 | gem push jekyll-theme-cayman-blog-*.gem && git tag "$tag" &&
42 | git push origin master && git push origin "$tag"
43 |
--------------------------------------------------------------------------------
/docs/script/server:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | bundle exec jekyll serve
4 |
--------------------------------------------------------------------------------
/docs/tutorial.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: page
3 | title: Getting started
4 | permalink: /getting-started.html
5 | ---
6 |
7 | just-dashboard let's you turn this:
8 | ```yaml
9 | dashboard "Food":
10 | - h1 text: Food
11 | - h2 text: By caloric content
12 | - 3 columns:
13 | - rows:
14 | - h3 text: Bananas
15 | - pie chart: {
16 | "columns": [
17 | ["Protein", 5], ["Sugar", 10], ["Other carbs", 40], ["Fat", 1]
18 | ]
19 | }
20 | - rows:
21 | - h3 text: Tofu
22 | - pie chart: {
23 | "columns": [
24 | ["Protein", 30], ["Sugar", 0], ["Other carbs", 40], ["Fat", 3]
25 | ]
26 | }
27 | - rows:
28 | - h3 text: Peanut butter
29 | - pie chart: {
30 | "columns": [
31 | ["Protein", 20], ["Sugar", 2], ["Other carbs", 20], ["Fat", 50]
32 | ]
33 | }
34 | ```
35 |
36 | Into this:
37 |
38 | 
39 |
40 | To host your dashboard, you can roll your own backend, or:
41 |
42 | - Create a public GitHub gist with a file named dashboard.yml or dashboard.json (depending on your preferred format)
43 | - Access it as a shareable dashboard at: `http://bottoml.in/e/{Github username}/{Gist ID}`
44 |
45 | In fact, I've created a Gist with the example above: [https://gist.github.com/kantord/2973bdd4ad689642562018bb4091ffbd](https://gist.github.com/kantord/2973bdd4ad689642562018bb4091ffbd);
46 | thus it's accessible as a dashboard at: [http://bottoml.in/e/kantord/2973bdd4ad689642562018bb4091ffbd](http://bottoml.in/e/kantord/2973bdd4ad689642562018bb4091ffbd)
47 |
48 | ## Don't repeat yourself
49 | As your dashboard is just data, you can generate it instead of repeating yourself. You can do that by generating the YAML or JSON file yourself, or you can use [jq queries](https://stedolan.github.io/jq/) in your YAML file.
50 |
51 | To illustrate that, I've created two separate GitHub Gists. One with only the
52 | data:
53 |
54 | ```json
55 | {
56 | "Bananas": [["Protein", 5], ["Sugar", 10], ["Other carbs", 40], ["Fat", 1]],
57 | "Tofu" : [["Protein", 30], ["Sugar", 0], ["Other carbs", 40], ["Fat", 3]],
58 | "Peanut butter": [["Protein", 20], ["Sugar", 2], ["Other carbs", 20], ["Fat", 50]]
59 | }
60 | ```
61 |
62 | And one with a dashboard that contains a component that can fetch the data from
63 | other other gist and turn it into 3 different charts, just like in the manually
64 | created example above:
65 |
66 | ```yaml
67 | dashboard "Food":
68 | - h1 text: Food
69 | - h2 text: By caloric content
70 | - 3 columns:
71 | - attr:query: '[to_entries | .[] | {"component": "rows", "data": [
72 | {"component": "text", "args": {"tagName": "h3"}, "data": .key},
73 | {"component": "chart", "args": {"type": "pie"}, "data": {"columns": .value}}
74 | ]}]'
75 | - data: https://gist.githubusercontent.com/kantord/2b2e3b22cb70be0415a7d50c395fa411/raw/47542f8a3db0d65aeeb48e28ddfaa8feabbc72b5/nutri.json
76 | ```
77 |
78 | You can see the results for yourself here: [http://bottoml.in/e/kantord/aa4a30d09343f0527b8969ad0810946e](http://bottoml.in/e/kantord/aa4a30d09343f0527b8969ad0810946e)
79 |
80 | Using the same principle, you can also loads parts from your dashboard from
81 | other files, or just JSON/CSV data for specific charts.
82 |
83 | ## Variables
84 | Suppose you are only interested in comparing foods by how much they contain of
85 | a single macronutrient. However, you want to be able to decide which
86 | macronutrient.
87 |
88 | In this case, you can use a variable to store which nutrient you're interested
89 | in, and a dropdown to make that variable configurable on your page. Then you
90 | can use some jq magic to transform your date for your chart.
91 |
92 | ```yaml
93 | dashboard "Food":
94 | - h1 text: Food
95 | - dropdown macro=Protein:
96 | - {"value": "Protein", "text": "Protein"}
97 | - {"value": "Fat", "text": "Fat"}
98 | - {"value": "Sugar", "text": "Sugar"}
99 | - {"value": "Other carbs", "text": "Other carbs"}
100 | - h2 text: By ${macro} content
101 | - bar chart:
102 | - attr:query: '{"columns": [to_entries | .[] | [.key, (.value | .[] | select(.[0] == "${macro}"))[1] ]]}'
103 | - data: https://gist.githubusercontent.com/kantord/2b2e3b22cb70be0415a7d50c395fa411/raw/47542f8a3db0d65aeeb48e28ddfaa8feabbc72b5/nutri.json
104 | ```
105 |
106 | 
107 |
108 | Try it live: [https://bottoml.in/e/kantord/866ebc270b4e0db5389b7de9bf181430](https://bottoml.in/e/kantord/866ebc270b4e0db5389b7de9bf181430)
109 |
110 | ## Using your own backend
111 |
112 | If you want to create a public dashboard, it's enough to simply link resources that you host, for example:
113 | ```yaml
114 | dashboard "Hello World":
115 | bar chart:
116 | https://my-awesome-backend.com/api/get_data
117 | ```
118 |
119 | If Gists don't work for you, for example because you need to implement authentication, you'll have to host a frontend for yourself.
120 |
121 | Install just-dashboard from npm:
122 | ```npm install --save just-dashboard```
123 |
124 | Here's a minimal example on using it as your project's frontend:
125 | ```javascript
126 | import { json_parser, yaml_parser } from 'just-dashboard'
127 | import * as d3 from 'd3'
128 |
129 | // Load data
130 | const dashboard_yaml = ... // Load your YAML here
131 | const dashboard = yaml_parser(dashboard_yaml)
132 |
133 | // Create render function
134 | const render_dashboard = json_parser(dashboard)
135 |
136 | // Render dashboard
137 | render_dashboard(d3.select("body"))
138 |
139 | ```
140 |
141 |
--------------------------------------------------------------------------------
/jsdoc.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "tags": {
3 | "allowUnknownTags": true,
4 | "dictionaries": ["jsdoc"]
5 | },
6 | "opts": {
7 | "template": "node_modules/minami",
8 | "encoding": "utf8",
9 | "destination": "./docs/",
10 | "recurse": true,
11 | "private": true
12 | },
13 | "plugins": ["plugins/markdown", "node_modules/jsdoc-babel"],
14 | "source": {
15 | "include": ["src"],
16 | "exclude": [],
17 | "includePattern": ".+\\.js(doc|x)?$",
18 | "excludePattern": "(^|\\/|\\\\)_"
19 | },
20 | "templates": {
21 | "cleverLinks": false,
22 | "monospaceLinks": true,
23 | "useLongnameInNav": false,
24 | "showInheritedInNav": true,
25 | "default": {
26 | "includeDate": false
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "just-dashboard",
3 | "version": "0.0.0-development",
4 | "description": "",
5 | "main": "dist/dashboard.js",
6 | "module": "src/index.js",
7 | "scripts": {
8 | "test": "mochapack \"src/**/*.test.js\" --webpack-config webpack.test.config.js --require @babel/polyfill --reporter mochawesome",
9 | "test:watch": "mocha-webpack --watch \"src/**/*.test.js\" --webpack-config webpack.test.config.js --require @babel/polyfill --reporter mochawesome",
10 | "build": "WEBPACK_ENV=build webpack",
11 | "dev": "WEBPACK_ENV=dev webpack --progress --colors --watch",
12 | "html-coverage": "cross-env NODE_ENV=test nyc --reporter=html mochapack \"src/**/*.test.js\" --webpack-config webpack.test.config.js",
13 | "generate-doc": "jsdoc -c jsdoc.conf.json --readme ./README.md",
14 | "lint": "eslint src",
15 | "lint:fix": "eslint src --fix",
16 | "coverage": "cross-env NODE_ENV=test nyc --reporter=lcov --reporter=text npm run test",
17 | "analyze:generate": "webpack --profile --json > stats.json",
18 | "analyze:tool": "webpack-bundle-analyzer stats.json",
19 | "analyze": "yarn analyze:generate && yarn analyze:tool",
20 | "manual-test": "webpack-dev-server --webpack-config webpack.test.config.js './src/manual_test.js' --hot --inline --output-filename 'test.js'",
21 | "semantic-release": "semantic-release",
22 | "test:e2e": "webpack src/manual_test.js --output-filename test.js --config webpack.config.js --mode development && mochapack \"src/e2e-test.js\" --timeout 80000 --webpack-config webpack.test.config.js --require @babel/polyfill"
23 | },
24 | "keywords": [],
25 | "author": "",
26 | "license": "MIT",
27 | "dependencies": {
28 | "billboard.js": "^1.4.1",
29 | "d3": "4.10.0",
30 | "emuto": "^1.37.1",
31 | "fast-deep-equal": "^2.0.1",
32 | "fd-slicer": "^1.1.0",
33 | "sassline": "^2.1.2",
34 | "spinkit": "^1.2.5",
35 | "yamljs": "^0.3.0"
36 | },
37 | "devDependencies": {
38 | "@babel/cli": "^7.2.3",
39 | "@babel/core": "^7.4.0",
40 | "@babel/plugin-syntax-dynamic-import": "^7.2.0",
41 | "@babel/plugin-transform-modules-commonjs": "^7.4.0",
42 | "@babel/polyfill": "^7.4.0",
43 | "@babel/preset-env": "^7.4.2",
44 | "@babel/register": "^7.4.0",
45 | "autoprefixer": "^9.5.0",
46 | "babel-eslint": "^10.0.1",
47 | "babel-loader": "^8.0.5",
48 | "babel-plugin-dynamic-import-node": "^2.2.0",
49 | "css-loader": "^2.1.1",
50 | "cz-conventional-changelog": "^2.1.0",
51 | "docdash": "^1.1.0",
52 | "eslint": "^5.16.0",
53 | "eslint-loader": "^2.1.2",
54 | "fake-style-loader": "^1.0.1",
55 | "file-loader": "^3.0.1",
56 | "get-stdin": "^6.0.0",
57 | "hard-source-webpack-plugin": "^0.13.1",
58 | "html-webpack-plugin": "^3.2.0",
59 | "inject-loader": "^4.0.1",
60 | "jsdoc": "^3.5.5",
61 | "jsdoc-babel": "^0.5.0",
62 | "jsdom": "^14.0.0",
63 | "jsdom-global": "^3.0.2",
64 | "json2yaml": "^1.1.0",
65 | "minami": "^1.2.3",
66 | "mocha": "^6.0.2",
67 | "mochapack": "^1.1.1",
68 | "mochawesome": "^3.1.1",
69 | "node-sass": "^4.11.0",
70 | "postcss-loader": "^3.0.0",
71 | "sass-loader": "^7.1.0",
72 | "selenium-webdriver": "^4.0.0-alpha.1",
73 | "semantic-release": "^15.13.3",
74 | "should": "^13.2.3",
75 | "should-sinon": "0.0.6",
76 | "sinon": "^7.3.1",
77 | "style-loader": "^0.23.1",
78 | "stylish": "^1.0.0",
79 | "webpack": "^4.29.6",
80 | "webpack-bundle-analyzer": "^3.1.0",
81 | "webpack-cli": "^3.3.0",
82 | "webpack-dev-server": "^3.2.1",
83 | "webpack-node-externals": "^1.7.2",
84 | "webshot": "^0.18.0"
85 | },
86 | "repository": {
87 | "type": "git",
88 | "url": "https://github.com/kantord/dashboard.git"
89 | },
90 | "config": {
91 | "commitizen": {
92 | "path": "./node_modules/cz-conventional-changelog"
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('autoprefixer')
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/screenshot.png
--------------------------------------------------------------------------------
/screenshot_variables.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kantord/just-dashboard/2e81afadd6d1895f72700e814da9aa2b691b963a/screenshot_variables.png
--------------------------------------------------------------------------------
/scripts/compile_yaml_to_json.js:
--------------------------------------------------------------------------------
1 | import yaml_parser from '../src/yaml-format/parser'
2 | import getStdin from 'get-stdin'
3 |
4 | getStdin().then(str => {
5 | console.log(JSON.stringify(yaml_parser(str), null, 2));
6 | });
7 |
--------------------------------------------------------------------------------
/src/components/base/bind.js:
--------------------------------------------------------------------------------
1 | import * as d3 from 'd3'
2 | import { format_arguments, has_state_handler } from './state_handling'
3 | import handle_external_data from './external_data'
4 | import render_component from './render'
5 | import show_error_message from './error_handling'
6 |
7 | const validate_selection = (selection) => {
8 | if (!(selection instanceof d3.selection))
9 | throw new Error('A d3 selection is required')
10 | }
11 |
12 | const create_element = (init_func, instance_args, selection) => {
13 | try {
14 | return (typeof init_func === 'function')
15 | ? init_func(instance_args, selection) : selection.append('span')
16 | } catch(error) {
17 | selection.call(show_error_message(`${error} [bind]`))
18 | return selection
19 | }
20 | }
21 |
22 | const create_bind_function = (args, instance_args) => (selection) => {
23 | validate_selection(selection)
24 | let element = create_element(args.init, format_arguments(instance_args),
25 | selection)
26 |
27 | return (raw_data) => {
28 | const render_ = () => handle_external_data(
29 | instance_args, selection, raw_data)(render_component(
30 | args, format_arguments(instance_args), selection, element))
31 | if (has_state_handler(instance_args)) {
32 | instance_args.state_handler.subscribe((state_handler, me) => {
33 | if (element === null || (element && element.node
34 | && !document.contains(element.node()))) return
35 | state_handler.subscribe(me)
36 | if (element) element.remove()
37 | element = create_element(
38 | args.init, format_arguments(instance_args), selection)
39 | render_()
40 | })
41 | }
42 | render_()
43 | }
44 | }
45 |
46 | export default create_bind_function
47 |
--------------------------------------------------------------------------------
/src/components/base/error_handling.js:
--------------------------------------------------------------------------------
1 | const show_error_message = (message) => (selection) =>
2 | selection.append('p').attr('class', 'error').text(message)
3 |
4 | export default show_error_message
5 |
--------------------------------------------------------------------------------
/src/components/base/external_data.js:
--------------------------------------------------------------------------------
1 | import with_loader from './loaders'
2 | import with_spinner from './spinner'
3 |
4 | const load_external_data = (raw_data) => (loader_func, spinner_func) =>
5 | spinner_func(loader_func(raw_data))
6 |
7 | const has_loader = (instance_args) =>
8 | instance_args !== undefined && instance_args.hasOwnProperty('loader')
9 |
10 | const handle_external_data = (instance_args, selection, raw_data) =>
11 | (resolve) =>
12 | has_loader(instance_args)
13 | ? load_external_data(raw_data)(
14 | with_loader(instance_args.loader, instance_args.file_loader,
15 | instance_args.is_file),
16 | with_spinner(selection))(
17 | (_, data) => resolve(data))
18 | : resolve(raw_data)
19 |
20 | export default handle_external_data
21 |
--------------------------------------------------------------------------------
/src/components/base/index.js:
--------------------------------------------------------------------------------
1 | import { format_arguments } from './state_handling'
2 | import create_bind_function from './bind'
3 |
4 | const execute_validations = (validators) => (args) =>
5 | validators.map((validator) => validator(args))
6 |
7 | const create_component_function = (args) => (instance_args) => {
8 | execute_validations(args.validators)(format_arguments(instance_args))
9 | return create_bind_function(args, instance_args)
10 | }
11 |
12 | const Component = (args) => {
13 | if (!args.hasOwnProperty('render'))
14 | throw new Error('A render() function is required')
15 | return create_component_function(args)
16 | }
17 |
18 | export default Component
19 |
--------------------------------------------------------------------------------
/src/components/base/loaders.js:
--------------------------------------------------------------------------------
1 | import * as d3 from 'd3'
2 |
3 | const loaders = {
4 |
5 | 'tsv': (path, callback, file_loader, is_file) => {
6 | if (!is_file) {
7 | d3.tsv(path, callback)
8 | } else {
9 | file_loader(path, (_, data) => callback(undefined, d3.tsvParse(data)))
10 | }
11 | },
12 |
13 | 'csv': (path, callback, file_loader, is_file) => {
14 | if (!is_file) {
15 | d3.csv(path, callback)
16 | } else {
17 | file_loader(path, (_, data) => callback(undefined, d3.csvParse(data)))
18 | }
19 | },
20 |
21 | 'text': (path, callback, file_loader, is_file) => {
22 | if (!is_file) {
23 | d3.text(path, callback)
24 | } else {
25 | file_loader(path, callback)
26 | }
27 | },
28 |
29 | 'json': (path, callback, file_loader, is_file) => {
30 | if (!is_file) {
31 | d3.json(path, callback)
32 | } else {
33 | file_loader(path, (_, data) => callback(undefined, JSON.parse(data)))
34 | }
35 | },
36 |
37 | }
38 |
39 | const loader_exists = (loader_name) =>
40 | loaders[loader_name] !== undefined
41 |
42 | const with_loader = (loader_name, file_loader, is_file) =>
43 | (source) => (callback) => {
44 | if (!loader_exists(loader_name)) throw new Error('Invalid loader')
45 | loaders[loader_name](source, callback, file_loader, is_file)
46 | }
47 |
48 | export default with_loader
49 |
--------------------------------------------------------------------------------
/src/components/base/render.js:
--------------------------------------------------------------------------------
1 | import with_spinner from './spinner'
2 | import emuto from '../../jq-web.js'
3 | import show_error_message from './error_handling'
4 | import { format_data } from './state_handling'
5 |
6 | const call_render_function = (args, instance_args, selection, element) =>
7 | (data) => {
8 | try {
9 | args.render(instance_args, selection, format_data(instance_args, data),
10 | element, data)
11 | } catch (error) {
12 | selection.call(show_error_message(`${error} [render]`))
13 | }
14 | }
15 |
16 | const execute_query = (query, data) =>
17 | (reject) => (resolve) =>
18 | emuto(data, query)
19 | .then((result) => {console.log('data!!!', result); return resolve(result)}) // eslint-disable-line
20 | .catch((error) => {reject(error)})
21 |
22 | const render_component_with_query = (args, instance_args, selection,
23 | element) => (data) =>
24 | with_spinner(selection)(
25 | execute_query(instance_args.query, data)(
26 | e => show_error_message(`${e} [render]`)(selection)))(
27 | call_render_function(args, instance_args, selection, element))
28 |
29 | const has_query = (instance_args) =>
30 | instance_args !== undefined && instance_args.hasOwnProperty('query')
31 |
32 | const render_component = (args, instance_args, ...rest) =>
33 | has_query(instance_args)
34 | ? render_component_with_query(args, instance_args, ...rest)
35 | : call_render_function(args, instance_args, ...rest)
36 |
37 |
38 | export default render_component
39 | export { execute_query }
40 |
--------------------------------------------------------------------------------
/src/components/base/spinner.js:
--------------------------------------------------------------------------------
1 | const create_spinner = (selection) =>
2 | selection.append('div')
3 | .attr('class', 'spinner sk-spinner sk-spinner-pulse')
4 |
5 | const with_spinner = (selection) => (func) => (callback) => {
6 | const spinner = create_spinner(selection)
7 | return func((...args) => {
8 | spinner.remove()
9 | callback(...args)
10 | })
11 | }
12 |
13 | export default with_spinner
14 |
--------------------------------------------------------------------------------
/src/components/base/state_handling.js:
--------------------------------------------------------------------------------
1 | import { format_value } from '../../interpolation.js'
2 |
3 | const format_arguments = (args) => {
4 | if (!has_state_handler(args)) return args
5 | return format_value(args, args.state_handler.get_state())
6 | }
7 |
8 | const format_data = (args, data) => {
9 | if (!has_state_handler(args)) return data
10 | return format_value(data, args.state_handler.get_state())
11 | }
12 |
13 | const has_state_handler = (args) => {
14 | if (args === undefined) return false
15 | if (args.state_handler === undefined) return false
16 | if (args.state_handler.get_state === undefined) return false
17 | return true
18 | }
19 |
20 |
21 |
22 | export { format_arguments, format_data, has_state_handler }
23 |
--------------------------------------------------------------------------------
/src/components/board/Board.js:
--------------------------------------------------------------------------------
1 | import ContainerComponent from '../container_base.js'
2 | import Wrapped from '../wrapped'
3 | import './Board.scss'
4 |
5 | const BoardComponent = Wrapped((args, selection) => selection
6 | .append('div')
7 | .attr('class', 'ds--board')
8 | )(ContainerComponent({
9 | 'wrapper_tag': 'div',
10 | }))
11 |
12 | export default BoardComponent
13 |
--------------------------------------------------------------------------------
/src/components/board/Board.scss:
--------------------------------------------------------------------------------
1 | @import "~sassline/assets/sass/sassline-base/_variables.scss";
2 | @import "~sassline/assets/sass/sassline-base/_mixins.scss";
3 |
4 | .ds--board {
5 | margin-left: -(map-get($gutterwidths, medium));
6 | margin-right: -(map-get($gutterwidths, medium));
7 | padding: map-get($gutterwidths, medium);
8 |
9 | >* {
10 | >.ds--columns {
11 | margin: 0;
12 | }
13 | >.ds--columns>.ds--column {
14 | box-shadow: 2px 1px 2px #00000026;
15 | border-radius: 3px;
16 | }
17 | >.ds--chart {
18 | box-shadow: 2px 1px 2px #00000026;
19 | border-radius: 3px;
20 | }
21 | }
22 |
23 | /* Hero pattern
24 | http://www.heropatterns.com/ */
25 | box-shadow: inset 0 0 10px #00000042;
26 | background-color: #dfeaeb;
27 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3E%3Cpath fill='%2379a9a3' fill-opacity='0.1' d='M1 3h1v1H1V3zm2-2h1v1H3V1z'%3E%3C/path%3E%3C/svg%3E");
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/board/Board.test.js:
--------------------------------------------------------------------------------
1 | import should from 'should' // eslint-disable-line no-unused-vars
2 | import * as d3 from 'd3'
3 | import assert from 'assert'
4 |
5 | describe('Board component', function() {
6 | beforeEach(function () {
7 | this.jsdom = require('jsdom-global')(undefined, {'url': 'https://fake.url.com'})
8 | })
9 |
10 | afterEach(function () {
11 | this.jsdom()
12 | })
13 |
14 | it('integration test', () => {
15 | const test_parser = require('../../test_parser.js').default
16 | const bind = test_parser({
17 | 'component': 'root', 'args': { 'title': '', 'state_handler': {} },
18 | 'data': [
19 | {'component': 'board',
20 | 'args': {'state_handler': {}},
21 | 'data': [
22 | {'component': 'text', 'args': {'tagName': 'h4'}, 'data': 'random'}
23 | ]
24 | }
25 | ]
26 | })
27 | bind(d3.selection())
28 | assert.equal(d3.selection().select('.ds--board').size(), 1)
29 | })
30 |
31 | })
32 |
--------------------------------------------------------------------------------
/src/components/board/gallery.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "data": [
4 | {
5 | "rows": [
6 | {"pie chart": {"columns": [["Apples", 3], ["Oranges", 2], ["Pears", 2]]}},
7 | {"p text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."}
8 | ]
9 | }
10 | ],
11 | "category": "Grid",
12 | "examples": {
13 | "board": "board"
14 | }
15 | }
16 | ]
17 |
--------------------------------------------------------------------------------
/src/components/board/index.js:
--------------------------------------------------------------------------------
1 | import BoardComponent from './Board.js'
2 |
3 | export default BoardComponent
4 |
--------------------------------------------------------------------------------
/src/components/chart/Chart.js:
--------------------------------------------------------------------------------
1 | import Component from '../base'
2 | import { required } from '../../validators'
3 | import './Chart.scss'
4 |
5 |
6 | /**
7 | * Creates a visualization
8 | * @module components/chart
9 | * @param args Component arguments
10 | * @param args.type Visualization type (`pie`, `bar`, `line`, etc.)
11 | * @returns {function}
12 | *
13 | * @example YAML format
14 | * pie chart:
15 | * ...
16 | *
17 | * @example
18 | * bar chart:
19 | * ...
20 | *
21 | * @example JSON format
22 | * {
23 | * "component": "chart",
24 | * "args": {"type": "pie"},
25 | * "data": [...]
26 | * }
27 | *
28 | * @example JavaScript format
29 | * import ChartComponent from 'components/chart'
30 | *
31 | * ChartComponent({'type': 'pie'})(d3.selection())(...)
32 | *
33 | *
34 | */
35 | const ChartComponent = Component({
36 | 'validators': [required('type')],
37 | 'init': (args, selection) => selection
38 | .append('div').attr('class', 'ds--chart'),
39 | 'render': (args, selection, data, element) => {
40 | const {bb} = require('billboard.js')
41 | const configuration = {
42 | 'bindto': element.node(),
43 | 'data': Object.assign(data, {'type': args.type})
44 | }
45 | if (args.stacked) {
46 | if (data.columns)
47 | configuration.data.groups = [data.columns.map(column => column[0])]
48 | if (data.rows)
49 | configuration.data.groups = [data.rows[0]]
50 | }
51 | if (args.axis) {
52 | configuration.axis = args.axis
53 | }
54 | bb.generate(configuration)
55 | }
56 | })
57 |
58 | export default ChartComponent
59 |
--------------------------------------------------------------------------------
/src/components/chart/Chart.scss:
--------------------------------------------------------------------------------
1 | @import "~sassline/assets/sass/sassline-base/_variables.scss";
2 |
3 | .ds--chart {
4 | background: white;
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/chart/Chart.test.js:
--------------------------------------------------------------------------------
1 | import should from 'should' // eslint-disable-line no-unused-vars
2 | import sinon from 'sinon'
3 | const ChartComponentInjector = require('inject-loader!./Chart')
4 |
5 |
6 | describe('ChartComponent', function() {
7 | beforeEach(function () {
8 | this.jsdom = require('jsdom-global')(undefined, {'url': 'https://fake.url.com'})
9 | })
10 |
11 | afterEach(function () {
12 | this.jsdom()
13 | })
14 |
15 | const call_render_with = (args) => {
16 | const fake_generate = sinon.spy()
17 | const ChartComponent = ChartComponentInjector(
18 | {'billboard.js': {'bb': {'generate': fake_generate}}}).default
19 | const bind = ChartComponent(args.component_args)
20 | const d3 = require('d3')
21 | const selection = d3.selection()
22 | selection.append = () => ({
23 | 'node': () => 'magic',
24 | 'attr': () => ({
25 | 'node': () => 'magic',
26 | })
27 | })
28 | const render = bind(selection)
29 | render(args.render_args)
30 |
31 | return { fake_generate, selection }
32 | }
33 |
34 | it('billboard called', function() {
35 | const { fake_generate } = call_render_with({
36 | 'component_args': {'type': 'spline'},
37 | 'render_args': {'columns': [
38 | ['x', 1, 2, 3],
39 | ['y', 1, 2, 3],
40 | ]}
41 | })
42 | fake_generate.should.be.called()
43 | })
44 |
45 | it('billboard called with correct arguments', function() {
46 | const { fake_generate, selection } = call_render_with({
47 | 'component_args': {'type': 'spline'},
48 | 'render_args': {'columns': [
49 | ['x', 1, 2, 3],
50 | ['y', 1, 2, 3],
51 | ]}
52 | })
53 | fake_generate.should.be.calledWith({
54 | 'bindto': selection.append().node(),
55 | 'data': {
56 | 'type': 'spline',
57 | 'columns': [
58 | ['x', 1, 2, 3],
59 | ['y', 1, 2, 3],
60 | ]
61 | }
62 | })
63 | })
64 |
65 | it('billboard called with correct arguments 2', function() {
66 | const { fake_generate, selection } = call_render_with({
67 | 'component_args': {'type': 'pie'},
68 | 'render_args': {'columns': [
69 | ['a', 1, 2, 3],
70 | ['b', 1, 2, 3],
71 | ]}
72 | })
73 | fake_generate.should.be.calledWith({
74 | 'bindto': selection.append().node(),
75 | 'data': {
76 | 'type': 'pie',
77 | 'columns': [
78 | ['a', 1, 2, 3],
79 | ['b', 1, 2, 3],
80 | ]
81 | }
82 | })
83 | })
84 |
85 | it('billboard called with correct arguments (stacked)', function() {
86 | const { fake_generate, selection } = call_render_with({
87 | 'component_args': {'type': 'pie', 'stacked': true},
88 | 'render_args': {'columns': [
89 | ['a', 1, 2, 3],
90 | ['b', 1, 2, 3],
91 | ]}
92 | })
93 | fake_generate.should.be.calledWith({
94 | 'bindto': selection.append().node(),
95 | 'data': {
96 | 'type': 'pie',
97 | 'groups': [['a', 'b']],
98 | 'columns': [
99 | ['a', 1, 2, 3],
100 | ['b', 1, 2, 3],
101 | ]
102 | }
103 | })
104 | })
105 |
106 | it('billboard called with correct arguments (horizontal)', function() {
107 | const { fake_generate, selection } = call_render_with({
108 | 'component_args': {'type': 'pie', 'axis': {'rotated': true}},
109 | 'render_args': {'columns': [
110 | ['bar', 1, 2, 3],
111 | ['foo', 1, 2, 3],
112 | ['x', 1, 2, 3],
113 | ]}
114 | })
115 | fake_generate.should.be.calledWith({
116 | 'bindto': selection.append().node(),
117 | 'axis': {
118 | 'rotated': true
119 | },
120 | 'data': {
121 | 'type': 'pie',
122 | 'columns': [
123 | ['bar', 1, 2, 3],
124 | ['foo', 1, 2, 3],
125 | ['x', 1, 2, 3],
126 | ]
127 | }
128 | })
129 | })
130 |
131 | it('billboard called with correct arguments (stacked 2)', function() {
132 | const { fake_generate, selection } = call_render_with({
133 | 'component_args': {'type': 'pie', 'stacked': true},
134 | 'render_args': {'columns': [
135 | ['bar', 1, 2, 3],
136 | ['foo', 1, 2, 3],
137 | ['x', 1, 2, 3],
138 | ]}
139 | })
140 | fake_generate.should.be.calledWith({
141 | 'bindto': selection.append().node(),
142 | 'data': {
143 | 'type': 'pie',
144 | 'groups': [['bar', 'foo', 'x']],
145 | 'columns': [
146 | ['bar', 1, 2, 3],
147 | ['foo', 1, 2, 3],
148 | ['x', 1, 2, 3],
149 | ]
150 | }
151 | })
152 | })
153 |
154 | it('billboard called with correct arguments (stacked, rows)', function() {
155 | const { fake_generate, selection } = call_render_with({
156 | 'component_args': {'type': 'pie', 'stacked': true},
157 | 'render_args': {'rows': [
158 | ['foo', 'bar'],
159 | [0, 1]
160 | ]}
161 | })
162 | fake_generate.should.be.calledWith({
163 | 'bindto': selection.append().node(),
164 | 'data': {
165 | 'type': 'pie',
166 | 'groups': [['foo', 'bar']],
167 | 'rows': [
168 | ['foo', 'bar'],
169 | [0, 1]
170 | ]
171 | }
172 | })
173 | })
174 |
175 | it('should take rows as well', function() {
176 | const { fake_generate, selection } = call_render_with({
177 | 'component_args': {'type': 'bar'},
178 | 'render_args': {'rows': [
179 | [1, 2, 3],
180 | [1, 2, 3],
181 | ]}
182 | })
183 | fake_generate.should.be.calledWith({
184 | 'bindto': selection.append().node(),
185 | 'data': {
186 | 'type': 'bar',
187 | 'rows': [
188 | [1, 2, 3],
189 | [1, 2, 3],
190 | ]
191 | }
192 | })
193 | })
194 |
195 | })
196 |
--------------------------------------------------------------------------------
/src/components/chart/gallery.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "data": {
4 | "columns": [
5 | ["Apples", 3],
6 | ["Oranges", 2],
7 | ["Pears", 2]
8 | ]
9 | },
10 | "category": "Charts",
11 | "examples": {
12 | "bar chart": "bar chart",
13 | "horizontal bar chart": "horizontal bar chart",
14 | "donut chart": "donut chart",
15 | "pie chart": "pie chart"
16 | }
17 | },
18 | {
19 | "data": {
20 | "columns": [
21 | ["Apples", 3, 2, 3, 4, 2],
22 | ["Oranges", 2, 1, 0, 1, 1],
23 | ["Pears", 2, 0, 0, 3, 4]
24 | ]
25 | },
26 | "category": "Charts",
27 | "examples": {
28 | "stacked bar chart": "stacked bar chart",
29 | "horizontal stacked bar chart": "horizontal stacked bar chart",
30 | "stacked line chart": "stacked line chart",
31 | "stacked spline chart": "stacked spline chart",
32 | "stacked area chart": "stacked area chart"
33 | }
34 | },
35 | {
36 | "data": {
37 | "columns": [
38 | ["data", 30.0]
39 | ]
40 | },
41 | "category": "Charts",
42 | "examples": {
43 | "gauge chart": "gauge chart"
44 | }
45 | },
46 | {
47 | "data": {
48 | "xs": {
49 | "setosa": "setosa_x",
50 | "versicolor": "versicolor_x"
51 | },
52 | "columns": [
53 | ["setosa_x", 3.5, 3, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.4, 3, 3, 4, 4.4, 3.9, 3.5, 3.8, 3.8, 3.4, 3.7, 3.6, 3.3, 3.4, 3, 3.4, 3.5, 3.4, 3.2, 3.1, 3.4, 4.1, 4.2, 3.1, 3.2, 3.5, 3.6, 3, 3.4, 3.5, 2.3, 3.2, 3.5, 3.8, 3, 3.8, 3.2, 3.7, 3.3],
54 | ["versicolor_x", 3.2, 3.2, 3.1, 2.3, 2.8, 2.8, 3.3, 2.4, 2.9, 2.7, 2, 3, 2.2, 2.9, 2.9, 3.1, 3, 2.7, 2.2, 2.5, 3.2, 2.8, 2.5, 2.8, 2.9, 3, 2.8, 3, 2.9, 2.6, 2.4, 2.4, 2.7, 2.7, 3, 3.4, 3.1, 2.3, 3, 2.5, 2.6, 3, 2.6, 2.3, 2.7, 3, 2.9, 2.9, 2.5, 2.8],
55 | ["setosa", 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.2, 0.1, 0.1, 0.2, 0.4, 0.4, 0.3, 0.3, 0.3, 0.2, 0.4, 0.2, 0.5, 0.2, 0.2, 0.4, 0.2, 0.2, 0.2, 0.2, 0.4, 0.1, 0.2, 0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 0.3, 0.3, 0.2, 0.6, 0.4, 0.3, 0.2, 0.2, 0.2, 0.2],
56 | ["versicolor", 1.4, 1.5, 1.5, 1.3, 1.5, 1.3, 1.6, 1, 1.3, 1.4, 1, 1.5, 1, 1.4, 1.3, 1.4, 1.5, 1, 1.5, 1.1, 1.8, 1.3, 1.5, 1.2, 1.3, 1.4, 1.4, 1.7, 1.5, 1, 1.1, 1, 1.2, 1.6, 1.5, 1.6, 1.5, 1.3, 1.3, 1.3, 1.2, 1.4, 1.2, 1, 1.3, 1.2, 1.3, 1.3, 1.1, 1.3]
57 | ]
58 | },
59 | "category": "Charts",
60 | "examples": {
61 | "scatter plot": "scatter plot"
62 | }
63 | },
64 | {
65 | "data": {
66 | "columns": [
67 | ["Apples", 30, 29, 25, 26, 27, 10],
68 | ["Oranges", 20, 21, 22, 20, 27, 19],
69 | ["Pears", 10, 9, 8, 8, 7, 8]
70 | ]
71 | },
72 | "category": "Charts",
73 | "examples": {
74 | "line chart": "line chart",
75 | "bubble chart": "bubble chart",
76 | "spline chart": "spline chart"
77 | }
78 | },
79 | {
80 | "data": [
81 | {"attr:axis": {
82 | "x": {
83 | "label": "Year",
84 | "type": "timeseries",
85 | "tick": {
86 | "format": "%Y"
87 | }
88 | },
89 | "y": {
90 | "label": "Amount consumed (tonnes)"
91 | }
92 | }},
93 | {"data": {
94 | "x": "x",
95 | "xFormat": "%Y",
96 | "columns": [
97 | ["x", "1999", "2001", "2002", "2004", "2007", "2008"],
98 | ["Apples", 30, 29, 25, 26, 27, 10],
99 | ["Oranges", 20, 21, 22, 20, 27, 19],
100 | ["Pears", 10, 9, 8, 8, 7, 8]
101 | ]
102 | }}
103 | ],
104 | "category": "Charts",
105 | "examples": {
106 | "custom axes": "line chart"
107 | }
108 | },
109 | {
110 | "data": {
111 | "columns": [
112 | ["Apples", 30, 29, 25, 26, 27, 10],
113 | ["Oranges", 20, 21, 22, 20, 27, 19]
114 | ]
115 | },
116 | "category": "Charts",
117 | "examples": {
118 | "area chart": "area chart",
119 | "step chart": "step chart"
120 | }
121 | }
122 | ]
123 |
--------------------------------------------------------------------------------
/src/components/chart/index.js:
--------------------------------------------------------------------------------
1 | import ChartComponent from './Chart'
2 |
3 | export default ChartComponent
4 |
--------------------------------------------------------------------------------
/src/components/columns/Columns.js:
--------------------------------------------------------------------------------
1 | import ContainerComponent from '../container_base.js'
2 | import Wrapped from '../wrapped'
3 | import './Columns.scss'
4 |
5 | /**
6 | * Useful for grouping elements in a horizontal layout. Similar to
7 | * Columns, but the elements are aligned in columns.
8 | * @module components/columns
9 | * @param args Component arguments
10 | * @param [args.columns=2] Ideal number of columns. Might be reduced on smaller
11 | * screens.
12 | * @returns {function}
13 | *
14 | * @example YAML format
15 | * columns:
16 | * ...
17 | *
18 | * @example
19 | * 4 columns:
20 | * ...
21 | *
22 | * @example JSON format
23 | * {
24 | * "component": "columns",
25 | * "data": [...]
26 | * }
27 | *
28 | * @example
29 | * {
30 | * "component": "columns",
31 | * "args": {"columns": 4},
32 | * "data": [...]
33 | * }
34 | * @example JavaScript format
35 | * import ColumnsComponent from 'components/columns'
36 | *
37 | * ColumnsComponent({})(d3.selection())(...)
38 | *
39 | * @example
40 | * import ColumnsComponent from 'components/columns'
41 | *
42 | * ColumnsComponent({'columns': '4'})(d3.selection())(...)
43 |
44 | */
45 | const ColumnsComponent = Wrapped((args, selection) => selection
46 | .append('div')
47 | .attr('class', 'ds--columns')
48 | .attr('data-ds--columns', (
49 | (args === undefined || args.columns === undefined) ? 2 : args.columns))
50 | )(ContainerComponent({
51 | 'wrapper_tag': 'div',
52 | 'wrapper_class': 'ds--column'
53 | }))
54 |
55 | export default ColumnsComponent
56 |
--------------------------------------------------------------------------------
/src/components/columns/Columns.scss:
--------------------------------------------------------------------------------
1 | @import "~sassline/assets/sass/sassline-base/_variables.scss";
2 | @import "~sassline/assets/sass/sassline-base/_mixins.scss";
3 |
4 | .ds--columns {
5 | display: flex;
6 | flex-wrap: wrap;
7 | margin-left: -(map-get($gutterwidths, medium) / 2 - 1rem);
8 | margin-right: -(map-get($gutterwidths, medium) / 2 - 1rem);
9 | box-sizing: border-box;
10 | justify-content: space-between;
11 |
12 | &[data-ds--columns="1"] >* {width: calc(100%/1 - ((1rem - 1rem) / 1));}
13 | &[data-ds--columns="1"] >* {width: -webkit-calc(100%/1 - ((1rem - 1rem) / 1));}
14 | &[data-ds--columns="2"] >* {width: calc(100%/2 - ((2rem - 1rem) / 2));}
15 | &[data-ds--columns="2"] >* {width: -webkit-calc(100%/2 - ((2rem - 1rem) / 2));}
16 | &[data-ds--columns="3"] >* {width: calc(100%/2 - ((2rem - 1rem) / 2));}
17 | &[data-ds--columns="3"] >* {width: -webkit-calc(100%/2 - ((2rem - 1rem) / 2));}
18 | &[data-ds--columns="4"] >* {width: calc(100%/2 - ((2rem - 1rem) / 2));}
19 | &[data-ds--columns="4"] >* {width: -webkit-calc(100%/2 - ((2rem - 1rem) / 2));}
20 | &[data-ds--columns="5"] >* {width: calc(100%/2 - ((2rem - 1rem) / 2));}
21 | &[data-ds--columns="5"] >* {width: -webkit-calc(100%/2 - ((2rem - 1rem) / 2));}
22 | &[data-ds--columns="6"] >* {width: calc(100%/2 - ((2rem - 1rem) / 2));}
23 | &[data-ds--columns="6"] >* {width: -webkit-calc(100%/2 - ((2rem - 1rem) / 2));}
24 |
25 | @include breakpoint(break-1) {
26 | &[data-ds--columns="1"] >* {width: calc(100%/1 - ((1rem - 1rem) / 1));}
27 | &[data-ds--columns="1"] >* {width: -webkit-calc(100%/1 - ((1rem - 1rem) / 1));}
28 | &[data-ds--columns="2"] >* {width: calc(100%/2 - ((2rem - 1rem) / 2));}
29 | &[data-ds--columns="2"] >* {width: -webkit-calc(100%/2 - ((2rem - 1rem) / 2));}
30 | &[data-ds--columns="3"] >* {width: calc(100%/3 - ((3rem - 1rem) / 3));}
31 | &[data-ds--columns="3"] >* {width: -webkit-calc(100%/3 - ((3rem - 1rem) / 3));}
32 | &[data-ds--columns="4"] >* {width: calc(100%/3 - ((3rem - 1rem) / 3));}
33 | &[data-ds--columns="4"] >* {width: -webkit-calc(100%/3 - ((3rem - 1rem) / 3));}
34 | &[data-ds--columns="5"] >* {width: calc(100%/3 - ((3rem - 1rem) / 3));}
35 | &[data-ds--columns="5"] >* {width: -webkit-calc(100%/3 - ((3rem - 1rem) / 3));}
36 | &[data-ds--columns="6"] >* {width: calc(100%/3 - ((3rem - 1rem) / 3));}
37 | &[data-ds--columns="6"] >* {width: -webkit-calc(100%/3 - ((3rem - 1rem) / 3));}
38 | }
39 |
40 | @include breakpoint(break-2) {
41 | &[data-ds--columns="1"] >* {width: calc(100%/1 - ((1rem - 1rem) / 1));}
42 | &[data-ds--columns="1"] >* {width: -webkit-calc(100%/1 - ((1rem - 1rem) / 1));}
43 | &[data-ds--columns="2"] >* {width: calc(100%/2 - ((2rem - 1rem) / 2));}
44 | &[data-ds--columns="2"] >* {width: -webkit-calc(100%/2 - ((2rem - 1rem) / 2));}
45 | &[data-ds--columns="3"] >* {width: calc(100%/3 - ((3rem - 1rem) / 3));}
46 | &[data-ds--columns="3"] >* {width: -webkit-calc(100%/3 - ((3rem - 1rem) / 3));}
47 | &[data-ds--columns="4"] >* {width: calc(100%/4 - ((4rem - 1rem) / 4));}
48 | &[data-ds--columns="4"] >* {width: -webkit-calc(100%/4 - ((4rem - 1rem) / 4));}
49 | &[data-ds--columns="5"] >* {width: calc(100%/5 - ((5rem - 1rem) / 5));}
50 | &[data-ds--columns="5"] >* {width: -webkit-calc(100%/5 - ((5rem - 1rem) / 5));}
51 | &[data-ds--columns="6"] >* {width: calc(100%/5 - ((5rem - 1rem) / 5));}
52 | &[data-ds--columns="6"] >* {width: -webkit-calc(100%/5 - ((5rem - 1rem) / 5));}
53 | }
54 |
55 | @include breakpoint(break-3) {
56 | &[data-ds--columns="1"] >* {width: calc(100%/1 - ((1rem - 1rem) / 1));}
57 | &[data-ds--columns="1"] >* {width: -webkit-calc(100%/1 - ((1rem - 1rem) / 1));}
58 | &[data-ds--columns="2"] >* {width: calc(100%/2 - ((2rem - 1rem) / 2));}
59 | &[data-ds--columns="2"] >* {width: -webkit-calc(100%/2 - ((2rem - 1rem) / 2));}
60 | &[data-ds--columns="3"] >* {width: calc(100%/3 - ((3rem - 1rem) / 3));}
61 | &[data-ds--columns="3"] >* {width: -webkit-calc(100%/3 - ((3rem - 1rem) / 3));}
62 | &[data-ds--columns="4"] >* {width: calc(100%/4 - ((4rem - 1rem) / 4));}
63 | &[data-ds--columns="4"] >* {width: -webkit-calc(100%/4 - ((4rem - 1rem) / 4));}
64 | &[data-ds--columns="5"] >* {width: calc(100%/5 - ((5rem - 1rem) / 5));}
65 | &[data-ds--columns="5"] >* {width: -webkit-calc(100%/5 - ((5rem - 1rem) / 5));}
66 | &[data-ds--columns="6"] >* {width: calc(100%/6 - ((6rem - 1rem) / 6));}
67 | &[data-ds--columns="6"] >* {width: -webkit-calc(100%/6 - ((6rem - 1rem) / 6));}
68 | }
69 |
70 | .ds--column {
71 | box-sizing: border-box;
72 | padding: 0 (map-get($gutterwidths, medium) / 2) map-get($gutterwidths, medium - 1rem);
73 | overflow-x: hidden;
74 | background: white;
75 | margin-bottom: 1rem;
76 |
77 | h1, h2, h3, h4, h5, h6 {
78 | white-space: nowrap;
79 | overflow: hidden;
80 | text-overflow: ellipsis;
81 | }
82 | }
83 | }
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/src/components/columns/Columns.test.js:
--------------------------------------------------------------------------------
1 | import should from 'should' // eslint-disable-line no-unused-vars
2 | import * as d3 from 'd3'
3 | import assert from 'assert'
4 |
5 | describe('Columns component', function() {
6 | beforeEach(function () {
7 | this.jsdom = require('jsdom-global')(undefined, {'url': 'https://fake.url.com'})
8 | })
9 |
10 | afterEach(function () {
11 | this.jsdom()
12 | })
13 |
14 | it('integration test', () => {
15 | const test_parser = require('../../test_parser.js').default
16 | const bind = test_parser({
17 | 'component': 'root', 'args': { 'title': '', 'state_handler': {} },
18 | 'data': [
19 | {'component': 'columns',
20 | 'args': {'state_handler': {}},
21 | 'data': [
22 | {'component': 'text', 'args': {'tagName': 'h4'}, 'data': 'random'}
23 | ]
24 | }
25 | ]
26 | })
27 | bind(d3.selection())
28 | assert.equal(d3.selection().select(
29 | '.ds--columns[data-ds--columns="2"] .ds--column h4').text(), 'random')
30 | })
31 |
32 | it('integration test - non default column count', () => {
33 | const test_parser = require('../../test_parser.js').default
34 | const bind = test_parser({
35 | 'component': 'root', 'args': { 'title': '', 'state_handler': {} },
36 | 'data': [
37 | {'component': 'columns',
38 | 'args': {'columns': 3},
39 | 'data': [
40 | {'component': 'text', 'args': {'tagName': 'h4'}, 'data': 'foobar'}
41 | ]
42 | }
43 | ]
44 | })
45 | bind(d3.selection())
46 | assert.equal(d3.selection().select(
47 | '.ds--columns[data-ds--columns="3"] .ds--column h4').text(), 'foobar')
48 | })
49 |
50 | })
51 |
--------------------------------------------------------------------------------
/src/components/columns/gallery.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "data": [
4 | {"pie chart": {"columns": [["Apples", 3], ["Oranges", 2], ["Pears", 2]]}},
5 | {"p text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."}
6 | ],
7 | "category": "Grid",
8 | "examples": {
9 | "columns": "2 columns"
10 | }
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/src/components/columns/index.js:
--------------------------------------------------------------------------------
1 | import ColumnsComponent from './Columns'
2 |
3 | export default ColumnsComponent
4 |
--------------------------------------------------------------------------------
/src/components/container_base.js:
--------------------------------------------------------------------------------
1 | import default_parser from '../default_parser.js'
2 | import Component from './base'
3 |
4 | const ContainerComponent = (
5 | { wrapper_tag, wrapper_class, validators, init }) => Component({
6 | 'render': (args, selection, _, __, data) => {
7 | selection.selectAll('*').remove()
8 | if (data instanceof Array) data.map((definition) => {
9 | const wrapper = selection
10 | .append(wrapper_tag)
11 | .attr('class', wrapper_class)
12 | const updated_definition = Object.assign({}, definition)
13 | if (updated_definition.args === undefined) updated_definition.args = {}
14 | if (args !== undefined) {
15 | updated_definition.args.state_handler = args.state_handler
16 | updated_definition.args.file_loader = args.file_loader
17 | }
18 | default_parser(updated_definition)(wrapper)
19 | })
20 | },
21 | 'init': init,
22 | 'validators': validators || [],
23 | })
24 |
25 | export default ContainerComponent
26 |
--------------------------------------------------------------------------------
/src/components/dropdown/Dropdown.js:
--------------------------------------------------------------------------------
1 | import Component from '../base'
2 | import { required } from '../../validators'
3 | import { regexp } from '../../validators'
4 | import * as d3 from 'd3'
5 |
6 | const set_values = (selection) => selection
7 | .property('value', d => d.value)
8 | .text(d => d.text)
9 |
10 | const enter = (selection) => selection
11 | .enter()
12 | .append('option')
13 | .attr('class', 'ds--select-option')
14 | .call(set_values)
15 |
16 | const exit = (selection) => selection
17 | .exit()
18 | .remove()
19 |
20 | const update = (selection) => selection
21 | .call(set_values)
22 |
23 | const update_pattern = (selection) =>
24 | [enter, exit, update].map(f => f(selection))
25 |
26 | /**
27 | * Creates a dropdown
28 | * @module components/dropdown
29 | * @param args Component arguments
30 | * @param args.variable Variable name
31 | * @param args.default Default value
32 | * @returns {function}
33 | *
34 | * @example YAML format
35 | * dropdown name=JohnDoe:
36 | * - {"value": "JohnDoe", "text": "John Doe"}
37 | * - {"value": "JoeSmith", "text": "Joe Smith"}
38 | *
39 | * @example
40 | * bar dropdown:
41 | * ...
42 | *
43 | * @example JSON format
44 | * {
45 | * "component": "dropdown",
46 | * "args": {"variable": "name", "default": "JohnDoe"},
47 | * "data": [
48 | * {"value": "JohnDoe", "text": "John Doe"},
49 | * {"value": "JoeSmith", "text": "Joe Smith"}
50 | * ]
51 | * }
52 | *
53 | * @example JavaScript format
54 | * import DropdownComponent from 'components/dropdown'
55 | *
56 | * DropdownComponent(
57 | * {"variable": "name", "default": "JohnDoe"})(d3.selection())(...)
58 | *
59 | *
60 | */
61 | const DropdownComponent = Component({
62 | 'validators': [
63 | required('variable'),
64 | regexp('variable', /^[A-Za-z]([_A-Za-z0-9-]*[_A-Za-z0-9])?$/),
65 | required('default')],
66 | 'init': (args, selection) => {
67 | args.state_handler.init_variable(args.variable, args.default)
68 | return selection
69 | .append('select').attr('class', 'ds--select')
70 | },
71 | 'render': (args, selection, data, item) => {
72 | const value = args.state_handler.get_state()[args.variable]
73 | if (value === args.default && args.default === '~first') {
74 | args.state_handler.set_variable(args.variable, data[0].value)
75 | }
76 | if (value === args.default && args.default === '~last') {
77 | args.state_handler.set_variable(args.variable, data.slice(-1)[0].value)
78 | }
79 |
80 | item
81 | .selectAll('option').data(data).call(update_pattern)
82 | item
83 | .property('value', value)
84 | item
85 | .on('change', function() {
86 | args.state_handler.set_variable(args.variable, d3.select(this)
87 | .property('value'))
88 | })
89 | }
90 | })
91 |
92 | export default DropdownComponent
93 |
--------------------------------------------------------------------------------
/src/components/dropdown/gallery.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "data": [
4 | {"value": "male", "text": "Male"},
5 | {"value": "female", "text": "Female"}
6 | ],
7 | "category": "Controls",
8 | "examples": {
9 | "dropdown": "dropdown gender=male"
10 | }
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/src/components/dropdown/index.js:
--------------------------------------------------------------------------------
1 | import DropdownComponent from './Dropdown'
2 |
3 | export default DropdownComponent
4 |
--------------------------------------------------------------------------------
/src/components/root/Root.js:
--------------------------------------------------------------------------------
1 | import Component from '../base'
2 | import default_parser from '../../default_parser.js'
3 | import { required } from '../../validators'
4 |
5 |
6 | /**
7 | * A Component to contain the dashboard.
8 | * @module components/root
9 | * @param args Component arguments
10 | * @param args.title Document title (``)
11 | * @returns {function}
12 | *
13 | * @example YAML format
14 | * dashboard "Hello World":
15 | * ...
16 | *
17 | * @example JSON format
18 | * {
19 | * "component": "root",
20 | * "args": {"title": "Hello World"},
21 | * "data": [...]
22 | * }
23 | *
24 | * @example JavaScript format
25 | * import RootComponent from 'components/root'
26 | *
27 | * RootComponent({'title': 'Hello World'})(d3.selection())(...)
28 | *
29 | */
30 | const RootComponent = Component({
31 | 'validators': [required('title')],
32 | 'init': (args, selection) => {selection.select('title').text(args.title)},
33 | 'render': (args, selection, data) => {
34 | const body = selection.select('body')
35 | body.selectAll('*').remove()
36 | // "unsubscribe" the elements we've just removed
37 | if (args.state_handler) args.state_handler.reset()
38 | if (data instanceof Array) data.map((definition) => {
39 | const updated_definition = Object.assign({'args': {}}, definition)
40 | updated_definition.args.state_handler = args.state_handler
41 | updated_definition.args.file_loader = args.file_loader
42 | default_parser(updated_definition)(body.append('div')
43 | .attr('class', 'ds--wrapper'))
44 | })
45 | }
46 | })
47 |
48 | export default RootComponent
49 |
--------------------------------------------------------------------------------
/src/components/root/index.js:
--------------------------------------------------------------------------------
1 | import RootComponent from './Root.js'
2 |
3 | export default RootComponent
4 |
--------------------------------------------------------------------------------
/src/components/rows/Rows.js:
--------------------------------------------------------------------------------
1 | import ContainerComponent from '../container_base.js'
2 | import Wrapped from '../wrapped'
3 | import './Rows.scss'
4 |
5 | /**
6 | * Useful for grouping elements in a vertical layout. Similar to RootComponent,
7 | * but doesn't alter the `` tag.
8 | * @module components/rows
9 | * @param args Component arguments
10 | * @param args.tagName HTML tag name (i.e. div, p, h1, h2, etc.)
11 | * @returns {function}
12 | *
13 | * @example YAML format
14 | * rows:
15 | * ...
16 | *
17 | * @example JSON format
18 | * {
19 | * "component": "rows",
20 | * "data": [...]
21 | * }
22 | *
23 | * @example JavaScript format
24 | * import RowsComponent from 'components/rows'
25 | *
26 | * RowsComponent({'tagName': 'p'})(d3.selection())(...)
27 | */
28 | const RowsComponent = Wrapped((args, selection) => selection
29 | .append('div')
30 | .attr('class', 'ds--rows')
31 | )(ContainerComponent({
32 | 'wrapper_tag': 'div',
33 | 'wrapper_class': 'ds--row'
34 | }))
35 |
36 | export default RowsComponent
37 |
--------------------------------------------------------------------------------
/src/components/rows/Rows.scss:
--------------------------------------------------------------------------------
1 | .ds--row {
2 | background: white;
3 | }
4 |
--------------------------------------------------------------------------------
/src/components/rows/Rows.test.js:
--------------------------------------------------------------------------------
1 | import should from 'should' // eslint-disable-line no-unused-vars
2 | import * as d3 from 'd3'
3 | import assert from 'assert'
4 |
5 | describe('Rows component', function() {
6 | beforeEach(function () {
7 | this.jsdom = require('jsdom-global')(undefined, {'url': 'https://fake.url.com'})
8 | })
9 |
10 | afterEach(function () {
11 | this.jsdom()
12 | })
13 |
14 | it('integration test', () => {
15 | const test_parser = require('../../test_parser.js').default
16 | const bind = test_parser({
17 | 'component': 'root', 'args': { 'title': '', 'state_handler': {} },
18 | 'data': [
19 | {'component': 'rows',
20 | 'args': {'state_handler': {}},
21 | 'data': [
22 | {'component': 'text', 'args': {'tagName': 'h4'}, 'data': 'random'}
23 | ]
24 | }
25 | ]
26 | })
27 | bind(d3.selection())
28 | assert.equal(d3.selection().select('.ds--rows .ds--row h4').text(),
29 | 'random')
30 | })
31 | })
32 |
--------------------------------------------------------------------------------
/src/components/rows/gallery.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "data": [
4 | {"rows": [
5 | {"h2 text": "Lorem ipsum dolor sit amet"},
6 | {"pie chart": {"columns": [["Apples", 3], ["Oranges", 2], ["Pears", 2]]}}
7 | ]},
8 | {"rows": [
9 | {"h2 text": "Lorem ipsum dolor sit amet"},
10 | {"p text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam dolor massa, luctus rhoncus justo vel, cursus placerat ligula. Proin pulvinar ipsum in enim rutrum, quis fermentum ex finibus. Nunc commodo urna tellus, tristique tempor magna tempor eget. Phasellus eu ex lacinia sapien viverra fermentum."}
11 | ]}
12 | ],
13 | "category": "Grid",
14 | "examples": {
15 | "rows": "2 columns"
16 | }
17 | }
18 | ]
19 |
--------------------------------------------------------------------------------
/src/components/rows/index.js:
--------------------------------------------------------------------------------
1 | import RowsComponent from './Rows'
2 |
3 | export default RowsComponent
4 |
--------------------------------------------------------------------------------
/src/components/text/Text.js:
--------------------------------------------------------------------------------
1 | import Component from '../base'
2 | import { required } from '../../validators'
3 | import { regexp } from '../../validators'
4 | import './Text.scss'
5 |
6 | /**
7 | * Creates a text element with the specified HTML tag name.
8 | * @module components/text
9 | * @param args Component arguments
10 | * @param args.tagName HTML tag name (i.e. div, p, h1, h2, etc.)
11 | * @returns {function}
12 | *
13 | * @example YAML format
14 | * h1 text:
15 | * ...
16 | *
17 | * @example
18 | * p text:
19 | * ...
20 | *
21 | * @example JSON format
22 | * {
23 | * "component": "text",
24 | * "args": {"tagName": "p"},
25 | * "data": [...]
26 | * }
27 | *
28 | * @example JavaScript format
29 | * import TextComponent from 'components/text'
30 | *
31 | * TextComponent({'tagName': 'p'})(d3.selection())(...)
32 | *
33 | *
34 | */
35 | const TextComponent = Component({
36 | 'validators': [required('tagName'),
37 | regexp('tagName', /^[A-Za-z]([A-Za-z0-9-]*[A-Za-z0-9])?$/)],
38 | 'init': (args, selection) => {
39 | const item = selection.append(args.tagName).attr('class', 'ds--text')
40 | if (args.hasOwnProperty('align'))
41 | item.attr('data-align', args.align)
42 |
43 | return item
44 | },
45 | 'render': (args, selection, data, item) => item.text(data)
46 | })
47 |
48 | export default TextComponent
49 |
--------------------------------------------------------------------------------
/src/components/text/Text.scss:
--------------------------------------------------------------------------------
1 | .ds--text {
2 | &[data-align="center"] {text-align: center;}
3 | &[data-align="justify"] {text-align: justify;}
4 | &[data-align="right"] {text-align: right;}
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/text/Text.test.js:
--------------------------------------------------------------------------------
1 | import should from 'should' // eslint-disable-line no-unused-vars
2 | import TextComponent from './Text'
3 | import assert from 'assert'
4 |
5 | describe('Text component', function() {
6 | beforeEach(function () {
7 | this.jsdom = require('jsdom-global')(undefined, {'url': 'https://fake.url.com'})
8 | })
9 |
10 | afterEach(function () {
11 | this.jsdom()
12 | })
13 |
14 | const get_render_function = (component_args) => {
15 | const bind = TextComponent(component_args)
16 | const d3 = require('d3')
17 | const render = bind(d3.selection())
18 | return { render, d3 }
19 | }
20 |
21 | it('should throw on invalid tag name', () => {
22 | (() => {TextComponent({'tagName': 'foo bar'})()()})
23 | .should.throw('Argument \'tagName\' is invalid')
24 | })
25 |
26 | it('text is rendered', function() {
27 | const { render, d3 } = get_render_function({'tagName': 'span'})
28 | render('Hello World from TextComponent')
29 | assert.equal(d3.selection().select('span').text(),
30 | 'Hello World from TextComponent')
31 | })
32 |
33 | it('update function updates text', function() {
34 | const { render, d3 } = get_render_function({'tagName': 'span'})
35 | render('Hello World from TextComponent')
36 | render('Second version')
37 | assert.equal(d3.selection().select('span').text(), 'Second version')
38 | })
39 |
40 | it('text align center', function() {
41 | const { render, d3 } = get_render_function(
42 | {'tagName': 'span', 'align': 'center'})
43 | render('Second version')
44 | assert.equal(d3.selection().select('[data-align="center"]').text(),
45 | 'Second version')
46 | })
47 |
48 | it('align only if attr is supplied', function() {
49 | const { render, d3 } = get_render_function({'tagName': 'span'})
50 | render('Second version')
51 | assert.equal(d3.selection().select('[data-align]').size(), 0)
52 | })
53 |
54 | it('text align right', function() {
55 | const { render, d3 } = get_render_function(
56 | {'tagName': 'span', 'align': 'right'})
57 | render('Second version')
58 | assert.equal(d3.selection().select('[data-align="right"]').text(),
59 | 'Second version')
60 | })
61 |
62 | it('proper class attached', function() {
63 | const { render, d3 } = get_render_function({'tagName': 'span'})
64 | render('Second version')
65 | assert.equal(d3.selection().select('.ds--text').size(), 1)
66 | })
67 |
68 | it('proper item is updated', () => {
69 | const d3 = require('d3')
70 | const render1 = TextComponent({'tagName': 'h4'})(d3.selection())
71 | const render2 = TextComponent({'tagName': 'h4'})(d3.selection())
72 | render1('a')
73 | render2('b')
74 | assert.equal(
75 | d3.select(d3.selection().selectAll('.ds--text').nodes()[0]).text(), 'a')
76 | assert.equal(
77 | d3.select(d3.selection().selectAll('.ds--text').nodes()[1]).text(), 'b')
78 | })
79 |
80 | })
81 |
82 |
--------------------------------------------------------------------------------
/src/components/text/gallery.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "data": "Lorem ipsum dolor sit amet",
4 | "category": "Text",
5 | "examples": {
6 | "h1 text": "h1 text",
7 | "h2 text": "h2 text",
8 | "h3 text": "h3 text",
9 | "p text": "p text"
10 | }
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/src/components/text/index.js:
--------------------------------------------------------------------------------
1 | import TextComponent from './Text'
2 |
3 | export default TextComponent
4 |
--------------------------------------------------------------------------------
/src/components/wrapped.js:
--------------------------------------------------------------------------------
1 | const Wrapped = (wrapper) => (component) => {
2 | if (!(typeof wrapper === 'function'))
3 | throw new Error('Invalid wrapper function')
4 | if (!(typeof component === 'function'))
5 | throw new Error('A component is required')
6 |
7 | return (args) => (selection) => {
8 | return component(args)(wrapper(args, selection))
9 | }
10 | }
11 |
12 | export default Wrapped
13 |
--------------------------------------------------------------------------------
/src/components/wrapped.test.js:
--------------------------------------------------------------------------------
1 | import should from 'should' // eslint-disable-line no-unused-vars
2 | import sinon from 'sinon'
3 | import Wrapped from './wrapped'
4 | import assert from 'assert'
5 |
6 | describe('Wrapped', function() {
7 | beforeEach(function () {
8 | this.jsdom = require('jsdom-global')(undefined, {'url': 'https://fake.url.com'})
9 | })
10 |
11 | afterEach(function () {
12 | this.jsdom()
13 | })
14 |
15 | const call_render_with = () => {
16 | const d3 = require('d3')
17 | const selection = d3.selection()
18 | const render = sinon.stub()
19 | const wrapped_selection = 'masdfsdafdsf'
20 | const wrapper = sinon.stub().returns(wrapped_selection)
21 | const fake_render = sinon.spy()
22 | const fake_bind = sinon.stub().returns(fake_render)
23 | const fake_component = sinon.stub().returns(fake_bind)
24 | const wrapped_component = Wrapped(wrapper)(fake_component)
25 | const args = {'42': 'abc'}
26 | const wrapped_render = wrapped_component(args)(selection)
27 | wrapped_render()
28 |
29 | return { selection, render, wrapper, fake_bind, wrapped_selection, args,
30 | fake_component, wrapped_render, fake_render }
31 | }
32 |
33 | it('requires wrapper function', function() {
34 | (() => {
35 | Wrapped()()
36 | }).should.throw('Invalid wrapper function')
37 | })
38 |
39 | it('requires component to wrap', function() {
40 | (() => {
41 | Wrapped(() => null)()
42 | }).should.throw('A component is required')
43 | })
44 |
45 | it('accepts function as component', function() {
46 | (() => {
47 | Wrapped(() => null)(() => null)
48 | }).should.not.throw('A component is required')
49 | })
50 |
51 | it('returns a component', () => {
52 | const wrapped_component = Wrapped(() => null)(() => () => () => null)
53 | wrapped_component.should.be.a.Function()
54 | wrapped_component({}).should.be.a.Function() // bind
55 | wrapped_component({})(null).should.be.a.Function() //render
56 | })
57 |
58 | it('wrapper function called with selection', function() {
59 | const { wrapper, selection, args } = call_render_with()
60 | wrapper.should.be.calledWith(args, selection)
61 | })
62 |
63 | it('bind is called with wrapped selection', function() {
64 | const { fake_bind, wrapped_selection } = call_render_with()
65 | fake_bind.should.be.calledWith(wrapped_selection)
66 | })
67 |
68 | it('should be called with correct args', function() {
69 | const { args, fake_component } = call_render_with()
70 | fake_component.should.be.calledWith(args)
71 | })
72 |
73 | it('should return correct render function', function() {
74 | const { fake_render, wrapped_render } = call_render_with()
75 | assert.equal(fake_render, wrapped_render)
76 | })
77 |
78 | })
79 |
--------------------------------------------------------------------------------
/src/default_parser.js:
--------------------------------------------------------------------------------
1 | import parse from './parser/parser.js'
2 | import RootComponent from './components/root/Root.js'
3 | import TextComponent from './components/text/Text.js'
4 | import ChartComponent from './components/chart/Chart.js'
5 | import ColumnsComponent from './components/columns/Columns.js'
6 | import RowsComponent from './components/rows/Rows.js'
7 | import BoardComponent from './components/board/Board.js'
8 | import DropdownComponent from './components/dropdown/Dropdown.js'
9 |
10 | const test_parser = parse((component) => ({
11 | 'root': RootComponent,
12 | 'columns': ColumnsComponent,
13 | 'rows': RowsComponent,
14 | 'board': BoardComponent,
15 | 'text': TextComponent,
16 | 'chart': ChartComponent,
17 | 'dropdown': DropdownComponent,
18 | }[component]))
19 |
20 | export default test_parser
21 |
--------------------------------------------------------------------------------
/src/e2e-test.js:
--------------------------------------------------------------------------------
1 | import should from 'should' // eslint-disable-line no-unused-vars
2 | const webdriver = require('selenium-webdriver'),
3 | By = webdriver.By
4 |
5 | const firefox = require('selenium-webdriver/firefox')
6 |
7 |
8 | const driver = new webdriver.Builder()
9 | .forBrowser('firefox')
10 | .setFirefoxOptions(new firefox.Options().addArguments('-headless'))
11 | .build()
12 |
13 | describe('Integration test', function() {
14 | before(function(done) {
15 | const express = require('express')
16 | const app = express()
17 | app.use(express.static('dist'))
18 | app.listen(3000, function(err) {
19 | if (err) { return done(err) }
20 | done()
21 | })
22 | })
23 |
24 | beforeEach(function (done) {
25 | setTimeout(function(){
26 | done()
27 | }, 500)
28 | })
29 |
30 | it('', function(done) {
31 | (async function() {
32 | try {
33 | await driver.get('http://localhost:3000/manual_test.html')
34 | const svgs = await driver.findElements(By.css('svg'))
35 | svgs.length.should.equal(78)
36 | const title_text = await driver.getTitle()
37 | title_text.should.equal('Cereals')
38 | const h1_text = await driver.findElement(By.css('h1')).getText()
39 | h1_text.should.equal('Cereals')
40 | const original_elements = await driver.findElements(
41 | By.css('svg'))
42 | const original_element_count = original_elements.length
43 | await driver.findElement(By.css('select option:nth-child(2)')).click()
44 | const new_elements = await driver.findElements(
45 | By.css('svg'))
46 | const new_element_count = new_elements.length
47 | original_element_count.should.equal(new_element_count)
48 | } finally {
49 | await driver.quit()
50 | }
51 | })().then(done, done)
52 | })
53 |
54 | })
55 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import '@babel/polyfill'
2 | import './style.scss'
3 | import json_parser from './default_parser'
4 | import yaml_parser from './yaml-format/parser'
5 | import * as d3 from 'd3'
6 |
7 | export { json_parser, yaml_parser, d3 }
8 |
--------------------------------------------------------------------------------
/src/interpolation.js:
--------------------------------------------------------------------------------
1 | const format_string = (input, state) => {
2 | if (Object.entries(state).length === 0) return input
3 | const first_key = Object.keys(state)[0]
4 | const copy = Object.assign({}, state)
5 | delete copy[first_key]
6 | return format_string(input.replace('${' + first_key + '}', state[first_key]),
7 | copy)
8 | }
9 |
10 | const format_array = (input, state) => {
11 | return input.map(item => format_value(item, state))
12 | }
13 |
14 | const format_value = (input, state) => {
15 | if (input === null) return null
16 | if (input instanceof Array) return format_array(input, state)
17 | if (typeof input === 'string') return format_string(input, state)
18 | if (typeof input === 'object') return format_object(input, state)
19 | return input
20 | }
21 |
22 | const format_object = (input, state) => {
23 | return Object.assign({}, ...Object.entries(input).map(
24 | ([key, value]) => (
25 | {[format_string(key, state)]: format_value(value, state)})
26 | ))
27 | }
28 |
29 | export { format_string , format_array, format_value, format_object }
30 |
--------------------------------------------------------------------------------
/src/interpolation.test.js:
--------------------------------------------------------------------------------
1 | const module = require('./interpolation.js')
2 |
3 | const string_tests = [
4 | ['foo', 'foo', {}],
5 | ['Hello World!', 'Hello World!', {}],
6 | ['Hello ${name}', 'Hello World!', {'name': 'World!'}],
7 | ['Hello ${name}!', 'Hello Person!', {'name': 'Person'}],
8 | ['${greeting} ${name}!', 'Hola Person!',
9 | {'name': 'Person', 'greeting': 'Hola'}],
10 | ['${foo} ${bar}!', 'Hola Person!', {'bar': 'Person', 'foo': 'Hola'}],
11 | ]
12 |
13 | const array_tests = [
14 | [[], [], {}],
15 | [['Hello ${name}!'], ['Hello World!'], {'name': 'World'}],
16 | [['Hello ${foo}!'], ['Hello World!'], {'foo': 'World'}],
17 | [['Hello ${foo}!', 'Hello ${foo}!'], ['Hello World!', 'Hello World!'],
18 | {'foo': 'World'}],
19 | [['Hello ${foo}!', ['Hello ${foo}!']], ['Hello World!', ['Hello World!']],
20 | {'foo': 'World'}],
21 | [['Hello ${foo}!', [{'${foo}': 42}]], ['Hello World!', [{'World': 42}]],
22 | {'foo': 'World'}],
23 | ]
24 |
25 | const static_tests = [
26 | [42, 42, {}],
27 | [false, false, {}],
28 | [889593.234234, 889593.234234, {}],
29 | [[null], [null], {}],
30 | ]
31 |
32 | const object_tests = [
33 | [{}, {}, {}],
34 | [{'foo': '${bar}'}, {'foo': 'hello'}, {'bar': 'hello'}],
35 | [{'foo': '${bar}', 'baz': '${asd}'}, {'foo': 'hello', 'baz': '33'},
36 | {'bar': 'hello', 'asd': '33'}],
37 | [{'${bar}': '${bar}', 'baz': '${asd}'}, {'hello': 'hello', 'baz': '33'},
38 | {'bar': 'hello', 'asd': '33'}],
39 | [{'${bar}': ['${bar}'], 'baz': '${asd}'}, {'hello': ['hello'], 'baz': '33'},
40 | {'bar': 'hello', 'asd': '33'}],
41 | ]
42 |
43 | const tests = [
44 | ['String interpolation', 'format_string', [string_tests]],
45 | ['Array interpolation', 'format_array', [array_tests]],
46 | ['Value interpolation', 'format_value', [string_tests, array_tests,
47 | static_tests, object_tests]],
48 | ['Object interpolation', 'format_object', [object_tests]],
49 | ]
50 |
51 | const stringify = JSON.stringify
52 |
53 | tests.forEach(([test_suite_name, function_name, test_sets]) =>
54 | describe(test_suite_name, () => {
55 | test_sets.forEach(test_set =>
56 | test_set.forEach(([input, output, state]) =>
57 | it(
58 | `${[stringify(input), stringify(output), stringify(state)]}`,
59 | () => {
60 | module[function_name](input, state).should.deepEqual(output)
61 | })
62 | )
63 | )
64 | })
65 | )
66 |
--------------------------------------------------------------------------------
/src/jq-web.js:
--------------------------------------------------------------------------------
1 |
2 | const emuto = async (data, query) => {
3 | const emuto_ = await import(/* webpackChunkName: "emuto" */ 'emuto/lib/interpreter')
4 | console.log(emuto_) // eslint-disable-line
5 | return emuto_.default(query)(data)
6 | }
7 |
8 | export default emuto
9 |
--------------------------------------------------------------------------------
/src/loader/loader.js:
--------------------------------------------------------------------------------
1 | const loader = (require_) => (component) =>
2 | require_(`../components/${component}`)
3 |
4 | export default loader
5 |
--------------------------------------------------------------------------------
/src/loader/loader.test.js:
--------------------------------------------------------------------------------
1 | import should from 'should' // eslint-disable-line no-unused-vars
2 | import loader from './loader'
3 | import sinon from 'sinon'
4 |
5 |
6 | describe('Loader', function() {
7 | it('calls require with correct argument', () => {
8 | const fake_require = sinon.spy()
9 | loader(fake_require)('root')
10 | fake_require.should.be.calledWith('../components/root')
11 | })
12 |
13 | it('calls require with correct argument 2', () => {
14 | const fake_require = sinon.spy()
15 | loader(fake_require)('MyFancyComponent2')
16 | fake_require.should.be.calledWith('../components/MyFancyComponent2')
17 | })
18 |
19 | it('component returned', () => {
20 | const fake_component = sinon.spy()
21 | const fake_require = () => fake_component
22 | const component = loader(fake_require)('MyFancyComponent2')
23 | component.should.equal(fake_component)
24 | })
25 | })
26 |
--------------------------------------------------------------------------------
/src/manual_test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import {json_parser, yaml_parser} from './index.js'
3 | import * as d3 from 'd3'
4 |
5 | const render_dashboard = (data) => {
6 | const parserd_yaml = yaml_parser(data)
7 | const file_loader = (path, callback) => {
8 | callback(undefined, 'a,b\nCereals,0')
9 | }
10 | json_parser(parserd_yaml, file_loader)(d3.selection())
11 | }
12 |
13 | render_dashboard(`
14 | dashboard "Cereals":
15 | - h1 text:
16 | - attr:query: '$[0].a'
17 | - data: file:///foo/bar.csv
18 | - h2 text: "By calories"
19 | - dropdown my_var=~last:
20 | - {"value": "foo", "text": "Foo"}
21 | - {"value": "bar", "text": "Bar"}
22 | - 2 columns:
23 | - p text: "foo \${my_var} bar"
24 | - p text: "foo \${my_var} bar"
25 | - bar chart:
26 | - attr:query: 'map ($ => [$.name, $.calories * 1]) | sortBy ($ => $[1] * -1) | { "columns": $ }'
27 | - data: https://gist.githubusercontent.com/ZeningQu/6184eaf8faa533e320abc938c4738c3e/raw/40f237de825061faa8721c2293b79c46979780b4/cereals.csv
28 | - h2 text: "By nutritional profile"
29 | - 4 columns:
30 | - attr:query: 'map ($ => {"component": "rows", "data": [{"component": "text", "args": {"tagName": "h3"}, "data": .name}, {"component": "chart", "args": {"type": "pie"}, "data": {"columns": ["protein": .protein, "fat": .fat, "carbo": .carbo]}}]})'
31 | - data: https://gist.githubusercontent.com/ZeningQu/6184eaf8faa533e320abc938c4738c3e/raw/40f237de825061faa8721c2293b79c46979780b4/cereals.csv
32 | `)
33 |
--------------------------------------------------------------------------------
/src/parser/parser.js:
--------------------------------------------------------------------------------
1 | import * as validators from '../validators.js'
2 | import create_state_handler from '../state_handler.js'
3 |
4 | /** Creates a function that parses a JSON component and compiles it into a
5 | * Javascript component
6 | * @param {Function} component_loader - A function that can load components*/
7 | const parse = (component_loader) => (input, file_loader) => {
8 | if (!(typeof input === 'object')) throw new Error('An object is required')
9 | validators.required('component')(input)
10 | validators.regexp('component', /^[A-z]\w*$/)(input)
11 |
12 | const component = component_loader(input.component)
13 | const args = Object.assign({}, input.args)
14 | if (input.component === 'root') {
15 | args.state_handler = create_state_handler()
16 | args.file_loader = file_loader
17 | }
18 | const bind = component(args)
19 |
20 | return (selection) => {
21 | const update = bind(selection)
22 | update(input.data)
23 |
24 | return update
25 | }
26 | }
27 |
28 | export default parse
29 |
--------------------------------------------------------------------------------
/src/screenshot.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | const express = require('express')
3 | const app = express()
4 | const webshot = require('webshot')
5 |
6 | console.log('Starting server')
7 | app.use(express.static('dist'))
8 | app.listen(3000)
9 | console.log('Server started')
10 |
11 | const options = {
12 | screenSize: {
13 | width: 640,
14 | height: 500
15 | },
16 | captureSelector: 'body>*',
17 | defaultWhiteBackground: true,
18 | renderDelay: 2000,
19 | errorIfJSException: true
20 | }
21 |
22 | console.log('Creating screenshot')
23 | const url ='http://localhost:3000/screenshot.html'
24 | webshot(url, process.argv[2], options, (err) => {
25 | if (err) {
26 | console.log(err)
27 | } else {
28 | console.log('Screenshot created')
29 | }
30 | process.exit()
31 | })
32 |
--------------------------------------------------------------------------------
/src/state_handler.js:
--------------------------------------------------------------------------------
1 | import isEqual from 'fast-deep-equal'
2 |
3 | const create_state_handler = () => {
4 | const state = {}
5 | let subscriptions = []
6 |
7 | const update_state = (variable, value) => {
8 | // Replace old state with new state, report if value has actually changed
9 | const old_state = Object.assign({}, state)
10 | state[variable] = value
11 | if (isEqual(old_state, state)) return false
12 | return true
13 | }
14 |
15 | const handle_change = (variable, value) => {
16 | // Return early if state didn't actually change
17 | if (!update_state(variable, value)) return
18 | const old_subscriptions = subscriptions.slice()
19 | subscriptions = []
20 | old_subscriptions.map(callback => callback(state_handler, callback))
21 | }
22 |
23 | const state_handler = {
24 | 'get_state': () => state,
25 | 'reset': () => {
26 | subscriptions = []
27 | },
28 | 'init_variable': (variable, value) => {
29 | if (state[variable] === undefined) {
30 | handle_change(variable, value)
31 | }
32 | },
33 | 'set_variable': (variable, value) => {
34 | handle_change(variable, value)
35 | },
36 | 'subscribe': (callback) => {
37 | subscriptions.push(callback)
38 | }
39 | }
40 |
41 | return state_handler
42 | }
43 |
44 | export default create_state_handler
45 |
--------------------------------------------------------------------------------
/src/state_handler.test.js:
--------------------------------------------------------------------------------
1 | import create_state_handler from './state_handler.js'
2 | import sinon from 'sinon'
3 |
4 |
5 | describe('state handler', () => {
6 | it('has a get_state method', () => {
7 | create_state_handler().should.have.property('get_state')
8 | })
9 |
10 | it('has a set_variable method', () => {
11 | create_state_handler().should.have.property('set_variable')
12 | })
13 |
14 | it('has an init_variable method', () => {
15 | create_state_handler().should.have.property('init_variable')
16 | })
17 |
18 | it('has empty initial state', () => {
19 | create_state_handler().get_state().should.deepEqual({})
20 | })
21 |
22 | it('init_variable should update state', () => {
23 | const state_handler = create_state_handler()
24 | state_handler.init_variable('foo', 42)
25 | state_handler.get_state().should.deepEqual({
26 | 'foo': 42
27 | })
28 | })
29 |
30 | it('init_variable should update state 2', () => {
31 | const state_handler = create_state_handler()
32 | state_handler.init_variable('bar', false)
33 | state_handler.init_variable('pi', 3.14)
34 | state_handler.get_state().should.deepEqual({
35 | 'bar': false,
36 | 'pi': 3.14
37 | })
38 | })
39 |
40 | it('init_variable does not update state if variable is defined', () => {
41 | const state_handler = create_state_handler()
42 | state_handler.init_variable('bar', false)
43 | state_handler.init_variable('pi', 3.14)
44 | state_handler.init_variable('pi', -3.14)
45 | state_handler.get_state().should.deepEqual({
46 | 'bar': false,
47 | 'pi': 3.14
48 | })
49 | })
50 |
51 | it('set_variable should update state', () => {
52 | const state_handler = create_state_handler()
53 | state_handler.set_variable('foo', 42)
54 | state_handler.get_state().should.deepEqual({
55 | 'foo': 42
56 | })
57 | })
58 |
59 | it('set_variable should update state 2', () => {
60 | const state_handler = create_state_handler()
61 | state_handler.set_variable('bar', false)
62 | state_handler.set_variable('pi', 3.14)
63 | state_handler.get_state().should.deepEqual({
64 | 'bar': false,
65 | 'pi': 3.14
66 | })
67 | })
68 |
69 | it('has a subscribe method', () => {
70 | create_state_handler().subscribe.should.be.a.Function()
71 | })
72 |
73 | it('has a reset method', () => {
74 | create_state_handler().reset.should.be.a.Function()
75 | })
76 |
77 | it('reset() undoes subscriptions', () => {
78 | const my_callback = sinon.spy()
79 | const state_handler = create_state_handler()
80 | state_handler.subscribe(my_callback)
81 | state_handler.reset()
82 | state_handler.set_variable('foo', 42)
83 | my_callback.should.not.be.called()
84 | })
85 |
86 |
87 | const methods_that_update_state = ['init_variable', 'set_variable']
88 |
89 | methods_that_update_state.forEach(method => {
90 | it(`callback should be called when variables change - ${method}`, () => {
91 | const my_callback = sinon.spy()
92 | const state_handler = create_state_handler()
93 | state_handler.subscribe(my_callback)
94 | state_handler[method]('foo', 42)
95 | my_callback.should.be.calledWith(state_handler, my_callback)
96 | })
97 |
98 | it(`callback not called when no actual change happens - ${method}`, () => {
99 | const my_callback = sinon.spy()
100 | const injector = require('inject-loader!./state_handler.js')
101 | const state_handler = injector({
102 | 'fast-deep-equal': () => true
103 | }).default()
104 | state_handler.subscribe(my_callback)
105 | state_handler[method]('foo', 42)
106 | my_callback.should.not.be.called()
107 | })
108 |
109 | it(`callback should retrieve collect state - ${method}`, () => {
110 | let retrieved_state = null
111 | const my_callback = (handler) => {
112 | retrieved_state = Object.assign({}, handler.get_state())}
113 | const state_handler = create_state_handler()
114 | state_handler.subscribe(my_callback)
115 | state_handler[method]('foo', 42)
116 | retrieved_state.should.deepEqual({'foo': 42})
117 | })
118 | })
119 |
120 | it('callback should not be called again without re-subscribe', () => {
121 | const my_callback = sinon.spy()
122 | const state_handler = create_state_handler()
123 | state_handler.subscribe(my_callback)
124 | state_handler.set_variable('foo', 42)
125 | state_handler.set_variable('foo', 42)
126 | my_callback.should.be.calledOnce()
127 | })
128 |
129 | it('no state change if init_variable called 2x on the same variable', () => {
130 | const my_callback = sinon.spy(
131 | function() {state_handler.subscribe(my_callback)})
132 | const state_handler = create_state_handler()
133 | state_handler.subscribe(my_callback)
134 | state_handler.init_variable('foo', 42)
135 | state_handler.init_variable('foo', 42)
136 | my_callback.should.be.calledOnce()
137 | })
138 |
139 | it('callback should be called again with re-subscribe', () => {
140 | const my_callback = sinon.spy(
141 | function() {state_handler.subscribe(my_callback)})
142 | const injector = require('inject-loader!./state_handler.js')
143 | const state_handler = injector({
144 | 'fast-deep-equal': () => false
145 | }).default()
146 | state_handler.subscribe(my_callback)
147 | state_handler.set_variable('foo', 42)
148 | state_handler.set_variable('foo', 42)
149 | my_callback.should.be.calledTwice()
150 | })
151 |
152 | })
153 |
--------------------------------------------------------------------------------
/src/style.scss:
--------------------------------------------------------------------------------
1 | @import "~sassline/assets/sass/sassline-base/reset";
2 | @import "~sassline/assets/sass/sassline-base/variables";
3 | @import "~sassline/assets/sass/sassline-base/modular-scale";
4 | @import "~sassline/assets/sass/sassline-base/mixins";
5 | @import "~billboard.js/dist/billboard.css";
6 | @import '~spinkit/scss/spinners/5-pulse';
7 |
8 |
9 | $bodytype: (
10 | font-family: 'Libre Baskerville, serif',
11 | regular: 400,
12 | bold: 700,
13 | italic: italic,
14 | cap-height: 0.77
15 | );
16 |
17 | $headingtype: (
18 | font-family: 'Merriweather, serif',
19 | regular: 400,
20 | bold: 700,
21 | cap-height: 0.72
22 | );
23 |
24 | body {
25 | padding: map-get($gutterwidths, medium);
26 | }
27 |
28 | @import "~sassline/assets/sass/sassline-base/typography";
29 | @import "~sassline/assets/sass/sassline-base/layouts";
30 | @import "~sassline/assets/sass/modules/_show-grid.scss";
31 |
--------------------------------------------------------------------------------
/src/test_parser.js:
--------------------------------------------------------------------------------
1 | import parse from './parser/parser.js'
2 | import RootComponent from './components/root/Root.js'
3 | import TextComponent from './components/text/Text.js'
4 |
5 | const test_parser = parse((component) => ({
6 | 'root': RootComponent,
7 | 'text': TextComponent,
8 | }[component]))
9 |
10 | export default test_parser
11 |
--------------------------------------------------------------------------------
/src/validators.js:
--------------------------------------------------------------------------------
1 | export const required = (arg_name) => (args) => {
2 | if (!args.hasOwnProperty(arg_name))
3 | throw new Error(`Argument '${arg_name}' is required but not supplied.`)
4 | }
5 |
6 | export const regexp = (arg_name, expression) => (args) =>{
7 | if (!args[arg_name].match(expression))
8 | throw new Error(`Argument '${arg_name}' is invalid`)
9 | }
10 |
--------------------------------------------------------------------------------
/src/validators.test.js:
--------------------------------------------------------------------------------
1 | import { required } from './validators'
2 | import { regexp } from './validators'
3 |
4 | describe('require validator', function() {
5 | it('should throw message when argument is not supplied', () => {
6 | (() => {required('title')({})})
7 | .should.throw('Argument \'title\' is required but not supplied.')
8 | })
9 |
10 | it('should not throw when argument is supplied', () => {
11 | (() => {required('title')({'title': 'asdf'})}).should.not.throw()
12 | })
13 |
14 | it('should throw message when argument is not supplied', () => {
15 | (() => {required('foo')({'title': ''})})
16 | .should.throw('Argument \'foo\' is required but not supplied.')
17 | })
18 | })
19 |
20 | describe('regexp validator', function() {
21 | it('should throw error when argument doesn\'t match regexp', () => {
22 | (() => {regexp('title', /bar/)({'title': 'foo'})})
23 | .should.throw('Argument \'title\' is invalid')
24 | })
25 |
26 | it('doesn\'t throw when format is valid', () => {
27 | (() => {regexp('title', /bar/)({'title': 'bar'})}).should.not.throw()
28 | })
29 |
30 | it('correct error message is shown', () => {
31 | (() => {regexp('class', /bar/)({'class': 'foo'})})
32 | .should.throw('Argument \'class\' is invalid')
33 | })
34 |
35 | it('correct regexp is used', () => {
36 | (() => {regexp('title', /[0-9]+/)({'title': '145'})}).should.not.throw()
37 | })
38 |
39 | })
40 |
--------------------------------------------------------------------------------
/src/yaml-format/parser.js:
--------------------------------------------------------------------------------
1 | import YAML from 'yamljs'
2 |
3 | const rules = [
4 | [[/dashboard ["]([^"]*)["]/, /dashboard [']([^']*)[']/], (match, value) => ({
5 | 'component': 'root',
6 | 'args': {'title': match[1]},
7 | 'data': value.map(parser)
8 | })],
9 | [[/(.*) text/], (match, value) => ({
10 | 'component': 'text',
11 | 'args': {'tagName': match[1]},
12 | 'data': value
13 | })],
14 | [[/rows/], (match, value) => ({
15 | 'component': 'rows',
16 | 'data': value.map(parser)
17 | })],
18 | [[/board/], (match, value) => ({
19 | 'component': 'board',
20 | 'data': value.map(parser)
21 | })],
22 | [[/([1-9]+) columns/], (match, value) => ({
23 | 'component': 'columns',
24 | 'args': {'columns': match[1] * 1},
25 | 'data': value.map(parser)
26 | })],
27 | [[/columns/], (match, value) => ({
28 | 'component': 'columns',
29 | 'data': value.map(parser)
30 | })],
31 | [[/dropdown ([^=]+)=(.*)/], (match, value) => ({
32 | 'component': 'dropdown',
33 | 'args': {'variable': match[1], 'default': match[2]},
34 | 'data': value
35 | })],
36 | [[/(horizontal|rotated)? *(stacked)? *([a-z]+|\${[A-z_0-9]+}) (chart|plot|diagram|graph)/], // eslint-disable-line
37 | (match, value) => ({
38 | 'component': 'chart',
39 | 'args': Object.assign({},
40 | {
41 | 'type': match[3],
42 | 'stacked': match[2] === 'stacked',
43 |
44 | },
45 | match[1] === 'horizontal' || match[1] === 'rotated' ?
46 | {
47 | 'axis': {
48 | 'rotated': true
49 | }
50 | }: {}
51 | ),
52 | 'data': value
53 | })],
54 | ]
55 |
56 | const handle_urls = (component) => {
57 | if (typeof component.data === 'string' && component.data.match(/https?:/))
58 | return {
59 | 'component': component.component,
60 | 'args': Object.assign({
61 | 'loader': component.data.match(/https?.*\.(.*)$/)[1]
62 | }, component.args),
63 | 'data': component.data
64 | }
65 |
66 | return component
67 | }
68 |
69 | const handle_files = (component) => {
70 | if (typeof component.data === 'string' && component.data.match(/file:/))
71 | return {
72 | 'component': component.component,
73 | 'args': Object.assign({
74 | 'loader': component.data.match(/file.*\.(.*)$/)[1],
75 | 'is_file': true
76 | }, component.args),
77 | 'data': component.data
78 | }
79 |
80 | return component
81 | }
82 |
83 | const handle_attr_syntax = (component) => {
84 | if (component.data.map === undefined) return component
85 | const attrs = component.data.filter((x) => Object.keys(x)[0].match(/attr:.*/))
86 | if (attrs.length === 0) return component
87 |
88 | const mapped_attrs = attrs
89 | .map((x) => [Object.keys(x)[0], Object.values(x)[0]])
90 | let parsed_args = {}
91 | mapped_attrs
92 | .map((value) => parsed_args[value[0].match(/attr:(.*)/)[1]] = value[1])
93 |
94 | return {
95 | 'component': component.component,
96 | 'args': Object.assign(parsed_args, component.args ? component.args : {}),
97 | 'data': component.data.filter((x) => Object.keys(x)[0] === 'data')[0].data
98 | }
99 | }
100 |
101 |
102 | export const error_message = message => ({
103 | 'component': 'root',
104 | 'args': {'title': message},
105 | 'data': [
106 | {
107 | 'component': 'text',
108 | 'args': {'tagName': 'p'},
109 | 'data': message
110 | }
111 | ]
112 | })
113 |
114 |
115 | /**
116 | * Compiles a YAML dashboard file into a JSON dashboard file
117 | * @param {string} input - YAML input
118 | * @returns {object}
119 | */
120 | const parser = (input) => {
121 | try {
122 | const yaml_contents = (typeof input === 'string')
123 | ? YAML.parse(input) : input
124 | if (yaml_contents === undefined)
125 | return error_message('A non-empty input file is required')
126 |
127 | const key = Object.keys(yaml_contents)[0]
128 | const value = Object.values(yaml_contents)[0]
129 |
130 | for (const rule of rules) {
131 | const [ patterns, func ] = rule
132 | for (const pattern of patterns) {
133 | if (key.match(pattern)) return handle_files(handle_urls(
134 | handle_attr_syntax(func(key.match(pattern), value))))
135 | }
136 | }
137 |
138 | return yaml_contents
139 | } catch (error) {
140 | return error_message(error.toString())
141 | }
142 | }
143 |
144 | export default parser
145 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const env = process.env.WEBPACK_ENV;
4 | const libraryName = 'dashboard';
5 |
6 | module.exports = {
7 | devServer: {
8 | contentBase: path.join(__dirname, "dist"),
9 | compress: true,
10 | port: 9000
11 | },
12 | entry: ["@babel/polyfill", './src/index.js'],
13 | target: 'web',
14 | mode: 'production',
15 | node: {
16 | fs: 'empty'
17 | },
18 | output: {
19 | filename: 'dashboard.js',
20 | chunkFilename: '[name].bundle.js',
21 | library: 'dashboard',
22 | libraryTarget: 'umd',
23 | umdNamedDefine: true,
24 | path: path.resolve(__dirname, 'dist')
25 | },
26 | module: {
27 | rules: [
28 | {
29 | test: /\.s?css$/,
30 | use: [{
31 | loader: "style-loader" // creates style nodes from JS strings
32 | }, {
33 | loader: "css-loader" // translates CSS into CommonJS
34 | }, {
35 | loader: "sass-loader" // compiles Sass to CSS
36 | }, {
37 | loader: "postcss-loader"
38 | }]
39 | },
40 | {
41 | enforce: "pre",
42 | test: /\.js$/,
43 | exclude: /node_modules/,
44 | loader: "eslint-loader",
45 | },
46 | {
47 | test: /\.js$/,
48 | exclude: /(node_modules|bower_components)/,
49 | use: {
50 | loader: 'babel-loader',
51 | options: {
52 | presets: ['@babel/preset-env']
53 | }
54 | }
55 | }
56 | ]
57 | }
58 | };
59 |
--------------------------------------------------------------------------------
/webpack.screenshot.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const nodeExternals = require('webpack-node-externals');
3 |
4 | module.exports = {
5 | entry: ["@babel/polyfill", './src/index.js'],
6 | target: 'web',
7 | devtool: 'source-map',
8 | mode: 'development',
9 | node: {
10 | fs: 'empty'
11 | },
12 | output: {
13 | filename: 'dashboard.js',
14 | chunkFilename: '[name].bundle.js',
15 | library: 'dashboard',
16 | libraryTarget: 'umd',
17 | umdNamedDefine: true,
18 | path: path.resolve(__dirname, 'dist')
19 | },
20 | devServer: {
21 | contentBase: './dist'
22 | },
23 | module: {
24 | rules: [
25 | {
26 | test: /\.s?css$/,
27 | use: [{
28 | loader: "style-loader" // creates style nodes from JS strings
29 | }, {
30 | loader: "css-loader" // translates CSS into CommonJS
31 | }, {
32 | loader: "sass-loader" // compiles Sass to CSS
33 | }, {
34 | loader: 'postcss-loader'
35 | }]
36 | },
37 | {
38 | test: /\.js$/,
39 | exclude: /(node_modules|bower_components)/,
40 | use: {
41 | loader: 'babel-loader',
42 | options: {
43 | presets: ['@babel/preset-env']
44 | }
45 | }
46 | }
47 | ]
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/webpack.test.config.js:
--------------------------------------------------------------------------------
1 |
2 | const path = require('path');
3 | const nodeExternals = require('webpack-node-externals');
4 |
5 | module.exports = {
6 | entry: ["@babel/polyfill", './src/index.js'],
7 | target: 'node',
8 | mode: 'development',
9 | externals: [nodeExternals()],
10 | output: {
11 | filename: 'bundle.js',
12 | path: path.resolve(__dirname, 'dist')
13 | },
14 | devServer: {
15 | contentBase: './dist'
16 | },
17 | module: {
18 | rules: [
19 | {
20 | test: /\.scss$/,
21 | use: [{
22 | loader: "fake-style-loader" // creates style nodes from JS strings
23 | }, {
24 | loader: "css-loader" // translates CSS into CommonJS
25 | }, {
26 | loader: "sass-loader" // compiles Sass to CSS
27 | }]
28 | },
29 | {
30 | test: /\.js$/,
31 | exclude: /(node_modules|bower_components)/,
32 | use: {
33 | loader: 'babel-loader',
34 | options: {
35 | presets: ['@babel/preset-env'],
36 | plugins: ["@babel/plugin-transform-modules-commonjs"]
37 | }
38 | }
39 | }
40 | ]
41 | }
42 | };
43 |
44 |
--------------------------------------------------------------------------------