Wagtail draftail paragraph style - wagtail

I'm looking to add a custom BlockFeature to the wagtail draftail editor that converts to a paragraph tag with a specific class.
<p>A normal paragraph</p>
<p class="margin-0">A special paragraph using my custom feature</p>
This is my attempt:
#hooks.register('register_rich_text_features')
def register_margin0_feature(features):
"""
Registering the `margin-0` feature, which uses the `blockquote` Draft.js block type,
and is stored as HTML with a `<p class="margin-0">` tag.
"""
feature_name = 'margin-0'
type_ = 'custom'
tag = 'p'
classname = "margin-0"
control = {
'type': type_,
'label': '❝',
'description': 'Paragraph with margin-0',
# Optionally, we can tell Draftail what element to use when displaying those blocks in the editor.
'element': 'blockquote',
}
features.register_editor_plugin(
'draftail', feature_name, draftail_features.BlockFeature(control)
)
features.register_converter_rule('contentstate', feature_name, {
'from_database_format': {'p[margin-0]': BlockElementHandler(type_)},
'to_database_format': {
'block_map': {
type_: {
'element': tag,
'props': {
'class': 'margin-0',
},
},
},
},
})
This saves correctly to the database and generates the correct page markup, however, when I open the page in wagtail admin the draftail editor mistakes it for a normal paragraph.
Looking through the wagtail source I noticed this in html_ruleset.py:
Look for a rule matching an HTML element with the given name and attribute dict, and return the corresponding result object. If no rule matches, return None.
If multiple rules match, the one chosen is undetermined.
Since there is a built in 'p' tag handler, does this make recognising 'p class="margin-0"' impossible?
It would be great to be able to just write the custom class you want on each paragraph in the editor.

Yes, unfortunately the ruleset system doesn't currently give precedence to more specific rules, so there's no way to define a rule that supersedes the default <p> rule. This is an open feature request: https://github.com/wagtail/wagtail/pull/4527
However, note that the selector p[margin-0] is incorrect, as this would match a <p> element with a margin-0 attribute rather than a class attribute - it should be p[class="margin-0"].

Related

How to get immediate text with webdriverIO

I have html DOM like this:
<div class="alert alert-danger">
<button class="close" aria-hidden="true" data-dismiss="alert" type="button">×</button>
Your username or password was incorrect.
</div>
I would like to get Your username or password was incorrect. text.
If I do:
$('.global-alerts div.alert-danger').getText()
Then I get this ×.
Is there a way to get the only text part inside that div element?
I managed to do something like this:
getErrorMessageText() {
return browser.execute(function () {
const selector = '.global-alerts div.alert-danger';
// #ts-ignore
return $(selector).contents().not($(selector).children()).text().trim();
});
}
And it works.
But does anybody have better idea? Or more of like webdriverIO approach here?
Does it work if you use something like this?
var innerHTML = $('.global-alerts div.alert-danger').getHTML(false);
the false argument tells indicates whether or not to include the selector element tag in the output.
Serious solution
I do not quite see any other way but to use execute in order to "grab" that information from the page.
I would however place it in a browser command (either do it in the config "before" hook, or add a service that adds the command in the before hook)
This is what I ended up with considering typescript as main language, ignoring the use of jQuery, and considering that you use the before hook:
/**
* Gets executed before test execution begins. At this point you can access to all global
* variables like `browser`. It is the perfect place to define custom commands.
* #param {Array.<Object>} capabilities list of capabilities details
* #param {Array.<String>} specs List of spec file paths that are to be run
* #param {Object} browser instance of created browser/device session
*/
before: function (_capabilities: any, _specs: any, browser: WebdriverIO.Browser) {
browser.addCommand(
'getNodeText',
async function() {
return this.execute(
(selector: string) =>
Array.from( document.querySelector(selector).childNodes || [])
.filter((n: HTMLElement) => n.nodeType === n.TEXT_NODE)
.map(n => n.textContent)
.reduce(function(f, c) {return f+c;}, '')
.replace('\n', '')
.trim(),
this.selector
);
},
true
);
},
With this approach, typescript might complain about the function that passed to webdriver to get executed, so you can either write it properly, or just move it to a .js file and be done with it.
Just watch for document.querySelector(selector), in theory, it should not be null since the command is executed on an already found by webdriver element.
The way you grab the text there is just await (await $('.alert.alert-danger').getNodeText());
This should return the full string from within the node itself, but not any subchild.
Note: If you end up with an element like: <div id="mytxt">my text style is <strong>strong</strong> and <italic> italic </italic>. - html fan</div> and you do this getNodeText(), you probably end up with the value my text style is and . - html fan.
The "don't get bothered to much" solution
This approach will also sort of check that the "x" button is still there.
await expect($('.global-alerts div.alert-danger')).toHaveText('xYour username or password was incorrect.')
in latest version of WebDriverIO (v8), you can use this selector: aria/YourContent. For example:
With the DOM like this:
<h1>Hello World!</h1>
You can use this selector
console.log(await $('aria/Hello World!').getText()) // outputs: "Hello World!"
Ref: https://webdriver.io/docs/selectors/#fetch-by-content

