├── .babelrc
├── .editorconfig
├── .eslintrc
├── .gitignore
├── .mjmlconfig
├── .prettierrc
├── .travis.yml
├── LICENSE
├── README.md
├── components
└── MjQrCode.js
├── examples
└── index.mjml
├── gulpfile.babel.js
├── lib
└── MjQrCode.js
├── package.json
├── test
└── mjml-qr-code.test.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"],
3 | "plugins": ["@babel/plugin-proposal-class-properties"]
4 | }
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | # A special property that should be specified at the top of the file outside of
4 | # any sections. Set to true to stop .editor config file search on current file
5 | root = true
6 |
7 | # Indentation style
8 | # Possible values - tab, space
9 | indent_style = space
10 |
11 | # Indentation size in single-spaced characters
12 | # Possible values - an integer, tab
13 | indent_size = 2
14 |
15 | # Line ending file format
16 | # Possible values - lf, crlf, cr
17 | end_of_line = lf
18 |
19 | # File character encoding
20 | # Possible values - latin1, utf-8, utf-16be, utf-16le
21 | charset = utf-8
22 |
23 | # Denotes whether to trim whitespace at the end of lines
24 | # Possible values - true, false
25 | trim_trailing_whitespace = true
26 |
27 | # Denotes whether file should end with a newline
28 | # Possible values - true, false
29 | insert_final_newline = true
30 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint-config-airbnb-base",
4 | "eslint-config-airbnb-base/rules/strict"
5 | ],
6 | "parser": "@babel/eslint-parser",
7 | "rules": {
8 |
9 | "padded-blocks": 0,
10 | "space-before-function-paren": 0,
11 | "semi": 0,
12 |
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | examples/**/*.html
2 | venv
3 |
4 | ### Vim ###
5 | [._]*.s[a-w][a-z]
6 | [._]s[a-w][a-z]
7 | *.un~
8 | Session.vim
9 | .netrwhist
10 | *~
11 |
12 |
13 | ### OSX ###
14 | .DS_Store
15 | .AppleDouble
16 | .LSOverride
17 |
18 | # Icon must end with two \r
19 | Icon
20 |
21 |
22 | # Thumbnails
23 | ._*
24 |
25 | # Files that might appear in the root of a volume
26 | .DocumentRevisions-V100
27 | .fseventsd
28 | .Spotlight-V100
29 | .TemporaryItems
30 | .Trashes
31 | .VolumeIcon.icns
32 |
33 | # Directories potentially created on remote AFP share
34 | .AppleDB
35 | .AppleDesktop
36 | Network Trash Folder
37 | Temporary Items
38 | .apdisk
39 |
40 |
41 | ### Python ###
42 | # Byte-compiled / optimized / DLL files
43 | __pycache__/
44 | *.py[cod]
45 | *$py.class
46 |
47 | # C extensions
48 | *.so
49 |
50 | # Distribution / packaging
51 | .Python
52 | env/
53 | build/
54 | develop-eggs/
55 | dist/
56 | downloads/
57 | eggs/
58 | .eggs/
59 | parts/
60 | sdist/
61 | var/
62 | *.egg-info/
63 | .installed.cfg
64 | *.egg
65 |
66 | # PyInstaller
67 | # Usually these files are written by a python script from a template
68 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
69 | *.manifest
70 | *.spec
71 |
72 | # Installer logs
73 | pip-log.txt
74 | pip-delete-this-directory.txt
75 |
76 | # Unit test / coverage reports
77 | htmlcov/
78 | .tox/
79 | .coverage
80 | .coverage.*
81 | .cache
82 | nosetests.xml
83 | coverage.xml
84 | *,cover
85 |
86 | # Translations
87 | *.mo
88 | *.pot
89 |
90 | # Django stuff:
91 | *.log
92 |
93 | # Sphinx documentation
94 | docs/_build/
95 |
96 | # PyBuilder
97 | target/
98 |
99 |
100 | ### Node ###
101 | # Logs
102 | logs
103 | *.log
104 | npm-debug.log*
105 |
106 | # Runtime data
107 | pids
108 | *.pid
109 | *.seed
110 |
111 | # Directory for instrumented libs generated by jscoverage/JSCover
112 | lib-cov
113 |
114 | # Coverage directory used by tools like istanbul
115 | coverage
116 |
117 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
118 | .grunt
119 |
120 | # node-waf configuration
121 | .lock-wscript
122 |
123 | # Compiled binary addons (http://nodejs.org/api/addons.html)
124 | build/Release
125 |
126 | # Dependency directory
127 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
128 | node_modules
129 |
130 |
--------------------------------------------------------------------------------
/.mjmlconfig:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | "./lib/MjQrCode.js"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 100,
3 | "semi": false,
4 | "singleQuote": true,
5 | "trailingComma": "all"
6 | }
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 16
4 | cache: yarn
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 QuickChart
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mjml-qr-code
2 | [](https://www.npmjs.com/package/mjml-qr-code)
3 | [](https://travis-ci.com/typpo/mjml-qr-code)
4 |
5 | A component for adding QR codes to your email using an open-source [QuickChart](https://quickchart.io) provider.
6 |
7 | ## Usage
8 |
9 | This mjml...
10 |
11 | ```html
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | ```
22 |
23 | Will show this QR code:
24 |
25 | 
26 |
27 | Customize the color, size, positioning, and other QR parameters using the attributes below.
28 |
29 | ## Setup
30 |
31 | Install via npm:
32 |
33 | ```
34 | npm install mjml-qr-code --save
35 | ```
36 |
37 | Then add the package to your `.mjmlconfig`:
38 |
39 | ```
40 | {
41 | "packages": [
42 | "mjml-qr-code/lib/MjQrCode.js"
43 | ]
44 | }
45 | ```
46 |
47 | ## Attributes
48 |
49 | The `` tag supports all the attributes of the `` tag. View those attributes [here](https://mjml.io/documentation/#mjml-image).
50 |
51 | In addition to regular image attributes which you can using for sizing and positioning, the component supports the following QR-specific attributes:
52 |
53 | | Name | Description | Required? | Default | |
54 | |------------------------|-----------------------------------------------------------|-----------|----------------|---|
55 | | value | The content encoded in your QR code | Yes | | |
56 | | color | The color of the QR code | No | 000000 (black) | |
57 | | background-color | The background of the QR code | No | ffffff (white) | |
58 | | qr-margin | The number of QR blocks to leave empty around the QR code | No | 4 | |
59 | | error-correction-level | The QR [error correction level](https://en.wikipedia.org/wiki/QR_code#Error_correction) | No | M | |
60 | | width | Width of the QR code image | No | 200 | |
61 | | host | The host of the QR image server | No | quickchart.io | |
62 | | protocol | The protocol of the QR image server | No | https | |
63 |
64 | ## Hosting
65 |
66 | By default, this component uses the public [QuickChart](https://quickchart.io) web service to render QR codes, but you can use the `host` attribute to point to your own QR renderer.
67 |
--------------------------------------------------------------------------------
/components/MjQrCode.js:
--------------------------------------------------------------------------------
1 | import { registerDependencies } from 'mjml-validator'
2 | import { BodyComponent } from 'mjml-core'
3 |
4 | registerDependencies({
5 | 'mj-image-text': [],
6 | 'mj-body': ['mj-qr-code'],
7 | 'mj-section': ['mj-qr-code'],
8 | 'mj-wrapper': ['mj-qr-code'],
9 | 'mj-column': ['mj-qr-code'],
10 | })
11 |
12 | export default class MjQrCode extends BodyComponent {
13 | static endingTag = true
14 |
15 | static allowedAttributes = {
16 | // QR-related attributes
17 | value: 'string',
18 | color: 'color',
19 | 'background-color': 'color',
20 | 'qr-margin': 'integer',
21 | 'error-correction-level': 'string',
22 | width: 'unit(px)',
23 | host: 'string',
24 | protocol: 'string',
25 |
26 | // Image attributes
27 | alt: 'string',
28 | href: 'string',
29 | name: 'string',
30 | src: 'string',
31 | srcset: 'string',
32 | title: 'string',
33 | rel: 'string',
34 | align: 'enum(left,center,right)',
35 | border: 'string',
36 | 'border-bottom': 'string',
37 | 'border-left': 'string',
38 | 'border-right': 'string',
39 | 'border-top': 'string',
40 | 'border-radius': 'unit(px,%){1,4}',
41 | 'container-background-color': 'color',
42 | 'fluid-on-mobile': 'boolean',
43 | padding: 'unit(px,%){1,4}',
44 | 'padding-bottom': 'unit(px,%)',
45 | 'padding-left': 'unit(px,%)',
46 | 'padding-right': 'unit(px,%)',
47 | 'padding-top': 'unit(px,%)',
48 | target: 'string',
49 | height: 'unit(px,auto)',
50 | 'max-height': 'unit(px,%)',
51 | 'font-size': 'unit(px)',
52 | usemap: 'string',
53 | }
54 |
55 | static defaultAttributes = {
56 | // QR-related attributes
57 | value: null,
58 | color: '#000000',
59 | 'background-color': '#ffffff',
60 | 'qr-margin': 4,
61 | 'error-correction-level': 'M',
62 | width: 200,
63 | host: 'quickchart.io',
64 | protocol: 'https',
65 | }
66 |
67 | getUrl() {
68 | const val = this.getAttribute('value')
69 | if (!val) {
70 | throw new Error('You must specify a "value" attribute for mjml-qr-code')
71 | }
72 | const content = encodeURIComponent(val)
73 | const width = this.getAttribute('width')
74 | const foregroundColor = encodeURIComponent(this.getAttribute('color').replace('#', ''))
75 | const backgroundColor = encodeURIComponent(
76 | this.getAttribute('background-color').replace('#', ''),
77 | )
78 | const ecLevel = this.getAttribute('error-correction-level')
79 | const margin = this.getAttribute('qr-margin')
80 | return `${this.getAttribute('protocol')}://${this.getAttribute(
81 | 'host',
82 | )}/qr?text=${content}&size=${width}&dark=${foregroundColor}&light=${backgroundColor}&ecLevel=${ecLevel}&margin=${margin}&ref=mjml`
83 | }
84 |
85 | renderImage() {
86 | const attributes = {}
87 | Object.keys(MjQrCode.allowedAttributes).forEach((key) => {
88 | attributes[key] = this.getAttribute(key)
89 | })
90 | attributes.src = this.getUrl()
91 | return `
92 |
95 |
96 | `
97 | }
98 |
99 | render() {
100 | return this.renderMJML(this.renderImage())
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/examples/index.mjml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Use the QR code below as your event ticket.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/gulpfile.babel.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp'
2 | import babel from 'gulp-babel'
3 | import watch from 'gulp-watch'
4 | import log from 'fancy-log'
5 | import fs from 'fs'
6 | import path from 'path'
7 | import mjml2html from 'mjml'
8 | import { registerComponent } from 'mjml-core'
9 |
10 | const walkSync = (dir, filelist = []) => {
11 | fs.readdirSync(dir).forEach(file => {
12 | filelist = fs.statSync(path.join(dir, file)).isDirectory()
13 | ? walkSync(path.join(dir, file), filelist)
14 | : filelist.concat(path.join(dir, file))
15 | })
16 | return filelist
17 | }
18 |
19 | const watchedComponents = walkSync('./components')
20 |
21 | const compile = () => {
22 | return gulp
23 | .src(path.normalize('components/**/*.js'))
24 | .pipe(babel())
25 | .on('error', log)
26 | .pipe(gulp.dest('lib'))
27 | .on('end', () => {
28 | watchedComponents.forEach(compPath => {
29 | if (compPath.endsWith('.swp')) {
30 | return;
31 | }
32 | const fullPath = path.join(process.cwd(), compPath.replace(/^components/, 'lib'))
33 | delete require.cache[fullPath]
34 | registerComponent(require(fullPath).default)
35 | })
36 |
37 | fs.readFile(path.normalize('./examples/index.mjml'), 'utf8', (err, data) => {
38 | if (err) throw err
39 | const result = mjml2html(data)
40 | fs.writeFileSync(path.normalize('./examples/index.html'), result.html)
41 | })
42 | })
43 | }
44 |
45 | gulp.task('build', compile)
46 |
47 | gulp.task('watch', () => {
48 | compile()
49 | return watch([path.normalize('components/**/*.js'), path.normalize('index.mjml')], compile)
50 | })
51 |
--------------------------------------------------------------------------------
/lib/MjQrCode.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
4 |
5 | Object.defineProperty(exports, "__esModule", {
6 | value: true
7 | });
8 | exports["default"] = void 0;
9 |
10 | var _mjmlValidator = require("mjml-validator");
11 |
12 | var _mjmlCore = require("mjml-core");
13 |
14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
15 |
16 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
17 |
18 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
19 |
20 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
21 |
22 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
23 |
24 | function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
25 |
26 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
27 |
28 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
29 |
30 | function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
31 |
32 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
33 |
34 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
35 |
36 | (0, _mjmlValidator.registerDependencies)({
37 | 'mj-image-text': [],
38 | 'mj-body': ['mj-qr-code'],
39 | 'mj-section': ['mj-qr-code'],
40 | 'mj-wrapper': ['mj-qr-code'],
41 | 'mj-column': ['mj-qr-code']
42 | });
43 |
44 | var MjQrCode = /*#__PURE__*/function (_BodyComponent) {
45 | _inherits(MjQrCode, _BodyComponent);
46 |
47 | var _super = _createSuper(MjQrCode);
48 |
49 | function MjQrCode() {
50 | _classCallCheck(this, MjQrCode);
51 |
52 | return _super.apply(this, arguments);
53 | }
54 |
55 | _createClass(MjQrCode, [{
56 | key: "getUrl",
57 | value: function getUrl() {
58 | var val = this.getAttribute('value');
59 |
60 | if (!val) {
61 | throw new Error('You must specify a "value" attribute for mjml-qr-code');
62 | }
63 |
64 | var content = encodeURIComponent(val);
65 | var width = this.getAttribute('width');
66 | var foregroundColor = encodeURIComponent(this.getAttribute('color').replace('#', ''));
67 | var backgroundColor = encodeURIComponent(this.getAttribute('background-color').replace('#', ''));
68 | var ecLevel = this.getAttribute('error-correction-level');
69 | var margin = this.getAttribute('qr-margin');
70 | return "".concat(this.getAttribute('protocol'), "://").concat(this.getAttribute('host'), "/qr?text=").concat(content, "&size=").concat(width, "&dark=").concat(foregroundColor, "&light=").concat(backgroundColor, "&ecLevel=").concat(ecLevel, "&margin=").concat(margin, "&ref=mjml");
71 | }
72 | }, {
73 | key: "renderImage",
74 | value: function renderImage() {
75 | var _this = this;
76 |
77 | var attributes = {};
78 | Object.keys(MjQrCode.allowedAttributes).forEach(function (key) {
79 | attributes[key] = _this.getAttribute(key);
80 | });
81 | attributes.src = this.getUrl();
82 | return "\n \n \n ");
83 | }
84 | }, {
85 | key: "render",
86 | value: function render() {
87 | return this.renderMJML(this.renderImage());
88 | }
89 | }]);
90 |
91 | return MjQrCode;
92 | }(_mjmlCore.BodyComponent);
93 |
94 | exports["default"] = MjQrCode;
95 |
96 | _defineProperty(MjQrCode, "endingTag", true);
97 |
98 | _defineProperty(MjQrCode, "allowedAttributes", {
99 | // QR-related attributes
100 | value: 'string',
101 | color: 'color',
102 | 'background-color': 'color',
103 | 'qr-margin': 'integer',
104 | 'error-correction-level': 'string',
105 | width: 'unit(px)',
106 | host: 'string',
107 | protocol: 'string',
108 | // Image attributes
109 | alt: 'string',
110 | href: 'string',
111 | name: 'string',
112 | src: 'string',
113 | srcset: 'string',
114 | title: 'string',
115 | rel: 'string',
116 | align: 'enum(left,center,right)',
117 | border: 'string',
118 | 'border-bottom': 'string',
119 | 'border-left': 'string',
120 | 'border-right': 'string',
121 | 'border-top': 'string',
122 | 'border-radius': 'unit(px,%){1,4}',
123 | 'container-background-color': 'color',
124 | 'fluid-on-mobile': 'boolean',
125 | padding: 'unit(px,%){1,4}',
126 | 'padding-bottom': 'unit(px,%)',
127 | 'padding-left': 'unit(px,%)',
128 | 'padding-right': 'unit(px,%)',
129 | 'padding-top': 'unit(px,%)',
130 | target: 'string',
131 | height: 'unit(px,auto)',
132 | 'max-height': 'unit(px,%)',
133 | 'font-size': 'unit(px)',
134 | usemap: 'string'
135 | });
136 |
137 | _defineProperty(MjQrCode, "defaultAttributes", {
138 | // QR-related attributes
139 | value: null,
140 | color: '#000000',
141 | 'background-color': '#ffffff',
142 | 'qr-margin': 4,
143 | 'error-correction-level': 'M',
144 | width: 200,
145 | host: 'quickchart.io',
146 | protocol: 'https'
147 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mjml-qr-code",
3 | "version": "2.0.1",
4 | "description": "Embed QR codes in your emails",
5 | "license": "MIT",
6 | "scripts": {
7 | "start": "gulp watch",
8 | "build": "gulp build",
9 | "lint": "eslint components",
10 | "format": "prettier --write --single-quote --trailing-comma all '{components,test}/**/*.js'",
11 | "test": "jest"
12 | },
13 | "author": "Ian Webster (quickchart.io)",
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/typpo/mjml-qr-code.git"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.18.5",
20 | "@babel/eslint-parser": "^7.18.2",
21 | "@babel/plugin-proposal-class-properties": "^7.17.12",
22 | "@babel/preset-env": "^7.18.2",
23 | "@babel/register": "^7.17.7",
24 | "eslint": "^8.18.0",
25 | "eslint-config-airbnb": "^19.0.4",
26 | "eslint-plugin-import": "^2.26.0",
27 | "eslint-plugin-jsx-a11y": "^6.5.1",
28 | "eslint-plugin-react": "^7.28.0",
29 | "eslint-plugin-react-hooks": "^4.6.0",
30 | "fancy-log": "^1.3.3",
31 | "gulp": "^4.0.2",
32 | "gulp-babel": "^8.0.0",
33 | "gulp-watch": "^5.0.1",
34 | "jest": "^26.0.1",
35 | "prettier": "^2.0.5"
36 | },
37 | "dependencies": {
38 | "mjml": "^4.13.0",
39 | "mjml-core": "^4.13.0",
40 | "mjml-validator": "^4.13.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/test/mjml-qr-code.test.js:
--------------------------------------------------------------------------------
1 | import mjml2html from 'mjml'
2 | import { registerComponent } from 'mjml-core'
3 |
4 | import MjQrCode from '../components/MjQrCode'
5 |
6 | function toHtml(mjml) {
7 | const conversion = mjml2html(mjml)
8 | const errors = conversion.errors
9 | if (errors.length > 0) {
10 | return errors
11 | }
12 | return conversion.html
13 | }
14 |
15 | describe('mjml-qr-code', () => {
16 | beforeAll(() => {
17 | registerComponent(MjQrCode)
18 | })
19 |
20 | it('should render the qr code', () => {
21 | expect(
22 | toHtml(`
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | `),
33 | ).toContain('
44 |
45 |
46 |
47 |
48 | `),
49 | ).toContain('&ecLevel=Q')
50 | })
51 |
52 | it('should support custom colors', () => {
53 | expect(
54 | toHtml(`
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | `),
65 | ).toContain('dark=ff0000&light=ffffff')
66 | })
67 |
68 | it('should support custom margin', () => {
69 | expect(
70 | toHtml(`
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | `),
81 | ).toContain('&margin=5')
82 | })
83 |
84 | it('should support custom host and protocol', () => {
85 | expect(
86 | toHtml(`
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | `),
97 | ).toContain('
108 |
109 |
110 |
111 |
112 | `)
113 | }).toThrow()
114 | })
115 | })
116 |
--------------------------------------------------------------------------------