CKEditor5 Insert Text without breaking current element - ckeditor5

I have the following code to insert Text at current position:
editor.model.change( writer => {
editor.model.insertContent( writer.createText('[Insert]') );
});
This works fine in most case like inserting inside a paragraph or headline.
Example given:
Before:
<h2>Sample</h2>
After insertion:
<h2>Samp[Insert]le</h2>
But if the text is preformatted for example with a custom font size it breaks the html element:
Before:
<p><span class="text-huge">Sample formatted text</span></p>
After insertion:
<p><span class="text-huge">Sample fo</span>[Insert]<span class="text-huge">rmatted text</span></p>
Notice, that the Element is split up and the text is inserted without applying the custom styles. The [Insert] is set between two spans...
How can I Insert a text directly without modifying html structure?

This happens because created text node has no attributes set. To do this you need to get current selection's attributes and pass it to the writer.createText() method. Then the created text node will have those attributes:
const model = editor.model;
model.change( writer => {
const currentAttributes = model.document.selection.getAttributes();
model.insertContent( writer.createText( '[Foo]', currentAttributes ) );
} );
Alternatively you can use writer.insertText() method if you're inserting text:
editor.model.change( writer => {
const selection = editor.model.document.selection;
const currentAttributes = selection.getAttributes();
const insertPosition = selection.focus;
writer.insertText( '[Foo]', currentAttributes, insertPosition );
} );

Related

How to click on a specific TD element with puppeteer without id and name

I'm having trouble while trying to find a way to click the submenu option in this nav menu
The submenu option I want to click and the code of it
Is there a way of selecting it by it's content of it or by other alternative?
I tried await page.click(); but since i don't have any id, name or value to identify that option it automatically closes the chromium page
also tried clicking wit the content
const select = require('puppeteer-select'); const element = await select(page).getElement('div:contains(Negociação)'); await element.click(); didn't work either.
It is possible to filter certain elements by its text. Here is an example on how to select a table element and search in all its cells for a given text.
I started by writing 2 helper functions for getting text from an element and another to search text within an array of elements:
// returns text for an array of elements
async function findElementByText(elements, searchText) {
for(let el of elements) {
// get the text of the element and return
// element if text matches
const text = await getText(el)
console.log(`found text: "${text}", searchText: "${searchText}"`)
// alternatively you could use this for being a little more error tolerant
// depends on your use case and comes with some pitfalls.
// return text.toUpperCase().includes(searchText.toUpperCase())
// compare text and return element if matched
if(searchText === text) {
return el
}
}
// NO element found..
console.log('No element found by text', searchText)
return null
}
// returns text from a html element
async function getText(element) {
const textElement = await element.getProperty('textContent')
const text = await textElement.jsonValue()
// make sure to remove white spaces.
return text.trim()
}
With given helper functions you can now select the table and search within its td elements (the cells) .
// This selects the first found table. You may wanna grab the correct table by a classname.
const table = await page.$('table')
// select all cells in the table.
const cells = await table.$$('td')
// search in the cells for a given text. "cell" will be a JSHandle
// which can be clicked!
const searchText = 'text4'
const cell = await findElementByText(cells, searchText)
if(!cell) {
throw 'Cell with text ' + searchText + ' not found!!.'
}
console.log('clicking cell now.')
// click the element
await cell.click()
If you host the html yourselve it would make life easier by setting a classname OR id to the cells you wanna click. (only if allowed and possible ofc) .
In your next question you should provide the html as plaint text instead of an image. Plain text can easily be copied by other users to test and debug.
Feel free to leave a comment.

How can I to empty the content of html tag?

Hello I am loading a few of data each API call, inside a tag, but when I do the second call this data is appended to the data of the previous call.
The question is how can I clear the content of a div?
I am using this for select that div
this.$refs.data
In order to add contet I am using the following code:
responseJSON.forEach(element => {
let card = Vue.extend(card)
let instance = new card({
propsData: {
ch: element
}
})
instance.$mount()
this.$refs.aaa.appendChild(instance.$el)
this.cards.push(instance)
});
Before running the responseJSON.forEach, you can clear everything in the div first by running
this.$refs.data.innerHTML = ""

ckeditor5 create element "image" with attributes

