Explorar o código

custom handlers for forms and reset after done

Benoît Hubert %!s(int64=8) %!d(string=hai) anos
pai
achega
d9ca7e7dac
Modificáronse 6 ficheiros con 142 adicións e 59 borrados
  1. 22 2
      js/test/ws.test.js
  2. 103 45
      js/ws-forms.js
  3. 2 1
      js/ws-menu.js
  4. 1 0
      js/ws-notify.js
  5. 11 9
      js/ws-ui-parts.js
  6. 3 2
      sandboxApp.js

+ 22 - 2
js/test/ws.test.js

@@ -7,6 +7,19 @@ function timeoutAsync(cb, delay) {
   });
 }
 
+function delayAsync(delay) {
+  return new Promise((resolve, reject) => {
+    setTimeout(function() {
+      resolve(true);
+    }, delay);
+  });
+}
+
+
+function getId() {
+  return Date.now().toString(36);
+}
+
 (function($) {
   $(document).ready(function() {
 
@@ -104,13 +117,16 @@ function timeoutAsync(cb, delay) {
     QUnit.test( "test adding a file", function( assert ) {
       var done = assert.async();
       var $addFile = $('#add-file');
+      var $addFileInput = $('#add-file').find('input[type="text"]');
       var $addFileAddBtn = $addFile.find('.add-btn');
       var $addFileForm = $addFile.find('form');
       $linkToRepo1.trigger('click');
       timeoutAsync(() => {
         var $linkToExmp1 = $menuExample.find('a:first');
+        console.log('##### clicked on first example', $linkToExmp1.prop('href'));
         $linkToExmp1.trigger( 'click' );
       }, 50)
+      .then(() => delayAsync(400))
       .then(() => {
         assert.equal( $addFile.length, 1, "there should be one #add-file element" );
         assert.equal( $addFileForm.length, 1, "#add-file should contain one form element" );
@@ -122,8 +138,12 @@ function timeoutAsync(cb, delay) {
         console.log( $addFileForm );
         // assert.ok( $addFileForm.is(':visible'), "clicking on the + btn should have made the form appear" );
         assert.ok( $addFileForm.width() > 0, "clicking on the + btn should have made the form appear" );
-        done();
-      }, 100));
+        $addFileInput.val('script-' + getId() + '.js');
+        $addFileForm.find('button[type="submit"]')
+        .trigger( 'click' );
+      }, 100))
+      .then(() => delayAsync(400))
+      .then(done);
     });
 
 

+ 103 - 45
js/ws-forms.js

@@ -43,57 +43,51 @@
   //   $addExampleBtn.toggle();
   //   $exampleForm.toggleClass("hidden");
   // }
+function checkPropsExist(obj, props) {
+  if(typeof obj !== 'object' || ! props) {
+    throw new Error('checkPropsExist was called with wrong arguments');
+  }
+  props = typeof props === 'string' ? [props] : props;
+  for(let p = 0 ; p < props.length ; p++) {
+    const prop = props[p];
+    if(! obj[prop]) {
+      throw new Error('obj does not have a `' + prop + '` parameter: please provide it.');
+    }
+  }
+}
 
-
-  function makeFormView(elemId, onSubmitPromise, options) {
-
+  function makeFormView(elemId, options) {
+    var callbacks = ['onSubmitAddPromise', 'onAddSuccess', 'onAddError'];
     // Fallback options & options.events
-    options = options || {};
+    checkPropsExist(options, callbacks)
     options.events = options.events || {};
 
     // Define events and optionally extend them with those
     // provided in options.events
-    var events = Object.assign({
-      'click .add-btn': function() {
-        this.render();
-      },
-
-      'click .icon-cross': function() {
-        this.$form.removeClass('in');
-        this.$btn.addClass('in');
-      },
-
-      'submit form': function(e) {
-        e.preventDefault();
-        if(this.cantSubmit) {
-          this.$input
-          .removeClass('input-success input-warning')
-          .addClass('input-error');
-          _ws.notify('error', this.cantSubmit);
-          return;
-        }
-        var title = this.$input.val();
-        // rp.post('/collections', { title: title })
-        onSubmitPromise(title)
-        .then(function(c) {
-          _ws.notify('success', 'Collection créée: ' + c.title);
-        })
-        .catch(function(err) {
-          console.error(err);
-          _ws.notify('error', 'Impossible de créer la collection: ' + err.message);
-        });
-      }
-    }, options.events);
 
     _ws.makeView(elemId, {
 
       init: function() {
+        const self = this;
         this.$btn   = this.$elem.find('.add-btn');
         this.$form  = this.$elem.find('form');
         this.$input = this.$form.find('input[name="title"]');
+        callbacks.forEach(function(cbName) {
+          console.log('binding cb', cbName);
+          self[cbName] = options[cbName].bind(self);
+        });
+        if(options.postInit) {
+          (options.postInit.bind(this))();
+        }
         console.log('init add repo form', this.$elem.prop('id'))
       },
 
+      reset: function() {
+        this.$btn.addClass('in');
+        this.$form.removeClass('in');
+        this.$input.val();
+      },
+
       render: function() {
         console.log('render form add ', this.$elem.prop('id'));
         this.$btn.removeClass('in');
@@ -101,28 +95,91 @@
         this.$input.focus();
       },
 
-      events: events
+      events: {
+        'click .add-btn': function() {
+          this.render();
+        },
+
+        'click .icon-cross': function() {
+          this.$form.removeClass('in');
+          this.$btn.addClass('in');
+        },
+
+        'submit form': function(e) {
+          const self = this;
+          e.preventDefault();
+          if(this.cantSubmit) {
+            this.$input
+            .removeClass('input-success input-warning')
+            .addClass('input-error');
+            _ws.notify('error', this.cantSubmit);
+            return;
+          }
+          var title = this.$input.val();
+          // rp.post('/collections', { title: title })
+          self.onSubmitAddPromise(title)
+          .then(function(data) {
+            self.onAddSuccess(data);
+            self.reset();
+          })
+          .catch(function(err) {
+            console.error(err);
+            self.onAddError(err);
+            self.reset();
+          });
+        }
+      }
 
     });
   }
 
-  makeFormView('add-repo', function(title) {
-    return rp.post('/repos', { title: title });
+  makeFormView('add-repo', {
+    onSubmitAddPromise: function(title) {
+      return rp.post('/repos', { title: title });
+    },
+    onAddSuccess: function(repo) {
+      console.log('****** onAddSuccess repo', repo);
+      _ws.notify('success', 'Collection créée: ' + repo.title);
+    },
+    onAddError: function(err) {
+     _ws.notify('error', 'Impossible de créer la collection: ' + err.message); 
+    }
   });
 
-  makeFormView('add-example', function(title) {
-    return rp.post('/' + _ws.repo.path + '/examples', { title: title });
+  makeFormView('add-example', {
+    onSubmitAddPromise: function(title) {
+      return rp.post('/' + _ws.repo.path + '/examples', { title: title });
+    },
+    onAddSuccess: function(example) {
+      console.log('****** onAddSuccess example', example);
+      _ws.notify('success', 'Exemple crée: ' + example.title);
+    },
+    onAddError: function(err) {
+     _ws.notify('error', "Impossible de créer l'exemple: " + err.message); 
+    }
   });
 
 
   makeFormView(
     'add-file',
-    function(name) {
-      return rp.post('/' + _ws.repo.path + '/examples/' + _ws.example.slug + '/file', { name: name });
-    },
     {
-      events: {
-        'keyup input[name="title"]': function(e) {
+      onSubmitAddPromise: function(name) {
+        return rp.post('/' + _ws.repo.path + '/examples/' + _ws.example.slug + '/files', { name: name });
+      },
+
+      onAddSuccess: function(file) {
+        _ws.notify('success', 'Fichier crée: ' + file.name);
+        console.log('****** onAddSuccess file', file, _ws.files);
+        _ws.files.push(file);
+        _ws.ui.tabs.render({ files: _ws.files });
+      },
+      onAddError: function(err) {
+       _ws.notify('error', "Impossible de créer le fichier: " + err.message); 
+      },
+
+      postInit: function() {
+        console.log('### exec postInit', this);
+        function onKeyup(e) {
           var filename = this.$input.val();
           var bits = filename.split('.');
           var lastIdx = bits.length - 1;
@@ -157,6 +214,7 @@
           this.$input.addClass('input-success');
           this.cantSubmit = undefined;
         }
+        this.$input.on('keyup', onKeyup.bind(this));
       }
     }
   );

+ 2 - 1
js/ws-menu.js

@@ -83,7 +83,8 @@
       else if(navState.example !== prevNavState.example) {
         rp.get('/parts/' + navState.repo + '/' + navState.example, 'json')
         .then(function(parts) {
-          console.log('nav: example changed');
+          console.log('nav: example changed', parts);
+          _ws.example = parts.example;
           _ws.files = parts.files;
           console.log(_ws.ui);
           // renderDetailsExample(parts);

+ 1 - 0
js/ws-notify.js

@@ -7,6 +7,7 @@
 
     _ws.notify = function(type, text) {
       $notification
+      .removeClass('success error')
       .addClass(type)
       .addClass('active');
       $notification.html(text);

+ 11 - 9
js/ws-ui-parts.js

@@ -15,17 +15,19 @@
     /**
      * Generic template-based rendering function
      */
-    function viewDefaultRender(data) {
-      if(! data) {
-        console.log('WARNING! You should provide data for #' + elemId + "'s render()");
+    function viewDefaultRender(elemId) {
+      return function(data) {
+        console.log('this is default render on', this, 'with data', data);
+        if(! data) {
+          throw new Error('WARNING! You should provide data for #' + elemId + "'s render()");
+        }
+        this.$elem.empty();
+        this.$elem.html( Mustache.render( this.tmpl, data ) );
+        this.$elem.show();
+        this.bindEvents();
       }
-      this.$elem.empty();
-      this.$elem.html( Mustache.render( this.tmpl, data ) );
-      this.$elem.show();
-      this.bindEvents();
     }
 
-
     /**
      * Wrapper around provided render function (show elem after render)
      */
@@ -97,7 +99,7 @@
       else {
         var $tmplEl = $('script[data-tmpl-for="' + elemId + '"]');
         v.tmpl = $tmplEl.html();
-        v.render = viewDefaultRender.bind(v);
+        v.render = (viewDefaultRender(elemId)).bind(v);
       }
 
       // Set event handlers

+ 3 - 2
sandboxApp.js

@@ -178,7 +178,7 @@ app.post('/repos',
   }
 );
 
-app.post('/:repoSlug/examples/:exampleSlug/file',
+app.post('/:repoSlug/examples/:exampleSlug/files',
   checkRepoExists,
   checkBodyPropsExist('name'),
   function(req, res) {
@@ -216,7 +216,8 @@ app.post('/:repoSlug/examples/:exampleSlug/file',
     .then(() => exStore.addExampleFile(repoSlug, exampleSlug, name))
     .then(() => res.json({
         name,
-        path: fullPath,
+        type: fileExt,
+        // path: fullPath,
         content: fileContent
       })
     )