UNPKG

featherlight

Version:

Featherlight is a very lightweight jQuery lightbox plugin. It's simple yet flexible and easy to use. Featherlight has minimal css and uses no inline styles, everything is name-spaced, it's completely customizable via configuration object and offers image,

484 lines (429 loc) 17.7 kB
var expect = chai.expect; var stubAjaxLoad = function(content) { var oldLoad = $.fn.load; $.fn.load = function(url, callback) { var $this = this; $.fn.load = oldLoad; window.setTimeout(function() { $this.html(content); callback(null, "success"); }); return this; }; }; (function($) { var $htmlFixtures = null; var resetFixtures = function(){ if (!$htmlFixtures) $htmlFixtures = $('#fixtures').detach(); $('body >:not(#mocha)').remove() $('body').append($htmlFixtures.clone(true)); }; var triggerKeyCode = function(keyCode) { $(window).trigger($.Event("keyup", { keyCode: keyCode })); }; var triggerEscape = function(){ triggerKeyCode(27); }; $.fx.off = true; $.fn.toJSON = function() { return 'jQuery: ' + this.selector + ' (' + this.length + ')' }; describe('Featherlight', function() { beforeEach(resetFixtures); after(resetFixtures); it ('works on items with data-featherlight by default', function(done) { expect($('img')).to.not.be.visible; $('#auto-bound').click(); patiently(done, [ function() { expect($('.featherlight img')).to.be.visible; expect($('.featherlight img')).to.have.attr('src').equal('fixtures/photo.jpeg'); $('.featherlight').click(); }, function() { expect($('img')).to.not.be.visible; } ]); }); it ('works on items with data-featherlight by default', function(done) { $('body').append('<div id="auto-bound-2" data-featherlight="fixtures/photo.jpeg">Dynamic</div>') expect($('img')).to.not.be.visible; $('#auto-bound-2').click(); patiently(done, [ function() { expect($('.featherlight img')).to.be.visible; expect($('.featherlight img')).to.have.attr('src').equal('fixtures/photo.jpeg'); $('.featherlight').click(); }, function() { expect($('img')).to.not.be.visible; } ]); }); it ('does not move content that was already placed in the featherlight by content-filters', function() { $.featherlight.contentFilters.advancedExample = { process: function() { return $('<p>Hello</p>').appendTo(this.$instance); } }; $.featherlight({advancedExample: 'dummy'}); expect($('.featherlight > p').length).to.equal(1); }); describe('jQuery#featherlight', function() { it('is chainable', function() { // Not a bad test to run on collection methods. var $all_links = $('a') expect($all_links.featherlight()).to.equal($all_links); }); it("won't open a dialog if the event is already prevented", function(){ $('#plain-photo-link').on('click', function(event) { event.preventDefault(); }) .featherlight().click(); expect($.featherlight.current()).to.be.null; }); }); describe('jQuery.featherlight', function() { it('opens a dialog box', function() { $.featherlight('<p class="testing">This is a test<p>'); expect($('.featherlight p.testing')).to.be.visible; }); }); describe('jQuery.featherlight#open', function() { it('returns a promise', function(done) { var fl = new $.featherlight('<p class="testing">This is a test<p>'); expect($.featherlight.current()).to.be.null; var evt = $.Event('dummy'); evt.preventDefault(); expect(fl.open(evt).state()).to.equal('rejected'); expect($.featherlight.current()).to.be.null; fl.open().then(function(){ done(); }); }); }); describe('jQuery.featherlight.current', function() { it('returns null if no dialogbox is currently opened', function() { expect($.featherlight.current()).to.be.null; /* even if opened and then closed */ $.featherlight('<p class="testing">This is a test<p>'); $('.featherlight').click(); expect($.featherlight.current()).to.be.null; /* even if savagely removed */ $.featherlight('<p class="testing">This is a test<p>'); $('.featherlight').remove(); expect($.featherlight.current()).to.be.null; }); it('returns the featherlight object of the last currently opened dialog', function() { var first = $.featherlight('<p>First<p>'); expect($.featherlight.current()).to.equal(first); var second = $.featherlight('<p>Inner<p>', {namespace: 'different_namespace'}); expect($.featherlight.current()).to.equal(second); $('.different_namespace').click(); expect($.featherlight.current()).to.equal(first); }); }); describe('jQuery.featherlight.opened', function() { it('returns [] ]if no dialogbox is currently opened', function() { expect($.featherlight.opened()).to.eql([]); /* even if opened and then closed */ $.featherlight('<p class="testing">This is a test<p>'); $('.featherlight').click(); expect($.featherlight.opened()).to.eql([]); /* even if savagely removed */ $.featherlight('<p class="testing">This is a test<p>'); $('.featherlight').remove(); expect($.featherlight.opened()).to.eql([]); }); it('returns the featherlight objects of the currently opened dialog', function() { var first = $.featherlight('<p>First<p>'); expect($.featherlight.opened()).to.eql([first]); var second = $.featherlight('<p>Inner<p>', {namespace: 'different_namespace'}); expect($.featherlight.opened()).to.eql([first, second]); $('.different_namespace').click(); expect($.featherlight.opened()).to.eql([first]); }); }); describe('jQuery.featherlight.close', function() { it('closes the currently opened window, if any', function() { $.featherlight('<p class="testing">This is a test<p>'); $.featherlight.close(); expect($.featherlight.current()).to.be.null; $.featherlight.close(); /* should not create error */ }); }); describe('jQuery.featherlight.close', function() { it('returns a promise', function(done) { $.featherlight('<p class="testing">This is a test<p>'); $.featherlight.close().then(function(){ done() }); }); }); describe('image content filter', function() { it('stores the natural width & height', function(done) { $('#plain-photo-link').featherlight({ afterOpen: function() { expect(this.$content.naturalWidth).to.equal(200); done(); } }).click(); }); }); describe('configuration', function() { it('can be set using data-featherlight-*', function() { $('#data-attr-test a').featherlight().click(); expect($.featherlight.current()).to.have.properties({ variant: 'test', closeOnEsc: false, closeSpeed: 42, }); expect($.featherlight.current().$instance.find('b')).to.have.text('Added in callback'); }); it('can be set using data-featherlight-* on filtered element too', function() { $('#data-attr-filter-test a:first').click(); expect($.featherlight.current()).to.have.property('variant').equal('wrapper'); $('#data-attr-filter-test a:last').click(); expect($.featherlight.current()).to.have.property('variant').equal('inner'); $('<a data-featherlight-variant="dynamic" href=".some-content">z</a>') .appendTo($('#data-attr-filter-test')).click() expect($.featherlight.current()).to.have.property('variant').equal('dynamic'); }); it('can specify to close or not on escape key', function() { var first = $.featherlight('<p/>'), second = $.featherlight('<p/>'), third = $.featherlight('<p/>', {closeOnEsc: false}), last = $.featherlight('<p/>', {closeOnEsc: true}); expect($.featherlight._globalHandlerInstalled).to.be.true; triggerEscape(); expect($.featherlight.current()).to.equal(third); triggerEscape(); expect($('.featherlight')).with.length(3); $('.featherlight:last').click(); expect($.featherlight.current()).to.equal(second); triggerEscape(); expect($.featherlight.current()).to.equal(first); expect($.featherlight._globalHandlerInstalled).to.be.true; $.featherlight.current().close(); expect($.featherlight._globalHandlerInstalled).to.be.false; }); it('can specify to a close icon', function() { var fl = $.featherlight('<p/>', {closeIcon: '<div class="test">X</div>'}); expect($.featherlight.current()).to.equal(fl); $('.test').click(); expect($.featherlight.current()).to.be.null; }); it('can specify a key handler', function() { var lastKeyCode; $.featherlight('<p/>', { onKeyUp: function(event) { lastKeyCode = event.keyCode; } }); triggerKeyCode(25); expect(lastKeyCode).to.equal(25); triggerKeyCode(27); expect(lastKeyCode).to./* still be */equal(25); /* since event is handled by FL */ expect($.featherlight.current()).to.be.null; }); it('can specify a resize handler', function() { var resizes = [], open = function() { $.featherlight('<p/>', { onResize: function(event) { resizes.push(this.id); } }); } open(); open(); $(window).trigger('resize'); open(); $.featherlight.current().close(); $.featherlight.current().close(); $(window).trigger('resize'); resizes = $.map(resizes, function(id) { return id - $.featherlight.current().id }); expect(resizes).to.eql([0, 1, 1, 0, 2, 0]); }); it('can specify a filter for events', function() { $("#filter-test .group").featherlight({filter: '.yes', type: 'text'}) .append('<span class="yes" href="filter Appended">Photo</span>'); $("#filter-test span").each(function(){ $(this).click(); }); expect($('.featherlight')).with.length(2); expect($('.featherlight:first .featherlight-content')).to.contain('filter Yes'); expect($('.featherlight:last .featherlight-content')).to.contain('filter Appended'); }); it('can specify explicit content', function() { $.featherlight({text: 'A <div> & "quoted text"'}); expect($('.featherlight')).to.contain('A <div> & "quoted text"'); }); it('can specify ajax content', function(done) { $.featherlight({ajax: 'featherlight.html .some-content'}); patiently(done, function() { expect($('.featherlight')).to.contain('Hello'); }); }); it('ajax content can be text only', function(done) { stubAjaxLoad('Hello <b>world</b>'); $.featherlight({ajax: 'stubbed'}); patiently(done, function() { expect($('.featherlight')).to.contain('Hello'); }); }); it('can specify an alternate close button selector', function() { var fl = $.featherlight('<div>Test<div class="close-me">close</div></div>', {otherClose: '.close-me'}); expect($.featherlight.current()).to.equal(fl); $('.close-me').click(); expect($.featherlight.current()).to.be.null; }); it('can specify an alternate root selector', function() { $.featherlight('<div>Test<div class="close-me">close</div></div>', {root: '#fixtures'}); expect($('#fixtures').children().last()).to.have.class('featherlight'); }); it('can specify callbacks to track the progress of the dialog', function() { var callbacks = ['beforeOpen', 'beforeContent', 'afterContent', 'afterOpen', 'beforeClose', 'afterClose'], lastCallback = undefined, options = {}; $.each(callbacks, function(i, cb){ options[cb] = function() { expect(lastCallback).to.equal(callbacks[i-1]); lastCallback = cb; } }); $.featherlight($('<p>Hello</p>'), options); expect(lastCallback).to.equal('afterOpen'); $.featherlight.current().close(); expect(lastCallback).to.equal('afterClose'); }); it('can be interrupted via beforeOpen', function() { $.featherlight({text: "Hello", beforeOpen: function() { return false; }}); expect($.featherlight.current()).to.be.null; // Tricky case: $.featherlight.defaults.beforeOpen = function() { return false; } $('#auto-bound').click(); expect($.featherlight.current()).to.be.null; $.featherlight.defaults.beforeOpen = $.noop; }); it('provides event to beforeClose when close via closebox', function() { $.featherlight({text: "Hello", beforeClose: function(evt) { expect(evt).to.be.an('object'); }}); $('.featherlight-close').click(); }); it('provides event to beforeClose & al when closed via escape', function() { $.featherlight({text: "Hello", beforeClose: function(evt) { expect(evt).to.be.an('object'); }}); triggerEscape(); }); it('passes event to beforeClose & al when closed via global function', function() { $.featherlight({text: "Hello", beforeClose: function(evt) { expect(evt).to.eql(42); }}); $.featherlight.close(42); }); it('can specify a loading text', function(done) { stubAjaxLoad('<b>Hi</b>'); $.featherlight({ajax: 'stubbed', loading: "Spinner!"}); expect($('.featherlight')).to.contain('Spinner!'); expect($('.featherlight')).to.have.class('featherlight-loading'); patiently(done, function() { expect($('.featherlight')).to.contain('Hi'); expect($('.featherlight')).not.to.contain('Spinner!'); expect($('.featherlight')).not.to.have.class('featherlight-loading'); }); }); it('can specify a different namespace', function() { $('#namespace-test a').featherlight({namespace: 'custom-namespace'}).click(); expect($.featherlight.current().$instance.find('b')).to.have.text('Namespace ok'); }); it('can specify to persist the content', function() { var $elem = $('<div class="keepme"/>').appendTo('body'); expect($('.keepme')).with.length(1).to.have.text(''); var fl = $.featherlight({ jquery: $elem, persist: true, afterContent: function() { this.$content.append('<i>1</i>'); }, afterOpen: function() { this.$content.append('<b>2</b>'); } }); expect($('.keepme')).with.length(1).to.have.text('12'); $.featherlight.close(); fl.open(); expect($('.keepme')).with.length(1).to.have.text('1212'); $.featherlight.close(); }); var persistTest = function(setting, lastValue) { $('<div class="keepme">Foo</div>').appendTo('body'); $('<div class="other">Bar</div>').appendTo('body'); var $buttons = $('<a href=".keepme"/><a href=".other"/>').appendTo('body'); $buttons.featherlight({ persist: setting }); $buttons.first().click(); $.featherlight.close(); $buttons.first().click(); expect($('.featherlight-inner')).to.have.text('Foo'); $.featherlight.close(); $buttons.last().click(); expect($('.featherlight-inner')).to.have.text(lastValue); }; it('can specify to persist the content individually', function() { persistTest(true, 'Bar'); }); it('can specify to persist the share the content as a group', function() { persistTest('shared', 'Foo'); }); }); describe('error handling', function() { it('is quite basic', function(done) { $.featherlight('does_not_exist.jpg'); patiently(done, function() { expect($('.featherlight-image')).to.have.attr('src').equal('does_not_exist.jpg'); expect($('.featherlight')).not.to.have.class('featherlight-loading'); }); }); }); describe('iframe content filter', function() { it('generates an iframe with the right attributes and css', function(done) { $('<a data-featherlight-iframe="#test-iframe" data-featherlight-iframe-allowfullscreen="true" '+ 'data-featherlight-iframe-width="323" data-featherlight-iframe-min-height="212">').featherlight().click(); expect($('.featherlight iframe')).to.have.attr('src').equal('#test-iframe'); expect($('.featherlight iframe')).to.have.css('width').equal('323px'); expect($('.featherlight iframe')).to.have.css('min-height').equal('212px'); expect($('.featherlight iframe')).to.have.attr('allowfullscreen').equal('true'); expect($('.featherlight iframe')).not.to.have.attr('min-height'); patiently(done, function() { expect($('.featherlight')).to.have.attr('class').equal('featherlight featherlight-iframe'); }); }); it('allows "about:blank"', function() { $('<a data-featherlight-iframe="about:blank">').featherlight().click(); expect($('.featherlight iframe')).to.have.attr('src').equal('about:blank'); }); }); describe('accessibility', function() { it('has a focusable close button', function() { $.featherlight({text: 'Hello'}); close = $('.featherlight-close-icon') close.focus(); expect($('.featherlight-close-icon').attr('tabindex')).to.be.undefined; expect($(document.activeElement)).to.have.class('featherlight-close-icon'); }); it('removes focus and then resets it', function() { $('.for-focus').focus(); $.featherlight({text: 'Hello'}); expect($('.for-focus')).to.have.attr('tabindex', '-1'); expect($(document.activeElement)).not.to.have.class('for-focus'); $.featherlight.close(); expect($(document.activeElement)).to.have.class('for-focus'); expect($('.for-focus').attr('tabindex')).to.be.undefined; }); it('resets tabindex properly', function() { $('.for-focus').attr('tabindex', 42); $.featherlight({text: 'Hello'}); $.featherlight.close(); expect($('.for-focus')).to.have.attr('tabindex').equal('42'); }); it('focussed on "autofocus" content, if any', function() { $.featherlight({html: '<input><input autofocus class="ok">'}); expect($(document.activeElement)).to.have.class('ok'); $.featherlight.close(); }); it('sets the "with-featherlight" class correctly on html', function() { expect($('html')).not.to.have.class('with-featherlight'); $.featherlight({html: 'hello'}); expect($('html')).to.have.class('with-featherlight'); $.featherlight({html: 'hello'}); $.featherlight.close(); $.featherlight.close(); expect($('html')).not.to.have.class('with-featherlight'); }); }); }); }(jQuery));