Skip to content

Commit

Permalink
Custom editor3 blocks (#4532)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaskikutis authored Jun 3, 2024
1 parent aa298f4 commit 2ec11fa
Show file tree
Hide file tree
Showing 72 changed files with 1,756 additions and 888 deletions.
2 changes: 2 additions & 0 deletions e2e/client/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export default defineConfig({

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',

screenshot: 'only-on-failure',
},

/* Configure projects for major browsers */
Expand Down
84 changes: 82 additions & 2 deletions e2e/client/playwright/editor3.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {test, expect} from '@playwright/test';
import {Monitoring} from './page-object-models/monitoring';
import {test, expect} from '@playwright/test';
import {restoreDatabaseSnapshot, s} from './utils';
import {getEditor3Paragraphs} from './utils/editor3';
import {getEditor3FormattingOptions, getEditor3Paragraphs} from './utils/editor3';
import {TreeSelectDriver} from './utils/tree-select-driver';

test('accepting a spelling suggestion', async ({page}) => {
const monitoring = new Monitoring(page);
Expand Down Expand Up @@ -214,3 +215,82 @@ test('tables maintaining cursor position when executing "redo" action', async ({
page.locator(s('authoring', 'authoring-field=body_html', 'table-block')).locator('[contenteditable]').first(),
).toHaveText('fobaro');
});

test('configuring a vocabulary for custom blocks', async ({page}) => {
await restoreDatabaseSnapshot();

await page.goto('/#/settings/vocabularies');

await page.locator(s('metadata-navigation')).getByRole('button', {name: 'Custom blocks'}).click();

await page.getByRole('button', {name: 'Add New'}).click();

// Input sample data
await page.locator(s('vocabulary-edit-content')).getByLabel('Id').fill('custom_blocks_2');

await page.locator(s('vocabulary-edit-content')).getByLabel('Name').fill('Custom blocks 2');

await new TreeSelectDriver(
page,
page.locator(s('vocabulary-edit-content', 'formatting-options')),
).setValue(['h1']);

await page.locator(s('vocabulary-edit-content', 'editor3')).getByRole('textbox').fill('test data');


// Apply formatting option to sample text data
await page.locator(s('editor3')).getByText('test data').click();
await page.locator(s('editor3', 'formatting-option=h1')).click();

// Save editor block
await page.locator(s('vocabulary-edit-footer')).getByRole('button', {name: 'Save'}).click();

await expect(page.locator(s('vocabulary-edit-content'))).not.toBeVisible(); // wait for saving to finish

// Edit custom block
await page.locator(s('vocabulary-item=Custom blocks 2')).hover();
await page.locator(s('vocabulary-item=Custom blocks 2', 'vocabulary-item--start-editing')).click();

// Check if formatting option, sample text data
await expect(page.locator(s('editor3', 'formatting-option=h1'))).toBeVisible();
await expect(page.locator(s('editor3')).getByRole('textbox')).toHaveText('test data');
});

test('adding a custom block inside editor3', async ({page}) => {
const monitoring = new Monitoring(page);

await restoreDatabaseSnapshot({snapshotName: 'custom-blocks'});

await page.goto('/#/workspace/monitoring');

await monitoring.selectDeskOrWorkspace('Sports');

await page.locator(
s('monitoring-group=Sports / Working Stage', 'article-item=test sports story'),
).dblclick();

await page.locator(s('authoring', 'authoring-field=body_html')).getByRole('textbox').click();

await page.locator(
s('authoring', 'authoring-field=body_html', 'toolbar'),
).getByRole('button', {name: 'Custom block'}).click();

await page.locator(s('tree-menu-popover'))
.getByRole('button', {name: 'Custom Block 1'})
.click();

await expect(
page.locator(s('authoring', 'authoring-field=body_html', 'custom-block')).getByRole('textbox').first(),
).toHaveText('custom block 1 template content');

await page.locator(
s('authoring', 'authoring-field=body_html', 'custom-block'),
).getByRole('textbox').click();

const result = await getEditor3FormattingOptions(
page.locator(s('authoring', 'authoring-field=body_html', 'editor3')),
);

expect(result).toEqual(['h2', 'italic', 'bold']);
});

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {Page, expect} from '@playwright/test';
import {s} from '../../utils';
import {TreeSelectDriver} from '../../utils/tree-select-driver';

interface IOptions {
profileName: string;
sectionName: string;
fieldName: string;
formattingOptionsToAdd: Array<string>;
}

export class ContentProfileSettings {
private page: Page;

constructor(page: Page) {
this.page = page;
}

public async addFormattingOptionToContentProfile(options: IOptions) {
await this.page.locator(s(`content-profile=${options.profileName}`))
.getByRole('button', {name: 'Actions'})
.click();
await this.page.locator(s('content-profile-actions-popover')).getByRole('button', {name: 'Edit'}).click();

await this.page.locator(s('content-profile-edit-view')).getByRole('tab', {name: options.sectionName}).click();
await this.page.locator(s('content-profile-edit-view', `field=${options.fieldName}`)).click();

await new TreeSelectDriver(
this.page,
this.page.locator(s('formatting-options-input')),
).setValue(options.formattingOptionsToAdd);

// this is required for validation. TODO: update DB snapshot to make current items already valid
await this.page.locator(s('generic-list-page', 'item-view-edit', 'gform-input--sdWidth')).selectOption('Full');

await this.page.locator(s('generic-list-page', 'item-view-edit', 'toolbar'))
.getByRole('button', {name: 'Apply'})
.click();

await this.page.locator(s('content-profile-edit-view--footer')).getByRole('button', {name: 'Save'}).click();

// wait for saving to finish and modal to close
await expect(this.page.locator(s('content-profile-edit-view'))).not.toBeVisible();
}
}
9 changes: 9 additions & 0 deletions e2e/client/playwright/utils/editor3.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Locator} from '@playwright/test';
import {s} from '.';

export function getEditor3Paragraphs(field: Locator): Promise<Array<string>> {
return field.locator('.DraftEditor-root')
Expand All @@ -8,4 +9,12 @@ export function getEditor3Paragraphs(field: Locator): Promise<Array<string>> {
.locator('> *')
.allInnerTexts()
.then((items) => items.filter((text) => text.trim().length > 0));
}

export function getEditor3FormattingOptions(field: Locator): Promise<Array<string>> {
return field.locator(s('toolbar', 'formatting-option'))
.all()
.then((elements) => Promise.all(
elements.map((element) => element.getAttribute('data-test-value')),
));
}
50 changes: 50 additions & 0 deletions e2e/client/playwright/utils/tree-select-driver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {Locator, Page} from '@playwright/test';
import {s} from '.';

export class TreeSelectDriver {
private page: Page;
private element: Locator;

constructor(page, element) {
this.page = page;
this.element = element;

this.getValue = this.getValue.bind(this);
this.addValue = this.addValue.bind(this);
this.setValue = this.setValue.bind(this);
}

public async getValue(): Promise<Array<string>> {
return this.element.locator(s('item')).all().then((buttons) =>
Promise.all(buttons.map((button) => button.innerText())),
);
}

public async addValue(...options: Array<Array<string> | string>): Promise<void> {
const setOptions = async (options: Array<Array<string> | string>) => {
for (const option of options) {
if (typeof option == 'string') {
await this.element.locator(s('open-popover')).click();
await this.page.locator(s('tree-select-popover'))
.getByRole('button', {name: new RegExp(option, 'i')})
.click();
} else if (option != null) {
await setOptions(option);
}
}
};

await setOptions(options);
}

public async setValue(...options: Array<Array<string> | string>) {
const removeButton = await this.element.getByRole('button', {name: 'remove-sign'});
const removeButtonVisible = await removeButton.isVisible();

if (removeButtonVisible) {
await removeButton.click();
}

await this.addValue(...options);
}
}
5 changes: 4 additions & 1 deletion e2e/server/dump/records/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
editor3-tables only adds tables formatting option to story content profile. It can be merged to main. The only reason it was added as a separate record is to avoid merge conflicts on open PRs.
# Can easily be ported to main snapshot:

* editor3-tables - adds tables formatting option to story content profile.
* custom-blocks - adds custom blocks to story/body_html content profile. Creates a new custom block vocabulary.
Binary file added e2e/server/dump/records/custom-blocks.json.bz2
Binary file not shown.
4 changes: 1 addition & 3 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ var path = require('path');
var grunt = require('grunt');
var makeConfig = require('./webpack.config.js');

require('karma-spec-reporter');

process.env.TZ = 'Europe/Prague';

module.exports = function(config) {
Expand Down Expand Up @@ -56,7 +54,7 @@ module.exports = function(config) {
},

// test results reporter to use
reporters: ['spec'],
reporters: ['dots'],

// web server port
port: 8080,
Expand Down
34 changes: 22 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
"sass-loader": "6.0.6",
"shortid": "2.2.8",
"style-loader": "0.20.2",
"superdesk-ui-framework": "^3.1.3",
"superdesk-ui-framework": "^3.1.9",
"ts-loader": "3.5.0",
"typescript": "4.9.5",
"uuid": "8.3.1",
Expand All @@ -145,7 +145,6 @@
"karma-jasmine": "^1.1.1",
"karma-ng-html2js-preprocessor": "^1.0.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "0.0.36",
"karma-webpack": "^2.0.13",
"react-addons-test-utils": "^15.6.0",
"react-test-renderer": "^16.13.1",
Expand Down
4 changes: 2 additions & 2 deletions scripts/apps/relations/directives/RelatedItemsDirective.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {gettext} from 'core/utils';
import {AuthoringWorkspaceService} from 'apps/authoring/authoring/services/AuthoringWorkspaceService';
import {IArticle, IVocabulary, IRendition} from 'superdesk-api';
import {IArticle, IRendition, IVocabularyRelatedContent, IVocabularyMedia} from 'superdesk-api';
import {IDirectiveScope} from 'types/Angular/DirectiveScope';
import {getAssociationsByFieldId} from '../../authoring/authoring/controllers/AssociationController';
import {getThumbnailForItem} from 'core/helpers/item';
Expand All @@ -14,7 +14,7 @@ interface IScope extends IDirectiveScope<void> {
relatedItemsNewButton: typeof RelatedItemCreateNewButton;
onCreated: (items: Array<IArticle>) => void;
gettext: (text: any, params?: any) => string;
field: IVocabulary;
field: IVocabularyMedia | IVocabularyRelatedContent;
editable: boolean;
item: IArticle;
loading: boolean;
Expand Down
4 changes: 2 additions & 2 deletions scripts/apps/relations/services/RelationsService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {zipObject} from 'lodash';
import {IArticle, IVocabulary} from 'superdesk-api';
import {IArticle, IVocabularyMedia, IVocabularyRelatedContent} from 'superdesk-api';
import {isPublished, isIngested} from 'apps/archive/utils';
import {gettext} from 'core/utils';

Expand Down Expand Up @@ -79,7 +79,7 @@ export function RelationsService(api, $q) {
})).then((values) => zipObject(relatedItemsKeys, values));
};

this.itemHasAllowedStatus = function(item: IArticle, field: IVocabulary) {
this.itemHasAllowedStatus = function(item: IArticle, field: IVocabularyRelatedContent | IVocabularyMedia) {
return validateWorkflow(item, field?.field_options?.allowed_workflows ?? {}).result;
};
}
13 changes: 12 additions & 1 deletion scripts/apps/vocabularies/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import {gettext} from 'core/utils';

export function getMediaTypes() {
interface IMediaType {
GALLERY: {
id: 'media';
label: string;
};
RELATED_CONTENT: {
id: 'related_content';
label: string;
}
}

export function getMediaTypes(): IMediaType {
return {
GALLERY: {
id: 'media',
Expand Down
Loading

0 comments on commit 2ec11fa

Please sign in to comment.