Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ETQ administrateur, je peux deplacer un champ via un select #9861

Merged
merged 8 commits into from
Jan 15, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ def procedure

def button_title
if annotations?
"Ajouter une annotation"
"Ajouter une annotation"
else
"Ajouter un champ"
"Ajouter un champ"
end
end

Expand Down
8 changes: 0 additions & 8 deletions app/components/types_de_champ_editor/block_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@ def initialize(block:, coordinates:, upper_coordinates: [])

private

def sortable_options
{
controller: 'sortable',
sortable_handle_value: '.handle',
sortable_group_value: block_id
}
end

def block_id
dom_id(@block, :types_de_champ_editor_block)
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
%ul.types-de-champ-block{ id: block_id, data: sortable_options }
- c = TypesDeChampEditor::SelectChampTemplatePositionComponent.new(block: @block, coordinates: @coordinates)
%ul.types-de-champ-block{ id: block_id, data: { controller: 'select-champ-position-template', 'select-champ-position-template-template-id-value': c.block_id } }
- @coordinates.each do |coordinate|
= render TypesDeChampEditor::ChampComponent.new(coordinate: coordinate, upper_coordinates: coordinate.upper_coordinates)
= render TypesDeChampEditor::ChampComponent.new(coordinate:, upper_coordinates: coordinate.upper_coordinates)
%li.hidden= render c

1 change: 0 additions & 1 deletion app/components/types_de_champ_editor/champ_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ def html_options
last: coordinate.last?),
data: {
controller: 'type-de-champ-editor',
type_de_champ_editor_move_url_value: move_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id),
type_de_champ_editor_move_up_url_value: move_up_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id),
type_de_champ_editor_move_down_url_value: move_down_admin_procedure_type_de_champ_path(procedure, type_de_champ.stable_id)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%li.type-de-champ.flex.column.justify-start{ html_options }
%li.type-de-champ.flex.column.justify-start.fr-mb-6w{ html_options }
.type-de-champ-container
.flex.justify-between.section.head
.fr-btn.fr-btn--tertiary-no-outline.handle.fr-icon-drag-move-2-line{ title: "Déplacer le champ vers le haut ou vers le bas" }
.position.flex.align-center= @coordinate.position.to_s
%button.fr-btn.fr-btn--tertiary-no-outline.fr-icon-arrow-up-line.move-up{ move_button_options(:up) }
%button.fr-btn.fr-btn--tertiary-no-outline.fr-icon-arrow-down-line.move-down{ move_button_options(:down) }

Expand Down Expand Up @@ -136,5 +136,6 @@

= render(Conditions::ChampsConditionsComponent.new(tdc: type_de_champ, upper_tdcs: @upper_coordinates.map(&:type_de_champ), procedure_id: procedure.id))

.type-de-champ-add-button{ class: class_names(root: !coordinate.child?) }
.type-de-champ-add-button{ class: class_names(root: !coordinate.child?, flex: true) }
= render TypesDeChampEditor::AddChampButtonComponent.new(revision: coordinate.revision, parent: coordinate&.parent, is_annotation: coordinate.private?, after_stable_id: type_de_champ.stable_id)
= render TypesDeChampEditor::SelectChampPositionComponent.new(revision:, coordinate:)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class TypesDeChampEditor::SelectChampPositionComponent < ApplicationComponent
def initialize(revision:, coordinate:)
@revision = revision
@coordinate = coordinate
end

def options
[["Selectionner une option", @coordinate.stable_id]]
end

def describedby_id
dom_id(@coordinate, :move_and_morph)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
= form_with(url: move_and_morph_admin_procedure_type_de_champ_path(@coordinate.revision.procedure, @coordinate.type_de_champ.stable_id), class: 'fr-ml-3w flex', method: :patch, data: { turbo: true }) do |f|
= label_tag :target_stable_id, "Déplacer ce champ à la place de ", for: describedby_id, class: 'flex align-center flex-no-shrink fr-mr-3w'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Est-ce qu'on garde ce wording ou on met plutot "Déplacer ce champ après" car la on dirait qu'on le remplace et ça amène un peu de confusion je trouve.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah oui ça m'avait échappé dans le refacto, je suis d'accord

