diff --git a/frontend/src/app/shared/components/autocompleter/project-autocompleter/project-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/project-autocompleter/project-autocompleter.component.ts index 8d4676f64bae..298b5c394b07 100644 --- a/frontend/src/app/shared/components/autocompleter/project-autocompleter/project-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/project-autocompleter/project-autocompleter.component.ts @@ -90,6 +90,8 @@ export class ProjectAutocompleterComponent extends OpAutocompleterComponent p.href || p.id); return projects.map((project) => { const isSelected = !!arrayedValue.find((selected) => selected === this.projectTracker(project)); + const id = project.id.toString(); + const disabled = isSelected || project.disabled || !!this.disabledProjects[id]; return { ...project, - disabled: isSelected || project.disabled, + disabled, + disabledReason: (typeof this.disabledProjects[id] === 'string') ? this.disabledProjects[id] as string : '', }; }); } @@ -176,14 +181,20 @@ export class ProjectAutocompleterComponent extends OpAutocompleterComponent projects.map((project) => ({ - id: project.id, - href: project._links.self.href, - name: project.name, - disabled: false, - ancestors: project._links.ancestors, - children: [], - }))), + map((projects) => projects.map((project) => { + const id = project.id.toString(); + const disabled = !!this.disabledProjects[id]; + + return { + id: project.id, + href: project._links.self.href, + name: project.name, + disabled, + disabledReason: (typeof this.disabledProjects[id] === 'string') ? this.disabledProjects[id] as string : '', + ancestors: project._links.ancestors, + children: [], + }; + })), map(this.mapResultsFn), map((projects) => { this.dataLoaded = true; diff --git a/lib/primer/open_project/forms/autocompleter.html.erb b/lib/primer/open_project/forms/autocompleter.html.erb index 1f2d040100a5..751846cec8bf 100644 --- a/lib/primer/open_project/forms/autocompleter.html.erb +++ b/lib/primer/open_project/forms/autocompleter.html.erb @@ -11,7 +11,7 @@ append_to: @autocomplete_options.fetch(:append_to, 'body') } %> <% else %> - <%= angular_component_tag @autocomplete_options.fetch(:component, 'opce-autocompleter'), + <%= angular_component_tag @autocomplete_options.fetch(:component), data: @autocomplete_options.delete(:data) { {} }, inputs: @autocomplete_options.merge( classes: "ng-select--primerized #{@input.invalid? ? '-error' : ''}", diff --git a/lib/primer/open_project/forms/dsl/autocompleter_input.rb b/lib/primer/open_project/forms/dsl/autocompleter_input.rb index ea4355547802..497658f27d91 100644 --- a/lib/primer/open_project/forms/dsl/autocompleter_input.rb +++ b/lib/primer/open_project/forms/dsl/autocompleter_input.rb @@ -28,7 +28,7 @@ def to_h def initialize(name:, label:, autocomplete_options:, wrapper_data_attributes: {}, **system_arguments) @name = name @label = label - @autocomplete_options = autocomplete_options + @autocomplete_options = derive_autocompleter_options(autocomplete_options) @wrapper_data_attributes = wrapper_data_attributes @select_options = [] @@ -37,6 +37,12 @@ def initialize(name:, label:, autocomplete_options:, wrapper_data_attributes: {} yield(self) if block_given? end + def derive_autocompleter_options(options) + options.reverse_merge( + component: "opce-autocompleter" + ) + end + def option(**args) @select_options << Option.new(**args) end diff --git a/lib/primer/open_project/forms/dsl/input_methods.rb b/lib/primer/open_project/forms/dsl/input_methods.rb index 28e31ef4eacd..5a4aa6148507 100644 --- a/lib/primer/open_project/forms/dsl/input_methods.rb +++ b/lib/primer/open_project/forms/dsl/input_methods.rb @@ -9,6 +9,14 @@ def autocompleter(**, &) add_input AutocompleterInput.new(builder: @builder, form: @form, **, &) end + def work_package_autocompleter(**, &) + add_input WorkPackageAutocompleterInput.new(builder: @builder, form: @form, **, &) + end + + def project_autocompleter(**, &) + add_input ProjectAutocompleterInput.new(builder: @builder, form: @form, **, &) + end + def rich_text_area(**) add_input RichTextAreaInput.new(builder: @builder, form: @form, **) end diff --git a/lib/primer/open_project/forms/dsl/project_autocompleter_input.rb b/lib/primer/open_project/forms/dsl/project_autocompleter_input.rb new file mode 100644 index 000000000000..5b58c0ba542f --- /dev/null +++ b/lib/primer/open_project/forms/dsl/project_autocompleter_input.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Primer + module OpenProject + module Forms + module Dsl + class ProjectAutocompleterInput < AutocompleterInput + def derive_autocompleter_options(options) + options.reverse_merge!( + component: "opce-project-autocompleter", + defaultData: false, + filters: [{ name: 'active', operator: '=', values: ['t'] }], + ) + + if options[:disabledProjects] + options[:disabledProjects].stringify_keys! + end + + options + end + end + end + end + end +end diff --git a/lib/primer/open_project/forms/dsl/work_package_autocompleter_input.rb b/lib/primer/open_project/forms/dsl/work_package_autocompleter_input.rb new file mode 100644 index 000000000000..8a96cee13973 --- /dev/null +++ b/lib/primer/open_project/forms/dsl/work_package_autocompleter_input.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Primer + module OpenProject + module Forms + module Dsl + class WorkPackageAutocompleterInput < AutocompleterInput + def derive_autocompleter_options(options) + options.reverse_merge( + component: "opce-autocompleter", + resource: "work_packages", + searchKey: "subjectOrId", + ) + end + end + end + end + end +end diff --git a/lookbook/docs/patterns/03-autocompleters.md.erb b/lookbook/docs/patterns/03-autocompleters.md.erb index 5e1781bb2b7c..c383ef24a31a 100644 --- a/lookbook/docs/patterns/03-autocompleters.md.erb +++ b/lookbook/docs/patterns/03-autocompleters.md.erb @@ -69,6 +69,13 @@ Keep in mind that this component uses the live work packages API, so results wil <%= embed OpenProject::Common::AutocompletePreview, :work_package, panels: %i[source] %> +### Project autocompletion + +For project autocompletion, you can use the same field, but with a different configuration. +Keep in mind that this component uses the live projects API, so results will depend on whether you are logged in. + +<%= embed OpenProject::Common::AutocompletePreview, :project, panels: %i[source] %> + ### Outside primer If you're not in a primer form, you can render the autocomplete directly like so: diff --git a/lookbook/previews/open_project/common/autocomplete_preview.rb b/lookbook/previews/open_project/common/autocomplete_preview.rb index 6405bcd032a8..3b0388849e3e 100644 --- a/lookbook/previews/open_project/common/autocomplete_preview.rb +++ b/lookbook/previews/open_project/common/autocomplete_preview.rb @@ -40,6 +40,11 @@ def decorated def work_package render_with_template end + + # @display min_height 250px + def project + render_with_template + end end end end diff --git a/lookbook/previews/open_project/common/autocomplete_preview/project.html.erb b/lookbook/previews/open_project/common/autocomplete_preview/project.html.erb new file mode 100644 index 000000000000..b597688638a2 --- /dev/null +++ b/lookbook/previews/open_project/common/autocomplete_preview/project.html.erb @@ -0,0 +1,28 @@ +<% + the_form = Class.new(ApplicationForm) do + form do |f| + f.project_autocompleter( + name: :id, + label: Project.model_name.human, + visually_hide_label: true, + autocomplete_options: { + openDirectly: false, + focusDirectly: false, + dropdownPosition: 'bottom', + disabledProjects: Project.visible.take(10).pluck(:id).to_h { |id| [id, "Disabled reason!"] } + } + ) + end + end + + model = Project.new +%> + + +<%= primer_form_with( + model:, + url: '/abc', + id: 'asdf') do |f| + render(the_form.new(f)) +end +%> diff --git a/lookbook/previews/open_project/common/autocomplete_preview/work_package.html.erb b/lookbook/previews/open_project/common/autocomplete_preview/work_package.html.erb index 3d5b6f719c2f..2408d9b885cd 100644 --- a/lookbook/previews/open_project/common/autocomplete_preview/work_package.html.erb +++ b/lookbook/previews/open_project/common/autocomplete_preview/work_package.html.erb @@ -1,7 +1,7 @@ <% the_form = Class.new(ApplicationForm) do form do |f| - f.autocompleter( + f.work_package_autocompleter( name: :id, label: WorkPackage.model_name.human, visually_hide_label: true, diff --git a/modules/meeting/app/forms/meeting_agenda_item/work_package.rb b/modules/meeting/app/forms/meeting_agenda_item/work_package.rb index 7b2f169b7d68..fcc6dd81d7bf 100644 --- a/modules/meeting/app/forms/meeting_agenda_item/work_package.rb +++ b/modules/meeting/app/forms/meeting_agenda_item/work_package.rb @@ -28,7 +28,7 @@ class MeetingAgendaItem::WorkPackage < ApplicationForm form do |agenda_item_form| - agenda_item_form.autocompleter( + agenda_item_form.work_package_autocompleter( name: :work_package_id, label: WorkPackage.model_name.human, visually_hide_label: true, @@ -37,8 +37,6 @@ class MeetingAgendaItem::WorkPackage < ApplicationForm data: { "test-selector": "op-agenda-items-wp-autocomplete" }, - resource: "work_packages", - searchKey: "subjectOrId", focusDirectly: true, disabled: @disabled }