I'm trying to create a custom plugin to insert an image from my already built media browser. I'd like to attach some attributes to the image. No matter what I try it only inserts the image with the src and alt attribute. In other words my image is always missing the data-source and class attribute. I've tried the data attribute key as dataSource but that also doesn't work.
const imageElement = writer.createElement( 'image', {
'src': src,
'alt': alt,
'data-sources': dataSources,
'class': cls
} );
editor.model.insertContent( imageElement, editor.model.document.selection );
Any ideas or suggestions would be greatly appreciated.
You need to make 2 things to process new attributes of an image.
First, you need to extend the schema with proper rules which inform model that given attributes are allowed in the editor.
Second thing is to inform the editor how to convert given attribute to model structure and vice-versa with proper converters.
Unfortunately, image converters are quite complicated, because the image is always wrapped with <figure>. Below you can find code and link to working sample how you can create such converters (converters are created based on the source code of the image plugin for CKEditor5). For the purpose of this sample, the data-source attribute is stored in the model as dSource attribute of an image element.
editor.model.schema.extend( 'image', { allowAttributes: 'dSource' } );
editor.conversion.for( 'upcast' ).attributeToAttribute( {
view: 'data-source',
model: 'dSource'
} );
editor.conversion.for( 'downcast' ).add( dispatcher => {
dispatcher.on( 'attribute:dSource:image', ( evt, data, conversionApi ) => {
if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
return;
}
const viewWriter = conversionApi.writer;
const figure = conversionApi.mapper.toViewElement( data.item );
const img = figure.getChild( 0 );
if ( data.attributeNewValue !== null ) {
viewWriter.setAttribute( 'data-source', data.attributeNewValue, img );
} else {
viewWriter.removeAttribute( 'data-source', img );
}
} );
} );
Link to working sample: https://codepen.io/msamsel/pen/pXKRed?editors=1010

Flutter - how to get Text widget on widget test

I'm trying to create a simple widget test in Flutter. I have a custom widget that receives some values, composes a string and shows a Text with that string. I got to create the widget and it works, but I'm having trouble reading the value of the Text component to assert that the generated text is correct.
I created a simple test that illustrates the issue. I want to get the text value, which is "text". I tried several ways, if I get the finder asString() I could interpret the string to get the value, but I don't consider that a good solution. I wanted to read the component as a Text so that I have access to all the properties.
So, how would I read the Text widget so that I can access the data property?
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('my first widget test', (WidgetTester tester) async {
await tester
.pumpWidget(MaterialApp(home: Text("text", key: Key('unit_text'))));
// This works and prints: (Text-[<'unit_text'>]("text"))
var finder = find.byKey(Key("unit_text"));
print(finder.evaluate().first);
// This also works and prints: (Text-[<'unit_text'>]("text"))
var finderByType = find.byType(Text);
print(finderByType.evaluate().first);
// This returns empty
print(finder.first.evaluate().whereType<Text>());
// This throws: type 'StatelessElement' is not a subtype of type 'Text' in type cast
print(finder.first.evaluate().cast<Text>().first);
});
}
I got it working. I had to access the widget property of the Element, and then cast it as text:
var text = finder.evaluate().single.widget as Text;
print(text.data);
Please check this simple example.
testWidgets('Test name', (WidgetTester tester) async {
// findig the widget
var textFind = find.text("text_of_field");
// checking widget present or not
expect(textFind, findsOneWidget);
//getting Text object
Text text = tester.firstWidget(textFind);
// validating properies
expect(text.style.color, Colors.black);
...
...
}
You can use find.text
https://flutter.io/docs/cookbook/testing/widget/finders#1-find-a-text-widget
testWidgets('finds a Text Widget', (WidgetTester tester) async {
// Build an App with a Text Widget that displays the letter 'H'
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Text('H'),
),
));
// Find a Widget that displays the letter 'H'
expect(find.text('H'), findsOneWidget);
});

Simple store connected list for dojo

Is there a simpler list type than DataGrid that can be connected to a store for Dojo?
I would like the data abstraction of the store, but I don't need the header and cell stucture. I would like to be more flexible in the representation of the datalines, where maybe each line calls an function to get laid out...
You ask a really good question. I actually have a blog post that is still in draft form called "The DataGrid should not be your first option".
I have done a couple thing using the store to display data from a store in a repeated form.
I have manually built an html table using dom-construct and for each.
var table = dojo.create('table', {}, parentNode);
var tbody = dojo.create('tbody', {}, table); // a version of IE needs this or it won't render the table
store.fetch({ // this is a dojo.data.ItemFileReadStore, but you cana dapt to the dojo.Store API
query: {},
onComplete: function(itms) {
dojo.forEach(itms, function(itm, idx) {
var tr = dojo.create('tr', {}, tbody);
// use idx to set odd/even css class
// create tds and the data that goes in them
});
}
});
I have also created a repeater, where I have an html template in a string form and use that to instantiate html for each row.
var htmlTemplate = '<div>${name}</div>'; // assumes name is in the data item
store.fetch({ // this is a dojo.data.ItemFileReadStore, but you cana dapt to the dojo.Store API
query: {},
onComplete: function(itms) {
dojo.forEach(itms, function(itm, idx) {
var expandedHtml = dojo.replace(htmlTemplate, itm);
// use dojo.place to put the html where you want it
});
}
});
You could also have a widget that you instantiate for each item.