From c658318389698729d2b7dd839ac6ef1e19b26876 Mon Sep 17 00:00:00 2001 From: Josh Chudy Date: Wed, 30 Aug 2023 18:00:58 -0700 Subject: [PATCH] Foreign key ux mode annotation support (#986) * functionality for foreign key inputDisplayMode in entry context * update annotation document * change options to facet-search-popup and simple-search-dropdown --- docs/user-docs/annotation.md | 3 + docs/user-docs/column-directive.md | 13 +- js/column.js | 4 + js/core.js | 29 +++- js/utils/constants.js | 2 + .../pseudo_column_schema/data/outbound_3.json | 1 + .../pseudo_column_schema/data/outbound_4.json | 1 + .../conf/pseudo_column_schema/schema.json | 136 +++++++++++++++++- test/specs/column/tests/03.pseudo_column.js | 30 +++- 9 files changed, 210 insertions(+), 9 deletions(-) create mode 100644 test/specs/column/conf/pseudo_column_schema/data/outbound_3.json create mode 100644 test/specs/column/conf/pseudo_column_schema/data/outbound_4.json diff --git a/docs/user-docs/annotation.md b/docs/user-docs/annotation.md index 176750e0a..3520db87c 100644 --- a/docs/user-docs/annotation.md +++ b/docs/user-docs/annotation.md @@ -445,6 +445,7 @@ Supported display _option_ syntax: - `"column_order": false`: Sorting by this foreign key psuedo-column should not be offered. - `"show_foreign_key_link": true`: Override the inherited behavior of foreign key display and add a link to the referred row. - `"show_foreign_key_link": false`: Override the inherited behavior of foreign key display by not adding any the extra. +- `"selector_ux_mode"`: The display mode for the recordedit input field when this foreign key relationship is part of the visible columns. Supported values are `"facet-search-popup"` and `"simple-search-dropdown"`, with `"facet-search-popup"` being the default. Currently only supported in `entry` contexts. Supported _columnorder_key_ syntax: @@ -587,6 +588,7 @@ Supported JSON _option_ payload patterns: - The provided _pathsuffix_ MUST provide the appropriate projection-list to form a valid `/attribute/` API URI. - The _pathsuffix_ MAY join additional tables to the path and MAY project from these tables as well as the table bound to the `S` table alias. - The _pathsuffix_ SHOULD reset the path context to `$S` if it has joined other tables. +- `"selector_ux_mode"`: The display mode for the recordedit input field when this table is part of a foreignkey relationship as the `outbound` table. Supported values are `"facet-search-popup"` and `"simple-search-dropdown"`, with `"facet-search-popup"` being the default. Currently only supported in `entry` contexts. It is not meaningful to use `page_markdown_pattern`, `row_markdown_pattern`, and `module` in for the same _context_. If they co-exist, the application will prefer `module` over `page_markdown_pattern` and `page_markdown_pattern` over `row_markdown_pattern`. @@ -1290,6 +1292,7 @@ The following attributes can be used to manipulate the presentation settings of - `ulist` for unordered bullet list. - `csv` for comma-seperated values. - `raw` for space-seperated values. + - `"selector_ux_mode"`: The display mode for the recordedit input field when this column directive is a foreign key relationship. Supported values are `"facet-search-popup"` and `"simple-search-dropdown"`, with `"facet-search-popup"` being the default. Currently only supported in `entry` contexts. - `array_display`: This property is _deprecated_. It is the same as `array_ux_mode` that is defined above under `display` property. - `array_options`: Applicaple only to read-only non-filter context of `visible-columns` annotation. This property is meant to be an object of properties that control the display of `array` or `array_d` aggregate column. These options will only affect the display (and templating environment) and have no effect on the generated ERMrest query. The available options are: - `order`: An alternative sort method to apply when a client wants to semantically sort by key values. It follows the same syntax as `column_order`. In scalar array aggregate, you cannot sort based on other columns values, you can only sort based on the scalar value of the column. diff --git a/docs/user-docs/column-directive.md b/docs/user-docs/column-directive.md index 4ce4fcc68..9abc7471d 100644 --- a/docs/user-docs/column-directive.md +++ b/docs/user-docs/column-directive.md @@ -64,7 +64,8 @@ In this category, you use the [`source`](#source) property to define the data so "wait_for": , "show_foreign_key_link": , "show_key_link": , - "array_ux_mode": + "array_ux_mode": , + "selector_ux_mode": }, "array_options": { "order": , @@ -95,7 +96,8 @@ In this category, the [`sourcekey`](#sourcekey) proprety is used to refer to one "wait_for": , "show_foreign_key_link": , "show_key_link": , - "array_ux_mode": + "array_ux_mode": , + "selector_ux_mode": }, "array_options":{ "order": , @@ -379,7 +381,8 @@ By using this attribute you can customize the presented value to the users. The "wait_for": , "show_foreign_key_link": , "show_key_link": - "array_ux_mode": + "array_ux_mode": , + "selector_ux_mode": } } ``` @@ -396,6 +399,10 @@ Used to signal Chaise that this column directive's `markdown_pattern` relies on While generating a default presentation for all outbound foreign key paths, ERMrestJS will display a link to the referred row. Using this attribute you can modify this behavior. If this attribute is missing, we are going to use the inherited behavior from the [foreign key](annotation.md#tag-2016-foreign-key) annotation defined on the last foreign key in the path. If that one is missing too, [display annotation](annotation.md#tag-2015-display) will be applied. +##### selector_ux_mode + +While generating a default presentation in `entry` mode for single outbound foreign key paths, Chaise will show a modal popup dialog for selecting rows. Using this attribute, you can modify this behavior. If this attribute is missing, we are going to use the inherited behavior from the [foreign key](annotation.md#tag-2016-foreign-key) annotation defined on the foreign key relationship. If that one is missing too, [table display](annotation.md#tag-2016-table-display) annotation will be applied. Supported values are `"facet-search-popup"` and `"simple-search-dropdown"`, with `"facet-search-popup"` being the default. + ##### show_key_link While generating a default presentation for key column directives (self link), ERMrestJS will add a link to the referred row. Using this attribute you can modify this behavior. If this attribute is missing, we are going to use the inherited behavior from the [key display](annotation.md#tag-2017-key-display) annotation. If that one is missing too, [display annotation](annotation.md#tag-2015-display) will be applied. diff --git a/js/column.js b/js/column.js index a91d7cc3d..3c89b75bc 100644 --- a/js/column.js +++ b/js/column.js @@ -2108,6 +2108,10 @@ Object.defineProperty(ForeignKeyPseudoColumn.prototype, "display", { sourceDisplay.sourceMarkdownPattern = displ.markdown_pattern; sourceDisplay.sourceTemplateEngine = displ.template_engine; } + + if (module._foreignKeyInputModes.indexOf(displ.selector_ux_mode) !== -1) { + sourceDisplay.inputDisplayMode = displ.selector_ux_mode; + } } if (this.sourceObject && typeof this.sourceObject.hide_column_header === "boolean") { diff --git a/js/core.js b/js/core.js index 3e6308a0f..f23f53818 100644 --- a/js/core.js +++ b/js/core.js @@ -4332,7 +4332,7 @@ getDisplay: function(context) { if (!(context in this._display)) { - var self = this, annotation = -1, columnOrder = [], showFKLink = true; + var self = this, annotation = -1, columnOrder = [], showFKLink = true, inputDisplayMode = module._foreignKeyInputModes[0], toTableAnnotation = -1; // NOTE: commenting out contextualized functionality since it isn't being supported just yet // var fromComment = null, fromCommentDisplay = "tooltip", toComment = null, toCommentDisplay = "tooltip"; if (this.annotations.contains(module._annotations.FOREIGN_KEY)) { @@ -4358,6 +4358,32 @@ } } + /** + * inputDisplayMode is set based on the following rules: + * 1. defined on display property in visible-columns + * 2. foreign key annotation + * 3. table-display annotation when defined on the leaf table of the fkey relationship + * 4. default value of 'popup' + * + * supported _foreignKeyInputModes are ['facet-search-popup', 'simple-search-dropdown'] + */ + + // NOTE: this property is only used when the table is used as the leaf for a foreign key + // using index 0 ensures we only support this on single outbound foreign key relationships when table-display is on the leaf table + var fromCol = this.colset.columns[0]; + var toCol = this.mapping.get(fromCol); + if (toCol.table.annotations.contains(module._annotations.TABLE_DISPLAY)) { + toTableAnnotation = module._getRecursiveAnnotationValue(context, toCol.table.annotations.get(module._annotations.TABLE_DISPLAY).content); + + if (toTableAnnotation.selector_ux_mode && module._foreignKeyInputModes.indexOf(toTableAnnotation.selector_ux_mode) !== -1) { + inputDisplayMode = toTableAnnotation.selector_ux_mode; + } + } + + if (annotation.selector_ux_mode && module._foreignKeyInputModes.indexOf(annotation.selector_ux_mode) !== -1) { + inputDisplayMode = annotation.selector_ux_mode; + } + // fromComment = _processModelComment(annotation.from_comment); // toComment = _processModelComment(annotation.to_comment); // fromCommentDisplay = (annotation.from_comment && typeof annotation.from_comment_display === "string") ? annotation.from_comment_display : "tooltip"; @@ -4367,6 +4393,7 @@ "columnOrder": columnOrder, // "fromComment": fromComment, // "fromCommentDisplay": fromCommentDisplay, + "inputDisplayMode": inputDisplayMode, "showForeignKeyLink": showFKLink, // "toComment": toComment, // "toCommentDisplay": toCommentDisplay diff --git a/js/utils/constants.js b/js/utils/constants.js index b5767fba5..b315e4bc2 100644 --- a/js/utils/constants.js +++ b/js/utils/constants.js @@ -172,6 +172,8 @@ "json" ]; + module._foreignKeyInputModes = ['facet-search-popup', 'simple-search-dropdown']; + module._facetUXModes = Object.freeze({ CHOICE: "choices", RANGE: "ranges", diff --git a/test/specs/column/conf/pseudo_column_schema/data/outbound_3.json b/test/specs/column/conf/pseudo_column_schema/data/outbound_3.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/test/specs/column/conf/pseudo_column_schema/data/outbound_3.json @@ -0,0 +1 @@ +[] diff --git a/test/specs/column/conf/pseudo_column_schema/data/outbound_4.json b/test/specs/column/conf/pseudo_column_schema/data/outbound_4.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/test/specs/column/conf/pseudo_column_schema/data/outbound_4.json @@ -0,0 +1 @@ +[] diff --git a/test/specs/column/conf/pseudo_column_schema/schema.json b/test/specs/column/conf/pseudo_column_schema/schema.json index 44064f6ee..2bd9d12a0 100644 --- a/test/specs/column/conf/pseudo_column_schema/schema.json +++ b/test/specs/column/conf/pseudo_column_schema/schema.json @@ -32,7 +32,16 @@ "schema_name": "pseudo_column_schema", "table_name": "outbound_1", "column_name": "id" - }] + }], + "annotations": { + "tag:isrd.isi.edu,2016:foreign-key": { + "display": { + "entry/edit": { + "selector_ux_mode": "facet-search-popup" + } + } + } + } }, { "names": [["pseudo_column_schema", "main_fk2"]], "foreign_key_columns": [{ @@ -45,6 +54,39 @@ "table_name": "outbound_2", "column_name": "id" }] + }, { + "names": [["pseudo_column_schema", "main_fk3"]], + "foreign_key_columns": [{ + "schema_name": "pseudo_column_schema", + "table_name": "main", + "column_name": "fk3" + }], + "referenced_columns": [{ + "schema_name": "pseudo_column_schema", + "table_name": "outbound_3", + "column_name": "id" + }], + "annotations": { + "tag:isrd.isi.edu,2016:foreign-key": { + "display": { + "entry/edit": { + "selector_ux_mode": "simple-search-dropdown" + } + } + } + } + }, { + "names": [["pseudo_column_schema", "main_fk4"]], + "foreign_key_columns": [{ + "schema_name": "pseudo_column_schema", + "table_name": "main", + "column_name": "fk4" + }], + "referenced_columns": [{ + "schema_name": "pseudo_column_schema", + "table_name": "outbound_4", + "column_name": "id" + }] } ], "column_definitions": [ @@ -90,6 +132,18 @@ "typename": "text" } }, + { + "name": "fk3", + "type": { + "typename": "text" + } + }, + { + "name": "fk4", + "type": { + "typename": "text" + } + }, { "name": "asset", "type": { @@ -272,7 +326,33 @@ "aggregate": "array" } ], - "entry": "detailed", + "entry/create": "detailed", + "entry/edit": [ + "main_table_id_col", + { + "source": [{"outbound": ["pseudo_column_schema", "main_fk2"]}, "id"], + "markdown_name": "main fk2 dropdown", + "comment": "main fk2 cm dropdown display" + }, + { + "source": [{"outbound": ["pseudo_column_schema", "main_fk1"]}, "id"], + "markdown_name": "main fk dropdown", + "comment": "main fk cm dropdown display" + }, + { + "source": [{"outbound": ["pseudo_column_schema", "main_fk3"]}, "id"], + "markdown_name": "main fk3 popup", + "comment": "main fk3 cm override popup display", + "display": { + "selector_ux_mode": "facet-search-popup" + } + }, + { + "source": [{"outbound": ["pseudo_column_schema", "main_fk4"]}, "id"], + "markdown_name": "main fk4 popup", + "comment": "main fk4 cm default popup display" + } + ], "compact/select": [ "col", "main_table_id_col", @@ -548,7 +628,10 @@ "tag:isrd.isi.edu,2016:table-display": { "*" : { "row_order": [{"column":"col", "descending":false}] - } + }, + "entry/edit": { + "selector_ux_mode": "simple-search-dropdown" + } } } }, @@ -1004,7 +1087,52 @@ "name": "fk", "type": {"typename": "text"} } - ] + ], + "annotations": { + "tag:isrd.isi.edu,2016:table-display": { + "entry/edit": { + "selector_ux_mode": "simple-search-dropdown" + } + } + } + }, + "outbound_3": { + "kind": "table", + "table_name": "outbound_3", + "schema_name": "pseudo_column_schema", + "keys": [{"unique_columns": ["id"]}], + "foreign_keys": [], + "column_definitions": [ + { + "name": "id", + "nullok": false, + "type": {"typename": "text"} + }, + { + "name": "col", + "type": {"typename": "text"} + } + ], + "annotations": {} + }, + "outbound_4": { + "kind": "table", + "table_name": "outbound_4", + "schema_name": "pseudo_column_schema", + "keys": [{"unique_columns": ["id"]}], + "foreign_keys": [], + "column_definitions": [ + { + "name": "id", + "nullok": false, + "type": {"typename": "text"} + }, + { + "name": "col", + "type": {"typename": "text"} + } + ], + "annotations": {} }, "outbound_2_outbound_2": { "comment": "outbound_2_outbound_2 comment", diff --git a/test/specs/column/tests/03.pseudo_column.js b/test/specs/column/tests/03.pseudo_column.js index 8ed0a446d..749d57a6b 100644 --- a/test/specs/column/tests/03.pseudo_column.js +++ b/test/specs/column/tests/03.pseudo_column.js @@ -161,7 +161,8 @@ exports.execute = function (options) { mainRef = response; mainRefDetailed = response.contextualize.detailed; detailedCols = mainRefDetailed.columns; - mainRefEntry = response.contextualize.entry; + mainRefEntry = response.contextualize.entryCreate; + mainRefEntryEdit = response.contextualize.entryEdit; mainRefCompactEntry = response.contextualize.compactEntry; return options.ermRest.resolve(invalidEntityUri, {cid: "test"}); }).then(function (response) { @@ -746,6 +747,33 @@ exports.execute = function (options) { }); // the rest of tests are in 02.referenced_column.js + }); + + describe(".inputDisplayMode", function () { + var entryCols; + beforeAll(function () { + entryCols = mainRefEntryEdit.columns; + }); + + it('should return the `selector_ux_mode` defined on the table-display annotation', function () { + // table-display on outbound_2 says 'simple-search-dropdown' that overrides default ('facet-search-popup') + expect(entryCols[1].display.inputDisplayMode).toBe('simple-search-dropdown', "missmatch for index=2"); + }); + + it('should return the `selector_ux_mode` defined on the foreign-key annotation', function () { + // foreign-key on ["pseudo_column_schema", "main_fk1"] says 'facet-search-popup' that overrides table-display 'simple-search-dropdown' + expect(entryCols[2].display.inputDisplayMode).toBe('facet-search-popup', "missmatch for index=2"); + }); + + it ("should return the `selector_ux_mode` defined on the source and ignore the foreign-key", function () { + // source says 'facet-search-popup'that overrides foreign-key on ["pseudo_column_schema", "main_fk3"] 'simple-search-dropdown' + expect(entryCols[3].display.inputDisplayMode).toBe('facet-search-popup', "missmatch for index=1"); + }); + + it ("should return the default value when no `selector_ux_mode` is defined", function () { + // foreign-key ["pseudo_column_schema", "main_fk4"] uses default value + expect(entryCols[4].display.inputDisplayMode).toBe('facet-search-popup', "missmatch for index=1"); + }); }) describe(".table, ", function () {