feat(blocks-api): blocks.update() now can update tunes as well as data (#2720)

* Update `tunes` data when new `tunes` data is provided

AFAIK, when you update block using `editor.blocks.update` method, only `data` attribute is merged and updated. I believe `tunes` data should be updated if provided.

* commit

* edit parameter type, move test

* restore package-lock and yarn lock

* update in api docs

* make data optional

* edit changelog

---------

Co-authored-by: Thomas <zawlintun@robust.best>
Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
This commit is contained in:
Thomas Brillion 2024-07-07 00:27:47 +07:00 committed by GitHub
commit 44c29dd645
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 113 additions and 12 deletions

View file

@ -4,6 +4,7 @@
- `New` Block Tunes now supports nesting items
- `New` Block Tunes now supports separator items
- `Improvment` - The API `blocks.update` now accepts `tunes` data as optional third argument and makes `data` - block data as optional.
- `New` "Convert to" control is now also available in Block Tunes
- `Improvement` — The ability to merge blocks of different types (if both tools provide the conversionConfig)
- `Fix``onChange` will be called when removing the entire text within a descendant element of a block.

View file

@ -79,7 +79,7 @@ use 'move' instead)
`insert(type?: string, data?: BlockToolData, config?: ToolConfig, index?: number, needToFocus?: boolean)` - insert new Block with passed parameters
`update(id: string, data: BlockToolData)` - updates data for the block with passed id
`update(id: string, data?: BlockToolData, tunes?: {[name: string]: BlockTuneData})` - updates block data and block tunes for the block with passed id
#### SanitizerAPI

View file

@ -4,7 +4,8 @@ import * as _ from './../../utils';
import BlockAPI from '../../block/api';
import Module from '../../__module';
import Block from '../../block';
import { capitalize } from './../../utils';
import { capitalize } from '../../utils';
import { BlockTuneData } from '../../../../types/block-tunes/block-tune-data';
/**
* @class BlocksAPI
@ -320,9 +321,10 @@ export default class BlocksAPI extends Module {
* Updates block data by id
*
* @param id - id of the block to update
* @param data - the new data
* @param data - (optional) the new data
* @param tunes - (optional) tune data
*/
public update = async (id: string, data: Partial<BlockToolData>): Promise<BlockAPIInterface> => {
public update = async (id: string, data?: Partial<BlockToolData>, tunes?: {[name: string]: BlockTuneData}): Promise<BlockAPIInterface> => {
const { BlockManager } = this.Editor;
const block = BlockManager.getBlockById(id);
@ -330,7 +332,7 @@ export default class BlocksAPI extends Module {
throw new Error(`Block with id "${id}" not found`);
}
const updatedBlock = await BlockManager.update(block, data);
const updatedBlock = await BlockManager.update(block, data, tunes);
// we cast to any because our BlockAPI has no "new" signature
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View file

@ -337,19 +337,26 @@ export default class BlockManager extends Module {
* Update Block data.
*
* Currently we don't have an 'update' method in the Tools API, so we just create a new block with the same id and type
* Should not trigger 'block-removed' or 'block-added' events
* Should not trigger 'block-removed' or 'block-added' events.
*
* If neither data nor tunes is provided, return the provided block instead.
*
* @param block - block to update
* @param data - new data
* @param data - (optional) new data
* @param tunes - (optional) tune data
*/
public async update(block: Block, data: Partial<BlockToolData>): Promise<Block> {
public async update(block: Block, data?: Partial<BlockToolData>, tunes?: {[name: string]: BlockTuneData}): Promise<Block> {
if (!data && !tunes) {
return block;
}
const existingData = await block.data;
const newBlock = this.composeBlock({
id: block.id,
tool: block.name,
data: Object.assign({}, existingData, data),
tunes: block.tunes,
data: Object.assign({}, existingData, data ?? {}),
tunes: tunes ?? block.tunes,
});
const blockIndex = this.getBlockIndex(block);

View file

@ -1,6 +1,7 @@
import type EditorJS from '../../../../types/index';
import type { ConversionConfig, ToolboxConfig } from '../../../../types';
import ToolMock from '../../fixtures/tools/ToolMock';
import {nanoid} from "nanoid";
/**
* There will be described test cases of 'blocks.*' API
@ -102,6 +103,94 @@ describe('api.blocks', () => {
});
});
it('should update tune data when it is provided', () => {
/**
* Example Tune Class
*/
class ExampleTune {
protected data: object;
/**
*
* @param data
*/
constructor({ data}) {
this.data = data;
}
/**
* Tell editor.js that this Tool is a Block Tune
*
* @returns {boolean}
*/
public static get isTune(): boolean {
return true;
}
/**
* Create Tunes controls wrapper that will be appended to the Block Tunes panel
*
* @returns {Element}
*/
public render(): Element {
return document.createElement('div');
}
/**
* CSS selectors used in Tune
*/
public static get CSS(): object {
return {};
}
/**
* Returns Tune state
*
* @returns {string}
*/
public save(): object | string {
return this.data || '';
}
}
cy.createEditor({
tools: {
exampleTune: ExampleTune,
},
tunes: [ 'exampleTune' ],
data: {
blocks: [
{
id: nanoid(),
type: 'paragraph',
data: {
text: 'First block',
},
tunes: {
exampleTune: 'citation',
},
},
],
},
}).as('editorInstance');
// Update the tunes data of a block
// Check if it is updated
cy.get<EditorJS>('@editorInstance')
.then(async (editor) => {
await editor.blocks.update(editor.blocks.getBlockByIndex(0).id, null, {
exampleTune: 'test',
});
const data = await editor.save();
const actual = JSON.stringify(data.blocks[0].tunes);
const expected = JSON.stringify({ exampleTune: 'test' });
expect(actual).to.eq(expected);
});
});
/**
* When incorrect id passed, editor should not update any block
*/

View file

@ -2,6 +2,7 @@ import Block from '../../src/components/block';
import {OutputBlockData, OutputData} from '../data-formats/output-data';
import {BlockToolData, ToolConfig} from '../tools';
import {BlockAPI} from './block';
import {BlockTuneData} from '../block-tunes/block-tune-data';
/**
* Describes methods to manipulate with Editor`s blocks
@ -142,9 +143,10 @@ export interface Blocks {
* Updates block data by id
*
* @param id - id of the block to update
* @param data - the new data. Can be partial.
* @param data - (optional) the new data. Can be partial.
* @param tunes - (optional) tune data
*/
update(id: string, data: Partial<BlockToolData>): Promise<BlockAPI>;
update(id: string, data?: Partial<BlockToolData>, tunes?: {[name: string]: BlockTuneData}): Promise<BlockAPI>;
/**
* Converts block to another type. Both blocks should provide the conversionConfig.