diff --git a/demos/javascript/vue-component.php b/demos/javascript/vue-component.php index 220168493c..020402b118 100644 --- a/demos/javascript/vue-component.php +++ b/demos/javascript/vue-component.php @@ -27,11 +27,16 @@ $subHeader = 'Try me. I will restore value on "Escape" or save it on "Enter" or when field get blur after it has been changed.'; Header::addTo($app, ['Inline editing.', 'size' => 3, 'subHeader' => $subHeader]); -$inline_edit = VueComponent\InlineEdit::addTo($app); -$inline_edit->fieldName = $model->fieldName()->name; -$inline_edit->setModel($model); - -$inline_edit->onChange(function (string $value) use ($app) { +View::addTo($app)->set('with autoSave'); +$inlineEditWithAutoSave = VueComponent\InlineEdit::addTo($app, ['autoSave' => true]); +$inlineEditWithAutoSave->fieldName = $model->fieldName()->name; +$inlineEditWithAutoSave->setModel($model); + +View::addTo($app)->set('with onChange callback'); +$inlineEditWithCallback = VueComponent\InlineEdit::addTo($app); +$inlineEditWithCallback->fieldName = $model->fieldName()->name; +$inlineEditWithCallback->setModel($model); +$inlineEditWithCallback->onChange(function (string $value) use ($app) { $view = new Message(); $view->setApp($app); $view->invokeInit(); diff --git a/js/package-lock.json b/js/package-lock.json index f97015a120..3fb43697fc 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -47,6 +47,7 @@ } }, ".eslint": { + "name": "@internal/eslint-plugin", "version": "1.0.0", "dev": true, "dependencies": { diff --git a/js/src/vue-components/inline-edit.component.js b/js/src/vue-components/inline-edit.component.js index 43a02bd7bd..383e9aa5aa 100644 --- a/js/src/vue-components/inline-edit.component.js +++ b/js/src/vue-components/inline-edit.component.js @@ -16,7 +16,6 @@ export default { { - this.flashError(count - 1); - }, 300); - }, update: function () { const that = this; $(this.$el).api({ @@ -98,9 +87,9 @@ export default { method: 'POST', onComplete: function (r, e) { if (r.hasValidationError) { - that.hasError = true; + that.clearError(); } else { - that.temp = that.value; + that.lastValueValid = that.value; } }, }); diff --git a/public/js/atk-vue-inline-edit.js b/public/js/atk-vue-inline-edit.js index 79cb7f0e39..eddb064175 100644 --- a/public/js/atk-vue-inline-edit.js +++ b/public/js/atk-vue-inline-edit.js @@ -31,7 +31,6 @@ __webpack_require__.r(__webpack_exports__); 0 && arguments[0] !== undefined ? arguments[0] : 4; - if (count === 0) { - this.hasError = false; - return; - } - this.hasError = !this.hasError; - setTimeout(() => { - this.flashError(count - 1); - }, 300); - }, update: function () { const that = this; external_jquery__WEBPACK_IMPORTED_MODULE_0___default()(this.$el).api({ @@ -116,9 +104,9 @@ __webpack_require__.r(__webpack_exports__); method: 'POST', onComplete: function (r, e) { if (r.hasValidationError) { - that.hasError = true; + that.clearError(); } else { - that.temp = that.value; + that.lastValueValid = that.value; } } }); diff --git a/public/js/atk-vue-inline-edit.js.map b/public/js/atk-vue-inline-edit.js.map index a19efef15d..2efb252ce1 100644 --- a/public/js/atk-vue-inline-edit.js.map +++ b/public/js/atk-vue-inline-edit.js.map @@ -1 +1 @@ -{"version":3,"file":"js/atk-vue-inline-edit.js","mappings":"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","sources":["webpack://atk/./src/vue-components/inline-edit.component.js"],"sourcesContent":["import $ from 'external/jquery';\n\n/**\n * Allow user to edit a db record inline and send\n * changes to server.\n *\n * Properties need for this component are:\n * context: string, a jQuery selector where the 'loading' class will be apply by Fomantic-UI - default to the requesting element.\n * url: string, the URL to call.\n * value: array, array of value to send to server.\n */\nexport default {\n name: 'AtkInlineEdit',\n template: `\n
\n \n \n
`,\n props: {\n url: String,\n initValue: String,\n saveOnBlur: Boolean,\n options: Object,\n },\n data: function () {\n return {\n value: this.initValue,\n temp: this.initValue,\n hasError: false,\n };\n },\n computed: {\n isDirty: function () {\n return this.temp !== this.value;\n },\n },\n methods: {\n onFocus: function () {\n if (this.hasError) {\n this.clearError();\n } else {\n this.temp = this.value;\n }\n },\n onKeyup: function (e) {\n const key = e.keyCode;\n this.clearError();\n if (key === 13) {\n this.onEnter();\n } else if (key === 27) {\n this.onEscape();\n }\n },\n onBlur: function () {\n if (this.isDirty && this.saveOnBlur) {\n this.update();\n } else {\n this.value = this.temp; // TODO will not save the value on 2nd edit and submit via enter\n }\n },\n onEscape: function () {\n this.value = this.temp;\n this.$el.querySelector('input').blur();\n },\n onEnter: function () {\n if (this.isDirty) {\n this.update();\n }\n },\n clearError: function () {\n this.hasError = false;\n },\n flashError: function (count = 4) {\n if (count === 0) {\n this.hasError = false;\n\n return;\n }\n this.hasError = !this.hasError;\n setTimeout(() => {\n this.flashError(count - 1);\n }, 300);\n },\n update: function () {\n const that = this;\n $(this.$el).api({\n on: 'now',\n url: this.url,\n data: { value: this.value },\n method: 'POST',\n onComplete: function (r, e) {\n if (r.hasValidationError) {\n that.hasError = true;\n } else {\n that.temp = that.value;\n }\n },\n });\n },\n },\n};\n"],"names":["$","name","template","props","url","String","initValue","saveOnBlur","Boolean","options","Object","data","value","temp","hasError","computed","isDirty","methods","onFocus","clearError","onKeyup","e","key","keyCode","onEnter","onEscape","onBlur","update","$el","querySelector","blur","flashError","count","arguments","length","undefined","setTimeout","that","api","on","method","onComplete","r","hasValidationError"],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"js/atk-vue-inline-edit.js","mappings":";;;;;;;;;;;;;;;AAAgC;;AAEhC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iEAAe;EACXC,IAAI,EAAE,eAAe;EACrBC,QAAQ,EAAG;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,eAAe;EACXC,KAAK,EAAE;IACHC,GAAG,EAAEC,MAAM;IACXC,SAAS,EAAED,MAAM;IACjBE,UAAU,EAAEC,OAAO;IACnBC,OAAO,EAAEC;EACb,CAAC;EACDC,IAAI,EAAE,SAAAA,CAAA,EAAY;IACd,OAAO;MACHC,KAAK,EAAE,IAAI,CAACN,SAAS;MACrBO,cAAc,EAAE,IAAI,CAACP,SAAS;MAC9BQ,QAAQ,EAAE;IACd,CAAC;EACL,CAAC;EACDC,QAAQ,EAAE;IACNC,OAAO,EAAE,SAAAA,CAAA,EAAY;MACjB,OAAO,IAAI,CAACH,cAAc,KAAK,IAAI,CAACD,KAAK;IAC7C;EACJ,CAAC;EACDK,OAAO,EAAE;IACLC,OAAO,EAAE,SAAAA,CAAA,EAAY;MACjB,IAAI,IAAI,CAACJ,QAAQ,EAAE;QACf,IAAI,CAACK,UAAU,EAAE;MACrB,CAAC,MAAM;QACH,IAAI,CAACN,cAAc,GAAG,IAAI,CAACD,KAAK;MACpC;IACJ,CAAC;IACDQ,OAAO,EAAE,SAAAA,CAAUC,CAAC,EAAE;MAClB,MAAMC,GAAG,GAAGD,CAAC,CAACE,OAAO;MACrB,IAAID,GAAG,KAAK,EAAE,EAAE;QACZ,IAAI,CAACE,OAAO,EAAE;MAClB,CAAC,MAAM,IAAIF,GAAG,KAAK,EAAE,EAAE;QACnB,IAAI,CAACG,QAAQ,EAAE;MACnB;IACJ,CAAC;IACDC,MAAM,EAAE,SAAAA,CAAA,EAAY;MAChB,IAAI,IAAI,CAACV,OAAO,EAAE;QACd,IAAI,IAAI,CAACT,UAAU,EAAE;UACjB,IAAI,CAACoB,MAAM,EAAE;QACjB,CAAC,MAAM;UACH,IAAI,CAACf,KAAK,GAAG,IAAI,CAACC,cAAc;QACpC;MACJ;IACJ,CAAC;IACDY,QAAQ,EAAE,SAAAA,CAAA,EAAY;MAClB,IAAI,CAACb,KAAK,GAAG,IAAI,CAACC,cAAc;MAChC,IAAI,CAACe,GAAG,CAACC,aAAa,CAAC,OAAO,CAAC,CAACC,IAAI,EAAE;IAC1C,CAAC;IACDN,OAAO,EAAE,SAAAA,CAAA,EAAY;MACjB,IAAI,IAAI,CAACR,OAAO,EAAE;QACd,IAAI,CAACW,MAAM,EAAE;MACjB;IACJ,CAAC;IACDR,UAAU,EAAE,SAAAA,CAAA,EAAY;MACpB,IAAI,CAACL,QAAQ,GAAG,KAAK;IACzB,CAAC;IACDa,MAAM,EAAE,SAAAA,CAAA,EAAY;MAChB,MAAMI,IAAI,GAAG,IAAI;MACjB/B,sDAAC,CAAC,IAAI,CAAC4B,GAAG,CAAC,CAACI,GAAG,CAAC;QACZC,EAAE,EAAE,KAAK;QACT7B,GAAG,EAAE,IAAI,CAACA,GAAG;QACbO,IAAI,EAAE;UAAEC,KAAK,EAAE,IAAI,CAACA;QAAM,CAAC;QAC3BsB,MAAM,EAAE,MAAM;QACdC,UAAU,EAAE,SAAAA,CAAUC,CAAC,EAAEf,CAAC,EAAE;UACxB,IAAIe,CAAC,CAACC,kBAAkB,EAAE;YACtBN,IAAI,CAACZ,UAAU,EAAE;UACrB,CAAC,MAAM;YACHY,IAAI,CAAClB,cAAc,GAAGkB,IAAI,CAACnB,KAAK;UACpC;QACJ;MACJ,CAAC,CAAC;IACN;EACJ;AACJ,CAAC","sources":["webpack://atk/./src/vue-components/inline-edit.component.js"],"sourcesContent":["import $ from 'external/jquery';\n\n/**\n * Allow user to edit a db record inline and send\n * changes to server.\n *\n * Properties need for this component are:\n * context: string, a jQuery selector where the 'loading' class will be apply by Fomantic-UI - default to the requesting element.\n * url: string, the URL to call.\n * value: array, array of value to send to server.\n */\nexport default {\n name: 'AtkInlineEdit',\n template: `\n
\n \n \n
`,\n props: {\n url: String,\n initValue: String,\n saveOnBlur: Boolean,\n options: Object,\n },\n data: function () {\n return {\n value: this.initValue,\n lastValueValid: this.initValue,\n hasError: false,\n };\n },\n computed: {\n isDirty: function () {\n return this.lastValueValid !== this.value;\n },\n },\n methods: {\n onFocus: function () {\n if (this.hasError) {\n this.clearError();\n } else {\n this.lastValueValid = this.value;\n }\n },\n onKeyup: function (e) {\n const key = e.keyCode;\n if (key === 13) {\n this.onEnter();\n } else if (key === 27) {\n this.onEscape();\n }\n },\n onBlur: function () {\n if (this.isDirty) {\n if (this.saveOnBlur) {\n this.update();\n } else {\n this.value = this.lastValueValid;\n }\n }\n },\n onEscape: function () {\n this.value = this.lastValueValid;\n this.$el.querySelector('input').blur();\n },\n onEnter: function () {\n if (this.isDirty) {\n this.update();\n }\n },\n clearError: function () {\n this.hasError = false;\n },\n update: function () {\n const that = this;\n $(this.$el).api({\n on: 'now',\n url: this.url,\n data: { value: this.value },\n method: 'POST',\n onComplete: function (r, e) {\n if (r.hasValidationError) {\n that.clearError();\n } else {\n that.lastValueValid = that.value;\n }\n },\n });\n },\n },\n};\n"],"names":["$","name","template","props","url","String","initValue","saveOnBlur","Boolean","options","Object","data","value","lastValueValid","hasError","computed","isDirty","methods","onFocus","clearError","onKeyup","e","key","keyCode","onEnter","onEscape","onBlur","update","$el","querySelector","blur","that","api","on","method","onComplete","r","hasValidationError"],"sourceRoot":""} \ No newline at end of file diff --git a/public/js/atk-vue-inline-edit.min.js b/public/js/atk-vue-inline-edit.min.js index 6524e3fea2..777acbc4ef 100644 --- a/public/js/atk-vue-inline-edit.min.js +++ b/public/js/atk-vue-inline-edit.min.js @@ -1,2 +1,2 @@ -"use strict";(self.webpackChunkatk=self.webpackChunkatk||[]).push([[477],{83078:(t,n,i)=>{i.r(n),i.d(n,{default:()=>e});var s=i(21145),r=i.n(s);const e={name:"AtkInlineEdit",template:'\n
\n \n \n
',props:{url:String,initValue:String,saveOnBlur:Boolean,options:Object},data:function(){return{value:this.initValue,temp:this.initValue,hasError:!1}},computed:{isDirty:function(){return this.temp!==this.value}},methods:{onFocus:function(){this.hasError?this.clearError():this.temp=this.value},onKeyup:function(t){const n=t.keyCode;this.clearError(),13===n?this.onEnter():27===n&&this.onEscape()},onBlur:function(){this.isDirty&&this.saveOnBlur?this.update():this.value=this.temp},onEscape:function(){this.value=this.temp,this.$el.querySelector("input").blur()},onEnter:function(){this.isDirty&&this.update()},clearError:function(){this.hasError=!1},flashError:function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:4;0!==t?(this.hasError=!this.hasError,setTimeout((()=>{this.flashError(t-1)}),300)):this.hasError=!1},update:function(){const t=this;r()(this.$el).api({on:"now",url:this.url,data:{value:this.value},method:"POST",onComplete:function(n,i){n.hasValidationError?t.hasError=!0:t.temp=t.value}})}}}}}]); +"use strict";(self.webpackChunkatk=self.webpackChunkatk||[]).push([[477],{83078:(t,n,i)=>{i.r(n),i.d(n,{default:()=>e});var s=i(21145),a=i.n(s);const e={name:"AtkInlineEdit",template:'\n
\n \n \n
',props:{url:String,initValue:String,saveOnBlur:Boolean,options:Object},data:function(){return{value:this.initValue,lastValueValid:this.initValue,hasError:!1}},computed:{isDirty:function(){return this.lastValueValid!==this.value}},methods:{onFocus:function(){this.hasError?this.clearError():this.lastValueValid=this.value},onKeyup:function(t){const n=t.keyCode;13===n?this.onEnter():27===n&&this.onEscape()},onBlur:function(){this.isDirty&&(this.saveOnBlur?this.update():this.value=this.lastValueValid)},onEscape:function(){this.value=this.lastValueValid,this.$el.querySelector("input").blur()},onEnter:function(){this.isDirty&&this.update()},clearError:function(){this.hasError=!1},update:function(){const t=this;a()(this.$el).api({on:"now",url:this.url,data:{value:this.value},method:"POST",onComplete:function(n,i){n.hasValidationError?t.clearError():t.lastValueValid=t.value}})}}}}}]); //# sourceMappingURL=atk-vue-inline-edit.min.js.map \ No newline at end of file diff --git a/public/js/atk-vue-inline-edit.min.js.map b/public/js/atk-vue-inline-edit.min.js.map index 99b080cbb1..61e2ade676 100644 --- a/public/js/atk-vue-inline-edit.min.js.map +++ b/public/js/atk-vue-inline-edit.min.js.map @@ -1 +1 @@ -{"version":3,"file":"js/atk-vue-inline-edit.min.js","mappings":"gJAWA,SACIA,KAAM,gBACNC,SAAW,uaAaXC,MAAO,CACHC,IAAKC,OACLC,UAAWD,OACXE,WAAYC,QACZC,QAASC,QAEbC,KAAM,WACF,MAAO,CACHC,MAAOC,KAAKP,UACZQ,KAAMD,KAAKP,UACXS,UAAU,EAElB,EACAC,SAAU,CACNC,QAAS,WACL,OAAOJ,KAAKC,OAASD,KAAKD,KAC9B,GAEJM,QAAS,CACLC,QAAS,WACDN,KAAKE,SACLF,KAAKO,aAELP,KAAKC,KAAOD,KAAKD,KAEzB,EACAS,QAAS,SAAUC,GACf,MAAMC,EAAMD,EAAEE,QACdX,KAAKO,aACO,KAARG,EACAV,KAAKY,UACU,KAARF,GACPV,KAAKa,UAEb,EACAC,OAAQ,WACAd,KAAKI,SAAWJ,KAAKN,WACrBM,KAAKe,SAELf,KAAKD,MAAQC,KAAKC,IAE1B,EACAY,SAAU,WACNb,KAAKD,MAAQC,KAAKC,KAClBD,KAAKgB,IAAIC,cAAc,SAASC,MACpC,EACAN,QAAS,WACDZ,KAAKI,SACLJ,KAAKe,QAEb,EACAR,WAAY,WACRP,KAAKE,UAAW,CACpB,EACAiB,WAAY,WAAqB,IAAXC,EAAKC,UAAAC,OAAA,QAAAC,IAAAF,UAAA,GAAAA,UAAA,GAAG,EACZ,IAAVD,GAKJpB,KAAKE,UAAYF,KAAKE,SACtBsB,YAAW,KACPxB,KAAKmB,WAAWC,EAAQ,EAAE,GAC3B,MAPCpB,KAAKE,UAAW,CAQxB,EACAa,OAAQ,WACJ,MAAMU,EAAOzB,KACb0B,IAAE1B,KAAKgB,KAAKW,IAAI,CACZC,GAAI,MACJrC,IAAKS,KAAKT,IACVO,KAAM,CAAEC,MAAOC,KAAKD,OACpB8B,OAAQ,OACRC,WAAY,SAAUC,EAAGtB,GACjBsB,EAAEC,mBACFP,EAAKvB,UAAW,EAEhBuB,EAAKxB,KAAOwB,EAAK1B,KAEzB,GAER,G","sources":["webpack://atk/./src/vue-components/inline-edit.component.js"],"sourcesContent":["import $ from 'external/jquery';\n\n/**\n * Allow user to edit a db record inline and send\n * changes to server.\n *\n * Properties need for this component are:\n * context: string, a jQuery selector where the 'loading' class will be apply by Fomantic-UI - default to the requesting element.\n * url: string, the URL to call.\n * value: array, array of value to send to server.\n */\nexport default {\n name: 'AtkInlineEdit',\n template: `\n
\n \n \n
`,\n props: {\n url: String,\n initValue: String,\n saveOnBlur: Boolean,\n options: Object,\n },\n data: function () {\n return {\n value: this.initValue,\n temp: this.initValue,\n hasError: false,\n };\n },\n computed: {\n isDirty: function () {\n return this.temp !== this.value;\n },\n },\n methods: {\n onFocus: function () {\n if (this.hasError) {\n this.clearError();\n } else {\n this.temp = this.value;\n }\n },\n onKeyup: function (e) {\n const key = e.keyCode;\n this.clearError();\n if (key === 13) {\n this.onEnter();\n } else if (key === 27) {\n this.onEscape();\n }\n },\n onBlur: function () {\n if (this.isDirty && this.saveOnBlur) {\n this.update();\n } else {\n this.value = this.temp; // TODO will not save the value on 2nd edit and submit via enter\n }\n },\n onEscape: function () {\n this.value = this.temp;\n this.$el.querySelector('input').blur();\n },\n onEnter: function () {\n if (this.isDirty) {\n this.update();\n }\n },\n clearError: function () {\n this.hasError = false;\n },\n flashError: function (count = 4) {\n if (count === 0) {\n this.hasError = false;\n\n return;\n }\n this.hasError = !this.hasError;\n setTimeout(() => {\n this.flashError(count - 1);\n }, 300);\n },\n update: function () {\n const that = this;\n $(this.$el).api({\n on: 'now',\n url: this.url,\n data: { value: this.value },\n method: 'POST',\n onComplete: function (r, e) {\n if (r.hasValidationError) {\n that.hasError = true;\n } else {\n that.temp = that.value;\n }\n },\n });\n },\n },\n};\n"],"names":["name","template","props","url","String","initValue","saveOnBlur","Boolean","options","Object","data","value","this","temp","hasError","computed","isDirty","methods","onFocus","clearError","onKeyup","e","key","keyCode","onEnter","onEscape","onBlur","update","$el","querySelector","blur","flashError","count","arguments","length","undefined","setTimeout","that","$","api","on","method","onComplete","r","hasValidationError"],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"js/atk-vue-inline-edit.min.js","mappings":"gJAWA,SACIA,KAAM,gBACNC,SAAW,4XAYXC,MAAO,CACHC,IAAKC,OACLC,UAAWD,OACXE,WAAYC,QACZC,QAASC,QAEbC,KAAM,WACF,MAAO,CACHC,MAAOC,KAAKP,UACZQ,eAAgBD,KAAKP,UACrBS,UAAU,EAElB,EACAC,SAAU,CACNC,QAAS,WACL,OAAOJ,KAAKC,iBAAmBD,KAAKD,KACxC,GAEJM,QAAS,CACLC,QAAS,WACDN,KAAKE,SACLF,KAAKO,aAELP,KAAKC,eAAiBD,KAAKD,KAEnC,EACAS,QAAS,SAAUC,GACf,MAAMC,EAAMD,EAAEE,QACF,KAARD,EACAV,KAAKY,UACU,KAARF,GACPV,KAAKa,UAEb,EACAC,OAAQ,WACAd,KAAKI,UACDJ,KAAKN,WACLM,KAAKe,SAELf,KAAKD,MAAQC,KAAKC,eAG9B,EACAY,SAAU,WACNb,KAAKD,MAAQC,KAAKC,eAClBD,KAAKgB,IAAIC,cAAc,SAASC,MACpC,EACAN,QAAS,WACDZ,KAAKI,SACLJ,KAAKe,QAEb,EACAR,WAAY,WACRP,KAAKE,UAAW,CACpB,EACAa,OAAQ,WACJ,MAAMI,EAAOnB,KACboB,IAAEpB,KAAKgB,KAAKK,IAAI,CACZC,GAAI,MACJ/B,IAAKS,KAAKT,IACVO,KAAM,CAAEC,MAAOC,KAAKD,OACpBwB,OAAQ,OACRC,WAAY,SAAUC,EAAGhB,GACjBgB,EAAEC,mBACFP,EAAKZ,aAELY,EAAKlB,eAAiBkB,EAAKpB,KAEnC,GAER,G","sources":["webpack://atk/./src/vue-components/inline-edit.component.js"],"sourcesContent":["import $ from 'external/jquery';\n\n/**\n * Allow user to edit a db record inline and send\n * changes to server.\n *\n * Properties need for this component are:\n * context: string, a jQuery selector where the 'loading' class will be apply by Fomantic-UI - default to the requesting element.\n * url: string, the URL to call.\n * value: array, array of value to send to server.\n */\nexport default {\n name: 'AtkInlineEdit',\n template: `\n
\n \n \n
`,\n props: {\n url: String,\n initValue: String,\n saveOnBlur: Boolean,\n options: Object,\n },\n data: function () {\n return {\n value: this.initValue,\n lastValueValid: this.initValue,\n hasError: false,\n };\n },\n computed: {\n isDirty: function () {\n return this.lastValueValid !== this.value;\n },\n },\n methods: {\n onFocus: function () {\n if (this.hasError) {\n this.clearError();\n } else {\n this.lastValueValid = this.value;\n }\n },\n onKeyup: function (e) {\n const key = e.keyCode;\n if (key === 13) {\n this.onEnter();\n } else if (key === 27) {\n this.onEscape();\n }\n },\n onBlur: function () {\n if (this.isDirty) {\n if (this.saveOnBlur) {\n this.update();\n } else {\n this.value = this.lastValueValid;\n }\n }\n },\n onEscape: function () {\n this.value = this.lastValueValid;\n this.$el.querySelector('input').blur();\n },\n onEnter: function () {\n if (this.isDirty) {\n this.update();\n }\n },\n clearError: function () {\n this.hasError = false;\n },\n update: function () {\n const that = this;\n $(this.$el).api({\n on: 'now',\n url: this.url,\n data: { value: this.value },\n method: 'POST',\n onComplete: function (r, e) {\n if (r.hasValidationError) {\n that.clearError();\n } else {\n that.lastValueValid = that.value;\n }\n },\n });\n },\n },\n};\n"],"names":["name","template","props","url","String","initValue","saveOnBlur","Boolean","options","Object","data","value","this","lastValueValid","hasError","computed","isDirty","methods","onFocus","clearError","onKeyup","e","key","keyCode","onEnter","onEscape","onBlur","update","$el","querySelector","blur","that","$","api","on","method","onComplete","r","hasValidationError"],"sourceRoot":""} \ No newline at end of file diff --git a/src/Behat/Context.php b/src/Behat/Context.php index b4e122fb1b..3ccbf66000 100644 --- a/src/Behat/Context.php +++ b/src/Behat/Context.php @@ -213,11 +213,11 @@ protected function assertNoDuplicateId(): void } /** - * @return array{'css'|'xpath', string} + * @return array{ 'css'|'xpath', string } */ protected function parseSelector(string $selector): array { - if (preg_match('~^xpath\((.+)\)$~s', $selector, $matches)) { + if (preg_match('~^\(*//~s', $selector)) { // add support for standard CSS class selector $xpath = preg_replace_callback( '~\'(?:[^\']+|\'\')*+\'\K|"(?:[^"]+|"")*+"\K|(?<=\w)\.([\w\-]+)~s', @@ -228,7 +228,7 @@ function ($matches) { return '[contains(concat(\' \', normalize-space(@class), \' \'), \' ' . $matches[1] . ' \')]'; }, - $matches[1] + $selector ); return ['xpath', $xpath]; @@ -293,7 +293,7 @@ public function iDragElementOnto(string $selector, string $selectorTarget): void */ public function iPressButton(string $btnLabel): void { - $button = $this->findElement(null, 'xpath(//div[text()="' . $btnLabel . '"])'); + $button = $this->findElement(null, '//div[text()="' . $btnLabel . '"]'); $button->click(); } @@ -303,7 +303,7 @@ public function iPressButton(string $btnLabel): void public function iPressMenuButton(string $btnLabel, string $selector): void { $menu = $this->findElement(null, $selector); - $link = $this->findElement($menu, 'xpath(//a[text()="' . $btnLabel . '"])'); + $link = $this->findElement($menu, '//a[text()="' . $btnLabel . '"]'); $this->getSession()->executeScript('$(\'#' . $link->getAttribute('id') . '\').click()'); } @@ -312,7 +312,7 @@ public function iPressMenuButton(string $btnLabel, string $selector): void */ public function iSeeButton(string $buttonLabel): void { - $this->findElement(null, 'xpath(//div[text()="' . $buttonLabel . '"])'); + $this->findElement(null, '//div[text()="' . $buttonLabel . '"]'); } /** @@ -320,7 +320,7 @@ public function iSeeButton(string $buttonLabel): void */ public function idontSeeButton(string $text): void { - $element = $this->findElement(null, 'xpath(//div[text()="' . $text . '"])'); + $element = $this->findElement(null, '//div[text()="' . $text . '"]'); if (!str_contains($element->getAttribute('style'), 'display: none')) { throw new \Exception('Element with text "' . $text . '" must be invisible'); } @@ -335,7 +335,7 @@ public function idontSeeButton(string $text): void */ public function iClickLink(string $label): void { - $this->findElement(null, 'xpath(//a[text()="' . $label . '"])')->click(); + $this->findElement(null, '//a[text()="' . $label . '"]')->click(); } /** @@ -374,7 +374,7 @@ public function iFillField(string $selector, string $value): void public function iPressModalButton(string $buttonLabel): void { $modal = $this->findElement(null, '.modal.visible.active.front'); - $btn = $this->findElement($modal, 'xpath(//div[text()="' . $buttonLabel . '"])'); + $btn = $this->findElement($modal, '//div[text()="' . $buttonLabel . '"]'); $btn->click(); } @@ -391,7 +391,7 @@ public function modalIsOpenWithText(string $text, string $selector = 'div'): voi : '"' . $text . '"'; $modal = $this->findElement(null, '.modal.visible.active.front'); - $this->findElement($modal, 'xpath(//' . $selector . '[text()[normalize-space()=' . $textEncoded . ']])'); + $this->findElement($modal, '//' . $selector . '[text()[normalize-space()=' . $textEncoded . ']]'); } /** @@ -440,7 +440,7 @@ public function panelIsOpen(): void public function panelIsOpenWithText(string $text, string $selector = 'div'): void { $panel = $this->findElement(null, '.atk-right-panel.atk-visible'); - $this->findElement($panel, 'xpath(//' . $selector . '[text()[normalize-space()="' . $text . '"]])'); + $this->findElement($panel, '//' . $selector . '[text()[normalize-space()="' . $text . '"]]'); } /** @@ -459,7 +459,7 @@ public function iFillPanelField(string $fieldName, string $value): void public function iPressPanelButton(string $buttonLabel): void { $panel = $this->findElement(null, '.atk-right-panel.atk-visible'); - $btn = $this->findElement($panel, 'xpath(//div[text()="' . $buttonLabel . '"])'); + $btn = $this->findElement($panel, '//div[text()="' . $buttonLabel . '"]'); $btn->click(); } @@ -473,7 +473,7 @@ public function iPressPanelButton(string $buttonLabel): void public function iClickTabWithTitle(string $tabTitle): void { $tabMenu = $this->findElement(null, '.ui.tabular.menu'); - $link = $this->findElement($tabMenu, 'xpath(//a[text()="' . $tabTitle . '"])'); + $link = $this->findElement($tabMenu, '//a[text()="' . $tabTitle . '"]'); $this->getSession()->executeScript('$(\'#' . $link->getAttribute('id') . '\').click()'); } @@ -532,14 +532,14 @@ public function iSearchGridFor(string $text): void public function iSelectValueInLookup(string $value, string $inputName): void { // get dropdown item from Fomantic-UI which is direct parent of input html element - $lookupElem = $this->findElement(null, 'xpath(//input[@name="' . $inputName . '"]/parent::div)'); + $lookupElem = $this->findElement(null, '//input[@name="' . $inputName . '"]/parent::div'); // open dropdown and wait till fully opened (just a click is not triggering it) $this->getSession()->executeScript('$(\'#' . $lookupElem->getAttribute('id') . '\').dropdown(\'show\')'); $this->jqueryWait('$(\'#' . $lookupElem->getAttribute('id') . '\').hasClass(\'visible\')'); // select value - $valueElem = $this->findElement($lookupElem, 'xpath(//div[text()="' . $value . '"])'); + $valueElem = $this->findElement($lookupElem, '//div[text()="' . $value . '"]'); $this->getSession()->executeScript('$(\'#' . $lookupElem->getAttribute('id') . '\').dropdown(\'set selected\', ' . $valueElem->getAttribute('data-value') . ');'); $this->jqueryWait(); @@ -556,7 +556,7 @@ public function iSelectValueInLookup(string $value, string $inputName): void */ public function iSelectFile(string $inputName, string $fileContent, string $fileName): void { - $element = $this->findElement(null, 'xpath(//input[@name="' . $inputName . '" and @type="hidden"]/following-sibling::input[@type="file"])'); + $element = $this->findElement(null, '//input[@name="' . $inputName . '" and @type="hidden"]/following-sibling::input[@type="file"]'); $this->getSession()->executeScript(<<<'EOF' const dataTransfer = new DataTransfer(); dataTransfer.items.add(new File([new Uint8Array(arguments[1])], arguments[2])); diff --git a/src/Table/Column.php b/src/Table/Column.php index 614f8aac4a..531ebc7984 100644 --- a/src/Table/Column.php +++ b/src/Table/Column.php @@ -84,8 +84,7 @@ public function addPopup(Popup $popup = null, $icon = 'table-filter-off') 'position' => 'bottom left', 'movePopup' => $this->columnData ? true : false, 'target' => $this->columnData ? 'th[data-column=' . $this->columnData . ']' : false, - 'distanceAway' => 10, - 'offset' => -2, + 'distanceAway' => -12, ] ); $popup->stopClickEvent = true; @@ -152,7 +151,7 @@ public function addDropdown(array $items, \Closure $fx, $icon = 'caret square do * * @param array $items * - * @return Column\JsHeader + * @return Column\JsHeaderDropdownCallback */ public function setHeaderDropdown($items, string $icon = 'caret square down', string $menuId = null): JsCallback { @@ -165,7 +164,7 @@ public function setHeaderDropdown($items, string $icon = 'caret square down', st ], ]]; - $cb = Column\JsHeader::addTo($this->table); + $cb = Column\JsHeaderDropdownCallback::addTo($this->table); $function = new JsExpression('function (value, text, item) { if (value === undefined || value === \'\' || value === null) { diff --git a/src/Table/Column/JsHeader.php b/src/Table/Column/JsHeaderDropdownCallback.php similarity index 83% rename from src/Table/Column/JsHeader.php rename to src/Table/Column/JsHeaderDropdownCallback.php index 8a7ab480cd..c78c3cacad 100644 --- a/src/Table/Column/JsHeader.php +++ b/src/Table/Column/JsHeaderDropdownCallback.php @@ -8,10 +8,7 @@ use Atk4\Ui\JsCallback; use Atk4\Ui\View; -/** - * Implement a callback for a column header dropdown menu. - */ -class JsHeader extends JsCallback +class JsHeaderDropdownCallback extends JsCallback { /** * Function to call when header menu item is select. diff --git a/src/VueComponent/InlineEdit.php b/src/VueComponent/InlineEdit.php index 71cf0043e9..6060415171 100644 --- a/src/VueComponent/InlineEdit.php +++ b/src/VueComponent/InlineEdit.php @@ -6,7 +6,6 @@ use Atk4\Data\Model; use Atk4\Data\ValidationException; -use Atk4\Ui\Exception; use Atk4\Ui\Js\JsExpressionable; use Atk4\Ui\Js\JsToast; use Atk4\Ui\JsCallback; @@ -67,7 +66,7 @@ class InlineEdit extends View * A default one is supply if this is null. * It receive the error ($e) as parameter. * - * @var \Closure|null + * @var \Closure(ValidationException, string): string|null */ public $formatErrorMsg; @@ -102,7 +101,7 @@ public function setModel(Model $entity): void $this->model->set($this->fieldName, $this->getApp()->uiPersistence->typecastLoadField($this->model->getField($this->fieldName), $value)); $this->model->save(); - return $this->jsSuccess('Update successfully'); + return $this->jsSuccess('Update saved'); } catch (ValidationException $e) { $this->getApp()->terminateJson([ 'success' => true, @@ -131,11 +130,6 @@ public function onChange(\Closure $fx): void } } - /** - * On success notifier. - * - * @return JsToast - */ public function jsSuccess(string $message): JsExpressionable { return new JsToast([ @@ -146,11 +140,7 @@ public function jsSuccess(string $message): JsExpressionable } /** - * On validation error notifier. - * * @param string $message - * - * @return JsToast */ public function jsError($message): JsExpressionable { @@ -163,20 +153,10 @@ public function jsError($message): JsExpressionable ]); } - /** - * Renders View. - */ protected function renderView(): void { parent::renderView(); - $type = $this->model && $this->fieldName ? $this->model->getField($this->fieldName)->type : 'string'; - $type = $type === 'string' ? 'text' : $type; - - if ($type !== 'text' && $type !== 'integer') { - throw new Exception('Only string or number field can be edited inline. Field Type = ' . $type); - } - if ($this->model && $this->model->isLoaded()) { $initValue = $this->model->get($this->fieldName); } else { @@ -189,7 +169,7 @@ protected function renderView(): void 'initValue' => $initValue, 'url' => $this->cb->getJsUrl(), 'saveOnBlur' => $this->saveOnBlur, - 'options' => ['fieldName' => $fieldName, 'fieldType' => $type, 'inputCss' => $this->inputCss], + 'options' => ['fieldName' => $fieldName, 'inputCss' => $this->inputCss], ]); } } diff --git a/tests-behat/accordion.feature b/tests-behat/accordion.feature index c1c3268180..ab423c3401 100644 --- a/tests-behat/accordion.feature +++ b/tests-behat/accordion.feature @@ -7,16 +7,16 @@ Feature: Accordion Scenario: Nested Accordion Given I am on "interactive/accordion-nested.php" - Then I click using selector "xpath((//div[text()='Static Text'])[1])" - Then I click using selector "xpath((//div[text()='Static Text'])[1])" - Then I click using selector "xpath((//div[text()='Static Text'])[1])" - Then I click using selector "xpath((//div[text()='Static Text'])[2])" - Then I click using selector "xpath((//div[text()='Dynamic Text'])[3])" - Then I click using selector "xpath((//div[text()='Dynamic Text'])[3])" - Then I click using selector "xpath((//div[text()='Dynamic Text'])[3])" - Then I click using selector "xpath((//div[text()='Dynamic Form'])[4])" - Then I click using selector "xpath((//div[text()='Dynamic Form'])[4])" - Then I click using selector "xpath((//div[text()='Dynamic Form'])[4])" + Then I click using selector "(//div[text()='Static Text'])[1]" + Then I click using selector "(//div[text()='Static Text'])[1]" + Then I click using selector "(//div[text()='Static Text'])[1]" + Then I click using selector "(//div[text()='Static Text'])[2]" + Then I click using selector "(//div[text()='Dynamic Text'])[3]" + Then I click using selector "(//div[text()='Dynamic Text'])[3]" + Then I click using selector "(//div[text()='Dynamic Text'])[3]" + Then I click using selector "(//div[text()='Dynamic Form'])[4]" + Then I click using selector "(//div[text()='Dynamic Form'])[4]" + Then I click using selector "(//div[text()='Dynamic Form'])[4]" Then I fill in "email" with "xxx@xxx.com" When I press button "Save" Then I should see "Subscribed xxx@xxx.com to newsletter." diff --git a/tests-behat/callback.feature b/tests-behat/callback.feature index f359f42377..e0e24cc559 100644 --- a/tests-behat/callback.feature +++ b/tests-behat/callback.feature @@ -16,7 +16,7 @@ Feature: Callback Then I should see "Loader-2" Then I should see "Loader-3" Then I click paginator page "2" - Then I click using selector "xpath((//div.ui.atk-test.button)[1])" + Then I click using selector "(//div.ui.atk-test.button)[1]" Then Modal is open with text "Edit Country" Then I press Modal button "Save" Then Toast display should contain text 'Country action "edit" with "Andorra" entity was executed.' diff --git a/tests-behat/crud.feature b/tests-behat/crud.feature index d9031a6bd0..91f239d8b9 100644 --- a/tests-behat/crud.feature +++ b/tests-behat/crud.feature @@ -60,25 +60,25 @@ Feature: Crud Scenario: Modal in modal Given I am on "_unit-test/crud-nested.php" - Then I click using selector "xpath((//div.ui.button[i.icon.book])[1])" + Then I click using selector "(//div.ui.button[i.icon.book])[1]" Then Modal is open with text "Edit product category" - Then I click using selector "xpath((//div.modal.active//div.ui.button[i.icon.edit])[1])" + Then I click using selector "(//div.modal.active//div.ui.button[i.icon.edit])[1]" Then Modal is open with text "Edit Product" Then input "atk_fp_product__name" value should start with "Mustard" When I press Modal button "Save" Then I click close modal - Then I click using selector "xpath((//div.ui.button[i.icon.book])[1])" + Then I click using selector "(//div.ui.button[i.icon.book])[1]" Then Modal is open with text "Edit product category" - Then I click using selector "xpath((//div.modal.active//div.ui.button[i.icon.edit])[2])" + Then I click using selector "(//div.modal.active//div.ui.button[i.icon.edit])[2]" Then Modal is open with text "Edit Product" Then input "atk_fp_product__name" value should start with "Ketchup" When I press Modal button "Save" Then I click close modal - Then I click using selector "xpath((//div.ui.button[i.icon.book])[2])" + Then I click using selector "(//div.ui.button[i.icon.book])[2]" Then Modal is open with text "Edit product category" - Then I click using selector "xpath((//div.modal.active//div.ui.button[i.icon.edit])[1])" + Then I click using selector "(//div.modal.active//div.ui.button[i.icon.edit])[1]" Then Modal is open with text "Edit Product" Then input "atk_fp_product__name" value should start with "Cola" When I press Modal button "Save" @@ -86,11 +86,11 @@ Feature: Crud Scenario: edit /w array persistence (strict comparison) Given I am on "collection/crud3.php" - Then I click using selector "xpath(//table//tr[3]//i.icon.edit)" + Then I click using selector "//table//tr[3]//i.icon.edit" Then Modal is open with text "Edit Country" Then I press Modal button "Save" Then Toast display should contain text "Record has been saved!" Scenario: delete /w array persistence (strict comparison) - Then I click using selector "xpath(//table//tr[3]//i.icon.trash)" + Then I click using selector "//table//tr[3]//i.icon.trash" Then Toast display should contain text "Record has been deleted!" diff --git a/tests-behat/grid.feature b/tests-behat/grid.feature index c80574c84d..b320c09b30 100644 --- a/tests-behat/grid.feature +++ b/tests-behat/grid.feature @@ -18,21 +18,21 @@ Feature: Grid Scenario: Checkbox click event must not bubble to row click Given I am on "_unit-test/grid-rowclick.php" - When I click using selector "xpath(//div[@id='grid']//tr[2]//td[2])" + When I click using selector "//div[@id='grid']//tr[2]//td[2]" Then Toast display should contain text "Clicked on row" - When I click using selector "xpath(//div[@id='grid']//tr[2]//div.ui.checkbox)" + When I click using selector "//div[@id='grid']//tr[2]//div.ui.checkbox" Then No toast should be displayed - When I click using selector "xpath(//div[@id='grid']//tr[2]//div.ui.button[text()='Action Button'])" + When I click using selector "//div[@id='grid']//tr[2]//div.ui.button[text()='Action Button']" Then Toast display should contain text "Clicked Action Button" - When I click using selector "xpath(//div[@id='grid']//tr[2]//div.ui.button[text()='Action Modal'])" + When I click using selector "//div[@id='grid']//tr[2]//div.ui.button[text()='Action Modal']" Then No toast should be displayed Then I should see "Clicked Action Modal: Albania" Then I hide js modal - When I click using selector "xpath(//div[@id='grid']//tr[2]//div.ui.dropdown[div[text()='Actions...']])" + When I click using selector "//div[@id='grid']//tr[2]//div.ui.dropdown[div[text()='Actions...']]" Then No toast should be displayed - When I click using selector "xpath(//div[@id='grid']//tr[2]//div.ui.dropdown[div[text()='Actions...']]//div.menu/div[text()='Action MenuItem'])" + When I click using selector "//div[@id='grid']//tr[2]//div.ui.dropdown[div[text()='Actions...']]//div.menu/div[text()='Action MenuItem']" Then Toast display should contain text "Clicked Action MenuItem" Then PATCH MINK the url should match "~_unit-test/grid-rowclick.php$~" - When I click using selector "xpath(//div[@id='grid']//tr[2]//a)" + When I click using selector "//div[@id='grid']//tr[2]//a" Then No toast should be displayed Then PATCH MINK the url should match "~_unit-test/grid-rowclick.php#test~" diff --git a/tests-behat/js.feature b/tests-behat/js.feature index c91a77676f..0e56bc82f1 100644 --- a/tests-behat/js.feature +++ b/tests-behat/js.feature @@ -20,11 +20,11 @@ Feature: JS Then I don't see button "C" Then I don't see button "Hide button C and self" - # "xpath(//div.ui.header[text()[normalize-space()='Callbacks']]/following-sibling::div.ui.button[1])" is long for nice CI output - When I check if text in "xpath(//div.ui.button[8])" match text "Callback Test" + # "//div.ui.header[text()[normalize-space()='Callbacks']]/following-sibling::div.ui.button[1]" is too long for nice CI output + When I check if text in "//div.ui.button[8]" match text "Callback Test" When I press button "Callback Test" - # "xpath(//div.ui.header[text()[normalize-space()='Callbacks']]/following-sibling::div.ui.button[1])" is long for nice CI output - Then I check if text in "xpath(//div.ui.button[8])" match regex "~^\d+$~" + # "//div.ui.header[text()[normalize-space()='Callbacks']]/following-sibling::div.ui.button[1]" is too long for nice CI output + Then I check if text in "//div.ui.button[8]" match regex "~^\d+$~" Scenario: JsCallback exception is displayed When I press button "failure" diff --git a/tests-behat/rightpanel.feature b/tests-behat/rightpanel.feature index 73d4481365..ab12801828 100644 --- a/tests-behat/rightpanel.feature +++ b/tests-behat/rightpanel.feature @@ -10,7 +10,7 @@ Feature: RightPanel Scenario: PanelModelAction Given I am on "layout/layout-panel.php" - Then I click using selector "xpath((//div.atk-card)[1])" + Then I click using selector "(//div.atk-card)[1]" When I press button "User Confirmation" When I press Modal button "Ok" Then Toast display should contain text "Confirm country" diff --git a/tests-behat/scroll.feature b/tests-behat/scroll.feature index cdc1e25dab..5a003be2ed 100644 --- a/tests-behat/scroll.feature +++ b/tests-behat/scroll.feature @@ -23,20 +23,20 @@ Feature: Dynamic scroll Given I am on "_unit-test/scroll.php" Then I should see "Argentina" Then I should not see "Denmark" - When I click using selector "xpath(//tr[td[text()='Austria']]//td[2])" + When I click using selector "//tr[td[text()='Austria']]//td[2]" Then Toast display should contain text "row clicked: 14" - When I click using selector "xpath(//tr[td[text()='Austria']]//i.icon.bell)" + When I click using selector "//tr[td[text()='Austria']]//i.icon.bell" Then Toast display should contain text "action clicked: 14" Then I should see "Argentina" Then I should not see "Denmark" When I scroll to bottom Then I should see "Denmark" - When I click using selector "xpath(//tr[td[text()='Denmark']]//td[2])" + When I click using selector "//tr[td[text()='Denmark']]//td[2]" Then Toast display should contain text "row clicked: 58" - When I click using selector "xpath(//tr[td[text()='Denmark']]//i.icon.bell)" + When I click using selector "//tr[td[text()='Denmark']]//i.icon.bell" Then Toast display should contain text "action clicked: 58" When I scroll to top - When I click using selector "xpath(//tr[td[text()='Austria']]//td[2])" + When I click using selector "//tr[td[text()='Austria']]//td[2]" Then Toast display should contain text "row clicked: 14" - When I click using selector "xpath(//tr[td[text()='Austria']]//i.icon.bell)" + When I click using selector "//tr[td[text()='Austria']]//i.icon.bell" Then Toast display should contain text "action clicked: 14" diff --git a/tests-behat/sortable.feature b/tests-behat/sortable.feature index 5cde45467b..59a2f32b2e 100644 --- a/tests-behat/sortable.feature +++ b/tests-behat/sortable.feature @@ -2,18 +2,18 @@ Feature: Sortable / Draggable Scenario: drag reorder list Given I am on "interactive/jssortable.php" - When I drag selector "xpath(//li[text()[normalize-space()='Argentina']])" onto selector "xpath(//li[text()[normalize-space()='Afghanistan']])" + When I drag selector "//li[text()[normalize-space()='Argentina']]" onto selector "//li[text()[normalize-space()='Afghanistan']]" Then Toast display should contain text "Argentina moved from position 9 to 0" - When I drag selector "xpath(//li[text()[normalize-space()='Bahamas']])" onto selector "xpath(//li[text()[normalize-space()='Bahamas']])" + When I drag selector "//li[text()[normalize-space()='Bahamas']]" onto selector "//li[text()[normalize-space()='Bahamas']]" Then No toast should be displayed - When I drag selector "xpath(//li[text()[normalize-space()='Bahamas']])" onto selector "xpath(//li[text()[normalize-space()='Argentina']])" + When I drag selector "//li[text()[normalize-space()='Bahamas']]" onto selector "//li[text()[normalize-space()='Argentina']]" Then Toast display should contain text "Bahamas moved from position 15 to 0" Scenario: drag reorder grid - When I drag selector "xpath(//tr[td[2][text()='Albania']]/td[1]/i)" onto selector "xpath(//tr[td[2][text()='Andorra']]/td[1]/i)" + When I drag selector "//tr[td[2][text()='Albania']]/td[1]/i" onto selector "//tr[td[2][text()='Andorra']]/td[1]/i" Then Toast display should contain text "New order: 1 - 3 - 4 - 5 - 2 - 6" Scenario: drag column resize Given I am on "collection/table2.php" - When I drag selector "xpath((//div.grip-resizable)[2])" onto selector "xpath((//div.grip-resizable)[1])" + When I drag selector "(//div.grip-resizable)[2]" onto selector "(//div.grip-resizable)[1]" Then Toast display should contain text 'New widths: { "action": "wide", "amount": "narrow", "amount_copy": "wide" }' diff --git a/tests-behat/upload.feature b/tests-behat/upload.feature index b251b9b9cb..e4111779bc 100644 --- a/tests-behat/upload.feature +++ b/tests-behat/upload.feature @@ -8,12 +8,12 @@ Feature: Upload When I select file input "file" with "Žlutý kůň" as "$kůň" Then Toast display should contain text "(name: $kůň, md5: b047fb155be776f5bbae061c7b08cdf0)" - When I click using selector "xpath(//div.action[.//input[@name='file']]//div.button)" + When I click using selector "//div.action[.//input[@name='file']]//div.button" Then Toast display should contain text "has been removed" When I select file input "img" with "Foo" as "bar.png" Then Toast display should contain text "is uploaded" - When I click using selector "xpath(//div.action[.//input[@name='img']]//div.button)" + When I click using selector "//div.action[.//input[@name='img']]//div.button" Then Toast display should contain text "has been removed" - Then Element "xpath(//div.action[.//div//input[@name='img']]//img)" attribute "src" should contain text "default.png" + Then Element "//div.action[.//div//input[@name='img']]//img" attribute "src" should contain text "default.png" diff --git a/tests-behat/vue.feature b/tests-behat/vue.feature index 878dd5fce1..5b4a1bbe87 100644 --- a/tests-behat/vue.feature +++ b/tests-behat/vue.feature @@ -1,9 +1,19 @@ Feature: Vue - Scenario: testing InlineEdit + Scenario: testing InlineEdit - /w autoSave Given I am on "javascript/vue-component.php" - When I fill in "atk_fp_country__name" with "test" - Then I should see "new value: test" + When I persist DB changes across requests + When I fill field using "(//input[@name='atk_fp_country__name'])[1]" with "test autoSave" + Then Toast display should contain text "Update saved" + Given I am on "javascript/vue-component.php" + Then I check if input value for "(//input[@name='atk_fp_country__name'])[1]" match text "test autoSave" + When I fill field using "(//input[@name='atk_fp_country__name'])[1]" with "Germany" + Then Toast display should contain text "Country name must be unique." + + Scenario: testing InlineEdit - /w onChange callback + Given I am on "javascript/vue-component.php" + When I fill field using "(//input[@name='atk_fp_country__name'])[2]" with "test callback" + Then I should see "new value: test callback" Then I hide js modal Scenario: testing ItemSearch