Copy link
Contributor Author

@mfo mfo Jan 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deplacé apres ne marche pas dans le sens bas vers le haut. c'est bien deplacer ce champ à la place de (a l'inverse du feedback que j'ai donné a lesim). si vous trouvez un autre wording ca serait top. mais de haut en bas, le 1er champ arrive apres le dernier champ. a l'inverse le dernier champ vient remplacer le 1er champ

= select_tag :target_stable_id, options_for_select(options), id: describedby_id, class: 'fr-select', aria: { discribedby: describedby_id }, data: { 'select-champ-position-template-target': 'select', selected: @coordinate.stable_id }
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class TypesDeChampEditor::SelectChampTemplatePositionComponent < ApplicationComponent
def initialize(block:, coordinates:)
@block = block
@coordinates = coordinates
end

def block_id
dom_id(@block, :types_de_champ_editor_select_champ_template)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
%div{ id: block_id, data: { 'select-champ-position-template-target': 'template', turbo_force: :server } }
%select
- @coordinates.each do |coordinate|
%option{ value: coordinate.stable_id }= "#{coordinate.position} #{coordinate.libelle}"
18 changes: 16 additions & 2 deletions app/controllers/administrateurs/types_de_champ_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,22 @@ def notice_explicative
end
end

def move
draft.move_type_de_champ(params[:stable_id], params[:position].to_i)
def move_and_morph
source_type_de_champ = draft.find_and_ensure_exclusive_use(params[:stable_id])
target_type_de_champ = draft.find_and_ensure_exclusive_use(params[:target_stable_id])
@coordinate = draft.coordinate_for(source_type_de_champ)
from = @coordinate.position
to = draft.coordinate_for(target_type_de_champ).position
@coordinate = draft.move_type_de_champ(@coordinate.stable_id, to)
@destroyed = @coordinate
@created = champ_component_from(@coordinate)
@morphed = @coordinate.siblings
if from > to # case of moved up, update components from target (> plus one) to origin
@morphed = @morphed.where("position > ?", to).where("position <= ?", from)
else # case of moved down, update components from origin up to target (< minus one)
@morphed = @morphed.where("position >= ?", from).where("position < ?", to)
end
@morphed = @morphed.map { |c| champ_component_from(c) }
end

def move_up
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { ApplicationController } from './application_controller';

export class SelectChampPositionTemplateController extends ApplicationController {
static targets = ['select', 'template'];
static values = {
templateId: String
};
// this element is updated via turbostream as the source of truth for all select
declare readonly templateIdValue: string;
declare readonly selectTargets: HTMLSelectElement[];

selectTargetConnected(selectElement: HTMLSelectElement) {
selectElement.addEventListener('focus', this);
selectElement.addEventListener('change', this);
}

selectTargetDisconnected(selectElement: HTMLSelectElement) {
selectElement.removeEventListener('focus', this);
selectElement.removeEventListener('change', this);
}

handleEvent(event: Event) {
switch (event.type) {
case 'focus':
this.onFocus(event);
break;
case 'change':
this.onChange(event);
break;
}
}

private onFocus(event: Event): void {
mfo marked this conversation as resolved.
Show resolved Hide resolved
const focusedSelect = event.target as HTMLSelectElement;
const focusedSelectStableId = this.getStableIdForSelect(focusedSelect);
const template = this.element.querySelector<HTMLElement>(
`#${this.templateIdValue}`
);

if (template) {
const fragment = template.cloneNode(true) as HTMLSelectElement;

const options = Array.from(fragment.querySelectorAll('option'))
.map((option) => {
if (option.value == focusedSelectStableId) {
option.setAttribute('selected', 'selected');
option.setAttribute('disabled', 'disabled');
}

return option.outerHTML;
})
.join('');
focusedSelect.innerHTML = options;
}
}

private onChange(event: Event): void {
const changedSelectTarget = event.target as HTMLSelectElement;
const stableIdDidChange =
changedSelectTarget.value !=
this.getStableIdForSelect(changedSelectTarget);
if (stableIdDidChange) {
changedSelectTarget.form?.requestSubmit();
}
event.stopImmediatePropagation();
}

private getStableIdForSelect(select: HTMLSelectElement): string | null {
return select.getAttribute('data-selected');
}
}
68 changes: 0 additions & 68 deletions app/javascript/controllers/sortable_controller.ts

