From 6e7393043a68d4f884b38cd64b2d262f3810c3de Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 10 Dec 2023 16:04:00 +0100 Subject: [PATCH] Fix: MultiLingual selectors to have a more clear usage (#407) * update dropdown component to have a selected item * update select input component to add searchable and displayField options * update language field component to au generate language name using ISO * update platform language to not use JS and add tooltip * update search language selector to add a tooltip and show more languages * update content language selector to have a tooltip and a button to edit --- app/assets/images/icons/earth.svg | 7 ++ app/assets/stylesheets/bioportal.scss | 1 + .../stylesheets/components/dropdown.scss | 4 + app/assets/stylesheets/components/index.scss | 1 + .../stylesheets/components/input_field.scss | 53 -------- app/assets/stylesheets/components/select.scss | 60 ++++++++++ app/assets/stylesheets/nav_bar.scss | 20 ++-- app/assets/stylesheets/ontologies.scss | 9 ++ .../dropdown_section_button_component.rb | 3 +- ...ropdown_section_button_component.html.haml | 6 +- .../input/language_selector_component.rb | 21 +++- app/components/language_field_component.rb | 26 +++- .../language_field_component.html.haml | 2 +- app/components/select_input_component.rb | 10 +- .../select_input_component_controller.js | 113 ++++++++++++------ app/controllers/application_controller.rb | 3 +- app/controllers/concerns/submission_filter.rb | 2 +- app/controllers/fair_score_controller.rb | 2 +- app/controllers/submissions_controller.rb | 8 +- app/helpers/application_helper.rb | 24 ++-- app/helpers/components_helper.rb | 19 +++ app/helpers/multi_languages_helper.rb | 97 +++++++++++++++ app/helpers/ontologies_helper.rb | 38 ++---- app/javascript/controllers/index.js | 3 - .../platform_language_controller.js | 21 ---- .../fair_score/_fair_service_header.html.haml | 3 +- app/views/layouts/_ontology_viewer.html.haml | 4 +- app/views/search/index.html.haml | 2 +- app/views/submissions/edit.html.haml | 43 +++---- config/bioportal_config_env.rb.sample | 2 +- config/bioportal_config_test.rb | 2 +- config/locales/fr.yml | 2 +- test/system/submission_flows_test.rb | 2 +- 33 files changed, 399 insertions(+), 214 deletions(-) create mode 100644 app/assets/images/icons/earth.svg create mode 100644 app/assets/stylesheets/components/select.scss delete mode 100644 app/javascript/controllers/platform_language_controller.js diff --git a/app/assets/images/icons/earth.svg b/app/assets/images/icons/earth.svg new file mode 100644 index 000000000..316885c03 --- /dev/null +++ b/app/assets/images/icons/earth.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/assets/stylesheets/bioportal.scss b/app/assets/stylesheets/bioportal.scss index 5badfe1aa..a00e324f2 100644 --- a/app/assets/stylesheets/bioportal.scss +++ b/app/assets/stylesheets/bioportal.scss @@ -50,6 +50,7 @@ body{ } .disabled-link{ + pointer-events: none; color: #888888 !important; span{ opacity: 0.6; diff --git a/app/assets/stylesheets/components/dropdown.scss b/app/assets/stylesheets/components/dropdown.scss index b9c73b7f4..9d325c5c7 100644 --- a/app/assets/stylesheets/components/dropdown.scss +++ b/app/assets/stylesheets/components/dropdown.scss @@ -14,4 +14,8 @@ border-radius: 5px; margin-bottom: 20px; margin-top: 20px; +} + +.dropdown-item-selected { + background-color: var(--light-color) !important; } \ No newline at end of file diff --git a/app/assets/stylesheets/components/index.scss b/app/assets/stylesheets/components/index.scss index 7ea4777c1..8a6ff12cc 100644 --- a/app/assets/stylesheets/components/index.scss +++ b/app/assets/stylesheets/components/index.scss @@ -26,3 +26,4 @@ @import "image"; @import "alert"; @import "progress_pages"; +@import "select"; diff --git a/app/assets/stylesheets/components/input_field.scss b/app/assets/stylesheets/components/input_field.scss index 9b80f4e54..533dd7dac 100644 --- a/app/assets/stylesheets/components/input_field.scss +++ b/app/assets/stylesheets/components/input_field.scss @@ -34,62 +34,9 @@ margin-top: 5px; } -.ts-control { - padding: 12px; - border-radius: 5px; - border-color: #BDBDBD; -} - -.ts-dropdown-content .option { - padding: 12px; -} - -.ts-dropdown .active { - background-color: #f8f8f8; - -} - -.ts-dropdown { - margin: 0; - color: #666666; -} .chosen-container { padding: 0; border-radius: 5px; } -.ts-wrapper.single .ts-control:after { - border-color: #343a40 transparent transparent; - border-style: solid; - border-width: 5px 5px 0; - content: " "; - display: block; - height: 0; - margin-top: -3px; - position: absolute; - right: calc(0.75rem + 5px); - top: 50%; - width: 0; -} - -.ts-wrapper.multi .ts-control>div{ - border-radius: 5px; - font-size: 11px; - padding: 2px 0 4px 6px; - color: #888888; -} - -.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove{ - border-left: none; - color: #888888 !important; - margin-left: 0; -} - -.ts-wrapper.plugin-remove_button .item .remove:hover { - background: unset; -} - -.has-items .ts-control > input { - width: unset !important; -} \ No newline at end of file diff --git a/app/assets/stylesheets/components/select.scss b/app/assets/stylesheets/components/select.scss new file mode 100644 index 000000000..b730bbb0f --- /dev/null +++ b/app/assets/stylesheets/components/select.scss @@ -0,0 +1,60 @@ +.ts-wrapper{ + display: flex; +} + +.ts-control { + padding: 12px; + border-radius: 5px; +} + +.ts-dropdown-content .option { + padding: 12px; +} + +.ts-dropdown .active { + background-color: #f8f8f8; +} + +.ts-dropdown { + flex: 1 1 auto; + min-width: 9rem; + margin: 0; + color: #666666; +} + + +.ts-wrapper.single .ts-control:after { + border-color: #343a40 transparent transparent; + border-style: solid; + border-width: 5px 5px 0; + content: " "; + display: block; + height: 0; + margin-top: -3px; + position: absolute; + right: calc(0.5rem); + top: 50%; + width: 0; +} + +.ts-wrapper.multi .ts-control>div{ + border-radius: 5px; + font-size: 11px; + padding: 2px 0 4px 6px; + color: #888888; +} + +.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove{ + border-left: none; + color: #888888 !important; + margin-left: 0; +} + +.ts-wrapper.plugin-remove_button .item .remove:hover { + background: unset; +} + +.has-items .ts-control > input { + width: unset !important; +} + diff --git a/app/assets/stylesheets/nav_bar.scss b/app/assets/stylesheets/nav_bar.scss index fdc92676e..427389b82 100644 --- a/app/assets/stylesheets/nav_bar.scss +++ b/app/assets/stylesheets/nav_bar.scss @@ -96,15 +96,21 @@ } -.nav-language{ - background-color: transparent; - width: 47px; - color: white; - border: none; - outline: none; - cursor: pointer; +.nav-language { + width: 47px !important; + .ts-control{ + background-color: transparent; + color: white; + border: none; + outline: none; + cursor: pointer; + } + &.single.input-active .ts-control{ + background: transparent !important; + } } + .nav-language option{ background-color: white; color: black; diff --git a/app/assets/stylesheets/ontologies.scss b/app/assets/stylesheets/ontologies.scss index 494655a31..c5acad290 100644 --- a/app/assets/stylesheets/ontologies.scss +++ b/app/assets/stylesheets/ontologies.scss @@ -284,3 +284,12 @@ $widget-table-border-color: #EFEFEF; flex-direction: row; } + +#select_content_language{ + & ~ .ts-wrapper .ts-control{ + padding: 6px 8px !important; + .item{ + margin-right: 15px; + } + } +} \ No newline at end of file diff --git a/app/components/dropdown_section_button_component.rb b/app/components/dropdown_section_button_component.rb index c46074f75..069e37e40 100644 --- a/app/components/dropdown_section_button_component.rb +++ b/app/components/dropdown_section_button_component.rb @@ -5,9 +5,10 @@ class DropdownSectionButtonComponent < ViewComponent::Base renders_one :header renders_many :items - def initialize(divide: true) + def initialize(divide: true, selected_index: nil) super @divide = divide + @selected = selected_index end def show_divider? diff --git a/app/components/dropdown_section_button_component/dropdown_section_button_component.html.haml b/app/components/dropdown_section_button_component/dropdown_section_button_component.html.haml index 3070f75de..0f829c820 100644 --- a/app/components/dropdown_section_button_component/dropdown_section_button_component.html.haml +++ b/app/components/dropdown_section_button_component/dropdown_section_button_component.html.haml @@ -2,6 +2,6 @@ %div.dropdown-divider - if header? %h6.dropdown-header= header -- items.each do |i| - %span.dropdown-item - = i \ No newline at end of file +- items.each_with_index do |text, index| + %span.dropdown-item{class: index.eql?(@selected) ? 'dropdown-item-selected' : ''} + = text \ No newline at end of file diff --git a/app/components/input/language_selector_component.rb b/app/components/input/language_selector_component.rb index 3a7ec435c..ae5a391d3 100644 --- a/app/components/input/language_selector_component.rb +++ b/app/components/input/language_selector_component.rb @@ -2,25 +2,34 @@ class Input::LanguageSelectorComponent < ViewComponent::Base - def initialize(languages:, selected: nil, id: '', name: '' ) + def initialize(languages:, selected: nil, id: '', name: '' , enable_all: false, **html_options) super @languages = languages @id = id - @name = languages + @name = name @selected = selected + @data = html_options[:data] || {} + @enable_all = enable_all + @html_options = html_options end - def languages_options - values = [['All languages', 'all']] + def languages + values = [] + values = [["
#{render(LanguageFieldComponent.new(label: 'All languages', icon: 'icons/earth.svg', value: 'en'))}
", 'all']] if @enable_all @languages.each do |key, label| - option = "
#{render(LanguageFieldComponent.new(value: key.to_s.downcase, label: label))}
" + option = "
#{render(LanguageFieldComponent.new(value: key.to_s.downcase, label: label, auto_label: true))}
" values += [[option, key.to_s.downcase]] end values end def call - render SelectInputComponent.new(id: @id, name: @name, values: languages_options, selected: @selected, placeholder: 'Select a language') + render SelectInputComponent.new(id: @id, name: @name, values: languages, + selected: @selected, + data: @data, + required: true, + open_to_add_values: false, + placeholder: 'Select a language', **@html_options) end end diff --git a/app/components/language_field_component.rb b/app/components/language_field_component.rb index 2473e7b1c..7998432a9 100644 --- a/app/components/language_field_component.rb +++ b/app/components/language_field_component.rb @@ -5,15 +5,35 @@ class LanguageFieldComponent < ViewComponent::Base include FlagIconsRails::Rails::ViewHelpers - def initialize(value:, label: nil) + def initialize(value:, label: nil, auto_label: false, icon: nil) super @value = value - @lang_code = value&.is_a?(String) ? ISO_639.find(value.split('/').last)&.alpha2 : nil + @lang_code = nil @label = label + @icon = icon + + iso = ISO_639.find(value.to_s.split('/').last) + if iso + @lang_code = iso.alpha2 + @label ||= iso.english_name if auto_label + end end def lang_code - @lang_code = 'gb' if @lang_code.eql?('en') + case @lang_code + when 'en' + @lang_code = 'gb' + when 'ar' + @lang_code = 'sa' + when 'hi' + @lang_code = 'in' + when 'ur' + @lang_code = 'pk' + when 'zh' + @lang_code = 'cn' + when 'ja' + @lang_code = 'jp' + end @lang_code end diff --git a/app/components/language_field_component/language_field_component.html.haml b/app/components/language_field_component/language_field_component.html.haml index 8530fae3d..66c514f38 100644 --- a/app/components/language_field_component/language_field_component.html.haml +++ b/app/components/language_field_component/language_field_component.html.haml @@ -1,6 +1,6 @@ - if @lang_code .d-flex.align-items-center - = flag_icon(lang_code) + = @icon ? inline_svg_tag(@icon, width: "17px", height: "17px") : flag_icon(lang_code) - if @label %div.ml-1 = @label diff --git a/app/components/select_input_component.rb b/app/components/select_input_component.rb index 25f905100..29dc41c7a 100644 --- a/app/components/select_input_component.rb +++ b/app/components/select_input_component.rb @@ -2,7 +2,7 @@ class SelectInputComponent < ViewComponent::Base - def initialize(id:, name:, values:, selected: nil, multiple: false, open_to_add_values: false, required: false, data: {}, placeholder: '') + def initialize(id:, name:, values:, selected: nil, multiple: false, open_to_add_values: false, required: false, data: {}, placeholder: '', **html_options) super @id = id || '' @name = name @@ -13,6 +13,7 @@ def initialize(id:, name:, values:, selected: nil, multiple: false, open_to_add_ @placeholder = placeholder @data = data @required = required + @html_options = html_options end def call @@ -39,8 +40,11 @@ def select_input_tag(id, name, values, selected, options = {}) placeholder: placeholder, autocomplete: 'off', multiple: multiple, - data: data - } + data: data, + }.merge(@html_options) + + select_html_options[:style] = "#{select_html_options[:style]}; visibility: hidden" + select_tag(name, options_for_select(values, selected), select_html_options) end diff --git a/app/components/select_input_component/select_input_component_controller.js b/app/components/select_input_component/select_input_component_controller.js index 1ec8b6499..c17b84c1f 100644 --- a/app/components/select_input_component/select_input_component_controller.js +++ b/app/components/select_input_component/select_input_component_controller.js @@ -1,48 +1,85 @@ -import {Controller} from "@hotwired/stimulus" -import {useTomSelect} from "../../javascript/mixins/useTomSelect" - -export default class extends Controller { - static values = { - multiple: {type: Boolean, default: false}, - openAdd: {type: Boolean, default: false}, - required: {type: Boolean, default: false} - }; - - - connect() { - let myOptions = {} - - myOptions = { - render: { - option: (data) => { - return `
${data.text}
` - }, - item: (data) => { - return `
${data.text}
` - } - } - } +import { Controller } from '@hotwired/stimulus' +import { useTomSelect } from '../../javascript/mixins/useTomSelect' - if (this.multipleValue) { - myOptions['onItemAdd'] = function(){ - this.setTextboxValue(''); - this.refreshOptions(); - } - myOptions['plugins'] = ['remove_button']; - } +export default class SelectInput extends Controller { + static DISPLAY_VALUE = 'value' + static DISPLAY_TEXT = 'text' - if (this.openAddValue) { - myOptions['create'] = true; + static values = { + multiple: { type: Boolean, default: false }, + openAdd: { type: Boolean, default: false }, + required: { type: Boolean, default: false }, + searchable: { type: Boolean, default: true }, + displayField: { type: String, default: SelectInput.DISPLAY_TEXT } + } + + connect () { + let myOptions = {} + myOptions = { + render: { + option: (data) => { + return `
${data.text}
` + }, + item: (data) => { + if (this.displayFieldValue === SelectInput.DISPLAY_TEXT) { + return `
${data.text}
` + } else { + return `
${data.value}
` + } } + } + } + + if(!this.searchableValue){ + myOptions['controlInput'] = null + } + + if (this.multipleValue) { + myOptions['onItemAdd'] = function () { + this.setTextboxValue('') + this.refreshOptions() + } + myOptions['plugins'] = ['remove_button'] + } - this.select = useTomSelect(this.element, myOptions, this.#triggerChange.bind(this)) + if (this.openAddValue) { + myOptions['create'] = true } - #triggerChange() { - if (this.requiredValue && !this.multipleValue && this.select.getValue() === ""){ - this.select.setValue(Object.keys(this.select.options)[0]) + this.select = useTomSelect(this.element, myOptions, this.#triggerChange.bind(this)) + this.element.style.visibilty = 'hidden'; + + [...this.element.attributes].forEach(attribute => { + if(attribute.name !== 'class' && attribute.name !== 'style' && attribute.name !== 'id' && attribute.name !== 'name'){ + this.select.control.setAttribute(attribute.name, attribute.value.replace('select-input', '')) + if(attribute.name === 'title'){ + this.select.control.setAttribute('data-controller', 'tooltip') } + } + }) + } - document.dispatchEvent(new Event('change', { target: this.element })) + #triggerChange () { + if (this.#isRequired() && !this.#isMultiple() && this.#isEmpty()) { + this.#selectFirstItem() } + + document.dispatchEvent(new Event('change', { target: this.element })) + } + + #isRequired () { + return this.requiredValue + } + + #isMultiple () { + return this.multipleValue + } + + #isEmpty () { + return this.select.getValue() === '' + } + + #selectFirstItem () { + this.select.setValue(Object.keys(this.select.options)[0], true) + } } \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5e76db965..a546c5f67 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -21,6 +21,7 @@ def set_locale I18n.locale = cookies[:locale] || detect_locale cookies.permanent[:locale] = I18n.locale if cookies[:locale].nil? logger.debug "* Locale set to '#{I18n.locale}'" + session[:locale] = I18n.locale end # Returns detedted locale based on the Accept-Language header of the request or the default locale if none is found. @@ -40,7 +41,7 @@ def detect_locale helper :all # include all helpers, all the time helper_method :bp_config_json, :current_license, :using_captcha? - unless Rails.env.development? + unless Rails.env.development? || Rails.env.test? rescue_from ActiveRecord::RecordNotFound, with: :not_found_record rescue_from StandardError, with: :internal_server_error end diff --git a/app/controllers/concerns/submission_filter.rb b/app/controllers/concerns/submission_filter.rb index 8072e8067..1c0ecedf0 100644 --- a/app/controllers/concerns/submission_filter.rb +++ b/app/controllers/concerns/submission_filter.rb @@ -171,7 +171,7 @@ def add_fair_score_metrics(ont_hash, ont) ont_hash[:fairScore] = @fair_scores[ont.acronym]['score'] ont_hash[:normalizedFairScore] = @fair_scores[ont.acronym]['normalizedScore'] else - ont_hash[:fairScore] = nil + ont_hash[:fairScore] = 0 ont_hash[:normalizedFairScore] = 0 end end diff --git a/app/controllers/fair_score_controller.rb b/app/controllers/fair_score_controller.rb index 9564a5875..8f62995b3 100644 --- a/app/controllers/fair_score_controller.rb +++ b/app/controllers/fair_score_controller.rb @@ -28,7 +28,7 @@ def get_fair @fair_scores_data = create_fair_scores_data(get_fair_score(@ontologies).values.first, 1) end rescue NameError - raise StandardError, 'Error: load failed' + #raise StandardError, 'Error: load failed' end end end \ No newline at end of file diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 683effbb5..1735f909c 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -74,8 +74,12 @@ def edit category_attributes['general'] << %w[acronym name groups administeredBy categories] category_attributes['licensing'] << 'viewingRestriction' category_attributes['relations'] << 'viewOf' - @categories_order = ['general', 'description', 'dates', 'licensing', 'persons and organizations', 'links', 'media', 'community', 'usage' ,'relations', 'content','methodology', 'object description properties'] - @category_attributes = category_attributes + @selected_attributes = Array(params[:properties]) + if @selected_attributes.empty? + @categories_order = ['general', 'description', 'dates', 'licensing', 'persons and organizations', 'links', 'media', 'community', 'usage' ,'relations', 'content','methodology', 'object description properties'] + @category_attributes = category_attributes + end + render 'submissions/edit', layout: params[:container_id] ? nil : 'ontology' end # When editing a submission (called when submit "Edit submission information" form) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 218b9d29a..e03ed69cb 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -15,9 +15,9 @@ module ApplicationHelper :rdf => "http://www.w3.org/1999/02/22-rdf-syntax-ns#", :rdfs => "http://www.w3.org/2000/01/rdf-schema#", :metadata => "http://data.bioontology.org/metadata/", :metadata_def => "http://data.bioontology.org/metadata/def/", :dc => "http://purl.org/dc/elements/1.1/", :xsd => "http://www.w3.org/2001/XMLSchema#", :oboinowl_gen => "http://www.geneontology.org/formats/oboInOwl#", :obo_purl => "http://purl.obolibrary.org/obo/", - :umls => "http://bioportal.bioontology.org/ontologies/umls/", :door => "http://kannel.open.ac.uk/ontology#", :dct => "http://purl.org/dc/terms/", - :void => "http://rdfs.org/ns/void#", :foaf => "http://xmlns.com/foaf/0.1/", :vann => "http://purl.org/vocab/vann/", :adms => "http://www.w3.org/ns/adms#", - :voaf => "http://purl.org/vocommons/voaf#", :dcat => "http://www.w3.org/ns/dcat#", :mod => "http://www.isibang.ac.in/ns/mod#", :prov => "http://www.w3.org/ns/prov#", + :umls => "http://bioportal.bioontology.org/ontologies/umls/", :door => "http://kannel.open.ac.uk/ontology#", :dct => "http://purl.org/dc/terms/", + :void => "http://rdfs.org/ns/void#", :foaf => "http://xmlns.com/foaf/0.1/", :vann => "http://purl.org/vocab/vann/", :adms => "http://www.w3.org/ns/adms#", + :voaf => "http://purl.org/vocommons/voaf#", :dcat => "http://www.w3.org/ns/dcat#", :mod => "http://www.isibang.ac.in/ns/mod#", :prov => "http://www.w3.org/ns/prov#", :cc => "http://creativecommons.org/ns#", :schema => "http://schema.org/", :doap => "http://usefulinc.com/ns/doap#", :bibo => "http://purl.org/ontology/bibo/", :wdrs => "http://www.w3.org/2007/05/powder-s#", :cito => "http://purl.org/spar/cito/", :pav => "http://purl.org/pav/", :nkos => "http://w3id.org/nkos/nkostype#", :oboInOwl => "http://www.geneontology.org/formats/oboInOwl#", :idot => "http://identifiers.org/idot/", :sd => "http://www.w3.org/ns/sparql-service-description#", @@ -195,7 +195,7 @@ def draw_tree(root, acronym, id = nil, concept_schemes = nil) def build_tree(node, string, id, acronym, concept_schemes: nil) return string if node.children.nil? || node.children.empty? - + node.children.sort! { |a, b| (main_language_label(a.prefLabel) || a.id).downcase <=> (main_language_label(a.prefLabel) || b.id).downcase } node.children.each do |child| active_style = child.id.eql?(id) ? "active" : '' @@ -320,7 +320,7 @@ def render_advanced_picker(custom_ontologies = nil, selected_ontologies = [], al selected_ontologies ||= [] init_ontology_picker(custom_ontologies, selected_ontologies) render :partial => "shared/ontology_picker_advanced", :locals => { - :custom_ontologies => custom_ontologies, :selected_ontologies => selected_ontologies, :align_to_dom_id => align_to_dom_id + :custom_ontologies => custom_ontologies, :selected_ontologies => selected_ontologies, :align_to_dom_id => align_to_dom_id } end @@ -678,12 +678,6 @@ def current_page?(path) request.path.eql?(path) end - def request_lang - lang = params[:language] || params[:lang] - lang = 'EN' unless lang - lang.upcase - end - def bp_config_json # For config settings, see # config/bioportal_config.rb @@ -719,12 +713,12 @@ def navitems ["/landscape", t("layout.header.landscape")]] end - def portal_language_selector(id: 'language-select') - languages = %w[en fr it de].map{|x| [x.upcase, x]} - select_tag('language',options_for_select(languages), id: id, class: 'nav-language', - data: { controller: "platform-language", action: "change->platform-language#handleLangChanged" }) + def beta_badge(text = 'beta', tooltip: 'This feature is experimental and may have issues') + return unless text + content_tag(:span, text, data: { controller: 'tooltip' }, title: tooltip, class: 'badge badge-pill bg-secondary text-white') end + def attribute_enforced_values(attr) submission_metadata.select {|x| x['@id'][attr]}.first['enforcedValues'] end diff --git a/app/helpers/components_helper.rb b/app/helpers/components_helper.rb index 0da2f9f2c..c67209696 100644 --- a/app/helpers/components_helper.rb +++ b/app/helpers/components_helper.rb @@ -80,4 +80,23 @@ def properties_dropdown(id, title, tooltip, properties, &block) end end end + + def form_save_button + render Buttons::RegularButtonComponent.new(id:'save-button', value: "Save", variant: "primary", size: "slim", type: "submit") do |btn| + btn.icon_left do + inline_svg_tag "check.svg" + end + end + end + + def form_cancel_button + render Buttons::RegularButtonComponent.new(id:'cancel-button', value: "Cancel", variant: "secondary", size: "slim") do |btn| + btn.icon_left do + inline_svg_tag "x.svg", width: "20", height: "20" + + end + end + end + + end diff --git a/app/helpers/multi_languages_helper.rb b/app/helpers/multi_languages_helper.rb index c52f7860d..f1c0f8364 100644 --- a/app/helpers/multi_languages_helper.rb +++ b/app/helpers/multi_languages_helper.rb @@ -1,4 +1,101 @@ module MultiLanguagesHelper + def portal_lang + session[:locale] || 'en' + end + def request_lang + lang = params[:language] || params[:lang] + lang = portal_lang unless lang + lang.upcase + end + + def portal_language_help_text + "Indicate the language in which the interfaces should appear" + end + def portal_languages + { + en: { badge: nil, disabled: false }, + fr: { badge: 'beta', disabled: false }, + it: { badge: 'coming', disabled: true }, + de: { badge: 'coming', disabled: true } + } + end + + def portal_language_selector + languages = portal_languages + selected_language = portal_lang + selected_language = content_tag(:span, selected_language.upcase, data: { controller: 'tooltip' }, title: portal_language_help_text) + render DropdownButtonComponent.new do |d| + d.header { selected_language } + d.section(divide: false, selected_index: languages.find_index(selected_language)) do |s| + languages.each do |lang, metadata| + s.item do + text = content_tag(:div, class: 'd-flex align-items-center') do + content_tag(:span, render(LanguageFieldComponent.new(value: lang, auto_label: true)), class: 'mr-1') + beta_badge(metadata[:badge]) + end + link_options = { data: { turbo: true } } + + if metadata[:disabled] + link_options[:class] = 'disabled-link' + link_options[:disabled] = 'disabled' + end + + link_to(text, "/locale/#{lang}", link_options) + end + end + + end + end + end + + def search_language_help_text + content_tag(:div, style: 'width: 300px; text-align: center') do + "Indicate the language on which to perform the search, restricting text matching exclusively to terms with that language" + end + end + def search_languages + # top ten spoken languages + portal_languages.keys + %w[zh es hi ar bn pt ru ur id] + end + def search_language_selector(id: 'search_language', name: 'search_language') + render Input::LanguageSelectorComponent.new(id: id, name: name, enable_all: true, + languages: search_languages, + 'data-select-input-searchable-value': false, + title: search_language_help_text) + + end + + def content_languages(submission = @submission || @submission_latest) + current_lang = request_lang.downcase + submission_lang = submission_languages(submission) + # Transform each language into a select option + submission_lang = submission_lang.map do |lang| + lang = lang.split('/').last.upcase + lang = ISO_639.find(lang.to_s.downcase) + next nil unless lang + [lang.alpha2, lang.english_name] + end.compact + + [submission_lang, current_lang] + end + def content_language_help_text + content_tag(:div, style: 'width: 350px;') do + concat content_tag(:div, "Indicate the language on which the content of this resource will be displayed.") + concat(content_tag(:div, class: "mt-1" ) do + content_tag(:span, "The available languages are specified by the resource admin.") + edit_sub_languages_button + end) + end + end + def content_language_selector(id: 'content_language', name: 'content_language') + languages, selected = content_languages + render Input::LanguageSelectorComponent.new(id: id, name: name, enable_all: true, + languages: languages, + selected: selected || 'all', + 'data-tooltip-interactive-value': true, + 'data-select-input-searchable-value': false, + title: content_language_help_text) + + end + def language_hash(concept_label) diff --git a/app/helpers/ontologies_helper.rb b/app/helpers/ontologies_helper.rb index 7c73966c0..f427c46ec 100644 --- a/app/helpers/ontologies_helper.rb +++ b/app/helpers/ontologies_helper.rb @@ -125,12 +125,12 @@ def agent?(sub_metadata, attr) def display_contact(contacts) contacts.map do |c| next unless c.member?(:name) && c.member?(:email) - + formatted_name = c[:name].titleize formatted_email = c[:email].downcase "#{formatted_name} (#{formatted_email})" end&.join(" and ") - end + end def count_links(ont_acronym, page_name = 'summary', count = 0) ont_url = "/ontologies/#{ont_acronym}" @@ -434,41 +434,27 @@ def dispaly_complex_text(definitions) return html.html_safe end - def language_selector_tag(name) - languages = languages_options + def edit_sub_languages_button(ontology = @ontology, submission = @submission_latest) + return unless ontology.admin?(session[:user]) - if languages.empty? && @submission_latest - return unless @ontology.admin?(session[:user]) - content_tag(:div, data: { 'ontology-viewer-tabs-target': 'languageSelector' }, style: "visibility: #{ontology_data_section? ? 'visible' : 'hidden'} ; margin-bottom: -1px;") do - edit_submission_property_link(@ontology.acronym, @submission_latest.submissionId, :naturalLanguage, container_id: '') do - ("Enable multilingual display " + content_tag(:i, "", class: "fas fa-lg fa-question-circle")).html_safe - end + link = edit_ontology_submission_path(ontology.acronym, submission.submissionId, properties: 'naturalLanguage', container_id: 'application_modal_content') + link_to_modal(nil, link, class: "btn", id:'fair-details-link', + data: { show_modal_title_value: "Edit natural languages of #{ontology.acronym}", show_modal_size_value: 'modal-md' }) do + render ChipButtonComponent.new(type: 'clickable', class: 'admin-background chip_button_small' ) do + ("Click here to edit available languages" + content_tag(:i, "", class: "fas fa-lg fa-edit")).html_safe end - else - select_tag name, languages_options, class: '', disabled: !ontology_data_section?, style: "visibility: #{ontology_data_section? ? 'visible' : 'hidden'}; border: none; outline: none;", data: { 'ontology-viewer-tabs-target': 'languageSelector' } end end + def language_selector_tag(name) + content_language_selector(id: name, name: name) + end def language_selector_hidden_tag(section) hidden_field_tag "language_selector_hidden_#{section}", '', data: { controller: "language-change", 'language-change-section-value': section, action: "change->language-change#dispatchLangChangeEvent" } end - def languages_options(submission = @submission || @submission_latest) - current_lang = request_lang.downcase - submission_lang = submission_languages(submission) - # Transform each language into a select option - submission_lang = submission_lang.map do |lang| - lang = lang.split('/').last.upcase - lang = ISO_639.find(lang.to_s.downcase)&.alpha2 || lang - [lang, lang, { selected: lang.eql?(current_lang) }] - end - # Add the option to select all language - submission_lang.push(['All', 'all', { selected: current_lang.eql?('all') }]) - - options_for_select(submission_lang) - end def display_complex_text(definitions) html = "" diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index a63383b8d..8d4ea6942 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -58,9 +58,6 @@ application.register("ontology-viewer-tabs", OntologyViewerTabsController) import OntoportalAutocompleteController from "./ontoportal_autocomplete_controller" application.register("ontoportal-autocomplete", OntoportalAutocompleteController) -import PlatformLanguageController from "./platform_language_controller" -application.register("platform-language", PlatformLanguageController) - import ShowFilterCountController from "./show_filter_count_controller" application.register("show-filter-count", ShowFilterCountController) diff --git a/app/javascript/controllers/platform_language_controller.js b/app/javascript/controllers/platform_language_controller.js deleted file mode 100644 index 682d8e901..000000000 --- a/app/javascript/controllers/platform_language_controller.js +++ /dev/null @@ -1,21 +0,0 @@ -import { Controller } from "@hotwired/stimulus" -import { Turbo } from "@hotwired/turbo-rails"; -import { getCookie } from "../mixins/cookie"; - -// Connects to data-controller="platform-language" -// this controller is used to change the language of the whole platform -export default class extends Controller { - - connect() { - const locale = getCookie('locale'); - - const option = document.querySelector(`#language-select option[value="${locale}"]`); - option && (option.selected = true); - - } - - handleLangChanged(event) { - const userPreferedLanguage = event.target.value; - Turbo.visit(`/locale/${userPreferedLanguage}`, { action: "replace" }); - } -} diff --git a/app/views/fair_score/_fair_service_header.html.haml b/app/views/fair_score/_fair_service_header.html.haml index df88dce67..c4dd1e462 100644 --- a/app/views/fair_score/_fair_service_header.html.haml +++ b/app/views/fair_score/_fair_service_header.html.haml @@ -1,7 +1,6 @@ %span %span - %span.badge.badge-pill.badge-secondary - beta + = beta_badge %span = render Display::InfoTooltipComponent.new(text: t("view_fair_scores_definitions")) = fairness_link \ No newline at end of file diff --git a/app/views/layouts/_ontology_viewer.html.haml b/app/views/layouts/_ontology_viewer.html.haml index eb457a580..dc05686d6 100644 --- a/app/views/layouts/_ontology_viewer.html.haml +++ b/app/views/layouts/_ontology_viewer.html.haml @@ -56,7 +56,9 @@ selected: selected_section?(section_title), page_name: ontology_viewer_page_name(@ontology.acronym, @concept&.prefLabel || '', section_title)) - t.pinned_right do - = language_selector_tag(:language_selector) + %div{style: "visibility: #{ontology_data_section? ? 'visible' : 'hidden'}; border: none; outline: none; padding: 1px", + data: {'ontology-viewer-tabs-target': 'languageSelector' }} + = language_selector_tag(:content_language) - t.item_content do %div.p-1{data: section_data(section_title)} = language_selector_hidden_tag(section_title) if ontology_data_section?(section_title) diff --git a/app/views/search/index.html.haml b/app/views/search/index.html.haml index cac1f241d..91664b0a8 100644 --- a/app/views/search/index.html.haml +++ b/app/views/search/index.html.haml @@ -20,7 +20,7 @@ %div.col-sm-2.mb-4 Search language %div.col-sm-10.mb-4 %div.w-25 - = render Input::LanguageSelectorComponent.new(id:'search_language' , name:'search_language', languages: {fr: 'French', en: 'English'}) + = search_language_selector %div.col-sm-2= t("search.include_in_search") + ":" %div.col-sm-10 %div.form-check diff --git a/app/views/submissions/edit.html.haml b/app/views/submissions/edit.html.haml index aebf09d8d..3bd739f9c 100644 --- a/app/views/submissions/edit.html.haml +++ b/app/views/submissions/edit.html.haml @@ -1,40 +1,41 @@ - section = params[:section] || 'general' .center .edit-ontology-container - = turbo_frame_tag("test") do + = turbo_frame_tag(params[:container_id]) do = form_for :submission, url: ontology_submission_path(@ontology.acronym, params["id"]), html: { id: "ontology_submission_form", method: :put, multipart: true, 'data-turbo': true, 'data-turbo-frame': '_top', novalidate: 'true'} do .edit-ontology-title %div Edit ontology %hr .edit-ontology-sub-container - .edit-ontology-left-column{:role => "tablist",onchange:"onMetadataChange()"} - .edit-ontology-desc - = submission_metadata_selector - %div.nav.nav-pills.flex-column#categories-tabs - - @categories_order.each_with_index do |key, index| - %a.edit-ontology-tab-item.d-block{href: "##{key.parameterize}-tab", "data-toggle" => "pill", class: section.eql?(key.parameterize) ? 'active show' : ''} - = key.humanize + - if @selected_attributes.empty? + .edit-ontology-left-column{:role => "tablist",onchange:"onMetadataChange()"} + .edit-ontology-desc + = submission_metadata_selector + %div.nav.nav-pills.flex-column#categories-tabs + - @categories_order.each_with_index do |key, index| + %a.edit-ontology-tab-item.d-block{href: "##{key.parameterize}-tab", "data-toggle" => "pill", class: section.eql?(key.parameterize) ? 'active show' : ''} + = key.humanize #myTabContent.edit-ontology-right-column.w-100 = render TurboFrameComponent.new(id:"metadata_by_ontology") do = metadata_help_link %div.tab-content - - @categories_order.each_with_index do |key, index| - - properties = @category_attributes[key] - .edit-ontology-tab.tab-pane.fade{id: key.parameterize+'-tab', class: section.eql?(key.parameterize) ? 'active show' : ''} - = render TurboFrameComponent.new(id: "ontology-content-#{index}", loading:"lazy", src: "edit_properties?properties=#{properties.join(',')}&container_id=ontology-content-#{index}") + - if @selected_attributes.empty? + - @categories_order.each_with_index do |key, index| + - properties = @category_attributes[key] + .edit-ontology-tab.tab-pane.fade{id: key.parameterize+'-tab', class: section.eql?(key.parameterize) ? 'active show' : ''} + = render TurboFrameComponent.new(id: "ontology-content-#{index}", loading:"lazy", src: "edit_properties?properties=#{properties.join(',')}&container_id=ontology-content-#{index}") + - else + - link = ontology_submission_edit_properties_path(@ontology.acronym, params[:id], properties: @selected_attributes.join(','), container_id: 'ontology-content-0') + = render TurboFrameComponent.new(id: "ontology-content-0", loading:"lazy", src: link) %hr#edit-ontology-actions-devider .edit-ontology-actions - .cancel-button.mx-2{onClick: 'window.history.back();'} - = render Buttons::RegularButtonComponent.new(id:'cancel-button', value: "Cancel", variant: "secondary", size: "slim") do |btn| - - btn.icon_left do - - inline_svg_tag "x.svg", width: "20", height: "20" - + - unless params[:container_id] + .cancel-button.mx-2{onClick: 'window.history.back();'} + = form_cancel_button .save-button - = render Buttons::RegularButtonComponent.new(id:'save-button', value: "Save", variant: "primary", size: "slim", type: "submit") do |btn| - - btn.icon_left do - - inline_svg_tag "check.svg" + = form_save_button :javascript @@ -51,7 +52,7 @@ selectedProperties = Array.from(properties.selectedOptions).map(({ value }) => value).join(',') frame.src = "./edit_properties?properties=" + selectedProperties } else { - location.reload() + Turbo.visit(location.href) } } diff --git a/config/bioportal_config_env.rb.sample b/config/bioportal_config_env.rb.sample index 154c6757f..4cc57816b 100644 --- a/config/bioportal_config_env.rb.sample +++ b/config/bioportal_config_env.rb.sample @@ -20,7 +20,7 @@ $API_KEY = ENV['API_KEY'] $REST_URL = ENV['API_URL'] # Annotator REST service address # $ANNOTATOR_URL = "http://services.stageportal.lirmm.fr/annotator" -$ANNOTATOR_URL = ENV['ANNOTATOR_URL'] +$ANNOTATOR_URL = $PROXY_URL = ENV['ANNOTATOR_URL'] # NCBO annotator URL and apikey $NCBO_ANNOTATORPLUS_ENABLED = ENV['NCBO_ANNOTATORPLUS_ENABLED'] $NCBO_ANNOTATOR_URL = ENV['NCBO_ANNOTATOR_URL'] diff --git a/config/bioportal_config_test.rb b/config/bioportal_config_test.rb index be327b655..81aad6510 100644 --- a/config/bioportal_config_test.rb +++ b/config/bioportal_config_test.rb @@ -9,7 +9,7 @@ $REST_URL = ENV['API_URL'] $BIOMIXER_URL = ENV['BIOMIXER_URL'] $ANNOTATOR_URL = $PROXY_URL = ENV['ANNOTATOR_URL'] -$FAIRNESS_URL = ENV['ANNOTATOR_URL'] +$FAIRNESS_URL = ENV['FAIRNESS_URL'] # config/initializers/omniauth_providers.rb $OMNIAUTH_PROVIDERS = { diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 4e19d5b79..78d888ff9 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -182,7 +182,7 @@ fr: publications: Publications recommender: Recommendeur release_notes: Notes de version - search_prompt: Rechercher dans %{portail_name} ... + search_prompt: Rechercher dans %{portal_name} ... submit_feedback: Envoyer un commentaire support: Assistance cite_us: Citez-nous diff --git a/test/system/submission_flows_test.rb b/test/system/submission_flows_test.rb index 999859966..b00e39ed7 100644 --- a/test/system/submission_flows_test.rb +++ b/test/system/submission_flows_test.rb @@ -322,7 +322,7 @@ class SubmissionFlowsTest < ApplicationSystemTestCase assert_equal existent_submission.URI, find_field('submission[URI]').value assert_equal existent_submission.description, find_field('submission[description]').value - assert_equal existent_submission.hasOntologyLanguage, find_field('submission[hasOntologyLanguage]').value + assert_equal existent_submission.hasOntologyLanguage, find_field('submission[hasOntologyLanguage]', visible: false).value assert_equal existent_submission.notes.sort, all('[name^="submission[notes]"]').map(&:value).sort assert_equal existent_submission.pullLocation, find_field('submission[pullLocation]').value