Why source-highlighter doesn't work in asciidoctor?

I use asciidoctor in reactjs app and I want use highlight.js for source code selection, but attributes doesn't work.
By default, asciidoctor produces just the HTML for the Asciidoc source passed to it, not an entire page. If you want the headers and footers for the page, including the code require to fetch and run highlight.js, you need to add standalone: true to your processing options:
const converted = asciidoc.convert(file, {
standalone: true,
attributes: {
'source-highlighter': 'highlight.js',
'highlightjs-theme': 'dark',
}
})
See: https://docs.asciidoctor.org/asciidoctor.js/latest/setup/quick-tour/
and: https://docs.asciidoctor.org/asciidoctor.js/latest/processor/convert-options/
Alternatively, you can add the code import highlight.js and ask it to highlight the HTML produced by asciidoctor.

How to wrap H2 tags on field in a node in Drupal 7

I want to wrap h2 tags around a 'content type' text field (the field "product_title"), as a default setting, that the user cannot change.
I have tried to add this snippet in the file template.php in my theme:
function mytheme_preprocess_field(&$vars) {
if($vars['element']['#field_name'] == 'field_produktnamn'){
$vars['items']['0']['#markup'] = '<h2>This works</h2>';
}
}
But I can't figure out how to keep the original field content. I only want to add h2 tags, not replace the text content.
Or maybe I should go about this another way?
try using this instead:
$vars['items']['0']['#prefix'] = '<h2>';
$vars['items']['0']['#suffix'] = '</h2>';

How to dynamically assign valdr rules?

Forgive my minimal knowledge of AngularJS and valdr...
I have an application using AngularJS where the ui is dynamically generated to edit some object with meta-data provided to determine the type to interpret the members of the object. I'm going to add extra meta-data to set validation rules for each member.
I found valdr and I wondered if it might be possible to add the rules using valdrProvider.addConstraints() called repeatedly for each editable field. Presumably the rule names would have to be made unique?
How could I remove rules from the rule set when data was unloaded?
Is this approach valid or should I just map the rule meta data directly using an AngularJS directive or something?
Your approach sounds ok. valdr offers a removeConstraint(constraintName) function that might do what you need. Note, however, that this removes all contraints for a given model type.
Take the example at https://github.com/netceteragroup/valdr#getting-started.
yourApp.config(function(valdrProvider) {
valdrProvider.addConstraints({
'Person': {
'lastName': {
'size': {
'min': 2,
'max': 10,
'message': 'Last name must be between 2 and 10 characters.'
},
'required': {
'message': 'Last name is required.'
}
},
'firstName': {
'size': {
'min': 2,
'max': 20,
'message': 'First name must be between 2 and 20 characters.'
}
}
}
});
Calling removeConstraint('Person') would remove all constraints for Person. If you just want to remove the firstName because you remove the first name input field you can call addConstraints again with an updated constraints definition for Person.
Final notes:
valdr doesn't impose you remove constraints if fields are removed (see discussion at https://github.com/netceteragroup/valdr/issues/46)
yes, constraint names are unique because they are bound to model types which should have unique names, there shouldn't be two Person types with different implementation

Custom Edit control inside a ExtJS Editor grid

Got an issue, and need your advices
I just started writing an editor grid. (I will actually use this grid as a search filter editor, i.e. columns with criteria name, operators and values).
Now, for the value field, I want to have different edit controls for different rows. For instance, when a criteria type is string I want to display a text box, when it's date time, I want a datetime editor.
So the fact is, I need to control the "edit control creation/display" just before editing starts. and it should be different among rows. Unlike the examples I found which are fixed for the columns.
In order to implement this, can you guys please suggest the steps I need to do? I can probably figure out it if one of you can direct me a way.
Thanks and best regards
Actually you can easily accomplish this by dynamically returning different editors and renders depending on the column you're in. In your ColumnModel object you can define something like this below. Note that i'm getting a type property of each record to determine its type. I have an object containing all my different types of editors, and the same for renderers, and then based on the the type i dish out a different editor or renderer for that cell.
editors: { 'default': {xtype:'textfield'},
texttype: {xtype:'textfield'},
numbertype: {xtype:'numberfield'},
combotype: {xtype:'combo'}....... etc. }
getCellEditor: function(colIndex, rowIndex) {
var store = Ext.getCmp('mygrid').getStore();
var field = this.getDataIndex(colIndex);
var rec = store.getAt(rowIndex);
var type = rec.get('type');
if (type in this.editors) {
return this.editors[type];
} else {
return this.editors['default'];
}
},
In the configuration section of your editorgrid, you will need to define your custom editors:
{
xtype: 'editorgrid',
id : 'mygridID',
stripeRows: true,
...
...
,customEditors : {
//configs go here or pre-define the configs prior to this
'columnName1' : new Ext.grid.GridEditor(new Ext.form.Combobox(configObject)),
//configs go here or pre-define the configs prior to this
'columnName7' : new Ext.grid.GridEditor(new Ext.form.CheckBox(configObject))
}
}
use this grid config - in order to select whole rows:
selType: 'rowmodel'

Resources