What's the cleanest way to check that each row in the table has an edit link?
example code:
class AccountPage
include PageObject
table(:cards, id: 'cards')
link(:edit, href: /edit/)
end
I want to be able to do something like this:
page.cards_element.each do |card|
card.edit? should == true
end
This wont work as the each block will return a PageObject table row and the only option is to iterate again to get cells and then only cell text can be achieved I suppose.
Solution 1 - Nested Locator
The quickest solution would be use the nested element methods while iterating through the table rows.
class AccountPage
include PageObject
table(:cards, id: 'cards')
end
page = AccountPage.new(browser)
page.cards_element.each do |card|
card.link_element(href: /edit/).visible?.should == true
end
The card.link_element(href: /edit/).visible? line is saying that for each card (ie table row), check that there is visible link element.
Solution 2 - Widget
The disadvantage of using the nested locator approach is that details of the page are now in the test code rather than the page object. This can be solved by using a custom widget.
You will need to define a widget that represents a table row:
class Card < PageObject::Elements::TableRow
def edit_element
link_element(href: /edit/)
end
end
PageObject.register_widget :card, Card, :tr
The page object would then be defined to include the widget:
class AccountPage
include PageObject
cards(:card, :css => 'table#cards tr')
end
This then allows you to write the test as:
page = AccountPage.new(browser)
page.card_elements.each do |card|
card.edit_element.visible?.should == true
end
Note that this will fail on the first row without an edit link. To improve readability and ensure all the rows are tested, I would suggest going to the expect syntax that includes an all method:
page = AccountPage.new(browser)
expect(page.card_elements.map(&:edit_element)).to all be_visible
Related
I need a quick search filter, where user can select what columns are searched. I didn't succeed to implement this behavior.
I tried this:
this.columns.forEach(column=>{
if (this.globalSearchSelectedColumns.indexOf(column.field)>-1) column.getQuickFilterText = (params)=> params.value.name;
else column.getQuickFilterText = ()=> '';
});
this.grid.api.setColumnDefs(this.columns);
this.grid.api.onFilterChanged();
this.grid.api.resetQuickFilter();
where this.columns is columns defs, this.grid is gridOptions, this.globalSearchSelectedColumns is the selected columns to search for, by column.field.
In order to selectively apply quickFilter form ag-Grid you should rewrite the property getQuickFilterText of the columnDef, by setting it to a function which returns an empty string like so:
First of all, you need to retrieve the column by a key through the gridColumnApi
Then you need to access its colDef
Lastly, all you left to do is to rewrite getQuickFilterText property
Assume, that in your class component you have a method disableFilterCol it can look something like this:
disableFilterCol = () => {
var col = this.gridColumnApi.getColumn("athlete");
var colDef = col.getColDef();
colDef.getQuickFilterText = () => "";
console.log("disable Athlete");
};
Once it called, quickFilter will be applied to your data grid excluding athlete column.
I created live demo for you on ReactJS.
You can improve the way you can select multiple columns that you want to rely on doing filtering.
I suppose that in your case you can try to add set getQuickFilterText = () => "" for either definition of colDef from the very beginning and let the user enabling particular columns, you can set getQuickFilterText property for them to undefined to provide sorting among them.
According to nakhodkiin solution I change my code like this:
this.grid.columnApi.getAllColumns().forEach(column=>{
let def = column.getColDef();
if (this.globalSearchSelectedColumns.indexOf(def.field)>-1) def.getQuickFilterText = undefined;
else def.getQuickFilterText = ()=> '';
});
this.grid.api.onFilterChanged();
And it's working;
I think the problem here lies in setting updated column defs.
Can you try this -
let newColDef= [];
this.columns.forEach(column=>{
if (this.globalSearchSelectedColumns.indexOf(column.field)>-1)
column.getQuickFilterText = (params)=> params.value.name;
else column.getQuickFilterText = ()=> '';
newColDef.push(column);
});
this.grid.api.setColumnDefs(newColDef);
this.grid.api.onFilterChanged();
this.grid.api.resetQuickFilter();
this.grid.api.refreshHeader();
Ag-grid updated its approach of detecting column changes since v19.1
More details here
As per doc -->
When new columns are set, the grid will compare with current columns and work out which > columns are old (to be removed), new (new
columns created) or kept (columns that remain will keep their state
including position, filter and sort).
Comparison of column definitions is done on 1) object reference comparison and 2)
column ID eg colDef.colId. If either the object
reference matches, or the column ID matches, then the grid treats the
columns as the same column.
Ag-grid team is also actively working on fixing this issue for v20.1. You can track it on github
I have a w2ui form that contains a w2ui Drop List of choices. The choices will be different depending on what the user selected to bring up the form. My question is: can the contents of a Drop List be changed after it has been rendered?
With standard HTML controls, I would do something like this:
$("#mySelect option[value='xyz']").remove();
or
$("#mySelect").append('<option value="abc">abc</option>');
Can these kinds of operations be done with a w2ui Drop List? Any example code?
In w2ui 1.5 you can use $jQueryElement.w2field() to access the w2fild object - and then manipulate it.
Example:
var field = $("#my_input").w2field();
field.options.items = ["my", "new", "items"];
// optionally: pre-select first item
field.setIndex(0);
// if you do NOT use "setIndex" you need to call "refresh" yourself!
// field.refresh();
Note: setIndex() internally calls refresh() - so as stated above, you do not need to call refresh yourself in that case.
If you want to completely clear/empty your field, you can call field.reset().
Edit: after clarification that it's about a form field:
// Note: ``this`` refers to the w2form
// ``field[8]`` refers to a field of type "select"
this.fields[8].options.items = ["my", "new", "items"];
this.record = {
field_select: 'new'
};
this.refresh();
I'm using the ListDataProvider example here as a guide. The columns are sorting fine as expectd based on the provided comparators. I'm trying to programatically apply a sort as alluded to on this line from the example:
// We know that the data is sorted alphabetically by default.
table.getColumnSortList().push(nameColumn);
What this does, is it makes the cell column appear to be sorted with the carrot sort indicator. However, the underlying data isn't sorted. Is there a way to get the table to actually apply the sort progarmatically. I suppose I could use this in conjunction with actually sorting the data via Collections.sort(), but I'd like to avoid that and do it in one place.
You can apply sorting on a column programatically with little exta code. The following code snippet does that -
When ever u set data to the cellTable you have to initialize the ListHandler as below code does -
cellTable.addColumnSortHandler( createColumnSortHandler() );
private ListHandler<T> createColumnSortHandler()
{
final ListHandler<T> listHandler = new ListHandler<T>(listDataProvider.getList());
listHandler.setComparator( sortColumn, comparator );
return listHandler;
}
And when u want to fire the SortEvent execute the following code snippet -
ColumnSortInfo columnSortInfo = new ColumnSortInfo( sortColumn, sortDirection );
cellTable.getColumnSortList().push( columnSortInfo );
ColumnSortEvent.fire( cellTable, cellTable.getColumnSortList());
you have to call setData on grid again.....
When a new row was added programmatically using an auto generated method on my strongly typed DataTable, How can I fire my custom validation which validate the maxleng of my field?
My client (C#)
DAL.ImportMarcDataSet.PublicationsRow newRow = importMarcDataSet.Publications.NewPublicationsRow();
newRow.CallNumber ="QA76.76.A65";
newRow.Title = "Programming WCF services";
newRow.ISBN = "0596526997";
importMarcDataSet.Publications.AddPublicationsRow(newRow);
My Data Access Layer (VB)
Partial Class ImportMarcDataSet
Partial Class PublicationsDataTable
Private Sub CallNumberMaxLength(ByVal pRow As PublicationsRow)
If pRow.CallNumber.Length > 25 Then
pRow.SetColumnError("CallNumber", "The value entered is over the maximum length")
Else
pRow.SetColumnError("CallNumber", "")
End If
End Sub
'this event is ok when user made changes to the CallNumber column of the current row
Private Sub PublicationsDataTable_ColumnChanged(ByVal sender As Object, ByVal e As System.Data.DataColumnChangeEventArgs) Handles Me.ColumnChanged
If e.Column Is Me.CallNumberColumn Then
CallNumberMaxLength(e.Row)
End If
End Sub
End Class
End Class
You can handle the table's RowChanging event. When the DataRowChangeEventArgs.Action is equal to Add or one of the change... actions do your validation.
It has been a long time since I did this, but I believe you can even cancel the edit if needed by calling CancelEdit on the DataRowChangeEventArgs.Row. Check the documentation. See Handling DataTable Events (ADO.NET) at http://msdn.microsoft.com/en-us/library/w9y9a401.aspx.
The TableNewRow will not help because it is only raised when NewRow is called.
I do have a form. This form submits for example 3 words (beer, coke, wine). In the next action I do want to have a three choice widgets with one or more choices:
-beer: //first choice field
* buddy lighty //choice one
* busch //choice two
* miler //choice three
-coke: //second choice field
* coke diet
* coke
* coke vanilla
-wine: //third choice field
* bordeaux
* suave
* champange
<submit-button>
I want every choice in one action. So if somebodey make a choice busch, coke, suave will be submittet. How can I realise it?
Update:
Thanks for the comment. I might forget to say that I don't know how many dropdown menus I need. There might be just beer and coke or beer, coke, wine and juice. It depends from what the user fill out the number of forms the action before! I tried to do it with a foreach-loop in forms.class.php. But it doesn't help.
I use Doctrine.
One simple way to do this (depends on your model, too) is to configure each item as nullable, and then use form options to show/hide certain widgets. e.g., if your schema looks like this lazy example:
DrinkOrder:
columns:
# ...
beer:
type: enum
values: [Old Peculier,Tribute,Deuchars]
notnull: false
wine:
type: enum
values: [Bordeaux,Suave,Champagne]
notnull: false
# ...etc
Configure your form like this:
class DrinkOrderForm extends BaseDrinkOrderForm
{
public function configure()
{
if ($this->getOption('hide_wine'))
{
$this->widgetSchema['wine'] = new sfWidgetFormInputHidden;
}
// … etc
}
}
And then when the action of the previous form submits you can pass options to the form, like:
$this->form = new DrinkOrderForm($drink_order, array(
'hide_wine' => true,
'hide_beer' => false,
));
This is just a quick example - instead of an ENUM type, you could use relations to another table (e.g. wine_id and an sfWidgetFormDoctrineChoice widget & validator).
One thing you can't do is have 3-4 separate forms, because web browsers will only submit one of them. You either have to embed forms within each other, or use the simpler technique above, depending on how your model's set up.
If the number of types of choice isn't fixed, then you'd want to look into using something like the form system's embedRelation method (or the ahDoctrineEasyEmbeddedRelationsPlugin) to dynamically add sub-forms. It's hard to know from your example just how far you want to go. :)