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
',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