├── .gitignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── README.md
├── bin
├── authorize-push.js
└── build.js
├── demo
├── demo.css
├── entry.js
├── index.html
└── webpack.config.js
├── navigable-table.js
├── package.json
└── test
└── navigable-table-test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist/
3 | log/
4 | demo/bundle.css
5 | demo/bundle.js
6 | demo/*.eot
7 | demo/*.woff2
8 | demo/*.woff2
9 | demo/*.woff
10 | demo/*.ttf
11 | demo/*.svg
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | cache:
4 | directories:
5 | - node_modules
6 | notifications:
7 | email: false
8 | node_js:
9 | - '4'
10 | before_install:
11 | - npm i -g npm
12 | - export DISPLAY=:99.0
13 | - sh -e /etc/init.d/xvfb start
14 | before_script:
15 | - npm prune
16 | after_success:
17 | - npm run semantic-release
18 | - if [[ $TRAVIS_BRANCH == 'master' ]]; then npm run deploy; fi
19 | env:
20 | matrix:
21 | - CLIENT=selenium:firefox
22 | - CLIENT=saucelabs:chrome
23 | branches:
24 | except:
25 | - "/^v\\d+\\.\\d+\\.\\d+$/"
26 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | - Using welcoming and inclusive language
18 | - Being respectful of differing viewpoints and experiences
19 | - Gracefully accepting constructive criticism
20 | - Focusing on what is best for the community
21 | - Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | - The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | - Trolling, insulting/derogatory comments, and personal or political attacks
28 | - Public or private harassment
29 | - Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | - Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at opensource+coc@martynus.net. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [https://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: https://contributor-covenant.org
74 | [version]: https://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Navigable Table – A jQuery plugin
2 |
3 | > A jQuery plugin for elegant editing of data collections.
4 |
5 | [](https://travis-ci.org/gr2m/navigable-table)
6 | [](https://david-dm.org/gr2m/navigable-table)
7 | [](https://david-dm.org/gr2m/navigable-table#info=devDependencies)
8 |
9 | ## Download / Installation
10 |
11 | You can download the latest JS & CSS code here:
12 |
13 | - https://npmcdn.com/navigable-table/dist/navigable-table.js
14 |
15 | Or install via [npm](https://www.npmjs.com/)
16 |
17 | ```
18 | npm install --save navigable-table
19 | ```
20 |
21 | The JS code can be required with
22 |
23 | ```js
24 | var jQuery = require('jquery')
25 | var navigableTable = require('navigable-table')
26 |
27 | // init
28 | navigableTable(jQuery)
29 | ```
30 |
31 | ## Usage
32 |
33 | ```html
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
67 | ```
68 |
69 | ## Events
70 |
71 | ```
72 | // bump event when there is no column / row to jump / move to
73 | $table.on('bump', function(event, direction) {});
74 | $table.on('bump:up', function(event) {});
75 | $table.on('bump:down', function(event) {});
76 |
77 | // move events, when a row is moved up or down
78 | $table.on('move', function(event, direction, index) {});
79 | $table.on('move:up', function(event, index) {});
80 | $table.on('move:down', function(event, index) {});
81 |
82 | // move events, before a row is moved up or down.
83 | // The 'move' events can be preventend by calling cancelMove()
84 | $table.on('before:move', function(event, direction, index, cancelMove) {});
85 | $table.on('before:move:up', function(event, index, cancelMove) {});
86 | $table.on('before:move:down', function(event, index, cancelMove) {});
87 | ```
88 |
89 | ## Local Setup
90 |
91 | ```bash
92 | git clone git@github.com:gr2m/navigable-table.git
93 | cd navigable-table
94 | npm install
95 | ```
96 |
97 | ## Test
98 |
99 | You can start a local dev server with
100 |
101 | ```bash
102 | npm start
103 | ```
104 |
105 | Run tests with
106 |
107 | ```bash
108 | npm test
109 | ```
110 |
111 | While working on the tests, you can start Selenium / Chrome driver
112 | once, and then tests re-run on each save
113 |
114 | ```bash
115 | npm run test:mocha:watch
116 | ```
117 |
118 | ## Fine Print
119 |
120 | The Expandable Input Plugin have been authored by [Gregor Martynus](https://github.com/gr2m),
121 | proud member of the [Hoodie Community](http://hood.ie/).
122 |
123 | License: MIT
124 |
--------------------------------------------------------------------------------
/bin/authorize-push.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var exec = require('child_process').exec
4 |
5 | var GH_TOKEN = process.env.GH_TOKEN
6 | var repo = require('../package.json').repository.url
7 |
8 | if (!(process.env.CI && GH_TOKEN && repo)) {
9 | console.log('[authorize-push] ignodered because condition not fullfillled (process.env.CI: %s, GH_TOKEN: %s, repo: %s)', !!process.env.CI, !!GH_TOKEN, !!repo)
10 | process.exit(1)
11 | }
12 |
13 | var commands = [
14 | 'git remote set-url origin ' + repo.replace('https://', 'https://' + GH_TOKEN + '@'),
15 | 'git config user.email "gregor@martynus.net"',
16 | 'git config user.name "gr2m"'
17 | ]
18 | commands.forEach(function (command) {
19 | console.log('[authorize-push] %s', command.replace(GH_TOKEN, '***GH_TOKEN***'))
20 | exec(command)
21 | })
22 |
--------------------------------------------------------------------------------
/bin/build.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var resolvePath = require('path').resolve
4 |
5 | var ExtractTextPlugin = require('extract-text-webpack-plugin')
6 | var webpack = require('webpack')
7 |
8 | // returns a Compiler instance
9 | var compiler = webpack({
10 | entry: [
11 | './navigable-table.js'
12 | ],
13 | output: {
14 | path: resolvePath(__dirname, '../dist'),
15 | filename: 'navigable-table.js'
16 | },
17 | module: {
18 | loaders: [
19 | {
20 | test: /\.css$/,
21 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader')
22 | }
23 | ]
24 | },
25 | modulesDirectories: [
26 | 'node_modules'
27 | ]
28 | })
29 |
30 | compiler.run(function (error, stats) {
31 | if (error) {
32 | throw error
33 | }
34 | })
35 |
--------------------------------------------------------------------------------
/demo/demo.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Helvetica, Arial;
3 | }
4 | header {
5 | padding: 18px 18px 24px;
6 | background: #222;
7 | color: #fff;
8 |
9 | /* hoodie hoodie hoodie! */
10 | background-image: linear-gradient(90deg, #2C2A86 20.5%, #078D2D 20.5%, #078D2D 37.5%, #FCB20D 37.5%, #FCB20D 55.5%, #EC5100 55.5%, #EC5100 72.5%, #6A3C06 72.5%, #6A3C06 79.5%, #C01C1B 79.5%);
11 | background-size: 300px 6px;
12 | background-repeat: repeat-x;
13 | background-position: 0 100%;
14 | }
15 | header a {
16 | color: #fff !important;
17 | }
18 | header h1 {
19 | margin: 0;
20 | font-size: 28px;
21 | font-weight: bold;
22 | }
23 | header h6 {
24 | margin: 0;
25 | }
26 | h3 {
27 | padding: 32px 8px 4px;
28 | font-size: 14px;
29 | font-weight: bold;
30 | color: #444;
31 | }
32 | .shortcuts {
33 | font-size: 12px;
34 | }
35 | .shortcuts dt {
36 | width: 80px;
37 | }
38 | .shortcuts dd {
39 | margin-left: 88px;
40 | }
41 | p.info {
42 | padding: 0 8px;
43 | font-size: 10px;
44 | color: #888;
45 | }
46 | p.info a {
47 | color: inherit;
48 | text-decoration: underline;
49 | }
50 | .os-mac .os-other,
51 | .os-other .os-mac {
52 | display: none;
53 | }
54 |
--------------------------------------------------------------------------------
/demo/entry.js:
--------------------------------------------------------------------------------
1 | require('bootstrap/dist/css/bootstrap.css')
2 | require('./demo.css')
3 |
4 | var jQuery = require('jquery')
5 | var navigableTable = require('../navigable-table')
6 | var editableTable = require('editable-table')
7 | var expandableInput = require('expandable-input')
8 |
9 | expandableInput(jQuery)
10 | editableTable(jQuery)
11 | navigableTable(jQuery)
12 |
13 | window.$ = window.jQuery = jQuery
14 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Navigable Table – A jQuery plugin
6 |
7 |
8 |
9 |
10 |
11 |
12 | Navigable Table
13 |
14 |
15 |
16 |
87 |
88 | Supported keyboard shortcuts
89 |
90 | jump ⌘ alt + arrow keys
91 | move ⌘ alt + shift + up/down
92 | insert ⌘ alt + enter (+ shift)
93 | duplicate ⌘ alt + d (+ shift)
94 | delete ⌘ alt + shift + backspace
95 |
96 |
97 |
98 | Note: This demo is using the Editable Table
99 | plugin for auto-adding of new rows, and for styling.
100 | Support keybord shortcuts:
101 |
102 |
103 |
104 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/demo/webpack.config.js:
--------------------------------------------------------------------------------
1 | var ExtractTextPlugin = require('extract-text-webpack-plugin')
2 |
3 | module.exports = {
4 | entry: './entry.js',
5 | output: {
6 | path: __dirname,
7 | filename: 'bundle.js'
8 | },
9 | module: {
10 | loaders: [
11 | {
12 | test: /\.css$/,
13 | // loader: 'style-loader!css-loader'
14 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader')
15 | },
16 | {
17 | test: /\.(png|woff|woff2|eot|ttf|svg)$/,
18 | loader: 'url-loader?limit=100000'
19 | }
20 | ]
21 | },
22 | modulesDirectories: [
23 | 'node_modules'
24 | ],
25 | plugins: [
26 | new ExtractTextPlugin('bundle.css')
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/navigable-table.js:
--------------------------------------------------------------------------------
1 | module.exports = navigableTable
2 |
3 | function navigableTable ($) {
4 | // NAVIGABLE TABLE CLASS DEFINITION
5 | // ================================
6 |
7 | //
8 | var NavigableTable = function (el) {
9 | var $table, $body
10 | var keyboardShortcutsMetakey
11 | var focusableSelector = '[name]:visible,a,[contenteditable]'
12 | var inputSelector = '[name]:visible,[contenteditable]'
13 |
14 | //
15 | //
16 | //
17 | function initialize () {
18 | $table = $(el)
19 | $body = $table.find('tbody')
20 |
21 | if (window.navigator.appVersion.indexOf('Mac') !== -1) {
22 | keyboardShortcutsMetakey = 'metaKey'
23 | } else {
24 | keyboardShortcutsMetakey = 'altKey'
25 | }
26 |
27 | $body.on('keydown', 'tr', handleKeyDown)
28 | }
29 |
30 | // Event handlers
31 | // --------------
32 |
33 | //
34 | //
35 | //
36 | function handleKeyDown (event) {
37 | var input = event.target
38 | var shiftKeyPressed = event.shiftKey
39 | var keyCode = event.keyCode
40 |
41 | if (!event[keyboardShortcutsMetakey]) return
42 |
43 | return navigate(input, keyCode, shiftKeyPressed)
44 | }
45 |
46 | // Methods
47 | // -------
48 |
49 | function navigate (input, keyCode, shiftKeyPressed) {
50 | switch (keyCode) {
51 | case 37: // left
52 | return shiftKeyPressed ? true : jump('left', input)
53 | case 39: // right
54 | return shiftKeyPressed ? true : jump('right', input)
55 | case 38: // up
56 | return shiftKeyPressed ? moveUp(input) : jump('up', input)
57 | case 40: // down
58 | return shiftKeyPressed ? moveDown(input) : jump('down', input)
59 | case 68: // d (duplicate)
60 | return shiftKeyPressed ? duplicateUp(input) : duplicateDown(input)
61 | case 13: // enter (insert)
62 | return shiftKeyPressed ? insertUp(input) : insertDown(input)
63 | case 8: // del
64 | return shiftKeyPressed ? remove(input) : true
65 | }
66 | }
67 |
68 | //
69 | function jump (direction, input) {
70 | var $input = $(input)
71 | var $targetInput = getJumpTargetInput(direction, $input)
72 |
73 | // workaround for:
74 | // https://github.com/gr2m/minutes.io/issues/67
75 | var value = $input.val()
76 | if ($input.is('select')) {
77 | setTimeout(function () {
78 | $input.val(value)
79 | })
80 | }
81 |
82 | if (!$targetInput.length) {
83 | $input.trigger('bump', [direction])
84 | $input.trigger('bump:' + direction)
85 | return
86 | }
87 |
88 | $targetInput.focus().select()
89 | return false
90 | }
91 |
92 | //
93 | // moves current row up, by moving the above row down.
94 | //
95 | function moveUp (input) {
96 | var $row = $(input).closest('tr')
97 | var $prev = $row.prev()
98 | var index
99 | var moveRows = true
100 |
101 | if ($prev.length === 0) return false
102 |
103 | function cancelMove () {
104 | moveRows = false
105 | }
106 | index = $row.index()
107 | $row.trigger('before:move', ['up', index, cancelMove])
108 | $row.trigger('before:move:up', [index, cancelMove])
109 |
110 | if (!moveRows) return
111 |
112 | $prev.insertAfter($row)
113 | index -= 1
114 | $row.trigger('move', ['up', index])
115 | $row.trigger('move:up', [index])
116 |
117 | return false
118 | }
119 |
120 | //
121 | // moves current row down, by moving the below row up.
122 | //
123 | function moveDown (input) {
124 | var $row = $(input).closest('tr')
125 | var $next = $row.next()
126 | var index
127 | var moveRows = true
128 |
129 | if ($next.length === 0) return false
130 |
131 | function cancelMove () {
132 | moveRows = false
133 | }
134 | index = $row.index()
135 | $row.trigger('before:move', ['down', index, cancelMove])
136 | $row.trigger('before:move:down', [index, cancelMove])
137 |
138 | if (!moveRows) return
139 | $next.insertBefore($row)
140 | index += 1
141 | $row.trigger('move', ['down', index])
142 | $row.trigger('move:down', [index])
143 |
144 | return false
145 | }
146 |
147 | //
148 | function duplicateUp (input) {
149 | var $row = $(input).closest('tr')
150 | var $newRow = $row.clone()
151 | passSelecectValues($row, $newRow)
152 | $row.before($newRow)
153 | $row.trigger('duplicate', ['up', $newRow])
154 | $row.trigger('duplicate:up', [$newRow])
155 | jump('up', input)
156 | return false
157 | }
158 |
159 | //
160 | function duplicateDown (input) {
161 | var $row = $(input).closest('tr')
162 | var $newRow = $row.clone()
163 | passSelecectValues($row, $newRow)
164 | $row.after($newRow)
165 | $row.trigger('duplicate', ['down', $newRow])
166 | $row.trigger('duplicate:down', [$newRow])
167 | jump('down', input)
168 | return false
169 | }
170 |
171 | //
172 | function insertUp (input) {
173 | var $row = $(input).closest('tr')
174 | var $newRow = $row.clone()
175 | $newRow.find(inputSelector).val('')
176 | $row.before($newRow)
177 | $row.trigger('insert', ['up', $newRow])
178 | $row.trigger('insert:up', [$newRow])
179 | jump('up', input)
180 | return false
181 | }
182 |
183 | //
184 | function insertDown (input) {
185 | var $row = $(input).closest('tr')
186 | var $newRow = $row.clone()
187 | $newRow.find(inputSelector).val('')
188 | $row.after($newRow)
189 | $row.trigger('insert', ['down', $newRow])
190 | $row.trigger('insert:down', [$newRow])
191 | jump('down', input)
192 | return false
193 | }
194 |
195 | //
196 | function remove (input) {
197 | var $row = $(input).closest('tr')
198 | var $next = $row.next()
199 | // if there is a next row, and it's not the last one ...
200 | if ($next.length && !$next.is(':last-child')) {
201 | jump('down', input)
202 | } else {
203 | jump('up', input)
204 | }
205 | $row.remove()
206 | return false
207 | }
208 |
209 | //
210 | //
211 | //
212 | function getJumpTargetInput (direction, $input) {
213 | if (direction === 'up' || direction === 'down') {
214 | return getJumpTargetRowInput(direction, $input)
215 | }
216 |
217 | return getJumpTargetColumnInput(direction, $input)
218 | }
219 |
220 | //
221 | function getJumpTargetRowInput (direction, $input) {
222 | var $cell = $input.closest('td')
223 | var $inputsInCell = $cell.find(focusableSelector)
224 | var currentInputIndex = $inputsInCell.index($input)
225 | var $row = $cell.parent()
226 | var $targetRow = (direction === 'up') ? $row.prev() : $row.next()
227 | var $targetCell = $targetRow.children('td,th').eq($cell.index())
228 |
229 | return $targetCell.find(focusableSelector).eq(currentInputIndex)
230 | }
231 |
232 | //
233 | function getJumpTargetColumnInput (direction, $input) {
234 | var $cell = $input.closest('td')
235 | var $inputsInCell = $cell.find(focusableSelector)
236 | var currentInputIndex
237 | var $targetCell
238 |
239 | // if there are more than one inputs in the current cell,
240 | // jump to the one on the left/right, if any
241 | if ($inputsInCell.length > 1) {
242 | currentInputIndex = $inputsInCell.index($input)
243 | if (direction === 'left' && currentInputIndex > 0) {
244 | return $inputsInCell.eq(currentInputIndex - 1)
245 | }
246 | if (direction === 'right' && currentInputIndex < $inputsInCell.length - 1) {
247 | return $inputsInCell.eq(currentInputIndex + 1)
248 | }
249 | }
250 |
251 | $targetCell = (direction === 'left') ? $cell.prev() : $cell.next()
252 | return $targetCell.find(focusableSelector).eq(0)
253 | }
254 |
255 | //
256 | // when cloning a DOM element, values of elements
257 | // do not get cloned. We do it manually with this helper
258 | //
259 | function passSelecectValues ($row, $newRow) {
260 | var $originalSelects = $row.find('select')
261 | var $newSelects = $newRow.find('select')
262 |
263 | $originalSelects.each(function (index) {
264 | $newSelects.eq(index).val(this.value)
265 | })
266 | }
267 |
268 | initialize()
269 | }
270 |
271 | // EDITABLE TABLE PLUGIN DEFINITION
272 | // ================================
273 |
274 | $.fn.navigableTable = function () {
275 | return this.each(function () {
276 | var $this = $(this)
277 | var api = $this.data('bs.navigableTable')
278 |
279 | if (!api) {
280 | $this.data('bs.navigableTable', (api = new NavigableTable(this)))
281 | }
282 | })
283 | }
284 |
285 | $.fn.navigableTable.Constructor = NavigableTable
286 |
287 | // EDITABLE TABLE DATA-API
288 | // =======================
289 |
290 | $(document).on('keydown.bs.navigableTable.data-api focus.bs.navigableTable.data-api', 'table[data-navigable-spy]', function (event) {
291 | var $table = $(event.currentTarget)
292 |
293 | event.preventDefault()
294 | event.stopImmediatePropagation()
295 |
296 | $table.removeAttr('data-navigable-spy')
297 | $table.navigableTable()
298 | $(event.target).trigger($.Event(event))
299 | })
300 | }
301 |
302 | // if run in a browser, init immediately
303 | if (typeof window !== 'undefined' && window.jQuery) {
304 | navigableTable(window.jQuery)
305 | }
306 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "navigable-table",
3 | "description": "A jQuery plugin to use <{span|div|...} contenteditable> as expandable inputs.",
4 | "main": "navigable-table.js",
5 | "files": [
6 | "demo/",
7 | "dist/"
8 | ],
9 | "directories": {
10 | "test": "test"
11 | },
12 | "scripts": {
13 | "build": "bin/build.js",
14 | "build:demo": "webpack --context demo/ --config demo/webpack.config.js -p",
15 | "deploy": "npm run build:demo && npm run deploydocs",
16 | "predeploydocs": "bin/authorize-push.js",
17 | "deploydocs": "gh-pages-deploy",
18 | "start": "frontend-test-server",
19 | "pretest": "standard",
20 | "test": "frontend-test-background mocha test/*.js",
21 | "test:watch": "frontend-test-background mocha test/*.js --watch --reporter min",
22 | "presemantic-release": "npm run build",
23 | "semantic-release": "semantic-release pre && npm publish && semantic-release post"
24 | },
25 | "repository": "github:gr2m/navigable-table",
26 | "keywords": [
27 | "bootstrap",
28 | "input",
29 | "browser"
30 | ],
31 | "author": "Gregor Martynus",
32 | "license": "MIT",
33 | "devDependencies": {
34 | "@gr2m/frontend-test-setup": "^1.1.6",
35 | "bootstrap": "^3.3.6",
36 | "browserify": "^13.0.0",
37 | "chai": "^3.5.0",
38 | "css-loader": "^0.23.1",
39 | "editable-table": "^1.0.0",
40 | "expandable-input": "^1.0.4",
41 | "extract-text-webpack-plugin": "^1.0.1",
42 | "file-loader": "^0.8.5",
43 | "gh-pages-deploy": "^0.4.0",
44 | "jquery": "^2.2.0",
45 | "mocha": "^2.4.5",
46 | "semantic-release": "^6.2.0",
47 | "standard": "^6.0.4",
48 | "style-loader": "^0.13.0",
49 | "uglify-js": "^2.6.1",
50 | "url-loader": "^0.5.7",
51 | "webpack": "^1.12.13",
52 | "webpack-dev-server": "^1.14.1"
53 | },
54 | "gh-pages-deploy": {
55 | "staticpath": "demo",
56 | "noprompt": true
57 | },
58 | "standard": {
59 | "ignore": [
60 | "demo",
61 | "dist"
62 | ]
63 | },
64 | "frontend-test-setup": {
65 | "server": {
66 | "cmd": "webpack-dev-server --context demo --config demo/webpack.config.js --content-base demo -d"
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/test/navigable-table-test.js:
--------------------------------------------------------------------------------
1 | /* global describe, beforeEach, it */
2 |
3 | // we delete the cached require of '@gr2m/frontend-test-setup'
4 | // to make it work with mocha --watch.
5 | delete require.cache[require.resolve('@gr2m/frontend-test-setup')]
6 | require('@gr2m/frontend-test-setup')
7 |
8 | var expect = require('chai').expect
9 |
10 | function toValue (result) {
11 | if (typeof result !== 'object') {
12 | return result
13 | }
14 |
15 | if (isError(result.value)) {
16 | var error = new Error(result.value.message)
17 | Object.keys(result.value).forEach(function (key) {
18 | error[key] = result.value[key]
19 | })
20 | throw error
21 | }
22 |
23 | return result.value
24 | }
25 |
26 | function isError (value) {
27 | return value && value.name && /error/i.test(value.name)
28 | }
29 |
30 | describe('=== navigable-table ===', function () {
31 | this.timeout(90000)
32 |
33 | beforeEach(function () {
34 | return this.client.url('/')
35 | })
36 |
37 | it('sanity check', function () {
38 | return this.client
39 | .url('/index.html')
40 | .execute(function getTitle () {
41 | return document.title
42 | }).then(toValue)
43 | .then(function (title) {
44 | expect(title).to.equal('Navigable Table – A jQuery plugin')
45 | })
46 | })
47 | })
48 |
--------------------------------------------------------------------------------