This file was deleted.

14 changes: 0 additions & 14 deletions app/javascript/controllers/type_de_champ_editor_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ const AUTOSAVE_DEBOUNCE_DELAY = debounce_delay;
export class TypeDeChampEditorController extends ApplicationController {
static values = {
typeDeChampStableId: String,
moveUrl: String,
moveUpUrl: String,
moveDownUrl: String
};

declare readonly moveUrlValue: string;
declare readonly moveUpUrlValue: string;
declare readonly moveDownUrlValue: string;
declare readonly isVisible: boolean;
Expand All @@ -33,9 +31,6 @@ export class TypeDeChampEditorController extends ApplicationController {
this.#latestPromise = Promise.resolve();
this.on('change', (event) => this.onChange(event));
this.on('input', (event) => this.onInput(event));
this.on('sortable:end', (event) =>
this.onSortableEnd(event as CustomEvent)
);
}

disconnect() {
Expand Down Expand Up @@ -77,15 +72,6 @@ export class TypeDeChampEditorController extends ApplicationController {
});
}

private onSortableEnd(event: CustomEvent<{ position: number }>) {
const position = event.detail.position;
if (event.target == this.element) {
const form = createForm(this.moveUrlValue, 'patch');
createHiddenInput(form, 'position', position);
this.requestSubmitForm(form);
}
}

private save(form?: HTMLFormElement | null): void {
if (form) {
createHiddenInput(form, 'should_render', true);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
- if @coordinate.present?
- if @coordinate.parent.present?
- c = TypesDeChampEditor::SelectChampTemplatePositionComponent.new(block: @coordinate.parent, coordinates: @coordinate.parent.revision_types_de_champ)
- else
- c = TypesDeChampEditor::SelectChampTemplatePositionComponent.new(block: @coordinate.revision, coordinates: @coordinate.private? ? @coordinate.revision.revision_types_de_champ_private : @coordinate.revision.revision_types_de_champ_public)

= turbo_stream.replace(c.block_id) do
= render c
= turbo_stream.replace 'breadcrumbs' , render(partial: 'administrateurs/breadcrumbs',
locals: { steps: [['Démarches', admin_procedures_path],
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
Expand Down Expand Up @@ -35,8 +43,6 @@
= turbo_stream.replace dom_id(@coordinate.revision, :estimated_fill_duration) do
- render TypesDeChampEditor::EstimatedFillDurationComponent.new(revision: @coordinate.revision, is_annotation: @coordinate.private?)

= turbo_stream.dispatch 'sortable:sort'

- if @created&.coordinate&.child?
= turbo_stream.hide dom_id(@created.coordinate.parent, :type_de_champ_add_button)
- elsif @destroyed&.child? && @destroyed.parent.empty?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
= render partial: 'insert'

2 changes: 1 addition & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@

resources :types_de_champ, only: [:create, :update, :destroy], param: :stable_id do
member do
patch :move
patch :move_and_morph
patch :move_up
patch :move_down
put :piece_justificative_template
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
"react-dom": "^18.2.0",
"react-popper": "^2.3.0",
"react-query": "^3.39.3",
"sortablejs": "^1.15.0",
"spectaql": "^2.3.0",
"stimulus-use": "^0.52.0",
"terser": "^5.18.2",
Expand Down
9 changes: 9 additions & 0 deletions spec/components/types_de_champ_editor/champ_component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,14 @@
expect(page).to have_css('input[type=file]')
end
end

describe 'select champ position' do
let(:tdc) { procedure.draft_revision.types_de_champ.first }
let(:coordinate) { procedure.draft_revision.revision_types_de_champ_public.first }
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :text, libelle: 'a' }]) }
it 'does not have select to move champs' do
expect(page).to have_css("select##{ActionView::RecordIdentifier.dom_id(coordinate, :move_and_morph)}")
end
end
end
end
Loading