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

Duplicating sections #2008

Merged
merged 9 commits into from
Sep 27, 2023
Merged
5 changes: 5 additions & 0 deletions app/assets/stylesheets/pageflow/editor/drop_down_button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
@include icon-only-button;
}

> button.ellipsis_icon {
@include fa-ellipsis-v-icon;
width: 31px;
}

&.full_width {
width: 100%;

Expand Down
5 changes: 0 additions & 5 deletions app/assets/stylesheets/pageflow/editor/inputs/file_input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,6 @@
}

.drop_down_button {
button {
@include fa-ellipsis-v-icon;
width: 31px;
}

position: absolute;
bottom: 0;
left: 70px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ class SectionsController < ActionController::Base

def create
chapter = Chapter.all_for_revision(@entry.draft).find(params[:chapter_id])
section = chapter.sections.create(section_params)
section = chapter.create_section(section_params)

render partial: 'pageflow_scrolled/sections/section',
render partial: 'pageflow_scrolled/editor/sections/section_with_content_elements',
locals: {section: section},
status: :created
end

def duplicate
section = Section.all_for_revision(@entry.draft).find(params[:id])

render partial: 'pageflow_scrolled/editor/sections/section_with_content_elements',
locals: {section: section.chapter.duplicate_section(section)},
status: :created
end

def update
section = Section.all_for_revision(@entry.draft).find(params[:id])
section.update(section_params)
Expand Down
23 changes: 23 additions & 0 deletions entry_types/scrolled/app/models/pageflow_scrolled/chapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,36 @@ class Chapter < Pageflow::ApplicationRecord
attr_accessor :revision # used on :create to lazily create storyline
before_validation :ensure_storyline, on: :create

def create_section(attributes = {})
shift_section_positions(from: attributes[:position])

section = sections.create!(attributes)
section.content_elements.create!(type_name: 'textBlock')

section
end

def duplicate_section(section)
shift_section_positions(from: section.position + 1)

section.duplicate do |new_section|
new_section.position = section.position + 1
end
end

def self.all_for_revision(revision)
joins(storyline: :revision)
.where(pageflow_scrolled_storylines: {revision_id: revision})
end

private

def shift_section_positions(from:)
sections
.where('position >= ?', from)
.update_all('position = position + 1')
end

def ensure_storyline
return if storyline.present?
unless Storyline.all_for_revision(revision).exists?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
json.key_format!(camelize: :lower)

json.partial! 'pageflow_scrolled/sections/section', section: section

json.content_elements do
json.array!(section.content_elements) do |content_element|
json.partial! 'pageflow_scrolled/content_elements/content_element',
content_element: content_element
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
de:
pageflow_scrolled:
editor:
section_item:
duplicate: Duplizieren
insert_section_above: Abschnitt oberhalb einfügen
insert_section_below: Abschnitt unterhalb einfügen
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
en:
pageflow_scrolled:
editor:
section_item:
duplicate: Duplicate
insert_section_above: Insert section above
insert_section_below: Insert section below
4 changes: 4 additions & 0 deletions entry_types/scrolled/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
put :order
end

member do
post :duplicate
end

resources :content_elements do
collection do
put :batch
Expand Down
184 changes: 183 additions & 1 deletion entry_types/scrolled/package/spec/editor/models/Chapter-spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'pageflow-scrolled/editor';
import {ScrolledEntry} from 'editor/models/ScrolledEntry';
import {factories, setupGlobals} from 'pageflow/testHelpers';
import {normalizeSeed} from 'support';
import {useFakeXhr, normalizeSeed} from 'support';

describe('Chapter', () => {
let testContext;
Expand Down Expand Up @@ -32,4 +32,186 @@ describe('Chapter', () => {
expect(section.configuration.get('transition')).toEqual('beforeAfter');
});
});

describe('#insertSection', () => {
beforeEach(() => {
testContext.entry = factories.entry(ScrolledEntry, {}, {
entryTypeSeed: normalizeSeed({
chapters: [{id: 10}],
sections: [
{id: 100, chapterId: 10, position: 0},
{id: 101, chapterId: 10, position: 1}
]
})
});
});

setupGlobals({
entry: () => testContext.entry
});

it('re-indexes pages when inserting before other section', () => {
const {entry} = testContext;
const chapter = entry.chapters.first();

chapter.insertSection({before: chapter.sections.last()});

expect(chapter.sections.pluck('position')).toEqual([0, 1, 2]);
expect(chapter.sections.pluck('id')).toEqual([100, undefined, 101]);
});

it('re-indexes pages when inserting after other section', () => {
const {entry} = testContext;
const chapter = entry.chapters.first();

chapter.insertSection({after: chapter.sections.first()});

expect(chapter.sections.pluck('position')).toEqual([0, 1, 2]);
expect(chapter.sections.pluck('id')).toEqual([100, undefined, 101]);
});

describe('with sparse positions', () => {
beforeEach(() => {
testContext.entry = factories.entry(ScrolledEntry, {}, {
entryTypeSeed: normalizeSeed({
chapters: [{id: 10}],
sections: [
{id: 100, chapterId: 10, position: 7},
{id: 101, chapterId: 10, position: 8}
]
})
});
});

it('re-indexes pages when inserting after other section', () => {
const {entry} = testContext;
const chapter = entry.chapters.first();

chapter.insertSection({after: chapter.sections.first()});

expect(chapter.sections.pluck('position')).toEqual([7, 8, 9]);
expect(chapter.sections.pluck('id')).toEqual([100, undefined, 101]);
});
});
});

describe('#duplicateSection', () => {
beforeEach(() => {
testContext.entry = factories.entry(ScrolledEntry, {id: 1}, {
entryTypeSeed: normalizeSeed({
chapters: [{id: 10}],
sections: [
{id: 100, chapterId: 10, position: 0, configuration: {transition: 'scroll'}},
{id: 101, chapterId: 10, position: 1}
],
contentElements: [
{id: 1000, permaId: 1, sectionId: 101, position: 0, typeName: 'inlineImage'},
{id: 1001, permaId: 2, sectionId: 101, position: 1, typeName: 'textBlock'}
]
})
});
});

setupGlobals({
entry: () => testContext.entry
});

useFakeXhr(() => testContext);

it('re-indexes pages when inserting before other section', () => {
const {entry} = testContext;
const chapter = entry.chapters.first();

chapter.duplicateSection(chapter.sections.first());

expect(chapter.sections.pluck('position')).toEqual([0, 1, 2]);
expect(chapter.sections.pluck('id')).toEqual([100, undefined, 101]);
});

it('posts requests to duplicate action and adds content elements', () => {
const {entry, server, requests} = testContext;
const chapter = entry.chapters.first();

chapter.duplicateSection(chapter.sections.first());

expect(requests[0].method).toBe('POST');
expect(requests[0].url).toBe('/editor/entries/1/scrolled/sections/100/duplicate');

expect(chapter.sections.pluck('position')).toEqual([0, 1, 2])
expect(chapter.sections.pluck('id')).toEqual([100, undefined, 101])

server.respond(
'POST', '/editor/entries/1/scrolled/sections/100/duplicate',
[200, {'Content-Type': 'application/json'}, JSON.stringify({
id: 102,
configuration: {transition: 'scroll'},
contentElements: [
{
id: 1002,
permaId: 3,
sectionId: 102,
position: 0,
typeName: 'inlineImage',
configuration: {image: 5}
},
{
id: 1003,
permaId: 4,
sectionId: 102,
position: 0,
typeName: 'textBlock',
configuration: {value: 'Some text'}
},
]
})]
);

expect(requests.length).toEqual(1);
expect(chapter.sections.pluck('id')).toEqual([100, 102, 101]);
const section = chapter.sections.get(102);

expect(section.configuration.get('transition')).toEqual('scroll');
expect(section.contentElements.pluck('id')).toEqual([1002, 1003]);
expect(section.contentElements.pluck('typeName')).toEqual(['inlineImage', 'textBlock']);
expect(section.contentElements.first().configuration.get('image')).toEqual(5);
expect(section.contentElements.last().configuration.get('value')).toEqual('Some text');
});

it('does not use duplicate url on subsequent save', () => {
const {entry, server, requests} = testContext;
const chapter = entry.chapters.first();

const section = chapter.duplicateSection(chapter.sections.first());

server.respond(
'POST', '/editor/entries/1/scrolled/sections/100/duplicate',
[200, {'Content-Type': 'application/json'}, JSON.stringify({
id: 102,
configuration: {transition: 'scroll'},
contentElements: [
{
id: 1002,
permaId: 3,
sectionId: 102,
position: 0,
typeName: 'inlineImage',
configuration: {image: 5}
},
{
id: 1003,
permaId: 4,
sectionId: 102,
position: 0,
typeName: 'textBlock',
configuration: {value: 'Some text'}
},
]
})]
);
section.save();

expect(requests.length).toEqual(2);
expect(requests[1].url).toBe('/editor/entries/1/scrolled/sections/102');
});
});
});
Loading