diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f1b0ed001204cb31f8f836f475c5c63bf426c10..cf321619c1e385e861796d3c314d59a9921eb2e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 A list of entity roles and names can be specified via the newly added `BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS` build variable. See the docstring of `queryForm.initFreeSearch` for more information. +* [#189](https://gitlab.com/caosdb/caosdb-webui/-/issues/189) The order in which + the properties of (Records of) a RecordType are displayed can be configured. ### Changed (for changes in existing functionality) diff --git a/src/core/js/ext_prop_display.js b/src/core/js/ext_prop_display.js index 802e49c97bb0cac017e8e0bab013001098b848c1..f74d700078b2b8ab69174ab05c9e95c6df33b109 100644 --- a/src/core/js/ext_prop_display.js +++ b/src/core/js/ext_prop_display.js @@ -51,7 +51,7 @@ var prop_display = new function ($, edit_mode, getEntityName, getEntityRole, get return $(".caosdb-entity-panel,.caosdb-entity-preview"); } - this.unhideProperties = function (entities, conf, allTypes, userName, userRoles) { + this.displayProperties = function (entities, conf, allTypes, userName, userRoles) { for (let ent of entities) { let parents = getParents(ent).map(par => par.name); @@ -66,9 +66,12 @@ var prop_display = new function ($, edit_mode, getEntityName, getEntityRole, get for (let typeName of Object.keys(conf)) { let typeConf = conf[typeName]; let allNames = allTypes.typesWithChildren[typeName]; - // only hide something if there is a match in at least one parent type + // only change the display something if there is a match in + // at least one parent type if (parents.some(par => allNames.includes(par)) || (getEntityRole(ent) == "RecordType" && allNames.includes(getEntityName(ent)))) { + // first sort the properties + this._sortProperties(ent, properties, typeConf); properties.forEach((prop, index) => { if (this._hide_property(getPropertyName(prop), userName, userRoles, typeConf)) { // Should be hidden by default but better safe than sorry @@ -87,6 +90,31 @@ var prop_display = new function ($, edit_mode, getEntityName, getEntityRole, get } } + this._sortProperties = function (entity, properties, conf) { + if (conf.order == undefined || conf.order.length == 0) { + return; + } + properties.sort(function (a, b) { + let confIndexA = conf.order.indexOf(getPropertyName(a)); + let confIndexB = conf.order.indexOf(getPropertyName(b)); + if (confIndexA < 0 && confIndexB < 0) { + // both are not part of order list + return 0; + } + if (confIndexA < 0) { + // only b is part of order list , so it is placed before a + return 1; + } + if (confIndexB < 0) { + // only a is part of order list, so it is placed before b + return -1; + } + // From here, we can assume that both are in the order list: + return confIndexA - confIndexB; + }); + $(entity).find(".caosdb-properties").append(properties); + } + this._hide_property = function (propname, userName, userRoles, conf) { // is this property only shown for certain users/groups? @@ -154,7 +182,7 @@ var prop_display = new function ($, edit_mode, getEntityName, getEntityRole, get return result; } - this._unhidePropertiesWrapper = function (original, conf, allTypes) { + this._displayPropertiesWrapper = function (original, conf, allTypes) { // Same as above, but for when there actually may be something to hide // (i.e., build variable is set and config is non-empty). const result = function (entitiy) { @@ -165,7 +193,7 @@ var prop_display = new function ($, edit_mode, getEntityName, getEntityRole, get var entities = prop_display.getEntitiesInView(); const userName = getUserName(); const userRoles = getUserRoles(); - prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); return original_return; } @@ -179,11 +207,11 @@ var prop_display = new function ($, edit_mode, getEntityName, getEntityRole, get var entities = this.getEntitiesInView(); const userName = getUserName(); const userRoles = getUserRoles(); - this.unhideProperties(entities, conf, allTypes, userName, userRoles); + this.displayProperties(entities, conf, allTypes, userName, userRoles); // If we are in the edit mode, (un)hide properties after ending // the editing of an entity document.body.addEventListener(edit_mode.start_edit.type, (e) => { - edit_mode.app.onAfterShowResults = this._unhidePropertiesWrapper(edit_mode.app.onAfterShowResults, conf, allTypes); + edit_mode.app.onAfterShowResults = this._displayPropertiesWrapper(edit_mode.app.onAfterShowResults, conf, allTypes); }, true); } else { diff --git a/src/doc/extension/display_of_properties.rst b/src/doc/extension/display_of_properties.rst index b5fdfe30198cf5042be6ca940fdcf67220d834d9..591ce9b2a07f84eb20fe12a2128f47858103e266 100644 --- a/src/doc/extension/display_of_properties.rst +++ b/src/doc/extension/display_of_properties.rst @@ -77,10 +77,66 @@ the ``price`` of all ``MusicalInstruments`` from ``anonymous`` and their } } +Defining the order of properties +******************************** + +Similar to above, the order in which properties are displayed can also be +specified. Again, the build variable ``BUILD_MODULE_EXT_PROPERTY_DISPLAY`` has +to be enabled. Then, the order in which the properties are displayed can be +configured in the same configuration file as above, i.e., in +``conf/ext/json/ext_prop_display.json``: + +.. code-block:: json + + { + "RecordTypeName": { + "order": ["ordered", "list", "of", "properties"] + }, + ... + } + +This ensures that the properties of all entities with the declared types are +displayed in the defined order if present. Additional properties, that are not +part of this list are appended in their original order as returned from the CaosDB +server. + +Using again the data of demo.indiscale.com for an example, a configuration might +look the following: + +.. code-block:: json + + { + "MusicalInstrument": { + "order": ["price", "Manufacturer"] + } + } + +In all ``MusicalInstrument`` entities, the ``price`` would then be shown first, +then the ``Manufacturer``, and then all remaining properties. If it doesn't have +a ``price`` property, ``Manufacturer`` is shown on top. + +Of course, this feature can be combined with the hiding of properties: + +.. code-block:: json + + { + "MusicalInstrument": { + "hide": [ + {"name": "price", "roles": ["anonymous"], "users": []}, + {"name": "Manufacturer", "roles": [], "users": ["admin"]} + ], + "order": ["price", "Manufacturer"] + } + } + + +In this example, ``price`` would still be displayed on top of the list of +property in every ``MusicalInstrument`` entity, but it is hidden for the +``anonymous`` role. + Future ****** -In the future, this feature will be extended to allow the RecordType-wise -`sorting <https://gitlab.com/caosdb/caosdb-webui/-/issues/189>`__ of properties -and to `toggle <https://gitlab.com/caosdb/caosdb-webui/-/issues/190>`__ -properties. +In the future, this feature will be extended to allow to `toggle +<https://gitlab.com/caosdb/caosdb-webui/-/issues/190>`__ properties of +predifined RecordTypes. diff --git a/test/core/js/modules/ext_prop_display.js.js b/test/core/js/modules/ext_prop_display.js.js index 18d2c54056609bf0cc6aafca2d06705ad7a40b02..8c2c0264b44810786ed64fc556d21fb21dbe9b1f 100644 --- a/test/core/js/modules/ext_prop_display.js.js +++ b/test/core/js/modules/ext_prop_display.js.js @@ -48,7 +48,7 @@ QUnit.test("unhide all properties", function (assert) { QUnit.test("hide properties garbage type", function (assert) { assert.ok(prop_display.getEntitiesInView, "getEntitiesInView available"); - assert.ok(prop_display.unhideProperties, "unhideProperties available"); + assert.ok(prop_display.displayProperties, "displayProperties available"); const conf = { "DoesntExist": { "hide": [{ @@ -70,14 +70,14 @@ QUnit.test("hide properties garbage type", function (assert) { const entities = prop_display.getEntitiesInView(); assert.equal(entities.length, 1, "only one entity in test data"); assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all properties hidden initially"); - prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); assert.equal($(document).find(".caosdb-v-hidden-property").length, 0, "no garbage-type entity, so no hidden properties"); assert.equal($(document).find(".caosdb-v-show-property").length, 3, "all properties are being shown"); }); QUnit.test("hide properties garbage property", function (assert) { assert.ok(prop_display.getEntitiesInView, "getEntitiesInView available"); - assert.ok(prop_display.unhideProperties, "unhideProperties available"); + assert.ok(prop_display.displayProperties, "displayProperties available"); const conf = { "MusicalInstrument": { "hide": [{ @@ -98,7 +98,7 @@ QUnit.test("hide properties garbage property", function (assert) { const entities = prop_display.getEntitiesInView(); assert.equal(entities.length, 1, "only one entity in test data"); assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all properties hidden initially"); - prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); assert.equal($(document).find(".caosdb-v-hidden-property").length, 0, "no garbage property, so no hidden properties"); assert.equal($(document).find(".caosdb-v-show-property").length, 3, "all properties are being shown"); }); @@ -106,7 +106,7 @@ QUnit.test("hide properties garbage property", function (assert) { QUnit.test("hide properties", function (assert) { assert.ok(prop_display.getEntitiesInView, "getEntitiesInView available"); - assert.ok(prop_display.unhideProperties, "unhideProperties available"); + assert.ok(prop_display.displayProperties, "displayProperties available"); const conf = { "MusicalInstrument": { "hide": [{ @@ -138,7 +138,7 @@ QUnit.test("hide properties", function (assert) { const entities = prop_display.getEntitiesInView(); assert.equal(entities.length, 1, "only one entity in test data"); assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all properties hidden initially"); - prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); assert.equal($(document).find(".caosdb-v-hidden-property").length, 1, "exactly one hidden property"); assert.equal($(document).find(".caosdb-v-show-property").length, 2, "the remaining two are shown"); assert.equal($("#1").hasClass("caosdb-v-hidden-property"), true, "first prop hidden"); @@ -146,11 +146,11 @@ QUnit.test("hide properties", function (assert) { assert.equal($("#3").hasClass("caosdb-v-show-property"), true, "third prop shown"); // reset - prop_display.unhideProperties(entities, conf, allTypes, "", []); + prop_display.displayProperties(entities, conf, allTypes, "", []); assert.equal($(document).find(".caosdb-v-show-property").length, 3, "all shown after reset"); userRoles = ["some_other_role"]; - prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); assert.equal($(document).find(".caosdb-v-hidden-property").length, 2, "two hidden properties"); assert.equal($(document).find(".caosdb-v-show-property").length, 1, "the remaining one is shown"); assert.equal($("#1").hasClass("caosdb-v-hidden-property"), true, "first prop hidden"); @@ -158,11 +158,11 @@ QUnit.test("hide properties", function (assert) { assert.equal($("#3").hasClass("caosdb-v-hidden-property"), true, "third prop hidden"); // reset - prop_display.unhideProperties(entities, conf, allTypes, "", []); + prop_display.displayProperties(entities, conf, allTypes, "", []); assert.equal($(document).find(".caosdb-v-show-property").length, 3, "all shown after reset"); userName = "someone else"; - prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); assert.equal($(document).find(".caosdb-v-hidden-property").length, 2, "two hidden properties"); assert.equal($(document).find(".caosdb-v-show-property").length, 1, "the remaining one is shown"); assert.equal($("#1").hasClass("caosdb-v-show-property"), true, "first prop shown"); @@ -170,11 +170,11 @@ QUnit.test("hide properties", function (assert) { assert.equal($("#3").hasClass("caosdb-v-hidden-property"), true, "third prop hidden"); // reset - prop_display.unhideProperties(entities, conf, allTypes, "", []); + prop_display.displayProperties(entities, conf, allTypes, "", []); assert.equal($(document).find(".caosdb-v-show-property").length, 3, "all shown after reset"); userRoles = ["some_role", "some_other_role"] - prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "two hidden properties"); assert.equal($(document).find(".caosdb-v-show-property").length, 0, "None is shown"); assert.equal($("#1").hasClass("caosdb-v-hidden-property"), true, "first prop hidden"); @@ -185,7 +185,7 @@ QUnit.test("hide properties", function (assert) { QUnit.test("show properties", function (assert) { assert.ok(prop_display.getEntitiesInView, "getEntitiesInView available"); - assert.ok(prop_display.unhideProperties, "unhideProperties available"); + assert.ok(prop_display.displayProperties, "displayProperties available"); const conf = { "MusicalInstrument": { "show": [{ @@ -217,7 +217,7 @@ QUnit.test("show properties", function (assert) { const entities = prop_display.getEntitiesInView(); assert.equal(entities.length, 1, "only one entity in test data"); assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all properties hidden initially"); - prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); assert.equal($(document).find(".caosdb-v-hidden-property").length, 2, "two hidden properties"); assert.equal($(document).find(".caosdb-v-show-property").length, 1, "the remaining one shown"); assert.equal($("#1").hasClass("caosdb-v-show-property"), true, "first prop shown"); @@ -225,11 +225,11 @@ QUnit.test("show properties", function (assert) { assert.equal($("#3").hasClass("caosdb-v-hidden-property"), true, "third prop hidden"); // reset - prop_display.unhideProperties(entities, conf, allTypes, "", []); + prop_display.displayProperties(entities, conf, allTypes, "", []); assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all hidden after reset"); userRoles = ["some_other_role"]; - prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); assert.equal($(document).find(".caosdb-v-hidden-property").length, 1, "one hidden properties"); assert.equal($(document).find(".caosdb-v-show-property").length, 2, "the remaining two are shown"); assert.equal($("#1").hasClass("caosdb-v-show-property"), true, "first prop shown"); @@ -237,11 +237,11 @@ QUnit.test("show properties", function (assert) { assert.equal($("#3").hasClass("caosdb-v-show-property"), true, "third prop shown"); // reset - prop_display.unhideProperties(entities, conf, allTypes, "", []); + prop_display.displayProperties(entities, conf, allTypes, "", []); assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all hidden after reset"); userName = "someone else"; - prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); assert.equal($(document).find(".caosdb-v-hidden-property").length, 1, "one hidden property"); assert.equal($(document).find(".caosdb-v-show-property").length, 2, "the remaining ones are shown"); assert.equal($("#1").hasClass("caosdb-v-hidden-property"), true, "first prop hidden"); @@ -249,11 +249,11 @@ QUnit.test("show properties", function (assert) { assert.equal($("#3").hasClass("caosdb-v-show-property"), true, "third prop shown"); // reset - prop_display.unhideProperties(entities, conf, allTypes, "", []); + prop_display.displayProperties(entities, conf, allTypes, "", []); assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all hidden after reset"); userRoles = ["some_role", "some_other_role"] - prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); assert.equal($(document).find(".caosdb-v-hidden-property").length, 0, "no hidden properties"); assert.equal($(document).find(".caosdb-v-show-property").length, 3, "All are shown"); assert.equal($("#1").hasClass("caosdb-v-show-property"), true, "first prop shown"); @@ -261,3 +261,64 @@ QUnit.test("show properties", function (assert) { assert.equal($("#3").hasClass("caosdb-v-show-property"), true, "third prop shown"); }); + +QUnit.test("Sort properties", function (assert) { + assert.ok(prop_display.getEntitiesInView, "getEntitiesInView available"); + assert.ok(prop_display.displayProperties, "displayProperties available"); + + var conf = { + "MusicalInstrument": { + "order": ["third prop", "first prop", "second prop"] + } + }; + const allTypes = { + "typesWithChildren": { + "MusicalInstrument": ["MusicalInstrument", "Guitar"] + }, + "allTypesOrChildren": ["MusicalInstrument", "Guitar"] + }; + // username and roles don't matter for sorting + const userName = ""; + const userRoles = []; + // initial order + var properties = $(document).find(".caosdb-v-property-row"); + assert.equal(properties.index($("#1")), 0, "first prop at first position"); + assert.equal(properties.index($("#2")), 1, "second prop at second position"); + assert.equal(properties.index($("#3")), 2, "third prop at third position"); + + var entities = prop_display.getEntitiesInView(); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); + properties = $(document).find(".caosdb-v-property-row"); + assert.equal(properties.index($("#1")), 1, "first prop at second position"); + assert.equal(properties.index($("#2")), 2, "second prop at third position"); + assert.equal(properties.index($("#3")), 0, "third prop at first position"); + + // only specify first prop, the rest is appended in the previous order. + conf = { + "MusicalInstrument": { + "order": ["first prop"] + } + }; + entities = prop_display.getEntitiesInView(); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); + properties = $(document).find(".caosdb-v-property-row"); + assert.equal(properties.index($("#1")), 0, "first prop at first position"); + assert.equal(properties.index($("#2")), 2, "second prop at third position"); + assert.equal(properties.index($("#3")), 1, "third prop at second position"); + + // two specified, the remaining prop is appended + conf = { + "MusicalInstrument": { + "order": ["second prop", "first prop"] + } + }; + entities = prop_display.getEntitiesInView(); + prop_display.displayProperties(entities, conf, allTypes, userName, userRoles); + properties = $(document).find(".caosdb-v-property-row"); + assert.equal(properties.index($("#1")), 1, "first prop at second position"); + assert.equal(properties.index($("#2")), 0, "second prop at first position"); + assert.equal(properties.index($("#3")), 2, "third prop at third position"); + + + +});