From bd63658db50428bc2339d504a27fccba53eb3648 Mon Sep 17 00:00:00 2001 From: Matthew Beale Date: Tue, 22 Sep 2015 15:27:39 -0400 Subject: [PATCH] Drop toolbars, migrate to Ember based demo --- Brocfile.js | 4 - README.md | 2 - broccoli/demo.js | 46 -- demo/.bowerrc | 4 + demo/.editorconfig | 34 ++ demo/.ember-cli | 9 + demo/.gitignore | 17 + demo/.jshintrc | 32 ++ demo/.watchmanconfig | 3 + demo/README.md | 53 ++ demo/app/app.js | 18 + demo/app/components/.gitkeep | 0 demo/app/components/mobiledoc-dom-renderer.js | 21 + demo/app/controllers/.gitkeep | 0 demo/app/controllers/index.js | 21 + demo/app/helpers/.gitkeep | 0 demo/app/helpers/content-kit-cards-list.js | 8 + demo/app/helpers/format-object.js | 7 + demo/app/index.html | 26 + demo/app/mobiledoc-cards/index.js | 15 + demo/app/mobiledoc-cards/input.js | 49 ++ demo/app/mobiledoc-cards/selfie.js | 80 +++ demo/app/mobiledoc-cards/simple.js | 17 + demo/app/mobiledocs/empty.js | 7 + demo/app/mobiledocs/index.js | 6 + demo/app/mobiledocs/input-card.js | 15 + demo/app/mobiledocs/selfie-card.js | 12 + demo/app/mobiledocs/simple-card.js | 9 + demo/app/mobiledocs/simple-list.js | 16 + demo/app/mobiledocs/simple.js | 14 + demo/app/models/.gitkeep | 0 demo/app/router.js | 11 + demo/app/routes/.gitkeep | 0 demo/{demo.css => app/styles/app.css} | 0 demo/app/templates/application.hbs | 7 + demo/app/templates/components/.gitkeep | 0 .../components/mobiledoc-dom-renderer.hbs | 2 + demo/app/templates/index.hbs | 54 ++ demo/bower.json | 15 + demo/config/environment.js | 47 ++ demo/demo.js | 493 ------------------ demo/ember-cli-build.js | 23 + demo/favicon.ico | Bin 1065 -> 0 bytes demo/index.html | 82 --- demo/package.json | 40 ++ demo/public/crossdomain.xml | 15 + demo/public/robots.txt | 3 + demo/testem.json | 12 + demo/tests/.jshintrc | 52 ++ demo/tests/helpers/resolver.js | 11 + demo/tests/helpers/start-app.js | 18 + demo/tests/index.html | 33 ++ .../components/mobiledoc-dom-renderer-test.js | 11 + demo/tests/test-helper.js | 6 + demo/tests/unit/.gitkeep | 0 .../helpers/content-kit-cards-list-test.js | 10 + demo/tests/unit/helpers/format-object-test.js | 10 + demo/vendor/.gitkeep | 0 demo/website-index.html | 10 - package.json | 1 + src/js/editor/editor.js | 19 +- src/js/views/text-format-toolbar.js | 69 --- tests/acceptance/editor-commands-test.js | 351 ------------- tests/acceptance/editor-selections-test.js | 330 +++++------- tests/unit/commands/bold-test.js | 126 +++++ tests/unit/commands/heading-test.js | 107 ++++ tests/unit/commands/italic-test.js | 58 +++ tests/unit/commands/link-test.js | 61 +++ tests/unit/commands/quote-test.js | 50 ++ tests/unit/commands/subheading-test.js | 51 ++ tests/unit/editor/editor-destroy-test.js | 48 -- 71 files changed, 1459 insertions(+), 1322 deletions(-) delete mode 100644 broccoli/demo.js create mode 100644 demo/.bowerrc create mode 100644 demo/.editorconfig create mode 100644 demo/.ember-cli create mode 100644 demo/.gitignore create mode 100644 demo/.jshintrc create mode 100644 demo/.watchmanconfig create mode 100644 demo/README.md create mode 100644 demo/app/app.js create mode 100644 demo/app/components/.gitkeep create mode 100644 demo/app/components/mobiledoc-dom-renderer.js create mode 100644 demo/app/controllers/.gitkeep create mode 100644 demo/app/controllers/index.js create mode 100644 demo/app/helpers/.gitkeep create mode 100644 demo/app/helpers/content-kit-cards-list.js create mode 100644 demo/app/helpers/format-object.js create mode 100644 demo/app/index.html create mode 100644 demo/app/mobiledoc-cards/index.js create mode 100644 demo/app/mobiledoc-cards/input.js create mode 100644 demo/app/mobiledoc-cards/selfie.js create mode 100644 demo/app/mobiledoc-cards/simple.js create mode 100644 demo/app/mobiledocs/empty.js create mode 100644 demo/app/mobiledocs/index.js create mode 100644 demo/app/mobiledocs/input-card.js create mode 100644 demo/app/mobiledocs/selfie-card.js create mode 100644 demo/app/mobiledocs/simple-card.js create mode 100644 demo/app/mobiledocs/simple-list.js create mode 100644 demo/app/mobiledocs/simple.js create mode 100644 demo/app/models/.gitkeep create mode 100644 demo/app/router.js create mode 100644 demo/app/routes/.gitkeep rename demo/{demo.css => app/styles/app.css} (100%) create mode 100644 demo/app/templates/application.hbs create mode 100644 demo/app/templates/components/.gitkeep create mode 100644 demo/app/templates/components/mobiledoc-dom-renderer.hbs create mode 100644 demo/app/templates/index.hbs create mode 100644 demo/bower.json create mode 100644 demo/config/environment.js delete mode 100644 demo/demo.js create mode 100644 demo/ember-cli-build.js delete mode 100644 demo/favicon.ico delete mode 100644 demo/index.html create mode 100644 demo/package.json create mode 100644 demo/public/crossdomain.xml create mode 100644 demo/public/robots.txt create mode 100644 demo/testem.json create mode 100644 demo/tests/.jshintrc create mode 100644 demo/tests/helpers/resolver.js create mode 100644 demo/tests/helpers/start-app.js create mode 100644 demo/tests/index.html create mode 100644 demo/tests/integration/components/mobiledoc-dom-renderer-test.js create mode 100644 demo/tests/test-helper.js create mode 100644 demo/tests/unit/.gitkeep create mode 100644 demo/tests/unit/helpers/content-kit-cards-list-test.js create mode 100644 demo/tests/unit/helpers/format-object-test.js create mode 100644 demo/vendor/.gitkeep delete mode 100644 demo/website-index.html delete mode 100644 src/js/views/text-format-toolbar.js delete mode 100644 tests/acceptance/editor-commands-test.js create mode 100644 tests/unit/commands/bold-test.js create mode 100644 tests/unit/commands/heading-test.js create mode 100644 tests/unit/commands/italic-test.js create mode 100644 tests/unit/commands/link-test.js create mode 100644 tests/unit/commands/quote-test.js create mode 100644 tests/unit/commands/subheading-test.js delete mode 100644 tests/unit/editor/editor-destroy-test.js diff --git a/Brocfile.js b/Brocfile.js index 42db94a37..7f87bb075 100644 --- a/Brocfile.js +++ b/Brocfile.js @@ -4,7 +4,6 @@ var builder = require('broccoli-multi-builder'); var mergeTrees = require('broccoli-merge-trees'); var testTreeBuilder = require('broccoli-test-builder'); var styles = require('./broccoli/styles'); -var demo = require('./broccoli/demo'); var jquery = require('./broccoli/jquery'); var vendoredModules = [ @@ -20,8 +19,6 @@ var buildOptions = { var testTree = testTreeBuilder.build({libDirName: 'src'}); testTree = jquery.build(testTree, '/tests/jquery'); -var demoTree = demo(); -demoTree = jquery.build(demoTree, '/demo/jquery'); module.exports = mergeTrees([ builder.build('amd', buildOptions), @@ -29,6 +26,5 @@ module.exports = mergeTrees([ // FIXME Later we may want to bring back the commonjs build // builder.build('commonjs', buildOptions), styles(), - demoTree, testTree ]); diff --git a/README.md b/README.md index eac5c9132..e1f07ac7a 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,6 @@ editor.render(element); `options` is an object which may include the following properties: -* `stickyToolbar` - a boolean option enabling a persistent header with - formatting tools. Default is true for touch devices. * `placeholder` - default text to show before a user starts typing. * `spellcheck` - a boolean option enabling spellcheck. Default is true. * `autofocus` - a boolean option for grabbing input focus when an editor is diff --git a/broccoli/demo.js b/broccoli/demo.js deleted file mode 100644 index 0a38b49b4..000000000 --- a/broccoli/demo.js +++ /dev/null @@ -1,46 +0,0 @@ -/* jshint node:true */ -'use strict'; - -var funnel = require('broccoli-funnel'); -var mergeTrees = require('broccoli-merge-trees'); -var path = require('path'); - -module.exports = function() { - var demoDir = 'demo'; - var rendererDir = path.join( - path.dirname( - require.resolve('mobiledoc-dom-renderer') - ), - 'dist', 'global' - ); - // find the global build relative to the commonjs `main` entrypoint - var htmlrendererDir = path.join( - path.dirname( - require.resolve('mobiledoc-html-renderer') - ), - '..', '..', 'global' - ); - return mergeTrees([ - funnel(demoDir, { - include: [ - '**/*.css', - '**/*.js', - 'favicon.ico', - '**/*.html' - ], - destDir: '/demo' - }), - funnel(rendererDir, { - include: [ - 'mobiledoc-dom-renderer.js' - ], - destDir: '/demo' - }), - funnel(htmlrendererDir, { - include: [ - 'mobiledoc-html-renderer.js' - ], - destDir: '/demo' - }) - ]); -}; diff --git a/demo/.bowerrc b/demo/.bowerrc new file mode 100644 index 000000000..959e1696e --- /dev/null +++ b/demo/.bowerrc @@ -0,0 +1,4 @@ +{ + "directory": "bower_components", + "analytics": false +} diff --git a/demo/.editorconfig b/demo/.editorconfig new file mode 100644 index 000000000..47c543840 --- /dev/null +++ b/demo/.editorconfig @@ -0,0 +1,34 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.js] +indent_style = space +indent_size = 2 + +[*.hbs] +insert_final_newline = false +indent_style = space +indent_size = 2 + +[*.css] +indent_style = space +indent_size = 2 + +[*.html] +indent_style = space +indent_size = 2 + +[*.{diff,md}] +trim_trailing_whitespace = false diff --git a/demo/.ember-cli b/demo/.ember-cli new file mode 100644 index 000000000..ee64cfed2 --- /dev/null +++ b/demo/.ember-cli @@ -0,0 +1,9 @@ +{ + /** + Ember CLI sends analytics information by default. The data is completely + anonymous, but there are times when you might want to disable this behavior. + + Setting `disableAnalytics` to true will prevent any data from being sent. + */ + "disableAnalytics": false +} diff --git a/demo/.gitignore b/demo/.gitignore new file mode 100644 index 000000000..86fceae7a --- /dev/null +++ b/demo/.gitignore @@ -0,0 +1,17 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp + +# dependencies +/node_modules +/bower_components + +# misc +/.sass-cache +/connect.lock +/coverage/* +/libpeerconnection.log +npm-debug.log +testem.log diff --git a/demo/.jshintrc b/demo/.jshintrc new file mode 100644 index 000000000..08096effa --- /dev/null +++ b/demo/.jshintrc @@ -0,0 +1,32 @@ +{ + "predef": [ + "document", + "window", + "-Promise" + ], + "browser": true, + "boss": true, + "curly": true, + "debug": false, + "devel": true, + "eqeqeq": true, + "evil": true, + "forin": false, + "immed": false, + "laxbreak": false, + "newcap": true, + "noarg": true, + "noempty": false, + "nonew": false, + "nomen": false, + "onevar": false, + "plusplus": false, + "regexp": false, + "undef": true, + "sub": true, + "strict": false, + "white": false, + "eqnull": true, + "esnext": true, + "unused": true +} diff --git a/demo/.watchmanconfig b/demo/.watchmanconfig new file mode 100644 index 000000000..5e9462c20 --- /dev/null +++ b/demo/.watchmanconfig @@ -0,0 +1,3 @@ +{ + "ignore_dirs": ["tmp"] +} diff --git a/demo/README.md b/demo/README.md new file mode 100644 index 000000000..c45c2e8f9 --- /dev/null +++ b/demo/README.md @@ -0,0 +1,53 @@ +# Content-kit-demo + +This README outlines the details of collaborating on this Ember application. +A short introduction of this app could easily go here. + +## Prerequisites + +You will need the following things properly installed on your computer. + +* [Git](http://git-scm.com/) +* [Node.js](http://nodejs.org/) (with NPM) +* [Bower](http://bower.io/) +* [Ember CLI](http://www.ember-cli.com/) +* [PhantomJS](http://phantomjs.org/) + +## Installation + +* `git clone ` this repository +* change into the new directory +* `npm install` +* `bower install` + +## Running / Development + +* `ember server` +* Visit your app at [http://localhost:4200](http://localhost:4200). + +### Code Generators + +Make use of the many generators for code, try `ember help generate` for more details + +### Running Tests + +* `ember test` +* `ember test --server` + +### Building + +* `ember build` (development) +* `ember build --environment production` (production) + +### Deploying + +Specify what it takes to deploy your app. + +## Further Reading / Useful Links + +* [ember.js](http://emberjs.com/) +* [ember-cli](http://www.ember-cli.com/) +* Development Browser Extensions + * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) + * [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/) + diff --git a/demo/app/app.js b/demo/app/app.js new file mode 100644 index 000000000..8d66b9587 --- /dev/null +++ b/demo/app/app.js @@ -0,0 +1,18 @@ +import Ember from 'ember'; +import Resolver from 'ember/resolver'; +import loadInitializers from 'ember/load-initializers'; +import config from './config/environment'; + +var App; + +Ember.MODEL_FACTORY_INJECTIONS = true; + +App = Ember.Application.extend({ + modulePrefix: config.modulePrefix, + podModulePrefix: config.podModulePrefix, + Resolver: Resolver +}); + +loadInitializers(App, config.modulePrefix); + +export default App; diff --git a/demo/app/components/.gitkeep b/demo/app/components/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/demo/app/components/mobiledoc-dom-renderer.js b/demo/app/components/mobiledoc-dom-renderer.js new file mode 100644 index 000000000..d5d4a254f --- /dev/null +++ b/demo/app/components/mobiledoc-dom-renderer.js @@ -0,0 +1,21 @@ +import Ember from 'ember'; +import { cardsHash } from '../mobiledoc-cards/index'; + +let { computed, run } = Ember; + +export default Ember.Component.extend({ + domRenderer: computed(function(){ + return new window.MobiledocDOMRenderer(); + }), + didRender() { + let domRenderer = this.get('domRenderer'); + let mobiledoc = this.get('mobiledoc'); + run(() => { + let target = this.$('.rendered-mobiledoc'); + target.empty(); + if (mobiledoc) { + domRenderer.render(mobiledoc, target[0], cardsHash); + } + }); + } +}); diff --git a/demo/app/controllers/.gitkeep b/demo/app/controllers/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/demo/app/controllers/index.js b/demo/app/controllers/index.js new file mode 100644 index 000000000..59b1c5516 --- /dev/null +++ b/demo/app/controllers/index.js @@ -0,0 +1,21 @@ +import Ember from 'ember'; + +import * as mobiledocs from '../mobiledocs/index'; + +let { $, computed } = Ember; + +export default Ember.Controller.extend({ + mobiledocName: 'simple', + mobiledoc: computed('mobiledocName', function() { + return mobiledocs[this.get('mobiledocName')]; + }), + actions: { + changeMobiledoc() { + let selectElement = $('#select-mobiledoc'); + this.set('mobiledocName', selectElement.val()); + }, + didEdit(value) { + this.set('editedMobiledoc', value); + } + } +}); diff --git a/demo/app/helpers/.gitkeep b/demo/app/helpers/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/demo/app/helpers/content-kit-cards-list.js b/demo/app/helpers/content-kit-cards-list.js new file mode 100644 index 000000000..d94a4eddb --- /dev/null +++ b/demo/app/helpers/content-kit-cards-list.js @@ -0,0 +1,8 @@ +import Ember from 'ember'; +import { cardsList } from '../mobiledoc-cards/index'; + +export function contentKitCardsList() { + return cardsList; +} + +export default Ember.Helper.helper(contentKitCardsList); diff --git a/demo/app/helpers/format-object.js b/demo/app/helpers/format-object.js new file mode 100644 index 000000000..43079defd --- /dev/null +++ b/demo/app/helpers/format-object.js @@ -0,0 +1,7 @@ +import Ember from 'ember'; + +export function formatObject([object]) { + return JSON.stringify(object, null, ' '); +} + +export default Ember.Helper.helper(formatObject); diff --git a/demo/app/index.html b/demo/app/index.html new file mode 100644 index 000000000..f4e34b1f3 --- /dev/null +++ b/demo/app/index.html @@ -0,0 +1,26 @@ + + + + + + Content-Kit Demo + + + + {{content-for 'head'}} + + + + + + {{content-for 'head-footer'}} + + + {{content-for 'body'}} + + + + + {{content-for 'body-footer'}} + + diff --git a/demo/app/mobiledoc-cards/index.js b/demo/app/mobiledoc-cards/index.js new file mode 100644 index 000000000..1ed76b6ab --- /dev/null +++ b/demo/app/mobiledoc-cards/index.js @@ -0,0 +1,15 @@ +import { inputCard } from './input'; +import { simpleCard } from './simple'; +import { selfieCard } from './selfie'; + +export let cardsList = [ + inputCard, + simpleCard, + selfieCard +]; + +export let cardsHash = { + ['input-card']: inputCard, + ['simple-card']: simpleCard, + ['selfie-card']: selfieCard +}; diff --git a/demo/app/mobiledoc-cards/input.js b/demo/app/mobiledoc-cards/input.js new file mode 100644 index 000000000..ed75d0e32 --- /dev/null +++ b/demo/app/mobiledoc-cards/input.js @@ -0,0 +1,49 @@ +import Ember from 'ember'; + +let { $ } = Ember; + +export let inputCard = { + name: 'input-card', + display: { + setup(element, options, env, payload) { + $(element).empty(); + + var text = 'I am in display mode'; + if (payload.name) { + text = 'Hello, ' + payload.name + '!'; + } + var card = document.createElement('div'); + card.innerText = text; + + var button = document.createElement('button'); + button.innerText = 'Edit'; + button.onclick = env.edit; + + if (env.edit) { + card.appendChild(button); + } + element.appendChild(card); + } + }, + edit: { + setup(element, options, env) { + $(element).empty(); + var card = document.createElement('div'); + card.innerHTML = 'What is your name?'; + + var input = document.createElement('input'); + input.placeholder = 'Enter your name...'; + + var button = document.createElement('button'); + button.innerText = 'Save'; + button.onclick = function() { + var name = input.value; + env.save({name:name}); + }; + + card.appendChild(input); + card.appendChild(button); + element.appendChild(card); + } + } +}; diff --git a/demo/app/mobiledoc-cards/selfie.js b/demo/app/mobiledoc-cards/selfie.js new file mode 100644 index 000000000..d9ecff21e --- /dev/null +++ b/demo/app/mobiledoc-cards/selfie.js @@ -0,0 +1,80 @@ +import Ember from 'ember'; + +let { $ } = Ember; + +export let selfieCard = { + name: 'selfie-card', + display: { + setup(element, options, env, payload) { + $(element).empty(); + + if (payload.src) { + element.appendChild( + $('' + + '
' + + '
' + + '
You look nice today.
' + + (env.edit ? "
" : "") + + '
' + + '')[0] + ); + } else { + element.appendChild($('' + + '
' + + 'Hello there!' + + (env.edit ? "" : "") + + '
')[0] + ); + } + + $('#go-edit').click(function() { + env.edit(); + }); + } + }, + edit: { + setup(element, options, env) { + $(element).empty(); + + var vid = $('' + + '
' + + '' + + '' + + '' + + '
' + + ''); + element.appendChild(vid[0]); + + var canvas = document.getElementById("canvas"), + context = canvas.getContext("2d"), + + video = document.getElementById("video"), + videoObj = { "video": true }, + errBack = function() { + alert('error getting video feed'); + }; + + navigator.getMedia = (navigator.getUserMedia || + navigator.webkitGetUserMedia || + navigator.mozGetUserMedia || + navigator.msGetUserMedia); + + navigator.getMedia(videoObj, function(stream) { + var vendorURL; + if (navigator.mozGetUserMedia) { + video.mozSrcObject = stream; + } else { + vendorURL = window.URL || window.webkitURL; + video.src = vendorURL.createObjectURL(stream); + video.play(); + } + + $('#snap').click(function() { + context.drawImage(video, 0, 0, 160, 120); + var src = canvas.toDataURL('image/png'); + env.save({src: src}); + }); + }, errBack); + } + } +}; diff --git a/demo/app/mobiledoc-cards/simple.js b/demo/app/mobiledoc-cards/simple.js new file mode 100644 index 000000000..805057f83 --- /dev/null +++ b/demo/app/mobiledoc-cards/simple.js @@ -0,0 +1,17 @@ +import Ember from 'ember'; + +let { $ } = Ember; + +export let simpleCard = { + name: 'simple-card', + display: { + setup(element, options, env) { + var card = document.createElement('span'); + card.innerHTML = 'Hello, world'; + element.appendChild(card); + var button = $(''); + button.on('click', env.remove); + $(element).append(button); + } + } +}; diff --git a/demo/app/mobiledocs/empty.js b/demo/app/mobiledocs/empty.js new file mode 100644 index 000000000..0ad683fb5 --- /dev/null +++ b/demo/app/mobiledocs/empty.js @@ -0,0 +1,7 @@ +export var empty = { + version: '0.2.0', + sections: [ + [], + [] + ] +}; diff --git a/demo/app/mobiledocs/index.js b/demo/app/mobiledocs/index.js new file mode 100644 index 000000000..afc44f1fb --- /dev/null +++ b/demo/app/mobiledocs/index.js @@ -0,0 +1,6 @@ +export { simple } from './simple'; +export { empty } from './empty'; +export { simpleList } from './simple-list'; +export { simpleCard } from './simple-card'; +export { inputCard } from './input-card'; +export { selfieCard } from './selfie-card'; diff --git a/demo/app/mobiledocs/input-card.js b/demo/app/mobiledocs/input-card.js new file mode 100644 index 000000000..1f21c5cde --- /dev/null +++ b/demo/app/mobiledocs/input-card.js @@ -0,0 +1,15 @@ +export var inputCard = { + version: '0.2.0', + sections: [ + [], + [ + [1, 'H2', [ + [[], 0, 'Input Card'] + ]], + [10, 'input-card'], + [1, 'P', [ + [[], 0, 'Text after the card.'] + ]] + ] + ] +}; diff --git a/demo/app/mobiledocs/selfie-card.js b/demo/app/mobiledocs/selfie-card.js new file mode 100644 index 000000000..0025410dc --- /dev/null +++ b/demo/app/mobiledocs/selfie-card.js @@ -0,0 +1,12 @@ +export var selfieCard = { + version: '0.2.0', + sections: [ + [], + [ + [1, 'H2', [ + [[], 0, 'Selfie Card'] + ]], + [10, 'selfie-card'] + ] + ] +}; diff --git a/demo/app/mobiledocs/simple-card.js b/demo/app/mobiledocs/simple-card.js new file mode 100644 index 000000000..25fd4cc05 --- /dev/null +++ b/demo/app/mobiledocs/simple-card.js @@ -0,0 +1,9 @@ +export var simpleCard = { + version: '0.2.0', + sections: [ + [], + [ + [10, 'simple-card'] + ] + ] +}; diff --git a/demo/app/mobiledocs/simple-list.js b/demo/app/mobiledocs/simple-list.js new file mode 100644 index 000000000..93958185b --- /dev/null +++ b/demo/app/mobiledocs/simple-list.js @@ -0,0 +1,16 @@ +export var simpleList = { + version: '0.2.0', + sections: [ + [], + [ + [1, 'H2', [ + [[], 0, 'To do today:'] + ]], + [3, 'ul', [ + [[[], 0, 'buy milk']], + [[[], 0, 'water plants']], + [[[], 0, 'world domination']] + ]] + ] + ] +}; diff --git a/demo/app/mobiledocs/simple.js b/demo/app/mobiledocs/simple.js new file mode 100644 index 000000000..077cbc799 --- /dev/null +++ b/demo/app/mobiledocs/simple.js @@ -0,0 +1,14 @@ +export var simple = { + version: '0.2.0', + sections: [ + [], + [ + [1, 'H2', [ + [[], 0, 'headline h2'] + ]], + [1, 'P', [ + [[], 0, 'hello world'] + ]] + ] + ] +}; diff --git a/demo/app/models/.gitkeep b/demo/app/models/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/demo/app/router.js b/demo/app/router.js new file mode 100644 index 000000000..cef554b3d --- /dev/null +++ b/demo/app/router.js @@ -0,0 +1,11 @@ +import Ember from 'ember'; +import config from './config/environment'; + +var Router = Ember.Router.extend({ + location: config.locationType +}); + +Router.map(function() { +}); + +export default Router; diff --git a/demo/app/routes/.gitkeep b/demo/app/routes/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/demo/demo.css b/demo/app/styles/app.css similarity index 100% rename from demo/demo.css rename to demo/app/styles/app.css diff --git a/demo/app/templates/application.hbs b/demo/app/templates/application.hbs new file mode 100644 index 000000000..a5ddee7dc --- /dev/null +++ b/demo/app/templates/application.hbs @@ -0,0 +1,7 @@ +
+

Content-Kitalpha!

+

A WYSIWYG editor for rich content

+
+
+ +{{outlet}} diff --git a/demo/app/templates/components/.gitkeep b/demo/app/templates/components/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/demo/app/templates/components/mobiledoc-dom-renderer.hbs b/demo/app/templates/components/mobiledoc-dom-renderer.hbs new file mode 100644 index 000000000..5e6e7684a --- /dev/null +++ b/demo/app/templates/components/mobiledoc-dom-renderer.hbs @@ -0,0 +1,2 @@ +
+
diff --git a/demo/app/templates/index.hbs b/demo/app/templates/index.hbs new file mode 100644 index 000000000..e74e264e0 --- /dev/null +++ b/demo/app/templates/index.hbs @@ -0,0 +1,54 @@ +
+

+ Content-Kit is a publishing solution designed for both text and + dynamically rendered cards. Posts are serialized into Mobiledoc, and + rendered to DOM in a reader's browser. +

+

+ Read more on the content-kit-editor + GitHub repo, or on the announcement blog post. +

+
+
+
+

Try a Demo

+
+
+
+
+ + {{#content-kit-editor + class='post-editor__editor' + mobiledoc=mobiledoc + cards=(content-kit-cards-list) + on-change=(action 'didEdit') + as |contentKit|}} + {{content-kit-toolbar contentKit=contentKit}} + {{/content-kit-editor}} +
+
+ +
+
+

Mobiledoc Output

+

+      {{~ format-object editedMobiledoc ~}}
+      
+
+
+
+
+

Rendered with DOM-Renderer

+ {{mobiledoc-dom-renderer mobiledoc=editedMobiledoc}} +
+
+ +
diff --git a/demo/bower.json b/demo/bower.json new file mode 100644 index 000000000..dbca97740 --- /dev/null +++ b/demo/bower.json @@ -0,0 +1,15 @@ +{ + "name": "content-kit-demo", + "dependencies": { + "ember": "1.13.7", + "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3", + "ember-cli-test-loader": "ember-cli-test-loader#0.1.3", + "ember-load-initializers": "ember-cli/ember-load-initializers#0.1.5", + "ember-qunit": "0.4.9", + "ember-qunit-notifications": "0.0.7", + "ember-resolver": "~0.1.18", + "jquery": "^1.11.3", + "loader.js": "ember-cli/loader.js#3.2.1", + "qunit": "~1.18.0" + } +} diff --git a/demo/config/environment.js b/demo/config/environment.js new file mode 100644 index 000000000..135cd2a72 --- /dev/null +++ b/demo/config/environment.js @@ -0,0 +1,47 @@ +/* jshint node: true */ + +module.exports = function(environment) { + var ENV = { + modulePrefix: 'content-kit-demo', + environment: environment, + baseURL: '/', + locationType: 'auto', + EmberENV: { + FEATURES: { + // Here you can enable experimental features on an ember canary build + // e.g. 'with-controller': true + } + }, + + APP: { + // Here you can pass flags/options to your application instance + // when it is created + } + }; + + if (environment === 'development') { + // ENV.APP.LOG_RESOLVER = true; + // ENV.APP.LOG_ACTIVE_GENERATION = true; + // ENV.APP.LOG_TRANSITIONS = true; + // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; + // ENV.APP.LOG_VIEW_LOOKUPS = true; + } + + if (environment === 'test') { + // Testem prefers this... + ENV.baseURL = '/'; + ENV.locationType = 'none'; + + // keep test console output quieter + ENV.APP.LOG_ACTIVE_GENERATION = false; + ENV.APP.LOG_VIEW_LOOKUPS = false; + + ENV.APP.rootElement = '#ember-testing'; + } + + if (environment === 'production') { + + } + + return ENV; +}; diff --git a/demo/demo.js b/demo/demo.js deleted file mode 100644 index 3066c7c41..000000000 --- a/demo/demo.js +++ /dev/null @@ -1,493 +0,0 @@ -(function(exports, document, undefined) { -'use strict'; - -function removeChildren(element) { - for (var i=0; i < element.childNodes.length; i++) { - element.removeChild(element.childNodes[i]); - } -} - -var editor; - -var selfieCard = { - name: 'selfie-card', - display: { - setup: function(element, options, env, payload) { - removeChildren(element); - - if (payload.src) { - element.appendChild( - $('' + - '
' + - '
' + - '
You look nice today.
' + - (env.edit ? "
" : "") + - '
' + - '')[0] - ); - } else { - element.appendChild($('' + - '
' + - 'Hello there!' + - (env.edit ? "" : "") + - '
')[0] - ); - } - - $('#go-edit').click(function() { - env.edit(); - }); - } - }, - edit: { - setup: function(element, options, env) { - removeChildren(element); - - var vid = $('' + - '
' + - '' + - '' + - '' + - '
' + - ''); - element.appendChild(vid[0]); - - var canvas = document.getElementById("canvas"), - context = canvas.getContext("2d"), - video = document.getElementById("video"), - videoObj = { "video": true }, - errBack = function(error) { - alert('error getting video feed'); - }; - - navigator.getMedia = (navigator.getUserMedia || - navigator.webkitGetUserMedia || - navigator.mozGetUserMedia || - navigator.msGetUserMedia); - - navigator.getMedia(videoObj, function(stream) { - var vendorURL; - if (navigator.mozGetUserMedia) { - video.mozSrcObject = stream; - } else { - vendorURL = window.URL || window.webkitURL; - video.src = vendorURL.createObjectURL(stream); - video.play(); - } - - $('#snap').click(function() { - context.drawImage(video, 0, 0, 160, 120); - var src = canvas.toDataURL('image/png'); - env.save({src: src}); - }); - }, errBack); - } - } -}; - -var simpleCard = { - name: 'simple-card', - display: { - setup: function(element, options, env) { - var card = document.createElement('span'); - card.innerHTML = 'Hello, world'; - element.appendChild(card); - var button = $(''); - button.on('click', env.remove); - $(element).append(button); - } - } -}; - -var cardWithEditMode = { - name: 'edit-card', - display: { - setup: function(element, options, env) { - removeChildren(element); - var card = document.createElement('div'); - card.innerHTML = 'I am in display mode'; - - var button = document.createElement('button'); - button.innerText = 'Change to edit'; - button.onclick = env.edit; - - if (env.edit) { - card.appendChild(button); - } - element.appendChild(card); - } - }, - edit: { - setup: function(element, options, env) { - removeChildren(element); - var card = document.createElement('div'); - card.innerHTML = 'I am in edit mode'; - - var button = document.createElement('button'); - button.innerText = 'Change to display'; - button.onclick = env.save; - - card.appendChild(button); - element.appendChild(card); - } - } -}; - -var cardWithInput = { - name: 'input-card', - display: { - setup: function(element, options, env, payload) { - removeChildren(element); - - var text = 'I am in display mode'; - if (payload.name) { - text = 'Hello, ' + payload.name + '!'; - } - var card = document.createElement('div'); - card.innerText = text; - - var button = document.createElement('button'); - button.innerText = 'Edit'; - button.onclick = env.edit; - - if (env.edit) { - card.appendChild(button); - } - element.appendChild(card); - } - }, - edit: { - setup: function(element, options, env) { - removeChildren(element); - var card = document.createElement('div'); - card.innerHTML = 'What is your name?'; - - var input = document.createElement('input'); - input.placeholder = 'Enter your name...'; - - var button = document.createElement('button'); - button.innerText = 'Save'; - button.onclick = function() { - var name = input.value; - env.save({name:name}); - }; - - card.appendChild(input); - card.appendChild(button); - element.appendChild(card); - } - } -}; - -var ContentKit = exports.ContentKit, - $ = exports.$, - MobiledocHTMLRenderer = exports.MobiledocHTMLRenderer, - MobiledocDOMRenderer = exports.MobiledocDOMRenderer; - -var ContentKitDemo = exports.ContentKitDemo = { - syncCodePane: function(editor) { - var codePaneJSON = document.getElementById('serialized-mobiledoc'); - var mobiledoc = editor.serialize(); - $(codePaneJSON).text(JSON.stringify(mobiledoc, null, ' ')); - - var cards = { - 'simple-card': simpleCard, - 'edit-card': cardWithEditMode, - 'input-card': cardWithInput, - 'selfie-card': selfieCard, - 'image': ContentKit.ImageCard - }; - var renderer = new MobiledocDOMRenderer(); - var rendered; - try { - rendered = renderer.render(mobiledoc, document.createElement('div'), cards); - } catch (e) { - rendered = document.createTextNode('Error rendering: ' + e); - } - - $('#rendered-mobiledoc').empty(); - $('#rendered-mobiledoc')[0].appendChild(rendered); - - var displayHTML = function(html) { - return html.replace(/&/g,'&').replace(//g,'>'); - }; - - // adds a pipe ("|") between adjacent text nodes for visual debugging - var debugNodeHTML = function(node) { - function convertTextNodes(parentNode, converterFn) { - var iterator = document.createNodeIterator(parentNode, NodeFilter.SHOW_TEXT); - var node = iterator.nextNode(); - while (node) { - converterFn(node); - node = iterator.nextNode(); - } - } - - function markAdjacentTextNodes(textNode) { - var boxChar = '\u2591', - emptySquareChar = '\u25A2', - invisibleChar = '\u200C'; - var nextSibling = textNode.nextSibling; - if (nextSibling && nextSibling.nodeType === Node.TEXT_NODE) { - textNode.textContent = textNode.textContent + boxChar; - } - textNode.textContent = textNode.textContent.replace(new RegExp(invisibleChar, 'g'), - emptySquareChar); - } - - var deep = true; - var cloned = node.cloneNode(deep); - convertTextNodes(cloned, markAdjacentTextNodes); - return displayHTML(cloned.innerHTML); - }; - - var htmlRenderer = new MobiledocHTMLRenderer(); - var renderedHTML; - try { - renderedHTML = htmlRenderer.render(mobiledoc); - } catch (e) { - renderedHTML = 'Error rendering: ' + e; - } - - $('#rendered-mobiledoc-html').html(displayHTML(renderedHTML)); - - var editorHTML = debugNodeHTML($('#editor')[0]); - $('#editor-html').html(editorHTML); - }, - - syntaxHighlight: function(json) { - // http://stackoverflow.com/a/7220510/189440 - if (typeof json !== 'string') { - json = JSON.stringify(json, undefined, 2); - } - json = json.replace(/&/g, '&').replace(//g, '>'); - return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { - var cls = 'json-number'; - if (/^"/.test(match)) { - if (/:$/.test(match)) { - cls = 'json-key'; - } else { - cls = 'json-string'; - } - } else if (/true|false/.test(match)) { - cls = 'json-boolean'; - } else if (/null/.test(match)) { - cls = 'json-null'; - } - return '' + match + ''; - }); - } - -}; - -function bootEditor(element, mobiledoc) { - if (editor) { - editor.destroy(); - } - editor = new ContentKit.Editor({ - autofocus: false, - mobiledoc: mobiledoc, - placeholder: 'Write something here...', - cards: [simpleCard, cardWithEditMode, cardWithInput, selfieCard], - cardOptions: { - image: { - uploadUrl: 'http://localhost:5000/upload' - } - } - }); - var didRenderCallback = function() {ContentKitDemo.syncCodePane(editor);}; - editor.didRender(didRenderCallback); - editor.render(element); -} - -function readMobiledoc(string) { - return JSON.parse(string); -} - -function isValidJSON(string) { - try { - window.JSON.parse(string); - return true; - } catch(e) { - return false; - } -} - -function attemptEditorReboot(editor, textPayload) { - if (isValidJSON(textPayload)) { - var mobiledoc = readMobiledoc(textPayload); - if (editor) { - editor.destroy(); - } - bootEditor($('#editor')[0], mobiledoc); - } -} - -var MOBILEDOC_VERSION = "0.1"; -var sampleMobiledocs = { - simpleMobiledoc: { - version: MOBILEDOC_VERSION, - sections: [ - [], - [ - [1, "H2", [ - [[], 0, "headline h2"] - ]], - [1, "P", [ - [[], 0, "hello world"] - ]] - ] - ] - }, - - emptyMobiledoc: { - version: MOBILEDOC_VERSION, - sections: [ - [], - [] - ] - }, - - simpleMobiledocWithList: { - version: MOBILEDOC_VERSION, - sections: [ - [], - [ - [1, "H2", [ - [[], 0, "To do today:"] - ]], - [3, 'ul', [ - [[[], 0, 'buy milk']], - [[[], 0, 'water plants']], - [[[], 0, 'world domination']] - ]] - ] - ] - }, - - mobileDocWithMarker: { - version: MOBILEDOC_VERSION, - sections: [ - [['B']], - [ - [1, "H2", [ - [[], 0, "headline h2"] - ]], - [1, "P", [ - [[0], 1, "bold world"] - ]] - ] - ] - }, - - mobileDocWithMultipleMarkers: { - version: MOBILEDOC_VERSION, - sections: [ - [['B'], ['I']], - [ - [1, "H2", [ - [[], 0, "headline h2"] - ]], - [1, "P", [ - [[], 0, "hello "], - [[0], 1, "bold, "], - [[1], 1, "italic "], - [[], 0, "world."] - ]] - ] - ] - }, - - mobileDocWithAttributeMarker: { - version: MOBILEDOC_VERSION, - sections: [ - [['A', ['href', 'http://github.com/bustlelabs/content-kit-editor']]], - [ - [1, "H2", [ - [[], 0, "headline h2"] - ]], - [1, "P", [ - [[], 0, "see it "], - [[0], 1, "on github"], - [[], 0, "."] - ]] - ] - ] - }, - - mobileDocWithSimpleCard: { - version: MOBILEDOC_VERSION, - sections: [ - [], - [ - [10, "simple-card"] - ] - ] - }, - - mobileDocWithEditCard: { - version: MOBILEDOC_VERSION, - sections: [ - [], - [ - [1, "H2", [ - [[], 0, "Edit Card"] - ]], - [10, "edit-card"] - ] - ] - }, - - mobileDocWithInputCard: { - version: MOBILEDOC_VERSION, - sections: [ - [], - [ - [1, "H2", [ - [[], 0, "Input Card"] - ]], - [10, "input-card"], - [1, "P", [ - [[], 0, "Text after the card."] - ]] - ] - ] - }, - - mobileDocWithSelfieCard: { - version: MOBILEDOC_VERSION, - sections: [ - [], - [ - [1, "H2", [ - [[], 0, "SelfieCard"] - ]], - [10, "selfie-card"] - ] - ] - } -}; - - -$(function() { - var editorEl = $('#editor')[0]; - var mobiledoc = sampleMobiledocs.simpleMobiledoc; - - var textarea = $('#mobiledoc-to-load textarea'); - textarea.val(window.JSON.stringify(mobiledoc, false, 2)); - - textarea.on('input', function() { - }); - - $('#select-mobiledoc').on('change', function() { - var mobiledocName = $(this).val(); - var mobiledoc = sampleMobiledocs[mobiledocName]; - var text = window.JSON.stringify(mobiledoc, false, 2); - attemptEditorReboot(editor, text); - }); - - bootEditor(editorEl, mobiledoc); - $(editorEl).focus(); -}); - -}(this, document)); diff --git a/demo/ember-cli-build.js b/demo/ember-cli-build.js new file mode 100644 index 000000000..a0c0b8462 --- /dev/null +++ b/demo/ember-cli-build.js @@ -0,0 +1,23 @@ +/* global require, module */ +var EmberApp = require('ember-cli/lib/broccoli/ember-app'); + +module.exports = function(defaults) { + var app = new EmberApp(defaults, { + // Add options here + }); + + // Use `app.import` to add additional libraries to the generated + // output files. + // + // If you need to use different assets in different + // environments, specify an object as the first parameter. That + // object's keys should be the environment name and the values + // should be the asset to use in that environment. + // + // If the library that you are including contains AMD or ES6 + // modules that you would like to import into your application + // please specify an object with the list of modules as keys + // along with the exports of each module as its value. + + return app.toTree(); +}; diff --git a/demo/favicon.ico b/demo/favicon.ico deleted file mode 100644 index 00930e1a3d650941bbc48c42f14ffb2fccbb3c53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1065 zcmaJ=O=#0l98b3?qhrN`iW?}8RE8gG()1%;Lu?lsyh9@ryqL*DnK_-JKqDNuAbnWKB8j`&C{``Kw|L40kJ~rIz z>~|6b(VHFNb9e>z*L@KGt$O_zUiwhFfF|@QR25A~BxJn^K~@!~VGfG&+?CfbLJ*xN zm3#peg!7!FtCVPCsG4eEHbF#UHA9rj5P>3`Rb{DK5unn!fNUN|q2ZIGV0jt5nrb z>E<-egu`K5!{_s0gvXlGkXZ9*mb<0ELrXFh11Y)&Y(=rCS5TD1k?y9T8hf&u)hrV( z7+n($nxVXQN-dxu{2!{SduR*g;C{US6t?nn2BdS)(krHf8#m>)T^U@;gd)<-yspo* zsyJTKk#3cA1EeNca7qv*MY9d38w^3Ql2hFV;CpBrk+wTD49uxB2c$}E&KrmliU(wx#hmOxv* zCRc7BORz1M#=+2bYyWC>%Y^60-tG-9KJ1M?)bPBUc(BR7YnSk)c#!22`PxSD_s&l2 zRlH;1hU4Ip>sV#&QD^43cZ)b`2C}20PdXmE!0dJBw&U~d&9@If4tyBQ~ zx(@X$X0H;(o<+Czj&p7-7v2y@%1guj#=3ixCC&8{5p(71r}@jJ1!?VG_fzE?F?nXy zF}fXp>AD$9{oqQj=ZT*)Ll+O< - - - - - Content Kit Editor Demo 0.2.0 - - - - - - - - - - - - - -
-

Content-Kitalpha!

-

A WYSIWYG editor for rich content

-
-
-
-

- Content-Kit is a publishing solution designed for both text and - dynamically rendered cards. Posts are serialized into Mobiledoc, and - rendered to DOM in a reader's browser. -

-

- Read more on the content-kit-editor - GitHub repo, or on the announcement blog post. -

-
-
-
-

Try a Demo

-
-
-
-
- -
-
-
-
- -
-
-

Mobiledoc Output

-

-        
-
-
-
-
-

Rendered with DOM-Renderer

-
-
-
-
- -
- - - - - - - - - - diff --git a/demo/package.json b/demo/package.json new file mode 100644 index 000000000..f4a799d4e --- /dev/null +++ b/demo/package.json @@ -0,0 +1,40 @@ +{ + "name": "content-kit-demo", + "version": "0.0.0", + "description": "Small description for content-kit-demo goes here", + "private": true, + "directories": { + "doc": "doc", + "test": "tests" + }, + "scripts": { + "build": "ember build", + "start": "ember server", + "test": "ember test" + }, + "repository": "", + "engines": { + "node": ">= 0.10.0" + }, + "author": "", + "license": "MIT", + "devDependencies": { + "broccoli-asset-rev": "^2.1.2", + "ember-cli": "1.13.8", + "ember-cli-app-version": "0.5.0", + "ember-cli-babel": "^5.1.3", + "ember-cli-dependency-checker": "^1.0.1", + "ember-cli-htmlbars": "0.7.9", + "ember-cli-htmlbars-inline-precompile": "^0.2.0", + "ember-cli-ic-ajax": "0.2.1", + "ember-cli-inject-live-reload": "^1.3.1", + "ember-cli-qunit": "^1.0.0", + "ember-cli-release": "0.2.3", + "ember-cli-sri": "^1.0.3", + "ember-cli-uglify": "^1.2.0", + "ember-content-kit": "0.1.0", + "ember-disable-proxy-controllers": "^1.0.0", + "ember-export-application-global": "^1.0.3", + "mobiledoc-dom-renderer": "^0.1.12" + } +} diff --git a/demo/public/crossdomain.xml b/demo/public/crossdomain.xml new file mode 100644 index 000000000..0c16a7a07 --- /dev/null +++ b/demo/public/crossdomain.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/demo/public/robots.txt b/demo/public/robots.txt new file mode 100644 index 000000000..f5916452e --- /dev/null +++ b/demo/public/robots.txt @@ -0,0 +1,3 @@ +# http://www.robotstxt.org +User-agent: * +Disallow: diff --git a/demo/testem.json b/demo/testem.json new file mode 100644 index 000000000..0f35392cf --- /dev/null +++ b/demo/testem.json @@ -0,0 +1,12 @@ +{ + "framework": "qunit", + "test_page": "tests/index.html?hidepassed", + "disable_watching": true, + "launch_in_ci": [ + "PhantomJS" + ], + "launch_in_dev": [ + "PhantomJS", + "Chrome" + ] +} diff --git a/demo/tests/.jshintrc b/demo/tests/.jshintrc new file mode 100644 index 000000000..6ec0b7c15 --- /dev/null +++ b/demo/tests/.jshintrc @@ -0,0 +1,52 @@ +{ + "predef": [ + "document", + "window", + "location", + "setTimeout", + "$", + "-Promise", + "define", + "console", + "visit", + "exists", + "fillIn", + "click", + "keyEvent", + "triggerEvent", + "find", + "findWithAssert", + "wait", + "DS", + "andThen", + "currentURL", + "currentPath", + "currentRouteName" + ], + "node": false, + "browser": false, + "boss": true, + "curly": true, + "debug": false, + "devel": false, + "eqeqeq": true, + "evil": true, + "forin": false, + "immed": false, + "laxbreak": false, + "newcap": true, + "noarg": true, + "noempty": false, + "nonew": false, + "nomen": false, + "onevar": false, + "plusplus": false, + "regexp": false, + "undef": true, + "sub": true, + "strict": false, + "white": false, + "eqnull": true, + "esnext": true, + "unused": true +} diff --git a/demo/tests/helpers/resolver.js b/demo/tests/helpers/resolver.js new file mode 100644 index 000000000..28f4ece46 --- /dev/null +++ b/demo/tests/helpers/resolver.js @@ -0,0 +1,11 @@ +import Resolver from 'ember/resolver'; +import config from '../../config/environment'; + +var resolver = Resolver.create(); + +resolver.namespace = { + modulePrefix: config.modulePrefix, + podModulePrefix: config.podModulePrefix +}; + +export default resolver; diff --git a/demo/tests/helpers/start-app.js b/demo/tests/helpers/start-app.js new file mode 100644 index 000000000..0f7aab1af --- /dev/null +++ b/demo/tests/helpers/start-app.js @@ -0,0 +1,18 @@ +import Ember from 'ember'; +import Application from '../../app'; +import config from '../../config/environment'; + +export default function startApp(attrs) { + var application; + + var attributes = Ember.merge({}, config.APP); + attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; + + Ember.run(function() { + application = Application.create(attributes); + application.setupForTesting(); + application.injectTestHelpers(); + }); + + return application; +} diff --git a/demo/tests/index.html b/demo/tests/index.html new file mode 100644 index 000000000..9f4aed2ad --- /dev/null +++ b/demo/tests/index.html @@ -0,0 +1,33 @@ + + + + + + ContentKitDemo Tests + + + + {{content-for 'head'}} + {{content-for 'test-head'}} + + + + + + {{content-for 'head-footer'}} + {{content-for 'test-head-footer'}} + + + + {{content-for 'body'}} + {{content-for 'test-body'}} + + + + + + + {{content-for 'body-footer'}} + {{content-for 'test-body-footer'}} + + diff --git a/demo/tests/integration/components/mobiledoc-dom-renderer-test.js b/demo/tests/integration/components/mobiledoc-dom-renderer-test.js new file mode 100644 index 000000000..eb2eab75f --- /dev/null +++ b/demo/tests/integration/components/mobiledoc-dom-renderer-test.js @@ -0,0 +1,11 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('mobiledoc-dom-renderer', 'Integration | Component | mobiledoc dom renderer', { + integration: true +}); + +test('it renders', function(assert) { + this.render(hbs`{{mobiledoc-dom-renderer}}`); + assert.equal(this.$().text().trim(), ''); +}); diff --git a/demo/tests/test-helper.js b/demo/tests/test-helper.js new file mode 100644 index 000000000..e6cfb70fe --- /dev/null +++ b/demo/tests/test-helper.js @@ -0,0 +1,6 @@ +import resolver from './helpers/resolver'; +import { + setResolver +} from 'ember-qunit'; + +setResolver(resolver); diff --git a/demo/tests/unit/.gitkeep b/demo/tests/unit/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/demo/tests/unit/helpers/content-kit-cards-list-test.js b/demo/tests/unit/helpers/content-kit-cards-list-test.js new file mode 100644 index 000000000..6a81e9b8c --- /dev/null +++ b/demo/tests/unit/helpers/content-kit-cards-list-test.js @@ -0,0 +1,10 @@ +import { contentKitCardsList } from '../../../helpers/content-kit-cards-list'; +import { module, test } from 'qunit'; + +module('Unit | Helper | content kit cards list'); + +// Replace this with your real tests. +test('it works', function(assert) { + var result = contentKitCardsList(); + assert.ok(result instanceof Array); +}); diff --git a/demo/tests/unit/helpers/format-object-test.js b/demo/tests/unit/helpers/format-object-test.js new file mode 100644 index 000000000..f3e08cea6 --- /dev/null +++ b/demo/tests/unit/helpers/format-object-test.js @@ -0,0 +1,10 @@ +import { formatObject } from '../../../helpers/format-object'; +import { module, test } from 'qunit'; + +module('Unit | Helper | format object'); + +// Replace this with your real tests. +test('it works', function(assert) { + var result = formatObject([{}]); + assert.ok(result); +}); diff --git a/demo/vendor/.gitkeep b/demo/vendor/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/demo/website-index.html b/demo/website-index.html deleted file mode 100644 index 9f2c4f1bf..000000000 --- a/demo/website-index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Content Kit Editor 0.2.0 - - - - Click to view the Demo. - - diff --git a/package.json b/package.json index 4562baca0..d44ef5e4a 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ }, "devDependencies": { "broccoli": "^0.16.3", + "broccoli-funnel": "^0.2.8", "broccoli-less-single": "^0.4.0", "broccoli-merge-trees": "^0.2.1", "broccoli-multi-builder": "^0.2.8", diff --git a/src/js/editor/editor.js b/src/js/editor/editor.js index 7777ed9c0..3b0ab621b 100644 --- a/src/js/editor/editor.js +++ b/src/js/editor/editor.js @@ -1,4 +1,3 @@ -import TextFormatToolbar from '../views/text-format-toolbar'; import Tooltip from '../views/tooltip'; import EmbedIntent from '../views/embed-intent'; import PostEditor from './post'; @@ -40,10 +39,6 @@ const defaults = { placeholder: 'Write here...', spellcheck: true, autofocus: true, - // FIXME PhantomJS has 'ontouchstart' in window, - // causing the stickyToolbar to accidentally be auto-activated - // in tests - stickyToolbar: false, // !!('ontouchstart' in window), cards: [], cardOptions: {}, unknownCardHandler: () => { @@ -147,7 +142,6 @@ class Editor { this._setupListeners(); this._addEmbedIntent(); - this._addToolbar(); this._addTooltip(); // A call to `run` will trigger the didUpdatePostCallbacks hooks with a @@ -158,19 +152,13 @@ class Editor { if (this.autofocus) { this.element.focus(); } } - _addToolbar() { - this.addView(new TextFormatToolbar({ - editor: this, + _addTooltip() { + this.addView(new Tooltip({ rootElement: this.element, - commands: [], - sticky: this.stickyToolbar + showForTag: 'a' })); } - _addTooltip() { - this.addView(new Tooltip({rootElement: this.element, showForTag: 'a'})); - } - get expansions() { if (!this._expansions) { this._expansions = []; } return this._expansions; @@ -254,7 +242,6 @@ class Editor { this.cursor.moveToSection(cursorSection); } - // FIXME it might be nice to use the toolbar's prompt instead showPrompt(message, defaultValue, callback) { callback(window.prompt(message, defaultValue)); } diff --git a/src/js/views/text-format-toolbar.js b/src/js/views/text-format-toolbar.js deleted file mode 100644 index 8a291b539..000000000 --- a/src/js/views/text-format-toolbar.js +++ /dev/null @@ -1,69 +0,0 @@ -import Toolbar from './toolbar'; -import ReversibleToolbarButton from './reversible-toolbar-button'; -import ReversiblePromptButton from './reversible-prompt-button'; -import BoldCommand from '../commands/bold'; -import ItalicCommand from '../commands/italic'; -import LinkCommand from '../commands/link'; -import QuoteCommand from '../commands/quote'; -import HeadingCommand from '../commands/heading'; -import SubheadingCommand from '../commands/subheading'; - -function makeButtons(editor) { - const headingCommand = new HeadingCommand(editor); - const headingButton = new ReversibleToolbarButton(headingCommand, editor); - - const subheadingCommand = new SubheadingCommand(editor); - const subheadingButton = new ReversibleToolbarButton(subheadingCommand, editor); - - const quoteCommand = new QuoteCommand(editor); - const quoteButton = new ReversibleToolbarButton(quoteCommand, editor); - - const boldCommand = new BoldCommand(editor); - const boldButton = new ReversibleToolbarButton(boldCommand, editor); - - const italicCommand = new ItalicCommand(editor); - const italicButton = new ReversibleToolbarButton(italicCommand, editor); - - const linkCommand = new LinkCommand(editor); - const linkButton = new ReversiblePromptButton(linkCommand, editor); - - return [ - headingButton, - subheadingButton, - quoteButton, - boldButton, - italicButton, - linkButton - ]; -} - - -export default class TextFormatToolbar extends Toolbar { - constructor(options={}) { - super(options); - - this.editor.on('selection', () => this.handleSelection()); - this.editor.on('selectionUpdated', () => this.handleSelection()); - this.editor.on('selectionEnded', () => this.handleSelectionEnded()); - this.editor.on('escapeKey', () => this.editor.cancelSelection()); - this.addEventListener(window, 'resize', () => this.handleResize()); - - let buttons = makeButtons(this.editor); - buttons.forEach(b => this.addButton(b)); - } - - handleResize() { - if (this.isShowing) { - this.positionToContent(); - } - } - - handleSelection() { - this.show(); - this.updateForSelection(); - } - - handleSelectionEnded() { - this.hide(); - } -} diff --git a/tests/acceptance/editor-commands-test.js b/tests/acceptance/editor-commands-test.js deleted file mode 100644 index f16e431ee..000000000 --- a/tests/acceptance/editor-commands-test.js +++ /dev/null @@ -1,351 +0,0 @@ -import { Editor } from 'content-kit-editor'; -import Helpers from '../test-helpers'; -import { MOBILEDOC_VERSION } from 'content-kit-editor/renderers/mobiledoc'; - -const { test, module } = Helpers; - -let fixture, editor, editorElement, selectedText; - -const mobiledoc = { - version: MOBILEDOC_VERSION, - sections: [ - [], - [[ - 1, 'P', [[[], 0, 'THIS IS A TEST']], - 1, 'P', [[[], 0, 'second section']] - ]] - ] -}; - -module('Acceptance: Editor commands', { - beforeEach() { - fixture = document.getElementById('qunit-fixture'); - editorElement = document.createElement('div'); - editorElement.setAttribute('id', 'editor'); - fixture.appendChild(editorElement); - editor = new Editor({mobiledoc}); - editor.render(editorElement); - - selectedText = 'IS A'; - Helpers.dom.selectText(selectedText, editorElement); - Helpers.dom.triggerEvent(document, 'mouseup'); - }, - - afterEach() { - if (editor) { editor.destroy(); } - } -}); - -test('when text is highlighted, shows toolbar', (assert) => { - let done = assert.async(); - - setTimeout(() => { - assert.hasElement('.ck-toolbar', 'displays toolbar'); - assert.hasElement('.ck-toolbar-btn', 'displays toolbar buttons'); - let boldBtnSelector = '.ck-toolbar-btn[title="bold"]'; - assert.hasElement(boldBtnSelector, 'has bold button'); - - done(); - }); -}); - -test('highlight text, click "bold" button bolds text', (assert) => { - let done = assert.async(); - - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'bold'); - assert.hasElement('#editor strong:contains(IS A)'); - - done(); - }); -}); - -test('highlight text, click "bold", type more text, re-select text, bold button is active', (assert) => { - let done = assert.async(); - - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'bold'); - assert.hasElement('#editor strong:contains(IS A)'); - - let boldTag = $('#editor strong:contains(IS A)')[0]; - let textNode = boldTag.childNodes[0]; - assert.equal(textNode.textContent, 'IS A', 'precond - correct node'); - - Helpers.dom.moveCursorTo(textNode, 'IS'.length); - Helpers.dom.insertText(editor, 'X'); - - assert.hasElement('strong:contains(ISX A)', 'adds text to bold'); - - Helpers.dom.selectText('ISX A', editorElement); - Helpers.dom.triggerEvent(document, 'mouseup'); - - setTimeout(() => { - Helpers.toolbar.assertActiveButton(assert, 'bold'); - done(); - }); - }); -}); - -test('highlight text, click "heading" button turns text into h2 header', (assert) => { - const done = assert.async(); - - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'heading'); - assert.hasElement('#editor h2:contains(THIS IS A TEST)'); - assert.selectedText('THIS IS A TEST', 'expands selection to entire section'); - - done(); - }); -}); - -test('click heading button triggers update', (assert) => { - const done = assert.async(); - const triggered = []; - const triggerFn = editor.trigger; - editor.trigger = (name, ...args) => { - triggered.push(name); - triggerFn.call(editor, name, ...args); - }; - - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'heading'); - assert.ok(triggered.indexOf('update') !== -1, - 'update was triggered'); - - done(); - }); -}); - -test('highlighting heading text activates toolbar button', (assert) => { - const done = assert.async(); - - setTimeout(() => { - assert.toolbarVisible(); - assert.inactiveButton('heading'); - Helpers.toolbar.assertInactiveButton(assert, 'heading'); - - Helpers.toolbar.clickButton(assert, 'heading'); - - assert.activeButton('heading'); - - // FIXME must actually trigger the mouseup - Helpers.dom.clearSelection(); - Helpers.dom.triggerEvent(document, 'mouseup'); - - setTimeout(() => { - Helpers.toolbar.assertHidden(assert); - - Helpers.dom.selectText(selectedText, editorElement); - Helpers.dom.triggerEvent(document, 'mouseup'); - - setTimeout(() => { - Helpers.toolbar.assertActiveButton(assert, 'heading', - 'heading button is active when text is selected'); - - done(); - }); - }); - }); -}); - -test('when heading text is highlighted, clicking heading button turns it to plain text', (assert) => { - const done = assert.async(); - - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'heading'); - assert.hasElement('#editor h2:contains(THIS IS A TEST)'); - - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'heading'); - - setTimeout(() => { - assert.hasNoElement('#editor h2:contains(THIS IS A TEST)'); - assert.hasElement('#editor p:contains(THIS IS A TEST)'); - - done(); - }); - }); - }); -}); - -test('clicking multiple heading buttons keeps the correct ones active', (assert) => { - const done = assert.async(); - - setTimeout(() => { - // click subheading, makes its button active, changes the display - Helpers.toolbar.clickButton(assert, 'subheading'); - assert.hasElement('#editor h3:contains(THIS IS A TEST)'); - Helpers.toolbar.assertActiveButton(assert, 'subheading'); - Helpers.toolbar.assertInactiveButton(assert, 'heading'); - - // click heading, makes its button active and no others, changes display - Helpers.toolbar.clickButton(assert, 'heading'); - assert.hasElement('#editor h2:contains(THIS IS A TEST)'); - Helpers.toolbar.assertActiveButton(assert, 'heading'); - Helpers.toolbar.assertInactiveButton(assert, 'subheading'); - - // click heading again, removes headline from display, no active buttons - Helpers.toolbar.clickButton(assert, 'heading'); - assert.hasElement('#editor p:contains(THIS IS A TEST)'); - Helpers.toolbar.assertInactiveButton(assert, 'heading'); - Helpers.toolbar.assertInactiveButton(assert, 'subheading'); - - done(); - }); -}); - -test('highlight text, click "subheading" button turns text into h3 header', (assert) => { - const done = assert.async(); - - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'subheading'); - assert.hasElement('#editor h3:contains(THIS IS A TEST)'); - - done(); - }); -}); - -test('highlight text, click "quote" button turns text into blockquote', (assert) => { - const done = assert.async(); - - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'quote'); - assert.hasElement('#editor blockquote:contains(THIS IS A TEST)'); - - done(); - }); -}); - -// FIXME PhantomJS doesn't create keyboard events properly (they have no keyCode or which) -// see https://bugs.webkit.org/show_bug.cgi?id=36423 -Helpers.skipInPhantom('highlight text, click "link" button shows input for URL, makes link', (assert) => { - const done = assert.async(); - - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'link'); - const input = assert.hasElement('.ck-toolbar-prompt input'); - const url = 'http://google.com'; - $(input).val(url); - Helpers.dom.triggerEnterKeyupEvent(input[0]); - - assert.toolbarHidden(); - - setTimeout(() => { - assert.hasElement(`#editor a[href="${url}"]:contains(${selectedText})`); - assert.selectedText(selectedText, 'text remains selected'); - Helpers.dom.triggerEvent(document, 'mouseup'); - - setTimeout(() => { - assert.toolbarVisible(); - assert.activeButton('link'); - - Helpers.toolbar.clickButton(assert, 'link'); - - assert.hasNoElement(`#editor a`, 'a tag is gone after unlinking'); - // test that typing in the link adds more text to it - // test that the toolbar displays the link status, can un-link - - done(); - }); - }); - }); -}); - -test('highlighting bold text shows bold button as active', (assert) => { - const done = assert.async(); - - setTimeout(() => { - Helpers.toolbar.assertInactiveButton(assert, 'bold', 'precond - bold button is not active'); - Helpers.toolbar.clickButton(assert, 'bold'); - Helpers.toolbar.assertActiveButton(assert, 'bold'); - - Helpers.dom.clearSelection(); - Helpers.dom.triggerEvent(document, 'mouseup'); - - setTimeout(() => { - Helpers.toolbar.assertHidden(assert); - - Helpers.dom.selectText(selectedText, editorElement); - Helpers.dom.triggerEvent(document, 'mouseup'); - - setTimeout(() => { - Helpers.toolbar.assertVisible(assert); - Helpers.toolbar.assertActiveButton(assert, 'bold'); - - done(); - }); - }); - }); -}); - -test('click bold button applies bold to selected text', (assert) => { - const done = assert.async(); - - setTimeout(() => { - Helpers.toolbar.assertInactiveButton(assert, 'bold', 'precond - bold button is not active'); - Helpers.toolbar.clickButton(assert, 'bold'); - Helpers.toolbar.assertActiveButton(assert, 'bold'); - - assert.hasNoElement('#editor strong:contains(THIS)'); - assert.hasNoElement('#editor strong:contains(TEST)'); - assert.hasElement('#editor strong:contains(IS A)'); - - assert.selectedText(selectedText); - - Helpers.toolbar.clickButton(assert, 'bold'); - - assert.hasNoElement('#editor strong:contains(IS A)', 'bold text is no longer bold'); - Helpers.toolbar.assertInactiveButton(assert, 'bold'); - - done(); - }); -}); - -test('can unbold part of a larger set of bold text', (assert) => { - const done = assert.async(); - - setTimeout(() => { - Helpers.toolbar.assertInactiveButton(assert, 'bold', 'precond - bold button is not active'); - Helpers.toolbar.clickButton(assert, 'bold'); - Helpers.toolbar.assertActiveButton(assert, 'bold'); - - assert.hasElement('#editor strong:contains(IS A)'); - - Helpers.dom.selectText('S A', editorElement); - Helpers.dom.triggerEvent(document, 'mouseup'); - - setTimeout(() => { - Helpers.toolbar.assertVisible(assert); - Helpers.toolbar.assertActiveButton(assert, 'bold'); - Helpers.toolbar.clickButton(assert, 'bold'); - - assert.hasElement('#editor strong:contains(I)', 'unselected text is bold'); - assert.hasNoElement('#editor strong:contains(IS A)', 'unselected text is bold'); - assert.hasElement('#editor p:contains(S A)', 'unselected text is bold'); - - done(); - }); - }); -}); - -test('can italicize text', (assert) => { - const done = assert.async(); - - setTimeout(() => { - Helpers.toolbar.assertInactiveButton(assert, 'italic'); - Helpers.toolbar.clickButton(assert, 'italic'); - - assert.hasElement('#editor em:contains(IS A)'); - assert.selectedText('IS A'); - Helpers.toolbar.assertActiveButton(assert, 'italic'); - - Helpers.toolbar.clickButton(assert, 'italic'); - assert.hasNoElement('#editor em:contains(IS A)'); - Helpers.toolbar.assertInactiveButton(assert, 'italic'); - - done(); - }); -}); - -// test selecting across markers and boldening -// test selecting across markers in sections and bolding diff --git a/tests/acceptance/editor-selections-test.js b/tests/acceptance/editor-selections-test.js index e87e54c2c..2b3beb327 100644 --- a/tests/acceptance/editor-selections-test.js +++ b/tests/acceptance/editor-selections-test.js @@ -1,6 +1,8 @@ import { Editor } from 'content-kit-editor'; import Helpers from '../test-helpers'; import { MOBILEDOC_VERSION } from 'content-kit-editor/renderers/mobiledoc'; +import BoldCommand from 'content-kit-editor/commands/bold'; +import HeadingCommand from 'content-kit-editor/commands/heading'; const { test, module } = Helpers; @@ -35,8 +37,6 @@ module('Acceptance: Editor Selections', { }); test('selecting across sections is possible', (assert) => { - const done = assert.async(); - editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); @@ -47,16 +47,10 @@ test('selecting across sections is possible', (assert) => { 'second', secondSection); Helpers.dom.triggerEvent(document, 'mouseup'); - - setTimeout(() => { - assert.equal(editor.activeSections.length, 2, 'selects 2 sections'); - done(); - }); + assert.equal(editor.activeSections.length, 2, 'selects 2 sections'); }); test('selecting an entire section and deleting removes it', (assert) => { - const done = assert.async(); - editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); @@ -70,8 +64,6 @@ test('selecting an entire section and deleting removes it', (assert) => { Helpers.dom.insertText(editor, 'X'); assert.hasElement('#editor p:eq(1):contains(X)', 'inserts text in correct spot'); - - done(); }); test('selecting text in a section and deleting deletes it', (assert) => { @@ -107,7 +99,6 @@ test('selecting text across sections and deleting joins sections', (assert) => { }); test('selecting text across markers and deleting joins markers', (assert) => { - const done = assert.async(); editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); @@ -115,128 +106,107 @@ test('selecting text across markers and deleting joins markers', (assert) => { Helpers.dom.selectText('rst sect', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'bold'); + let command = new BoldCommand(editor); + command.exec(); - let firstTextNode = editorElement + let firstTextNode = editorElement + .childNodes[0] // p + .childNodes[1] // b + .childNodes[0]; // textNode containing "rst sect" + let secondTextNode = editorElement .childNodes[0] // p - .childNodes[1] // b - .childNodes[0]; // textNode containing "rst sect" - let secondTextNode = editorElement - .childNodes[0] // p - .childNodes[2]; // textNode containing "ion" - - assert.equal(firstTextNode.textContent, 'rst sect', 'correct first text node'); - assert.equal(secondTextNode.textContent, 'ion', 'correct second text node'); - Helpers.dom.selectText('t sect', firstTextNode, - 'ion', secondTextNode); - Helpers.dom.triggerDelete(editor); - - assert.hasElement('p:contains(firs)', 'deletes across markers'); - assert.hasElement('strong:contains(rs)', 'maintains bold text'); - - firstTextNode = editorElement - .childNodes[0] // p - .childNodes[1] // b - .childNodes[0]; // textNode now containing "rs" - - assert.deepEqual(Helpers.dom.getCursorPosition(), - {node: firstTextNode, offset: 2}); - - done(); - }); + .childNodes[2]; // textNode containing "ion" + + assert.equal(firstTextNode.textContent, 'rst sect', 'correct first text node'); + assert.equal(secondTextNode.textContent, 'ion', 'correct second text node'); + Helpers.dom.selectText('t sect', firstTextNode, + 'ion', secondTextNode); + Helpers.dom.triggerDelete(editor); + + assert.hasElement('p:contains(firs)', 'deletes across markers'); + assert.hasElement('strong:contains(rs)', 'maintains bold text'); + + firstTextNode = editorElement + .childNodes[0] // p + .childNodes[1] // b + .childNodes[0]; // textNode now containing "rs" + + assert.deepEqual(Helpers.dom.getCursorPosition(), + {node: firstTextNode, offset: 2}); }); test('select text and apply markup multiple times', (assert) => { - const done = assert.async(); editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); Helpers.dom.selectText('t sect', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'bold'); - - Helpers.dom.selectText('fir', editorElement); - Helpers.dom.triggerEvent(document, 'mouseup'); + let command = new BoldCommand(editor); + command.exec(); - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'bold'); + Helpers.dom.selectText('fir', editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); - assert.hasElement('p:contains(first section)', 'correct first section'); - assert.hasElement('strong:contains(fir)', 'strong "fir"'); - assert.hasElement('strong:contains(t sect)', 'strong "t sect"'); + command.exec(); - done(); - }); - }); + assert.hasElement('p:contains(first section)', 'correct first section'); + assert.hasElement('strong:contains(fir)', 'strong "fir"'); + assert.hasElement('strong:contains(t sect)', 'strong "t sect"'); }); test('selecting text across markers deletes intermediary markers', (assert) => { - const done = assert.async(); editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); Helpers.dom.selectText('rst sec', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'bold'); - - const textNode1 = editorElement.childNodes[0].childNodes[0], - textNode2 = editorElement.childNodes[0].childNodes[2]; - Helpers.dom.selectText('i', textNode1, - 'tio', textNode2); - Helpers.dom.triggerEvent(document, 'mouseup'); + let command = new BoldCommand(editor); + command.exec(); - setTimeout(() => { - Helpers.dom.triggerDelete(editor); + const textNode1 = editorElement.childNodes[0].childNodes[0], + textNode2 = editorElement.childNodes[0].childNodes[2]; + Helpers.dom.selectText('i', textNode1, + 'tio', textNode2); + Helpers.dom.triggerEvent(document, 'mouseup'); - assert.hasElement('p:contains(fn)', 'has remaining first section'); - assert.deepEqual(Helpers.dom.getCursorPosition(), - {node: editorElement.childNodes[0].childNodes[0], - offset: 1}); + Helpers.dom.triggerDelete(editor); - done(); - }); - }); + assert.hasElement('p:contains(fn)', 'has remaining first section'); + assert.deepEqual(Helpers.dom.getCursorPosition(), + {node: editorElement.childNodes[0].childNodes[0], + offset: 1}); }); test('selecting text across markers preserves node after', (assert) => { - const done = assert.async(); editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); Helpers.dom.selectText('rst sec', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'bold'); - - const textNode1 = editorElement.childNodes[0].childNodes[0], - textNode2 = editorElement.childNodes[0].childNodes[1]; - Helpers.dom.selectText('i', textNode1, - 'sec', textNode2); - Helpers.dom.triggerEvent(document, 'mouseup'); - - setTimeout(() => { - Helpers.dom.triggerDelete(editor); - - assert.deepEqual( - editorElement.childNodes[0].innerHTML, 'ftion', - 'has remaining first section' - ); - assert.deepEqual(Helpers.dom.getCursorPosition(), - {node: editorElement.childNodes[0].childNodes[0], - offset: 1}); - done(); - }); - }); + let command = new BoldCommand(editor); + command.exec(); + + const textNode1 = editorElement.childNodes[0].childNodes[0], + textNode2 = editorElement.childNodes[0].childNodes[1]; + Helpers.dom.selectText('i', textNode1, + 'sec', textNode2); + Helpers.dom.triggerEvent(document, 'mouseup'); + + Helpers.dom.triggerDelete(editor); + + assert.deepEqual( + editorElement.childNodes[0].innerHTML, 'ftion', + 'has remaining first section' + ); + assert.deepEqual(Helpers.dom.getCursorPosition(), + {node: editorElement.childNodes[0].childNodes[0], + offset: 1}); }); test('selecting text across sections and hitting enter deletes and moves cursor to last selected section', (assert) => { - const done = assert.async(); editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); @@ -248,49 +218,41 @@ test('selecting text across sections and hitting enter deletes and moves cursor Helpers.dom.triggerEnter(editor); - setTimeout(() => { - assert.equal($('#editor p').length, 2, 'still 2 sections'); - assert.equal($('#editor p:eq(0)').text(), 'first', 'correct text in 1st section'); - assert.equal($('#editor p:eq(1)').text(), 'section', 'correct text in 2nd section'); + assert.equal($('#editor p').length, 2, 'still 2 sections'); + assert.equal($('#editor p:eq(0)').text(), 'first', 'correct text in 1st section'); + assert.equal($('#editor p:eq(1)').text(), 'section', 'correct text in 2nd section'); - let secondSectionTextNode = editor.element.childNodes[1].childNodes[0]; - assert.deepEqual(Helpers.dom.getCursorPosition(), - {node: secondSectionTextNode, offset: 0}, - 'cursor is at start of second section'); - done(); - }); + let secondSectionTextNode = editor.element.childNodes[1].childNodes[0]; + assert.deepEqual(Helpers.dom.getCursorPosition(), + {node: secondSectionTextNode, offset: 0}, + 'cursor is at start of second section'); }); test('keystroke of printable character while text is selected deletes the text', (assert) => { - const done = assert.async(); editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); Helpers.dom.selectText('first section', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'heading'); - - assert.ok($('#editor h2:contains(first section)').length, - 'first section is a heading'); + let command = new HeadingCommand(editor); + command.exec(); - const firstSectionTextNode = editorElement.childNodes[0].childNodes[0]; - const secondSectionTextNode = editorElement.childNodes[1].childNodes[0]; - Helpers.dom.selectText('section', firstSectionTextNode, - 'secon', secondSectionTextNode); + assert.ok($('#editor h2:contains(first section)').length, + 'first section is a heading'); - Helpers.dom.insertText(editor, 'X'); + const firstSectionTextNode = editorElement.childNodes[0].childNodes[0]; + const secondSectionTextNode = editorElement.childNodes[1].childNodes[0]; + Helpers.dom.selectText('section', firstSectionTextNode, + 'secon', secondSectionTextNode); - assert.ok($(`#editor h2:contains(first Xd section)`).length, - 'updates the section'); + Helpers.dom.insertText(editor, 'X'); - done(); - }); + assert.ok($(`#editor h2:contains(first Xd section)`).length, + 'updates the section'); }); test('selecting all text across sections and hitting enter deletes and moves cursor to empty section', (assert) => { - const done = assert.async(); editor = new Editor({mobiledoc: mobileDocWith2Sections}); editor.render(editorElement); @@ -302,19 +264,15 @@ test('selecting all text across sections and hitting enter deletes and moves cur Helpers.dom.triggerEnter(editor); - setTimeout(() => { - assert.equal($('#editor p').length, 1, 'single section'); - assert.equal($('#editor p:eq(0)').text(), '', 'blank text'); + assert.equal($('#editor p').length, 1, 'single section'); + assert.equal($('#editor p:eq(0)').text(), '', 'blank text'); - assert.deepEqual(Helpers.dom.getCursorPosition(), - {node: $('#editor p')[0], offset: 0}, - 'cursor is at start of second section'); - done(); - }); + assert.deepEqual(Helpers.dom.getCursorPosition(), + {node: $('#editor p')[0], offset: 0}, + 'cursor is at start of second section'); }); test('selecting text across markup and list sections', (assert) => { - const done = assert.async(); const build = Helpers.mobiledoc.build; const mobiledoc = build(({post, markupSection, listSection, listItem, marker}) => post([ @@ -331,22 +289,18 @@ test('selecting text across markup and list sections', (assert) => { Helpers.dom.selectText('bc', editorElement, '12', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - Helpers.dom.triggerDelete(editor); + Helpers.dom.triggerDelete(editor); - assert.hasElement('#editor p:contains(a3)', - 'combines partially-selected list item onto markup section'); + assert.hasElement('#editor p:contains(a3)', + 'combines partially-selected list item onto markup section'); - assert.hasNoElement('#editor p:contains(bc)', 'deletes selected text "bc"'); - assert.hasNoElement('#editor p:contains(12)', 'deletes selected text "12"'); + assert.hasNoElement('#editor p:contains(bc)', 'deletes selected text "bc"'); + assert.hasNoElement('#editor p:contains(12)', 'deletes selected text "12"'); - assert.hasElement('#editor li:contains(6)', 'leaves remaining text in list item'); - done(); - }); + assert.hasElement('#editor li:contains(6)', 'leaves remaining text in list item'); }); test('selecting text that covers a list section', (assert) => { - const done = assert.async(); const build = Helpers.mobiledoc.build; const mobiledoc = build(({post, markupSection, listSection, listItem, marker}) => post([ @@ -364,22 +318,17 @@ test('selecting text that covers a list section', (assert) => { Helpers.dom.selectText('bc', editorElement, 'de', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - Helpers.dom.triggerDelete(editor); - - assert.hasElement('#editor p:contains(af)', - 'combines sides of selection'); + Helpers.dom.triggerDelete(editor); - assert.hasNoElement('#editor li:contains(123)', 'deletes li 1'); - assert.hasNoElement('#editor li:contains(456)', 'deletes li 2'); - assert.hasNoElement('#editor ul', 'removes ul'); + assert.hasElement('#editor p:contains(af)', + 'combines sides of selection'); - done(); - }); + assert.hasNoElement('#editor li:contains(123)', 'deletes li 1'); + assert.hasNoElement('#editor li:contains(456)', 'deletes li 2'); + assert.hasNoElement('#editor ul', 'removes ul'); }); test('selecting text that starts in a list item and ends in a markup section', (assert) => { - const done = assert.async(); const build = Helpers.mobiledoc.build; const mobiledoc = build(({post, markupSection, listSection, listItem, marker}) => post([ @@ -396,23 +345,18 @@ test('selecting text that starts in a list item and ends in a markup section', ( Helpers.dom.selectText('23', editorElement, 'de', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - Helpers.dom.triggerDelete(editor); - - assert.hasElement('#editor li:contains(1f)', - 'combines sides of selection'); + Helpers.dom.triggerDelete(editor); - assert.hasNoElement('#editor li:contains(123)', 'deletes li 1'); - assert.hasNoElement('#editor li:contains(456)', 'deletes li 2'); - assert.hasNoElement('#editor p:contains(def)', 'deletes p content'); - assert.hasNoElement('#editor p', 'removes p entirely'); + assert.hasElement('#editor li:contains(1f)', + 'combines sides of selection'); - done(); - }); + assert.hasNoElement('#editor li:contains(123)', 'deletes li 1'); + assert.hasNoElement('#editor li:contains(456)', 'deletes li 2'); + assert.hasNoElement('#editor p:contains(def)', 'deletes p content'); + assert.hasNoElement('#editor p', 'removes p entirely'); }); test('selecting text that includes a card section and deleting deletes card section', (assert) => { - const done = assert.async(); const build = Helpers.mobiledoc.build; const mobiledoc = build(({post, markupSection, cardSection, marker}) => post([ @@ -439,21 +383,16 @@ test('selecting text that includes a card section and deleting deletes card sect Helpers.dom.selectText('bc', editorElement, 'de', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - Helpers.dom.triggerDelete(editor); - - assert.hasElement('#editor p:contains(af)', 'combines sides of selection'); + Helpers.dom.triggerDelete(editor); - assert.hasNoElement('#editor span#card-el', 'card el is removed'); - assert.hasNoElement('#editor p:contains(abc)', 'previous section 1 is removed'); - assert.hasNoElement('#editor p:contains(def)', 'previous section 2 is removed'); + assert.hasElement('#editor p:contains(af)', 'combines sides of selection'); - done(); - }); + assert.hasNoElement('#editor span#card-el', 'card el is removed'); + assert.hasNoElement('#editor p:contains(abc)', 'previous section 1 is removed'); + assert.hasNoElement('#editor p:contains(def)', 'previous section 2 is removed'); }); -test('selecting text that touches bold text should not be considered bold by the toolbar', (assert) => { - const done = assert.async(); +test('selecting text that touches bold text should not be considered bold', (assert) => { const mobiledoc = Helpers.mobiledoc.build(({post, markupSection, marker}) => { return post([markupSection('p', [marker('abc')])]); @@ -464,25 +403,20 @@ test('selecting text that touches bold text should not be considered bold by the Helpers.dom.selectText('b', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - Helpers.toolbar.clickButton(assert, 'bold'); + let command = new BoldCommand(editor); + command.exec(); - assert.hasElement('#editor strong:contains(b)', 'precond - bold text'); + assert.hasElement('#editor strong:contains(b)', 'precond - bold text'); - Helpers.dom.selectText('c', editorElement); - Helpers.dom.triggerEvent(document, 'mouseup'); + Helpers.dom.selectText('c', editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - assert.inactiveButton('bold', 'when selecting text next to bold text, bold button is inactive'); - done(); - }); - }); + let bold = editor.builder.createMarkup('strong'); + assert.ok(editor.markupsInSelection.indexOf(bold) === -1, 'strong is not in selection'); }); // https://github.com/bustlelabs/content-kit-editor/issues/121 test('selecting text that includes a 1-character marker and unbolding it', (assert) => { - const done = assert.async(); - const mobiledoc = Helpers.mobiledoc.build(({post, markupSection, marker, markup}) => { const b = markup('strong'); return post([markupSection('p', [ @@ -499,19 +433,16 @@ test('selecting text that includes a 1-character marker and unbolding it', (asse Helpers.dom.selectText('b', editorElement, 'c', editorElement); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - assert.activeButton('bold'); - Helpers.toolbar.clickButton(assert, 'bold'); - - assert.hasNoElement('#editor strong', 'bold text is unboldened'); + let bold = editor.builder.createMarkup('strong'); + assert.ok(editor.markupsInSelection.indexOf(bold) !== -1, 'strong is in selection'); + let command = new BoldCommand(editor); + command.exec(); - done(); - }); + assert.hasNoElement('#editor strong', 'bold text is unboldened'); }); // see https://github.com/bustlelabs/content-kit-editor/issues/128 test('selecting text that includes an empty section and applying markup to it', (assert) => { - const done = assert.async(); const mobiledoc = Helpers.mobiledoc.build(({post, markupSection, marker}) => { return post([ markupSection('p', [marker('abc')]), @@ -531,13 +462,10 @@ test('selecting text that includes an empty section and applying markup to it', Helpers.dom.moveCursorTo(t1, 0, p2, 0); Helpers.dom.triggerEvent(document, 'mouseup'); - setTimeout(() => { - assert.toolbarVisible(); - Helpers.toolbar.clickButton(assert, 'bold'); + let command = new BoldCommand(editor); + command.exec(); - assert.hasElement('#editor p strong:contains(abc)', 'bold is applied to text'); - done(); - }); + assert.hasElement('#editor p strong:contains(abc)', 'bold is applied to text'); }); // see https://github.com/bustlelabs/content-kit-editor/issues/155 diff --git a/tests/unit/commands/bold-test.js b/tests/unit/commands/bold-test.js new file mode 100644 index 000000000..e8c2eeac4 --- /dev/null +++ b/tests/unit/commands/bold-test.js @@ -0,0 +1,126 @@ +import { Editor } from 'content-kit-editor'; +import Helpers from '../../test-helpers'; +import { MOBILEDOC_VERSION } from 'content-kit-editor/renderers/mobiledoc'; +import BoldCommand from 'content-kit-editor/commands/bold'; + +const { test, module } = Helpers; + +let fixture, editor, editorElement, selectedText, command; + +const mobiledoc = { + version: MOBILEDOC_VERSION, + sections: [ + [], + [[ + 1, 'P', [[[], 0, 'THIS IS A TEST']], + 1, 'P', [[[], 0, 'second section']] + ]] + ] +}; + +module('Unit: BoldCommand', { + beforeEach() { + fixture = document.getElementById('qunit-fixture'); + editorElement = document.createElement('div'); + editorElement.setAttribute('id', 'editor'); + fixture.appendChild(editorElement); + editor = new Editor({mobiledoc}); + editor.render(editorElement); + command = new BoldCommand(editor); + + selectedText = 'IS A'; + Helpers.dom.selectText(selectedText, editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); + }, + + afterEach() { + if (editor) { editor.destroy(); } + } +}); + +test('highlight text, click "bold" button bolds text', (assert) => { + let done = assert.async(); + + setTimeout(() => { + command.exec(); + assert.hasElement('#editor strong:contains(IS A)'); + + done(); + }); +}); + +test('highlight text, click "bold", type more text, re-select text, bold button is active', (assert) => { + let done = assert.async(); + + setTimeout(() => { + command.exec(); + assert.hasElement('#editor strong:contains(IS A)'); + + let boldTag = $('#editor strong:contains(IS A)')[0]; + let textNode = boldTag.childNodes[0]; + assert.equal(textNode.textContent, 'IS A', 'precond - correct node'); + + Helpers.dom.moveCursorTo(textNode, 'IS'.length); + Helpers.dom.insertText(editor, 'X'); + + assert.hasElement('strong:contains(ISX A)', 'adds text to bold'); + + Helpers.dom.selectText('ISX A', editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); + + setTimeout(() => { + let bold = editor.builder.createMarkup('strong'); + assert.ok(editor.markupsInSelection.indexOf(bold) !== -1, 'strong is in selection'); + done(); + }); + }); +}); + +test('exec bold command applies bold to selected text', (assert) => { + const done = assert.async(); + + setTimeout(() => { + assert.ok(editor.markupsInSelection.map(m => m.tagName).indexOf('strong') === -1, 'strong not in selection'); + command.exec(); + assert.ok(editor.markupsInSelection.map(m => m.tagName).indexOf('strong') !== -1, 'strong in selection'); + + assert.hasNoElement('#editor strong:contains(THIS)'); + assert.hasNoElement('#editor strong:contains(TEST)'); + assert.hasElement('#editor strong:contains(IS A)'); + + assert.selectedText(selectedText); + + command.unexec(); + + assert.hasNoElement('#editor strong:contains(IS A)', 'bold text is no longer bold'); + assert.ok(editor.markupsInSelection.map(m => m.tagName).indexOf('strong') === -1, 'strong not in selection'); + + done(); + }); +}); + +test('can unbold part of a larger set of bold text', (assert) => { + const done = assert.async(); + + setTimeout(() => { + assert.ok(editor.markupsInSelection.map(m => m.tagName).indexOf('strong') === -1, 'strong not in selection'); + command.exec(); + assert.ok(editor.markupsInSelection.map(m => m.tagName).indexOf('strong') !== -1, 'strong in selection'); + + assert.hasElement('#editor strong:contains(IS A)'); + + Helpers.dom.selectText('S A', editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); + + setTimeout(() => { + assert.ok(editor.markupsInSelection.map(m => m.tagName).indexOf('strong') !== -1, 'strong in selection'); + command.unexec(); + + assert.hasElement('#editor strong:contains(I)', 'unselected text is bold'); + assert.hasNoElement('#editor strong:contains(IS A)', 'unselected text is bold'); + assert.hasElement('#editor p:contains(S A)', 'unselected text is bold'); + + done(); + }); + }); +}); diff --git a/tests/unit/commands/heading-test.js b/tests/unit/commands/heading-test.js new file mode 100644 index 000000000..9059bdb50 --- /dev/null +++ b/tests/unit/commands/heading-test.js @@ -0,0 +1,107 @@ +import { Editor } from 'content-kit-editor'; +import Helpers from '../../test-helpers'; +import { MOBILEDOC_VERSION } from 'content-kit-editor/renderers/mobiledoc'; +import HeadingCommand from 'content-kit-editor/commands/heading'; +import SubheadingCommand from 'content-kit-editor/commands/subheading'; + +const { test, module } = Helpers; + +let fixture, editor, editorElement, selectedText, command; + +const mobiledoc = { + version: MOBILEDOC_VERSION, + sections: [ + [], + [[ + 1, 'P', [[[], 0, 'THIS IS A TEST']], + 1, 'P', [[[], 0, 'second section']] + ]] + ] +}; + +module('Unit: HeadingCommand', { + beforeEach() { + fixture = document.getElementById('qunit-fixture'); + editorElement = document.createElement('div'); + editorElement.setAttribute('id', 'editor'); + fixture.appendChild(editorElement); + editor = new Editor({mobiledoc}); + editor.render(editorElement); + command = new HeadingCommand(editor); + + selectedText = 'IS A'; + Helpers.dom.selectText(selectedText, editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); + }, + + afterEach() { + if (editor) { editor.destroy(); } + } +}); + +test('highlight text, click "heading" button turns text into h2 header', (assert) => { + command.exec(); + assert.hasElement('#editor h2:contains(THIS IS A TEST)'); + assert.selectedText('THIS IS A TEST', 'expands selection to entire section'); +}); + +test('click heading button triggers update', (assert) => { + const triggered = []; + const triggerFn = editor.trigger; + editor.trigger = (name, ...args) => { + triggered.push(name); + triggerFn.call(editor, name, ...args); + }; + + command.exec(); + assert.ok(triggered.indexOf('update') !== -1, + 'update was triggered'); +}); + +test('highlighting heading text creates section', (assert) => { + assert.inactiveButton('heading'); + assert.ok(editor.activeSections.map(s => s.tagName).indexOf('h2') === -1, 'heading is not in sections'); + + command.exec(); + + assert.ok(editor.activeSections.map(s => s.tagName).indexOf('h2') !== -1, 'heading is in sections'); + + // FIXME must actually trigger the mouseup + Helpers.dom.clearSelection(); + Helpers.dom.triggerEvent(document, 'mouseup'); + + Helpers.dom.selectText(selectedText, editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); + + assert.ok(editor.activeSections.map(s => s.tagName).indexOf('h2') !== -1, 'heading is in sections'); +}); + +test('when heading text is highlighted, exec turns it to plain text', (assert) => { + command.exec(); + assert.hasElement('#editor h2:contains(THIS IS A TEST)'); + + command.unexec(); + + assert.hasNoElement('#editor h2:contains(THIS IS A TEST)'); + assert.hasElement('#editor p:contains(THIS IS A TEST)'); +}); + +test('clicking multiple heading buttons keeps the correct ones active', (assert) => { + let subheadingCommand = new SubheadingCommand(editor); + + subheadingCommand.exec(); + assert.hasElement('#editor h3:contains(THIS IS A TEST)'); + assert.ok(editor.activeSections.map(s => s.tagName).indexOf('h3') !== -1, 'subheading is in sections'); + assert.ok(editor.activeSections.map(s => s.tagName).indexOf('h2') === -1, 'heading is not in sections'); + + command.exec(); + assert.hasElement('#editor h2:contains(THIS IS A TEST)'); + assert.ok(editor.activeSections.map(s => s.tagName).indexOf('h3') === -1, 'subheading is not in sections'); + assert.ok(editor.activeSections.map(s => s.tagName).indexOf('h2') !== -1, 'heading is not in sections'); + + + command.unexec(); + assert.hasElement('#editor p:contains(THIS IS A TEST)'); + assert.ok(editor.activeSections.map(s => s.tagName).indexOf('h3') === -1, 'subheading is not in sections'); + assert.ok(editor.activeSections.map(s => s.tagName).indexOf('h2') === -1, 'heading is not in sections'); +}); diff --git a/tests/unit/commands/italic-test.js b/tests/unit/commands/italic-test.js new file mode 100644 index 000000000..f7d2dc657 --- /dev/null +++ b/tests/unit/commands/italic-test.js @@ -0,0 +1,58 @@ +import { Editor } from 'content-kit-editor'; +import Helpers from '../../test-helpers'; +import { MOBILEDOC_VERSION } from 'content-kit-editor/renderers/mobiledoc'; +import ItalicCommand from 'content-kit-editor/commands/italic'; + +const { test, module } = Helpers; + +let fixture, editor, editorElement, selectedText, command; + +const mobiledoc = { + version: MOBILEDOC_VERSION, + sections: [ + [], + [[ + 1, 'P', [[[], 0, 'THIS IS A TEST']], + 1, 'P', [[[], 0, 'second section']] + ]] + ] +}; + +module('Unit: ItalicCommand', { + beforeEach() { + fixture = document.getElementById('qunit-fixture'); + editorElement = document.createElement('div'); + editorElement.setAttribute('id', 'editor'); + fixture.appendChild(editorElement); + editor = new Editor({mobiledoc}); + editor.render(editorElement); + command = new ItalicCommand(editor); + + selectedText = 'IS A'; + Helpers.dom.selectText(selectedText, editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); + }, + + afterEach() { + if (editor) { editor.destroy(); } + } +}); + +test('can italicize text', (assert) => { + const done = assert.async(); + + setTimeout(() => { + assert.ok(editor.markupsInSelection.map(m => m.tagName).indexOf('em') === -1, 'em not in selection'); + command.exec(); + + assert.hasElement('#editor em:contains(IS A)'); + assert.selectedText('IS A'); + assert.ok(editor.markupsInSelection.map(m => m.tagName).indexOf('em') !== -1, 'em in selection'); + + command.unexec(); + assert.hasNoElement('#editor em:contains(IS A)'); + assert.ok(editor.markupsInSelection.map(m => m.tagName).indexOf('em') === -1, 'em not in selection'); + + done(); + }); +}); diff --git a/tests/unit/commands/link-test.js b/tests/unit/commands/link-test.js new file mode 100644 index 000000000..5ae09e645 --- /dev/null +++ b/tests/unit/commands/link-test.js @@ -0,0 +1,61 @@ +import { Editor } from 'content-kit-editor'; +import Helpers from '../../test-helpers'; +import { MOBILEDOC_VERSION } from 'content-kit-editor/renderers/mobiledoc'; +import LinkCommand from 'content-kit-editor/commands/link'; + +const { module } = Helpers; + +let fixture, editor, editorElement, selectedText, command; + +const mobiledoc = { + version: MOBILEDOC_VERSION, + sections: [ + [], + [[ + 1, 'P', [[[], 0, 'THIS IS A TEST']], + 1, 'P', [[[], 0, 'second section']] + ]] + ] +}; + +module('Unit: LinkCommand', { + beforeEach() { + fixture = document.getElementById('qunit-fixture'); + editorElement = document.createElement('div'); + editorElement.setAttribute('id', 'editor'); + fixture.appendChild(editorElement); + editor = new Editor({mobiledoc}); + editor.render(editorElement); + command = new LinkCommand(editor); + + selectedText = 'IS A'; + Helpers.dom.selectText(selectedText, editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); + }, + + afterEach() { + if (editor) { editor.destroy(); } + } +}); + +Helpers.skipInPhantom('highlight text, exec link command shows input for URL, makes link', (assert) => { + const done = assert.async(); + + setTimeout(() => { + let url = 'http://example.com'; + command.exec(url); + + setTimeout(() => { + assert.hasElement(`#editor a[href="${url}"]:contains(${selectedText})`); + assert.selectedText(selectedText, 'text remains selected'); + Helpers.dom.triggerEvent(document, 'mouseup'); + + setTimeout(() => { + assert.ok(editor.markupsInSelection.map(m => m.tagName).indexOf('a') !== -1, 'a is in selection'); + command.unexec(); + done(); + }); + }); + }); +}); + diff --git a/tests/unit/commands/quote-test.js b/tests/unit/commands/quote-test.js new file mode 100644 index 000000000..3f2fa8bb7 --- /dev/null +++ b/tests/unit/commands/quote-test.js @@ -0,0 +1,50 @@ +import { Editor } from 'content-kit-editor'; +import Helpers from '../../test-helpers'; +import { MOBILEDOC_VERSION } from 'content-kit-editor/renderers/mobiledoc'; +import QuoteCommand from 'content-kit-editor/commands/quote'; + +const { test, module } = Helpers; + +let fixture, editor, editorElement, selectedText, command; + +const mobiledoc = { + version: MOBILEDOC_VERSION, + sections: [ + [], + [[ + 1, 'P', [[[], 0, 'THIS IS A TEST']], + 1, 'P', [[[], 0, 'second section']] + ]] + ] +}; + +module('Unit: QuoteCommand', { + beforeEach() { + fixture = document.getElementById('qunit-fixture'); + editorElement = document.createElement('div'); + editorElement.setAttribute('id', 'editor'); + fixture.appendChild(editorElement); + editor = new Editor({mobiledoc}); + editor.render(editorElement); + command = new QuoteCommand(editor); + + selectedText = 'IS A'; + Helpers.dom.selectText(selectedText, editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); + }, + + afterEach() { + if (editor) { editor.destroy(); } + } +}); + +test('highlight text, exec quote command turns text into blockquote', (assert) => { + const done = assert.async(); + + setTimeout(() => { + command.exec(); + assert.hasElement('#editor blockquote:contains(THIS IS A TEST)'); + + done(); + }); +}); diff --git a/tests/unit/commands/subheading-test.js b/tests/unit/commands/subheading-test.js new file mode 100644 index 000000000..9334906b5 --- /dev/null +++ b/tests/unit/commands/subheading-test.js @@ -0,0 +1,51 @@ +import { Editor } from 'content-kit-editor'; +import Helpers from '../../test-helpers'; +import { MOBILEDOC_VERSION } from 'content-kit-editor/renderers/mobiledoc'; +import SubheadingCommand from 'content-kit-editor/commands/subheading'; + +const { test, module } = Helpers; + +let fixture, editor, editorElement, selectedText, command; + +const mobiledoc = { + version: MOBILEDOC_VERSION, + sections: [ + [], + [[ + 1, 'P', [[[], 0, 'THIS IS A TEST']], + 1, 'P', [[[], 0, 'second section']] + ]] + ] +}; + +module('Unit: SubheadingCommand', { + beforeEach() { + fixture = document.getElementById('qunit-fixture'); + editorElement = document.createElement('div'); + editorElement.setAttribute('id', 'editor'); + fixture.appendChild(editorElement); + editor = new Editor({mobiledoc}); + editor.render(editorElement); + command = new SubheadingCommand(editor); + + selectedText = 'IS A'; + Helpers.dom.selectText(selectedText, editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); + }, + + afterEach() { + if (editor) { editor.destroy(); } + } +}); + +test('highlight text, exec subheading turns text into h3 header', (assert) => { + const done = assert.async(); + + setTimeout(() => { + command.exec(); + assert.hasElement('#editor h3:contains(THIS IS A TEST)'); + + done(); + }); +}); + diff --git a/tests/unit/editor/editor-destroy-test.js b/tests/unit/editor/editor-destroy-test.js deleted file mode 100644 index e449d19f1..000000000 --- a/tests/unit/editor/editor-destroy-test.js +++ /dev/null @@ -1,48 +0,0 @@ -const { module, test } = window.QUnit; -import Helpers from '../../test-helpers'; -import { MOBILEDOC_VERSION } from 'content-kit-editor/renderers/mobiledoc'; - -import { Editor } from 'content-kit-editor'; - -let editor; -let editorElement; - -const mobiledoc = { - version: MOBILEDOC_VERSION, - sections: [ - [], - [[ - 1, 'P', [[[], 0, 'HELLO']] - ]] - ] -}; - - -module('Unit: Editor #destroy', { - beforeEach() { - let fixture = $('#qunit-fixture')[0]; - editorElement = document.createElement('div'); - fixture.appendChild(editorElement); - editor = new Editor({mobiledoc}); - editor.render(editorElement); - }, - afterEach() { - if (editor && !editor._isDestroyed) { - editor.destroy(); - } - } -}); - -test('removes toolbar from DOM', (assert) => { - let done = assert.async(); - - Helpers.dom.selectText('HELLO', editorElement); - Helpers.dom.triggerEvent(document, 'mouseup'); - - setTimeout(() => { - assert.hasElement('.ck-toolbar', 'toolbar is shown'); - editor.destroy(); - assert.hasNoElement('.ck-toolbar', 'toolbar is removed'); - done(); - }); -});