Fixes vaadin/framework8-issues#390tags/8.0.0.alpha10
@@ -176,11 +176,13 @@ | |||
<li><tt>required-indicator-visible</tt> attribute replaces the <tt>required</tt> attribute</li> | |||
<li><tt>placeholder</tt> attribute replaces the <tt>input-prompt</tt> attribute for input components</li> | |||
<li><tt>multi-select</tt> attribute is no longer used for select components</li> | |||
<li><tt>v-option-group</tt> with attribute <tt>multi-select=true</tt> is replaced by <tt>v-check-box-group</tt></li> | |||
<li><tt>v-option-group</tt> with attribute <tt>multi-select=false</tt> is replaced by <tt>v-radio-button-group</tt></li> | |||
<li><tt>vaadin-option-group</tt> with attribute <tt>multi-select=true</tt> is replaced by <tt>v-check-box-group</tt></li> | |||
<li><tt>vaadin-option-group</tt> with attribute <tt>multi-select=false</tt> is replaced by <tt>v-radio-button-group</tt></li> | |||
<li><tt>immediate</tt> attribute is not used for any component</li> | |||
<li><tt>read-only</tt> attribute is now only used for field components instead of all components</li> | |||
<li><tt>v-upload</tt> has a new attribute <tt>immediate-mode</tt> that replaces the removed <tt>immediate</tt> attribue</li> | |||
<li><tt>vaadin-upload</tt> has a new attribute <tt>immediate-mode</tt> that replaces the removed <tt>immediate</tt> attribue</li> | |||
<li><tt>vaadin-grid</tt> column elements don't have <tt>property-id</tt> attribute anymore. Columns aren't addressed by a property anymore but they have an id. So there is <tt>column-id</tt> attribute instead</li> | |||
<li><tt>vaadin-grid</tt> doesn't have <tt>readonly</tt> attribute anymore. It is replaced by <tt>selection-allowed</tt> attribute which has special meaning for a <tt>Grid</tt></li> | |||
</ul> | |||
<ul id="legacycomponents"><h4>Legacy components in the v7 compatiblity package <tt>com.vaadin.v7.ui</tt> available as a separate dependency</h4> | |||
<li><tt>Calendar</tt> - no replacement in 8</li> |
@@ -269,7 +269,26 @@ public abstract class AbstractListing<T> extends AbstractComponent { | |||
@Override | |||
public void writeDesign(Element design, DesignContext designContext) { | |||
super.writeDesign(design, designContext); | |||
doWriteDesign(design, designContext); | |||
} | |||
/** | |||
* Writes listing specific state into the given design. | |||
* <p> | |||
* This method is separated from {@link writeDesign(Element, DesignContext)} | |||
* to be overridable in subclasses that need to replace this, but still must | |||
* be able to call {@code super.writeDesign(...)}. | |||
* | |||
* @see #doReadDesign(Element, DesignContext) | |||
* | |||
* @param design | |||
* The element to write the component state to. Any previous | |||
* attributes or child nodes are <i>not</i> cleared. | |||
* @param designContext | |||
* The DesignContext instance used for writing the design | |||
* | |||
*/ | |||
protected void doWriteDesign(Element design, DesignContext designContext) { | |||
// Write options if warranted | |||
if (designContext.shouldWriteData(this)) { | |||
writeItems(design, designContext); | |||
@@ -330,6 +349,24 @@ public abstract class AbstractListing<T> extends AbstractComponent { | |||
@Override | |||
public void readDesign(Element design, DesignContext context) { | |||
super.readDesign(design, context); | |||
doReadDesign(design, context); | |||
} | |||
/** | |||
* Reads the listing specific state from the given design. | |||
* <p> | |||
* This method is separated from {@link readDesign(Element, DesignContext)} | |||
* to be overridable in subclasses that need to replace this, but still must | |||
* be able to call {@code super.readDesign(...)}. | |||
* | |||
* @see #doWriteDesign(Element, DesignContext) | |||
* | |||
* @param design | |||
* The element to obtain the state from | |||
* @param context | |||
* The DesignContext instance used for parsing the design | |||
*/ | |||
protected void doReadDesign(Element design, DesignContext context) { | |||
Attributes attr = design.attributes(); | |||
if (attr.hasKey("readonly")) { | |||
setReadOnly(DesignAttributeHandler.readAttribute("readonly", attr, | |||
@@ -441,7 +478,7 @@ public abstract class AbstractListing<T> extends AbstractComponent { | |||
* <p> | |||
* Default implementation delegates a call to {@code item.toString()}. | |||
* | |||
* @see #serializeDeclarativeRepresentation(Object) | |||
* @see #deserializeDeclarativeRepresentation(String) | |||
* | |||
* @param item | |||
* a data item |
@@ -0,0 +1,52 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.ui; | |||
import java.util.IdentityHashMap; | |||
import java.util.Map; | |||
import com.vaadin.server.SerializableFunction; | |||
/** | |||
* Value provider class for declarative support. | |||
* <p> | |||
* Provides a straightforward mapping between an item and its value. | |||
* | |||
* @param <T> | |||
* item type | |||
*/ | |||
class DeclarativeValueProvider<T> implements SerializableFunction<T, String> { | |||
private final Map<T, String> values = new IdentityHashMap<>(); | |||
@Override | |||
public String apply(T t) { | |||
return values.get(t); | |||
} | |||
/** | |||
* Sets a {@code value} for the item {@code t}. | |||
* | |||
* @param t | |||
* a data item | |||
* @param value | |||
* a value for the item {@code t} | |||
*/ | |||
void addValue(T t, String value) { | |||
values.put(t, value); | |||
} | |||
} |
@@ -30,7 +30,9 @@ import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Objects; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.UUID; | |||
import java.util.function.BiFunction; | |||
import java.util.function.BinaryOperator; | |||
import java.util.function.Consumer; | |||
@@ -38,7 +40,9 @@ import java.util.function.Function; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.Stream; | |||
import org.jsoup.nodes.Attributes; | |||
import org.jsoup.nodes.Element; | |||
import org.jsoup.select.Elements; | |||
import com.vaadin.data.Binder; | |||
import com.vaadin.data.BinderValidationStatus; | |||
@@ -57,6 +61,7 @@ import com.vaadin.server.SerializableComparator; | |||
import com.vaadin.server.SerializableFunction; | |||
import com.vaadin.server.data.DataCommunicator; | |||
import com.vaadin.server.data.DataProvider; | |||
import com.vaadin.server.data.Query; | |||
import com.vaadin.server.data.SortOrder; | |||
import com.vaadin.shared.MouseEventDetails; | |||
import com.vaadin.shared.Registration; | |||
@@ -74,6 +79,7 @@ import com.vaadin.shared.ui.grid.HeightMode; | |||
import com.vaadin.shared.ui.grid.SectionState; | |||
import com.vaadin.shared.util.SharedUtil; | |||
import com.vaadin.ui.Grid.FooterRow; | |||
import com.vaadin.ui.Grid.SelectionMode; | |||
import com.vaadin.ui.components.grid.AbstractSelectionModel; | |||
import com.vaadin.ui.components.grid.EditorImpl; | |||
import com.vaadin.ui.components.grid.Footer; | |||
@@ -82,8 +88,12 @@ import com.vaadin.ui.components.grid.Header.Row; | |||
import com.vaadin.ui.components.grid.MultiSelectionModelImpl; | |||
import com.vaadin.ui.components.grid.NoSelectionModel; | |||
import com.vaadin.ui.components.grid.SingleSelectionModelImpl; | |||
import com.vaadin.ui.declarative.DesignAttributeHandler; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
import com.vaadin.ui.declarative.DesignException; | |||
import com.vaadin.ui.declarative.DesignFormatter; | |||
import com.vaadin.ui.renderers.AbstractRenderer; | |||
import com.vaadin.ui.renderers.HtmlRenderer; | |||
import com.vaadin.ui.renderers.Renderer; | |||
import com.vaadin.ui.renderers.TextRenderer; | |||
import com.vaadin.util.ReflectTools; | |||
@@ -1780,6 +1790,136 @@ public class Grid<T> extends AbstractListing<T> | |||
"Column is no longer attached to a grid."); | |||
} | |||
} | |||
/** | |||
* Writes the design attributes for this column into given element. | |||
* | |||
* @since 7.5.0 | |||
* | |||
* @param element | |||
* Element to write attributes into | |||
* | |||
* @param designContext | |||
* the design context | |||
*/ | |||
protected void writeDesign(Element element, | |||
DesignContext designContext) { | |||
Attributes attributes = element.attributes(); | |||
ColumnState defaultState = new ColumnState(); | |||
DesignAttributeHandler.writeAttribute("column-id", attributes, | |||
getId(), null, String.class, designContext); | |||
// Sortable is a special attribute that depends on the data | |||
// provider. | |||
DesignAttributeHandler.writeAttribute("sortable", attributes, | |||
isSortable(), null, boolean.class, designContext); | |||
DesignAttributeHandler.writeAttribute("editable", attributes, | |||
isEditable(), defaultState.editable, boolean.class, | |||
designContext); | |||
DesignAttributeHandler.writeAttribute("resizable", attributes, | |||
isResizable(), defaultState.resizable, boolean.class, | |||
designContext); | |||
DesignAttributeHandler.writeAttribute("hidable", attributes, | |||
isHidable(), defaultState.hidable, boolean.class, | |||
designContext); | |||
DesignAttributeHandler.writeAttribute("hidden", attributes, | |||
isHidden(), defaultState.hidden, boolean.class, | |||
designContext); | |||
DesignAttributeHandler.writeAttribute("hiding-toggle-caption", | |||
attributes, getHidingToggleCaption(), | |||
defaultState.hidingToggleCaption, String.class, | |||
designContext); | |||
DesignAttributeHandler.writeAttribute("width", attributes, | |||
getWidth(), defaultState.width, Double.class, | |||
designContext); | |||
DesignAttributeHandler.writeAttribute("min-width", attributes, | |||
getMinimumWidth(), defaultState.minWidth, Double.class, | |||
designContext); | |||
DesignAttributeHandler.writeAttribute("max-width", attributes, | |||
getMaximumWidth(), defaultState.maxWidth, Double.class, | |||
designContext); | |||
DesignAttributeHandler.writeAttribute("expand", attributes, | |||
getExpandRatio(), defaultState.expandRatio, Integer.class, | |||
designContext); | |||
} | |||
/** | |||
* Reads the design attributes for this column from given element. | |||
* | |||
* @since 7.5.0 | |||
* @param design | |||
* Element to read attributes from | |||
* @param designContext | |||
* the design context | |||
*/ | |||
protected void readDesign(Element design, DesignContext designContext) { | |||
Attributes attributes = design.attributes(); | |||
if (design.hasAttr("sortable")) { | |||
setSortable(DesignAttributeHandler.readAttribute("sortable", | |||
attributes, boolean.class)); | |||
} else { | |||
setSortable(false); | |||
} | |||
if (design.hasAttr("editable")) { | |||
/* | |||
* This is a fake editor just to have something (otherwise | |||
* "setEditable" throws an exception. | |||
* | |||
* Let's use TextField here because we support only Strings as | |||
* inline data type. It will work incorrectly for other types | |||
* but we don't support them anyway. | |||
*/ | |||
setEditorComponentGenerator(item -> new TextField( | |||
Optional.ofNullable(valueProvider.apply(item)) | |||
.map(Object::toString).orElse(""))); | |||
setEditable(DesignAttributeHandler.readAttribute("editable", | |||
attributes, boolean.class)); | |||
} | |||
if (design.hasAttr("resizable")) { | |||
setResizable(DesignAttributeHandler.readAttribute("resizable", | |||
attributes, boolean.class)); | |||
} | |||
if (design.hasAttr("hidable")) { | |||
setHidable(DesignAttributeHandler.readAttribute("hidable", | |||
attributes, boolean.class)); | |||
} | |||
if (design.hasAttr("hidden")) { | |||
setHidden(DesignAttributeHandler.readAttribute("hidden", | |||
attributes, boolean.class)); | |||
} | |||
if (design.hasAttr("hiding-toggle-caption")) { | |||
setHidingToggleCaption(DesignAttributeHandler.readAttribute( | |||
"hiding-toggle-caption", attributes, String.class)); | |||
} | |||
// Read size info where necessary. | |||
if (design.hasAttr("width")) { | |||
setWidth(DesignAttributeHandler.readAttribute("width", | |||
attributes, Double.class)); | |||
} | |||
if (design.hasAttr("min-width")) { | |||
setMinimumWidth(DesignAttributeHandler | |||
.readAttribute("min-width", attributes, Double.class)); | |||
} | |||
if (design.hasAttr("max-width")) { | |||
setMaximumWidth(DesignAttributeHandler | |||
.readAttribute("max-width", attributes, Double.class)); | |||
} | |||
if (design.hasAttr("expand")) { | |||
if (design.attr("expand").isEmpty()) { | |||
setExpandRatio(1); | |||
} else { | |||
setExpandRatio(DesignAttributeHandler.readAttribute( | |||
"expand", attributes, Integer.class)); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
@@ -3306,15 +3446,8 @@ public class Grid<T> extends AbstractListing<T> | |||
fireEvent(new ColumnResizeEvent(this, column, userOriginated)); | |||
} | |||
@Override | |||
protected Element writeItem(Element design, T item, DesignContext context) { | |||
// TODO see vaadin/framework8-issues#390 | |||
return null; | |||
} | |||
@Override | |||
protected List<T> readItems(Element design, DesignContext context) { | |||
// TODO see vaadin/framework8-issues#390 | |||
return Collections.emptyList(); | |||
} | |||
@@ -3328,4 +3461,224 @@ public class Grid<T> extends AbstractListing<T> | |||
internalSetDataProvider(dataProvider); | |||
} | |||
@Override | |||
protected void doReadDesign(Element design, DesignContext context) { | |||
Attributes attrs = design.attributes(); | |||
if (attrs.hasKey("selection-mode")) { | |||
setSelectionMode(DesignAttributeHandler.readAttribute( | |||
"selection-mode", attrs, SelectionMode.class)); | |||
} | |||
Attributes attr = design.attributes(); | |||
if (attr.hasKey("selection-allowed")) { | |||
setReadOnly(DesignAttributeHandler | |||
.readAttribute("selection-allowed", attr, Boolean.class)); | |||
} | |||
if (attrs.hasKey("rows")) { | |||
setHeightByRows(DesignAttributeHandler.readAttribute("rows", attrs, | |||
double.class)); | |||
} | |||
readStructure(design, context); | |||
// Read frozen columns after columns are read. | |||
if (attrs.hasKey("frozen-columns")) { | |||
setFrozenColumnCount(DesignAttributeHandler | |||
.readAttribute("frozen-columns", attrs, int.class)); | |||
} | |||
} | |||
@Override | |||
protected void doWriteDesign(Element design, DesignContext designContext) { | |||
Attributes attr = design.attributes(); | |||
DesignAttributeHandler.writeAttribute("selection-allowed", attr, | |||
isReadOnly(), false, Boolean.class, designContext); | |||
Attributes attrs = design.attributes(); | |||
Grid<?> defaultInstance = designContext.getDefaultInstance(this); | |||
DesignAttributeHandler.writeAttribute("frozen-columns", attrs, | |||
getFrozenColumnCount(), defaultInstance.getFrozenColumnCount(), | |||
int.class, designContext); | |||
if (HeightMode.ROW.equals(getHeightMode())) { | |||
DesignAttributeHandler.writeAttribute("rows", attrs, | |||
getHeightByRows(), defaultInstance.getHeightByRows(), | |||
double.class, designContext); | |||
} | |||
SelectionMode mode = getSelectionMode(); | |||
if (mode != null) { | |||
DesignAttributeHandler.writeAttribute("selection-mode", attrs, mode, | |||
SelectionMode.SINGLE, SelectionMode.class, designContext); | |||
} | |||
writeStructure(design, designContext); | |||
} | |||
@Override | |||
protected T deserializeDeclarativeRepresentation(String item) { | |||
if (item == null) { | |||
return super.deserializeDeclarativeRepresentation( | |||
new String(UUID.randomUUID().toString())); | |||
} | |||
return super.deserializeDeclarativeRepresentation(new String(item)); | |||
} | |||
@Override | |||
protected boolean isReadOnly() { | |||
SelectionMode selectionMode = getSelectionMode(); | |||
if (SelectionMode.SINGLE.equals(selectionMode)) { | |||
return asSingleSelect().isReadOnly(); | |||
} else if (SelectionMode.MULTI.equals(selectionMode)) { | |||
return asMultiSelect().isReadOnly(); | |||
} | |||
return false; | |||
} | |||
@Override | |||
protected void setReadOnly(boolean readOnly) { | |||
SelectionMode selectionMode = getSelectionMode(); | |||
if (SelectionMode.SINGLE.equals(selectionMode)) { | |||
asSingleSelect().setReadOnly(readOnly); | |||
} else if (SelectionMode.MULTI.equals(selectionMode)) { | |||
asMultiSelect().setReadOnly(readOnly); | |||
} | |||
} | |||
private void readStructure(Element design, DesignContext context) { | |||
if (design.children().isEmpty()) { | |||
return; | |||
} | |||
if (design.children().size() > 1 | |||
|| !design.child(0).tagName().equals("table")) { | |||
throw new DesignException( | |||
"Grid needs to have a table element as its only child"); | |||
} | |||
Element table = design.child(0); | |||
Elements colgroups = table.getElementsByTag("colgroup"); | |||
if (colgroups.size() != 1) { | |||
throw new DesignException( | |||
"Table element in declarative Grid needs to have a" | |||
+ " colgroup defining the columns used in Grid"); | |||
} | |||
List<DeclarativeValueProvider<T>> providers = new ArrayList<>(); | |||
for (Element col : colgroups.get(0).getElementsByTag("col")) { | |||
String id = DesignAttributeHandler.readAttribute("column-id", | |||
col.attributes(), null, String.class); | |||
Column<T, String> column; | |||
DeclarativeValueProvider<T> provider = new DeclarativeValueProvider<>(); | |||
if (id != null) { | |||
column = addColumn(id, provider, new HtmlRenderer()); | |||
} else { | |||
column = addColumn(provider, new HtmlRenderer()); | |||
} | |||
providers.add(provider); | |||
column.readDesign(col, context); | |||
} | |||
for (Element child : table.children()) { | |||
if (child.tagName().equals("thead")) { | |||
getHeader().readDesign(child, context); | |||
} else if (child.tagName().equals("tbody")) { | |||
readData(child, providers); | |||
} else if (child.tagName().equals("tfoot")) { | |||
getFooter().readDesign(child, context); | |||
} | |||
} | |||
} | |||
private void readData(Element body, | |||
List<DeclarativeValueProvider<T>> providers) { | |||
getSelectionModel().deselectAll(); | |||
List<T> items = new ArrayList<>(); | |||
for (Element row : body.children()) { | |||
T item = deserializeDeclarativeRepresentation(row.attr("item")); | |||
if (row.hasAttr("selected")) { | |||
getSelectionModel().select(item); | |||
} | |||
Elements cells = row.children(); | |||
int i = 0; | |||
for (Element cell : cells) { | |||
providers.get(i).addValue(item, cell.html()); | |||
i++; | |||
} | |||
} | |||
setItems(items); | |||
} | |||
private void writeStructure(Element design, DesignContext designContext) { | |||
if (getColumns().isEmpty()) { | |||
return; | |||
} | |||
Element tableElement = design.appendElement("table"); | |||
Element colGroup = tableElement.appendElement("colgroup"); | |||
getColumns().forEach(column -> column | |||
.writeDesign(colGroup.appendElement("col"), designContext)); | |||
// Always write thead. Reads correctly when there no header rows | |||
getHeader().writeDesign(tableElement.appendElement("thead"), | |||
designContext); | |||
if (designContext.shouldWriteData(this)) { | |||
Element bodyElement = tableElement.appendElement("tbody"); | |||
getDataProvider().fetch(new Query<>()).forEach( | |||
item -> writeRow(bodyElement, item, designContext)); | |||
} | |||
if (getFooter().getRowCount() > 0) { | |||
getFooter().writeDesign(tableElement.appendElement("tfoot"), | |||
designContext); | |||
} | |||
} | |||
private void writeRow(Element container, T item, DesignContext context) { | |||
Element tableRow = container.appendElement("tr"); | |||
tableRow.attr("item", serializeDeclarativeRepresentation(item)); | |||
if (getSelectionModel().isSelected(item)) { | |||
tableRow.attr("selected", ""); | |||
} | |||
for (Column<T, ?> column : getColumns()) { | |||
Object value = column.valueProvider.apply(item); | |||
tableRow.appendElement("td") | |||
.append((Optional.ofNullable(value).map(Object::toString) | |||
.map(DesignFormatter::encodeForTextNode) | |||
.orElse(""))); | |||
} | |||
} | |||
private SelectionMode getSelectionMode() { | |||
GridSelectionModel<T> selectionModel = getSelectionModel(); | |||
SelectionMode mode = null; | |||
if (selectionModel.getClass().equals(SingleSelectionModelImpl.class)) { | |||
mode = SelectionMode.SINGLE; | |||
} else if (selectionModel.getClass() | |||
.equals(MultiSelectionModelImpl.class)) { | |||
mode = SelectionMode.MULTI; | |||
} else if (selectionModel.getClass().equals(NoSelectionModel.class)) { | |||
mode = SelectionMode.NONE; | |||
} | |||
return mode; | |||
} | |||
@Override | |||
protected Collection<String> getCustomAttributes() { | |||
Collection<String> result = super.getCustomAttributes(); | |||
// "rename" for frozen column count | |||
result.add("frozen-column-count"); | |||
result.add("frozen-columns"); | |||
// "rename" for height-mode | |||
result.add("height-by-rows"); | |||
result.add("rows"); | |||
// add a selection-mode attribute | |||
result.add("selection-mode"); | |||
return result; | |||
} | |||
} |
@@ -15,12 +15,12 @@ | |||
*/ | |||
package com.vaadin.ui.components.grid; | |||
import com.vaadin.ui.Grid; | |||
import java.util.Arrays; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import com.vaadin.ui.Grid; | |||
/** | |||
* Represents the footer section of a Grid. | |||
* | |||
@@ -67,12 +67,14 @@ public abstract class Footer extends StaticSection<Footer.Row> { | |||
} | |||
/** | |||
* Merges column cells in the row. Original cells are hidden, and new merged cell is shown instead. | |||
* The cell has a width of all merged cells together, inherits styles of the first merged cell | |||
* but has empty caption. | |||
* Merges column cells in the row. Original cells are hidden, and new | |||
* merged cell is shown instead. The cell has a width of all merged | |||
* cells together, inherits styles of the first merged cell but has | |||
* empty caption. | |||
* | |||
* @param cellsToMerge | |||
* the cells which should be merged. The cells should not be merged to any other cell set. | |||
* the cells which should be merged. The cells should not be | |||
* merged to any other cell set. | |||
* @return the remaining visible cell after the merge | |||
* | |||
* @see #join(Grid.FooterCell...) | |||
@@ -97,12 +99,14 @@ public abstract class Footer extends StaticSection<Footer.Row> { | |||
} | |||
/** | |||
* Merges column cells in the row. Original cells are hidden, and new merged cell is shown instead. | |||
* The cell has a width of all merged cells together, inherits styles of the first merged cell | |||
* but has empty caption. | |||
* Merges column cells in the row. Original cells are hidden, and new | |||
* merged cell is shown instead. The cell has a width of all merged | |||
* cells together, inherits styles of the first merged cell but has | |||
* empty caption. | |||
* | |||
* @param cellsToMerge | |||
* the cells which should be merged. The cells should not be merged to any other cell set. | |||
* the cells which should be merged. The cells should not be | |||
* merged to any other cell set. | |||
* @return the remaining visible cell after the merge | |||
* | |||
* @see #join(Set) | |||
@@ -110,15 +114,16 @@ public abstract class Footer extends StaticSection<Footer.Row> { | |||
*/ | |||
@Override | |||
public Grid.FooterCell join(Grid.FooterCell... cellsToMerge) { | |||
Set<Grid.FooterCell> footerCells = new HashSet<>(Arrays.asList(cellsToMerge)); | |||
Set<Grid.FooterCell> footerCells = new HashSet<>( | |||
Arrays.asList(cellsToMerge)); | |||
return join(footerCells); | |||
} | |||
} | |||
@Override | |||
public Row createRow() { | |||
return new Row(); | |||
} | |||
} |
@@ -15,12 +15,16 @@ | |||
*/ | |||
package com.vaadin.ui.components.grid; | |||
import com.vaadin.ui.Grid; | |||
import java.util.Arrays; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import org.jsoup.nodes.Element; | |||
import com.vaadin.ui.Grid; | |||
import com.vaadin.ui.declarative.DesignAttributeHandler; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
/** | |||
* Represents the header section of a Grid. | |||
* | |||
@@ -39,8 +43,8 @@ public abstract class Header extends StaticSection<Header.Row> { | |||
/** | |||
* A cell in a Grid header row. | |||
*/ | |||
public class Cell extends StaticSection.StaticCell implements | |||
Grid.HeaderCell { | |||
public class Cell extends StaticSection.StaticCell | |||
implements Grid.HeaderCell { | |||
/** | |||
* Creates a new header cell. | |||
*/ | |||
@@ -87,12 +91,14 @@ public abstract class Header extends StaticSection<Header.Row> { | |||
} | |||
/** | |||
* Merges column cells in the row. Original cells are hidden, and new merged cell is shown instead. | |||
* The cell has a width of all merged cells together, inherits styles of the first merged cell | |||
* but has empty caption. | |||
* Merges column cells in the row. Original cells are hidden, and new | |||
* merged cell is shown instead. The cell has a width of all merged | |||
* cells together, inherits styles of the first merged cell but has | |||
* empty caption. | |||
* | |||
* @param cellsToMerge | |||
* the cells which should be merged. The cells should not be merged to any other cell set. | |||
* the cells which should be merged. The cells should not be | |||
* merged to any other cell set. | |||
* @return the remaining visible cell after the merge | |||
* | |||
* @see #join(Grid.HeaderCell...) | |||
@@ -117,12 +123,14 @@ public abstract class Header extends StaticSection<Header.Row> { | |||
} | |||
/** | |||
* Merges column cells in the row. Original cells are hidden, and new merged cell is shown instead. | |||
* The cell has a width of all merged cells together, inherits styles of the first merged cell | |||
* but has empty caption. | |||
* Merges column cells in the row. Original cells are hidden, and new | |||
* merged cell is shown instead. The cell has a width of all merged | |||
* cells together, inherits styles of the first merged cell but has | |||
* empty caption. | |||
* | |||
* @param cellsToMerge | |||
* the cells which should be merged. The cells should not be merged to any other cell set. | |||
* the cells which should be merged. The cells should not be | |||
* merged to any other cell set. | |||
* @return the remaining visible cell after the merge | |||
* | |||
* @see #join(Set) | |||
@@ -130,10 +138,34 @@ public abstract class Header extends StaticSection<Header.Row> { | |||
*/ | |||
@Override | |||
public Grid.HeaderCell join(Grid.HeaderCell... cellsToMerge) { | |||
Set<Grid.HeaderCell> headerCells = new HashSet<>(Arrays.asList(cellsToMerge)); | |||
Set<Grid.HeaderCell> headerCells = new HashSet<>( | |||
Arrays.asList(cellsToMerge)); | |||
return join(headerCells); | |||
} | |||
@Override | |||
protected void readDesign(Element trElement, | |||
DesignContext designContext) { | |||
super.readDesign(trElement, designContext); | |||
boolean defaultRow = DesignAttributeHandler.readAttribute("default", | |||
trElement.attributes(), false, boolean.class); | |||
if (defaultRow) { | |||
setDefault(true); | |||
} | |||
} | |||
@Override | |||
protected void writeDesign(Element trElement, | |||
DesignContext designContext) { | |||
super.writeDesign(trElement, designContext); | |||
if (isDefault()) { | |||
DesignAttributeHandler.writeAttribute("default", | |||
trElement.attributes(), true, null, boolean.class, | |||
designContext); | |||
} | |||
} | |||
} | |||
@Override | |||
@@ -156,9 +188,7 @@ public abstract class Header extends StaticSection<Header.Row> { | |||
* @return the default row, or {@code null} if there is no default row | |||
*/ | |||
public Row getDefaultRow() { | |||
return getRows().stream() | |||
.filter(Row::isDefault) | |||
.findAny().orElse(null); | |||
return getRows().stream().filter(Row::isDefault).findAny().orElse(null); | |||
} | |||
/** | |||
@@ -185,4 +215,5 @@ public abstract class Header extends StaticSection<Header.Row> { | |||
markAsDirty(); | |||
} | |||
} |
@@ -316,12 +316,12 @@ public class MultiSelectionModelImpl<T> extends AbstractSelectionModel<T> | |||
@Override | |||
public void setReadOnly(boolean readOnly) { | |||
getState().selectionAllowed = readOnly; | |||
getState().selectionAllowed = !readOnly; | |||
} | |||
@Override | |||
public boolean isReadOnly() { | |||
return isUserSelectionAllowed(); | |||
return !isUserSelectionAllowed(); | |||
} | |||
@Override |
@@ -269,12 +269,12 @@ public class SingleSelectionModelImpl<T> extends AbstractSelectionModel<T> | |||
@Override | |||
public void setReadOnly(boolean readOnly) { | |||
getState().selectionAllowed = readOnly; | |||
getState().selectionAllowed = !readOnly; | |||
} | |||
@Override | |||
public boolean isReadOnly() { | |||
return isUserSelectionAllowed(); | |||
return !isUserSelectionAllowed(); | |||
} | |||
}; | |||
} |
@@ -19,12 +19,21 @@ import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import java.util.LinkedHashMap; | |||
import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Map.Entry; | |||
import java.util.Objects; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.Stream; | |||
import org.jsoup.nodes.Element; | |||
import org.jsoup.select.Elements; | |||
import com.vaadin.shared.ui.grid.GridStaticCellType; | |||
import com.vaadin.shared.ui.grid.SectionState; | |||
@@ -33,6 +42,10 @@ import com.vaadin.shared.ui.grid.SectionState.RowState; | |||
import com.vaadin.ui.Component; | |||
import com.vaadin.ui.Grid; | |||
import com.vaadin.ui.Grid.Column; | |||
import com.vaadin.ui.declarative.DesignAttributeHandler; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
import com.vaadin.ui.declarative.DesignException; | |||
import com.vaadin.ui.declarative.DesignFormatter; | |||
/** | |||
* Represents the header or footer section of a Grid. | |||
@@ -108,10 +121,11 @@ public abstract class StaticSection<ROW extends StaticSection.StaticRow<?>> | |||
CELL cell = cells.remove(columnId); | |||
if (cell != null) { | |||
rowState.cells.remove(columnId); | |||
for (Iterator<Set<String>> iterator = rowState.cellGroups.values().iterator(); iterator.hasNext(); ) { | |||
for (Iterator<Set<String>> iterator = rowState.cellGroups | |||
.values().iterator(); iterator.hasNext();) { | |||
Set<String> group = iterator.next(); | |||
group.remove(columnId); | |||
if(group.size() < 2) { | |||
if (group.size() < 2) { | |||
iterator.remove(); | |||
} | |||
} | |||
@@ -147,6 +161,143 @@ public abstract class StaticSection<ROW extends StaticSection.StaticRow<?>> | |||
return cell; | |||
} | |||
/** | |||
* Reads the declarative design from the given table row element. | |||
* | |||
* @since 7.5.0 | |||
* @param trElement | |||
* Element to read design from | |||
* @param designContext | |||
* the design context | |||
* @throws DesignException | |||
* if the given table row contains unexpected children | |||
*/ | |||
protected void readDesign(Element trElement, | |||
DesignContext designContext) throws DesignException { | |||
Elements cellElements = trElement.children(); | |||
for (int i = 0; i < cellElements.size(); i++) { | |||
Element element = cellElements.get(i); | |||
if (!element.tagName().equals(getCellTagName())) { | |||
throw new DesignException( | |||
"Unexpected element in tr while expecting " | |||
+ getCellTagName() + ": " | |||
+ element.tagName()); | |||
} | |||
int colspan = DesignAttributeHandler.readAttribute("colspan", | |||
element.attributes(), 1, int.class); | |||
String columnIdsString = DesignAttributeHandler.readAttribute( | |||
"column-ids", element.attributes(), "", String.class); | |||
if (columnIdsString.trim().isEmpty()) { | |||
throw new DesignException( | |||
"Unexpected 'column-ids' attribute value '" | |||
+ columnIdsString | |||
+ "'. It cannot be empty and must " | |||
+ "be comma separated column identifiers"); | |||
} | |||
String[] columnIds = columnIdsString.split(","); | |||
if (columnIds.length != colspan) { | |||
throw new DesignException( | |||
"Unexpected 'colspan' attribute value '" + colspan | |||
+ "' whereas there is " + columnIds.length | |||
+ " column identifiers specified : '" | |||
+ columnIdsString + "'"); | |||
} | |||
Stream.of(columnIds).forEach(this::addCell); | |||
Stream<String> idsStream = Stream.of(columnIds); | |||
if (colspan > 1) { | |||
CELL newCell = createCell(); | |||
addMergedCell(createCell(), | |||
idsStream.collect(Collectors.toSet())); | |||
newCell.readDesign(element, designContext); | |||
} else { | |||
idsStream.map(this::getCell).forEach( | |||
cell -> cell.readDesign(element, designContext)); | |||
} | |||
} | |||
} | |||
/** | |||
* Writes the declarative design to the given table row element. | |||
* | |||
* @since 7.5.0 | |||
* @param trElement | |||
* Element to write design to | |||
* @param designContext | |||
* the design context | |||
*/ | |||
protected void writeDesign(Element trElement, | |||
DesignContext designContext) { | |||
Set<String> visited = new HashSet<>(); | |||
for (Entry<String, CELL> entry : cells.entrySet()) { | |||
if (visited.contains(entry.getKey())) { | |||
continue; | |||
} | |||
visited.add(entry.getKey()); | |||
Element cellElement = trElement.appendElement(getCellTagName()); | |||
Optional<Entry<CellState, Set<String>>> groupCell = getRowState().cellGroups | |||
.entrySet().stream().filter(groupEntry -> groupEntry | |||
.getValue().contains(entry.getKey())) | |||
.findFirst(); | |||
Stream<String> columnIds = Stream.of(entry.getKey()); | |||
if (groupCell.isPresent()) { | |||
Set<String> orderedSet = new LinkedHashSet<>( | |||
cells.keySet()); | |||
orderedSet.retainAll(groupCell.get().getValue()); | |||
columnIds = orderedSet.stream(); | |||
visited.addAll(orderedSet); | |||
cellElement.attr("colspan", "" + orderedSet.size()); | |||
writeCellState(cellElement, designContext, | |||
groupCell.get().getKey()); | |||
} else { | |||
writeCellState(cellElement, designContext, | |||
entry.getValue().getCellState()); | |||
} | |||
cellElement.attr("column-ids", | |||
columnIds.collect(Collectors.joining(","))); | |||
} | |||
} | |||
/** | |||
* | |||
* Writes declarative design for the cell using its {@code state} to the | |||
* given table cell element. | |||
* <p> | |||
* The method is used instead of StaticCell::writeDesign because | |||
* sometimes there is no a reference to the cell which should be written | |||
* (merged cell) but only its state is available (the cell is virtual | |||
* and is not stored). | |||
* | |||
* @param cellElement | |||
* Element to write design to | |||
* @param context | |||
* the design context | |||
* @param state | |||
* a cell state | |||
*/ | |||
protected void writeCellState(Element cellElement, | |||
DesignContext context, CellState state) { | |||
switch (state.type) { | |||
case TEXT: | |||
cellElement.attr("plain-text", true); | |||
cellElement | |||
.appendText(Optional.ofNullable(state.text).orElse("")); | |||
break; | |||
case HTML: | |||
cellElement.append(Optional.ofNullable(state.html).orElse("")); | |||
break; | |||
case WIDGET: | |||
cellElement.appendChild( | |||
context.createElement((Component) state.connector)); | |||
break; | |||
} | |||
} | |||
void detach() { | |||
for (CELL cell : cells.values()) { | |||
cell.detach(); | |||
@@ -299,6 +450,31 @@ public abstract class StaticSection<ROW extends StaticSection.StaticRow<?>> | |||
return cellState.type; | |||
} | |||
/** | |||
* Reads the declarative design from the given table cell element. | |||
* | |||
* @since 7.5.0 | |||
* @param cellElement | |||
* Element to read design from | |||
* @param designContext | |||
* the design context | |||
*/ | |||
protected void readDesign(Element cellElement, | |||
DesignContext designContext) { | |||
if (!cellElement.hasAttr("plain-text")) { | |||
if (cellElement.children().size() > 0 | |||
&& cellElement.child(0).tagName().contains("-")) { | |||
setComponent( | |||
designContext.readDesign(cellElement.child(0))); | |||
} else { | |||
setHtml(cellElement.html()); | |||
} | |||
} else { | |||
// text – need to unescape HTML entities | |||
setText(DesignFormatter.decodeFromTextNode(cellElement.html())); | |||
} | |||
} | |||
private void removeComponentIfPresent() { | |||
Component component = (Component) cellState.connector; | |||
if (component != null) { | |||
@@ -440,6 +616,48 @@ public abstract class StaticSection<ROW extends StaticSection.StaticRow<?>> | |||
markAsDirty(); | |||
} | |||
/** | |||
* Writes the declarative design to the given table section element. | |||
* | |||
* @param tableSectionElement | |||
* Element to write design to | |||
* @param designContext | |||
* the design context | |||
*/ | |||
public void writeDesign(Element tableSectionElement, | |||
DesignContext designContext) { | |||
for (ROW row : getRows()) { | |||
Element tr = tableSectionElement.appendElement("tr"); | |||
row.writeDesign(tr, designContext); | |||
} | |||
} | |||
/** | |||
* Reads the declarative design from the given table section element. | |||
* | |||
* @since 7.5.0 | |||
* @param tableSectionElement | |||
* Element to read design from | |||
* @param designContext | |||
* the design context | |||
* @throws DesignException | |||
* if the table section contains unexpected children | |||
*/ | |||
public void readDesign(Element tableSectionElement, | |||
DesignContext designContext) throws DesignException { | |||
while (getRowCount() > 0) { | |||
removeRow(0); | |||
} | |||
for (Element row : tableSectionElement.children()) { | |||
if (!row.tagName().equals("tr")) { | |||
throw new DesignException("Unexpected element in " | |||
+ tableSectionElement.tagName() + ": " + row.tagName()); | |||
} | |||
addRowAt(getRowCount()).readDesign(row, designContext); | |||
} | |||
} | |||
/** | |||
* Returns an unmodifiable list of the rows in this section. | |||
* | |||
@@ -448,4 +666,5 @@ public abstract class StaticSection<ROW extends StaticSection.StaticRow<?>> | |||
protected List<ROW> getRows() { | |||
return Collections.unmodifiableList(rows); | |||
} | |||
} |
@@ -221,9 +221,9 @@ public abstract class DeclarativeTestBaseBase<T extends Component> { | |||
return context; | |||
} | |||
public void testWrite(String design, T expected) { | |||
public void testWrite(String expected, T component) { | |||
TestLogHandler l = new TestLogHandler(); | |||
testWrite(design, expected, false); | |||
testWrite(expected, component, false); | |||
Assert.assertEquals("", l.getMessages()); | |||
} | |||
@@ -92,16 +92,24 @@ public abstract class AbstractComponentDeclarativeTestBase<T extends AbstractCom | |||
boolean visible = false; | |||
boolean requiredIndicator = true; | |||
T component = getComponentClass().newInstance(); | |||
boolean hasReadOnly = callBooleanSetter(readOnly, "setReadOnly", | |||
component); | |||
boolean hasRequiredIndicator = callBooleanSetter(requiredIndicator, | |||
"setRequiredIndicatorVisible", component); | |||
String design = String.format( | |||
"<%s id='%s' caption='%s' caption-as-html description='%s' " | |||
+ "error='%s' enabled='false' width='%s' height='%s' " | |||
+ "icon='%s' locale='%s' primary-style-name='%s' " | |||
+ "readonly responsive style-name='%s' visible='false' " | |||
+ "required-indicator-visible/>", | |||
+ "%s responsive style-name='%s' visible='false' " | |||
+ "%s/>", | |||
getComponentTag(), id, caption, description, error, width, | |||
height, icon, locale.toString(), primaryStyle, styleName); | |||
height, icon, locale.toString(), primaryStyle, | |||
hasReadOnly ? "readonly" : "", styleName, | |||
hasRequiredIndicator ? "required-indicator-visible" : ""); | |||
T component = getComponentClass().newInstance(); | |||
component.setId(id); | |||
component.setCaption(caption); | |||
component.setCaptionAsHtml(captionAsHtml); | |||
@@ -115,9 +123,6 @@ public abstract class AbstractComponentDeclarativeTestBase<T extends AbstractCom | |||
component.setIcon(new FileResource(new File(icon))); | |||
component.setLocale(locale); | |||
component.setPrimaryStyleName(primaryStyle); | |||
callBooleanSetter(readOnly, "setReadOnly", component); | |||
callBooleanSetter(requiredIndicator, "setRequiredIndicatorVisible", | |||
component); | |||
component.setResponsive(responsive); | |||
component.setStyleName(styleName); | |||
component.setVisible(visible); | |||
@@ -126,15 +131,17 @@ public abstract class AbstractComponentDeclarativeTestBase<T extends AbstractCom | |||
testWrite(design, component); | |||
} | |||
private void callBooleanSetter(boolean value, String setterName, | |||
private boolean callBooleanSetter(boolean value, String setterName, | |||
T component) | |||
throws IllegalAccessException, InvocationTargetException { | |||
try { | |||
Method method = component.getClass().getMethod(setterName, | |||
new Class[] { boolean.class }); | |||
method.invoke(component, value); | |||
return true; | |||
} catch (NoSuchMethodException ignore) { | |||
// ignore if there is no such method | |||
return false; | |||
} | |||
} | |||
@@ -0,0 +1,731 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.server.component.grid; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
import java.util.List; | |||
import java.util.Locale; | |||
import org.jsoup.nodes.Element; | |||
import org.jsoup.parser.Tag; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import com.vaadin.data.SelectionModel.Multi; | |||
import com.vaadin.data.SelectionModel.Single; | |||
import com.vaadin.server.data.DataProvider; | |||
import com.vaadin.shared.ui.grid.HeightMode; | |||
import com.vaadin.shared.ui.label.ContentMode; | |||
import com.vaadin.tests.data.bean.Person; | |||
import com.vaadin.tests.server.component.abstractlisting.AbstractListingDeclarativeTest; | |||
import com.vaadin.ui.Grid; | |||
import com.vaadin.ui.Grid.Column; | |||
import com.vaadin.ui.Grid.FooterCell; | |||
import com.vaadin.ui.Grid.FooterRow; | |||
import com.vaadin.ui.Grid.HeaderCell; | |||
import com.vaadin.ui.Grid.HeaderRow; | |||
import com.vaadin.ui.Grid.SelectionMode; | |||
import com.vaadin.ui.Label; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
import com.vaadin.ui.declarative.DesignException; | |||
/** | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class GridDeclarativeTest extends AbstractListingDeclarativeTest<Grid> { | |||
@Test | |||
public void gridAttributes() { | |||
Grid<Person> grid = new Grid<>(); | |||
int frozenColumns = 1; | |||
HeightMode heightMode = HeightMode.ROW; | |||
double heightByRows = 13.7d; | |||
grid.addColumn(Person::getFirstName); | |||
grid.addColumn("id", Person::getLastName); | |||
grid.setFrozenColumnCount(frozenColumns); | |||
grid.setSelectionMode(SelectionMode.MULTI); | |||
grid.setHeightMode(heightMode); | |||
grid.setHeightByRows(heightByRows); | |||
String design = String.format( | |||
"<%s height-mode='%s' frozen-columns='%d' rows='%s' selection-mode='%s'><table><colgroup>" | |||
+ "<col column-id='generatedColumn0' sortable>" | |||
+ "<col column-id='id' sortable>" + "</colgroup><thead>" | |||
+ "<tr default><th plain-text column-ids='generatedColumn0'>Generated Column0</th>" | |||
+ "<th plain-text column-ids='id'>Id</th></tr>" | |||
+ "</thead></table></%s>", | |||
getComponentTag(), | |||
heightMode.toString().toLowerCase(Locale.ENGLISH), | |||
frozenColumns, heightByRows, | |||
SelectionMode.MULTI.toString().toLowerCase(Locale.ENGLISH), | |||
getComponentTag()); | |||
testRead(design, grid); | |||
testWrite(design, grid); | |||
} | |||
@Test | |||
public void mergedHeaderCells() { | |||
Grid<Person> grid = new Grid<>(); | |||
Column<Person, String> column1 = grid.addColumn(Person::getFirstName); | |||
Column<Person, String> column2 = grid.addColumn("id", | |||
Person::getLastName); | |||
Column<Person, String> column3 = grid.addColumn("mail", | |||
Person::getEmail); | |||
HeaderRow header = grid.addHeaderRowAt(1); | |||
String headerRowText1 = "foo"; | |||
header.getCell(column1).setText(headerRowText1); | |||
HeaderCell cell2 = header.getCell(column2); | |||
HeaderCell join = header.join(cell2, header.getCell(column3)); | |||
String headerRowText3 = "foobar"; | |||
join.setText(headerRowText3); | |||
String design = String.format("<%s><table><colgroup>" | |||
+ "<col column-id='generatedColumn0' sortable>" | |||
+ "<col column-id='id' sortable>" | |||
+ "<col column-id='mail' sortable>" + "</colgroup><thead>" | |||
+ "<tr default><th plain-text column-ids='generatedColumn0'>Generated Column0</th>" | |||
+ "<th plain-text column-ids='id'>Id</th>" | |||
+ "<th plain-text column-ids='mail'>Mail</th></tr>" | |||
+ "<tr><th plain-text column-ids='generatedColumn0'>%s</th>" | |||
+ "<th colspan='2' plain-text column-ids='id,mail'>foobar</th></tr>" | |||
+ "</thead></table></%s>", getComponentTag(), headerRowText1, | |||
headerRowText3, getComponentTag()); | |||
testRead(design, grid); | |||
testWrite(design, grid); | |||
} | |||
@Test | |||
public void mergedFooterCells() { | |||
Grid<Person> grid = new Grid<>(); | |||
Column<Person, String> column1 = grid.addColumn(Person::getFirstName); | |||
Column<Person, String> column2 = grid.addColumn("id", | |||
Person::getLastName); | |||
Column<Person, String> column3 = grid.addColumn("mail", | |||
Person::getEmail); | |||
FooterRow footer = grid.addFooterRowAt(0); | |||
FooterCell cell1 = footer.getCell(column1); | |||
String footerRowText1 = "foo"; | |||
cell1.setText(footerRowText1); | |||
FooterCell cell2 = footer.getCell(column2); | |||
FooterCell cell3 = footer.getCell(column3); | |||
String footerRowText2 = "foobar"; | |||
footer.join(cell2, cell3).setHtml(footerRowText2); | |||
String design = String.format("<%s><table><colgroup>" | |||
+ "<col column-id='generatedColumn0' sortable>" | |||
+ "<col column-id='id' sortable>" | |||
+ "<col column-id='mail' sortable>" + "</colgroup><thead>" | |||
+ "<tr default><th plain-text column-ids='generatedColumn0'>Generated Column0</th>" | |||
+ "<th plain-text column-ids='id'>Id</th>" | |||
+ "<th plain-text column-ids='mail'>Mail</th></tr></thead>" | |||
+ "<tfoot><tr><td plain-text column-ids='generatedColumn0'>%s</td>" | |||
+ "<td colspan='2' column-ids='id,mail'>%s</td></tr></tfoot>" | |||
+ "</table></%s>", getComponentTag(), footerRowText1, | |||
footerRowText2, getComponentTag()); | |||
testRead(design, grid); | |||
testWrite(design, grid); | |||
} | |||
@Test | |||
public void columnAttributes() { | |||
Grid<Person> grid = new Grid<>(); | |||
String secondColumnId = "id"; | |||
Column<Person, String> column1 = grid.addColumn(Person::getFirstName); | |||
Column<Person, String> column2 = grid.addColumn(secondColumnId, | |||
Person::getLastName); | |||
String caption = "test-caption"; | |||
column1.setCaption(caption); | |||
boolean sortable = false; | |||
column1.setSortable(sortable); | |||
boolean editable = true; | |||
column1.setEditorComponentGenerator(component -> null); | |||
column1.setEditable(editable); | |||
boolean resizable = false; | |||
column1.setResizable(resizable); | |||
boolean hidable = true; | |||
column1.setHidable(hidable); | |||
boolean hidden = true; | |||
column1.setHidden(hidden); | |||
String hidingToggleCaption = "toggle-caption"; | |||
column2.setHidingToggleCaption(hidingToggleCaption); | |||
double width = 17.3; | |||
column2.setWidth(width); | |||
double minWidth = 37.3; | |||
column2.setMinimumWidth(minWidth); | |||
double maxWidth = 63.4; | |||
column2.setMaximumWidth(maxWidth); | |||
int expandRatio = 83; | |||
column2.setExpandRatio(expandRatio); | |||
String design = String.format( | |||
"<%s><table><colgroup>" | |||
+ "<col column-id='generatedColumn0' sortable='%s' editable resizable='%s' hidable hidden>" | |||
+ "<col column-id='id' sortable hiding-toggle-caption='%s' width='%s' min-width='%s' max-width='%s' expand='%s'>" | |||
+ "</colgroup><thead>" | |||
+ "<tr default><th plain-text column-ids='generatedColumn0'>%s</th>" | |||
+ "<th plain-text column-ids='id'>%s</th>" | |||
+ "</tr></thead>" + "</table></%s>", | |||
getComponentTag(), sortable, resizable, hidingToggleCaption, | |||
width, minWidth, maxWidth, expandRatio, caption, "Id", | |||
getComponentTag()); | |||
testRead(design, grid, true); | |||
testWrite(design, grid); | |||
} | |||
@Test | |||
public void headerFooterSerialization() { | |||
Grid<Person> grid = new Grid<>(); | |||
Column<Person, String> column1 = grid.addColumn(Person::getFirstName); | |||
Column<Person, String> column2 = grid.addColumn("id", | |||
Person::getLastName); | |||
FooterRow footerRow = grid.addFooterRowAt(0); | |||
footerRow.getCell(column1).setText("x"); | |||
footerRow.getCell(column2).setHtml("y"); | |||
String design = String.format("<%s><table><colgroup>" | |||
+ "<col column-id='generatedColumn0' sortable>" | |||
+ "<col column-id='id' sortable></colgroup><thead>" | |||
+ "<tr default><th plain-text column-ids='generatedColumn0'>Generated Column0</th>" | |||
+ "<th plain-text column-ids='id'>Id</th></tr>" | |||
+ "</thead><tbody></tbody>" | |||
+ "<tfoot><tr><td plain-text column-ids='generatedColumn0'>x</td>" | |||
+ "<td column-ids='id'>y</td></tr></tfoot>" + "</table></%s>", | |||
getComponentTag(), getComponentTag()); | |||
testRead(design, grid); | |||
testWrite(design, grid, true); | |||
} | |||
@Override | |||
public void dataSerialization() throws InstantiationException, | |||
IllegalAccessException, InvocationTargetException { | |||
Grid<Person> grid = new Grid<>(); | |||
Person person1 = createPerson("foo", "bar"); | |||
Person person2 = createPerson("name", "last-name"); | |||
grid.setItems(person1, person2); | |||
grid.addColumn(Person::getFirstName); | |||
grid.addColumn("id", Person::getLastName); | |||
String design = String.format( | |||
"<%s><table><colgroup>" | |||
+ "<col column-id='generatedColumn0' sortable>" | |||
+ "<col column-id='id' sortable></colgroup><thead>" | |||
+ "<tr default><th plain-text column-ids='generatedColumn0'>Generated Column0</th>" | |||
+ "<th plain-text column-ids='id'>Id</th></tr>" | |||
+ "</thead><tbody>" | |||
+ "<tr item='%s'><td>%s</td><td>%s</td></tr>" | |||
+ "<tr item='%s'><td>%s</td><td>%s</td></tr>" | |||
+ "</tbody></table></%s>", | |||
getComponentTag(), person1.toString(), person1.getFirstName(), | |||
person1.getLastName(), person2.toString(), | |||
person2.getFirstName(), person2.getLastName(), | |||
getComponentTag()); | |||
testRead(design, grid); | |||
testWrite(design, grid, true); | |||
} | |||
/** | |||
* Value for single select | |||
*/ | |||
@Override | |||
@Test | |||
public void valueSerialization() throws InstantiationException, | |||
IllegalAccessException, InvocationTargetException { | |||
valueSingleSelectSerialization(); | |||
} | |||
@SuppressWarnings("unchecked") | |||
@Test | |||
public void valueMultiSelectSerialization() throws InstantiationException, | |||
IllegalAccessException, InvocationTargetException { | |||
Grid<Person> grid = new Grid<>(); | |||
Person person1 = createPerson("foo", "bar"); | |||
Person person2 = createPerson("name", "last-name"); | |||
Person person3 = createPerson("foo", "last-name"); | |||
grid.setItems(person1, person2, person3); | |||
grid.addColumn(Person::getFirstName); | |||
grid.addColumn("id", Person::getLastName); | |||
Multi<Person> model = (Multi<Person>) grid | |||
.setSelectionMode(SelectionMode.MULTI); | |||
model.selectItems(person1, person3); | |||
String design = String.format( | |||
"<%s selection-mode='multi'><table><colgroup>" | |||
+ "<col column-id='generatedColumn0' sortable>" | |||
+ "<col column-id='id' sortable></colgroup><thead>" | |||
+ "<tr default><th plain-text column-ids='generatedColumn0'>Generated Column0</th>" | |||
+ "<th plain-text column-ids='id'>Id</th></tr>" | |||
+ "</thead><tbody>" | |||
+ "<tr item='%s' selected><td>%s</td><td>%s</td></tr>" | |||
+ "<tr item='%s'><td>%s</td><td>%s</td></tr>" | |||
+ "<tr item='%s' selected><td>%s</td><td>%s</td></tr>" | |||
+ "</tbody></table></%s>", | |||
getComponentTag(), person1.toString(), person1.getFirstName(), | |||
person1.getLastName(), person2.toString(), | |||
person2.getFirstName(), person2.getLastName(), | |||
person3.toString(), person3.getFirstName(), | |||
person3.getLastName(), getComponentTag()); | |||
testRead(design, grid); | |||
testWrite(design, grid, true); | |||
} | |||
@SuppressWarnings("unchecked") | |||
private void valueSingleSelectSerialization() throws InstantiationException, | |||
IllegalAccessException, InvocationTargetException { | |||
Grid<Person> grid = new Grid<>(); | |||
Person person1 = createPerson("foo", "bar"); | |||
Person person2 = createPerson("name", "last-name"); | |||
grid.setItems(person1, person2); | |||
grid.addColumn(Person::getFirstName); | |||
grid.addColumn("id", Person::getLastName); | |||
Single<Person> model = (Single<Person>) grid | |||
.setSelectionMode(SelectionMode.SINGLE); | |||
model.select(person2); | |||
String design = String.format( | |||
"<%s><table><colgroup>" | |||
+ "<col column-id='generatedColumn0' sortable>" | |||
+ "<col column-id='id' sortable></colgroup><thead>" | |||
+ "<tr default><th plain-text column-ids='generatedColumn0'>Generated Column0</th>" | |||
+ "<th plain-text column-ids='id'>Id</th></tr>" | |||
+ "</thead><tbody>" | |||
+ "<tr item='%s'><td>%s</td><td>%s</td></tr>" | |||
+ "<tr item='%s' selected><td>%s</td><td>%s</td></tr>" | |||
+ "</tbody></table></%s>", | |||
getComponentTag(), person1.toString(), person1.getFirstName(), | |||
person1.getLastName(), person2.toString(), | |||
person2.getFirstName(), person2.getLastName(), | |||
getComponentTag()); | |||
testRead(design, grid); | |||
testWrite(design, grid, true); | |||
} | |||
@Override | |||
public void readOnlySelection() throws InstantiationException, | |||
IllegalAccessException, InvocationTargetException { | |||
Grid<Person> grid = new Grid<>(); | |||
Person person1 = createPerson("foo", "bar"); | |||
Person person2 = createPerson("name", "last-name"); | |||
grid.setItems(person1, person2); | |||
grid.addColumn(Person::getFirstName); | |||
grid.addColumn("id", Person::getLastName); | |||
grid.setSelectionMode(SelectionMode.MULTI); | |||
grid.asMultiSelect().setReadOnly(true); | |||
String formatString = "<%s %s selection-allowed><table><colgroup>" | |||
+ "<col column-id='generatedColumn0' sortable>" | |||
+ "<col column-id='id' sortable>" + "</colgroup><thead>" | |||
+ "<tr default><th plain-text column-ids='generatedColumn0'>Generated Column0</th>" | |||
+ "<th plain-text column-ids='id'>Id</th></tr>" | |||
+ "</thead><tbody>" | |||
+ "<tr item='%s'><td>%s</td><td>%s</td></tr>" | |||
+ "<tr item='%s'><td>%s</td><td>%s</td></tr>" | |||
+ "</tbody></table></%s>"; | |||
String design = String.format(formatString, getComponentTag(), | |||
"selection-mode='multi'", person1.toString(), | |||
person1.getFirstName(), person1.getLastName(), | |||
person2.toString(), person2.getFirstName(), | |||
person2.getLastName(), getComponentTag()); | |||
testRead(design, grid); | |||
testWrite(design, grid, true); | |||
grid.setSelectionMode(SelectionMode.SINGLE); | |||
grid.asSingleSelect().setReadOnly(true); | |||
design = String.format(formatString, getComponentTag(), "", | |||
person1.toString(), person1.getFirstName(), | |||
person1.getLastName(), person2.toString(), | |||
person2.getFirstName(), person2.getLastName(), | |||
getComponentTag()); | |||
testRead(design, grid); | |||
testWrite(design, grid, true); | |||
} | |||
@Test | |||
public void testComponentInGridHeader() { | |||
Grid<Person> grid = new Grid<>(); | |||
Column<Person, String> column = grid.addColumn(Person::getFirstName); | |||
String html = "<b>Foo</b>"; | |||
Label component = new Label(html); | |||
component.setContentMode(ContentMode.HTML); | |||
//@formatter:off | |||
String design = String.format( "<%s><table>" | |||
+ "<colgroup>" | |||
+ " <col sortable column-id='generatedColumn0'>" | |||
+ "</colgroup>" | |||
+ "<thead>" | |||
+ "<tr default><th column-ids='generatedColumn0'><vaadin-label>%s</vaadin-label></th></tr>" | |||
+ "</thead>" | |||
+ "</table></%s>", getComponentTag(), html, getComponentTag()); | |||
//@formatter:on | |||
grid.getDefaultHeaderRow().getCell(column.getId()) | |||
.setComponent(component); | |||
testRead(design, grid, true); | |||
testWrite(design, grid); | |||
} | |||
@Test | |||
public void testComponentInGridFooter() { | |||
Grid<Person> grid = new Grid<>(); | |||
Column<Person, String> column = grid.addColumn(Person::getFirstName); | |||
String html = "<b>Foo</b>"; | |||
Label component = new Label(html); | |||
component.setContentMode(ContentMode.HTML); | |||
grid.prependFooterRow().getCell(column).setComponent(component); | |||
grid.removeHeaderRow(grid.getDefaultHeaderRow()); | |||
//@formatter:off | |||
String design = String.format( "<%s><table>" | |||
+ "<colgroup>" | |||
+ " <col sortable column-id='generatedColumn0'>" | |||
+ "</colgroup>" | |||
+ "<thead>" | |||
+"<tfoot>" | |||
+ "<tr><td column-ids='generatedColumn0'><vaadin-label>%s</vaadin-label></td></tr>" | |||
+ "</tfoot>" | |||
+ "</table>" | |||
+ "</%s>", getComponentTag(), html, getComponentTag()); | |||
//@formatter:on | |||
testRead(design, grid, true); | |||
testWrite(design, grid); | |||
} | |||
@Test | |||
public void testNoHeaderRows() { | |||
//@formatter:off | |||
String design = "<vaadin-grid><table>" | |||
+ "<colgroup>" | |||
+ " <col sortable column-id='generatedColumn0'>" | |||
+ "</colgroup>" | |||
+ "<thead />" | |||
+ "</table>" | |||
+ "</vaadin-grid>"; | |||
//@formatter:on | |||
Grid<Person> grid = new Grid<>(); | |||
grid.addColumn(Person::getFirstName); | |||
grid.removeHeaderRow(grid.getDefaultHeaderRow()); | |||
testWrite(design, grid); | |||
testRead(design, grid, true); | |||
} | |||
@Test | |||
public void testReadEmptyGrid() { | |||
String design = "<vaadin-grid />"; | |||
testRead(design, new Grid<String>(), false); | |||
} | |||
@Test | |||
public void testEmptyGrid() { | |||
String design = "<vaadin-grid></vaadin-grid>"; | |||
Grid<String> expected = new Grid<>(); | |||
testWrite(design, expected); | |||
testRead(design, expected, true); | |||
} | |||
@Test(expected = DesignException.class) | |||
public void testMalformedGrid() { | |||
String design = "<vaadin-grid><vaadin-label /></vaadin-grid>"; | |||
testRead(design, new Grid<String>()); | |||
} | |||
@Test(expected = DesignException.class) | |||
public void testGridWithNoColGroup() { | |||
String design = "<vaadin-grid><table><thead><tr><th>Foo</tr></thead></table></vaadin-grid>"; | |||
testRead(design, new Grid<String>()); | |||
} | |||
@Test | |||
@SuppressWarnings("unchecked") | |||
public void testHtmlEntitiesinGridHeaderFooter() { | |||
String id = "> id"; | |||
String plainText = "plain-text"; | |||
//@formatter:off | |||
String design = String.format( "<%s><table>" | |||
+ "<colgroup>" | |||
+ " <col sortable column-id='%s'>" | |||
+ "</colgroup>" | |||
+ "<thead>" | |||
+" <tr default><th %s column-ids='%s'>> Test</th>" | |||
+ "</thead>" | |||
+ "<tfoot>" | |||
+ "<tr><td %s column-ids='%s'>> Test</td></tr>" | |||
+ "</tfoot>" | |||
+ "<tbody />" | |||
+ "</table></%s>", | |||
getComponentTag() , id, plainText, id, plainText, id, getComponentTag()); | |||
//@formatter:on | |||
Grid<Person> grid = read(design); | |||
String actualHeader = grid.getHeaderRow(0).getCell(id).getText(); | |||
String actualFooter = grid.getFooterRow(0).getCell(id).getText(); | |||
String expected = "> Test"; | |||
Assert.assertEquals(expected, actualHeader); | |||
Assert.assertEquals(expected, actualFooter); | |||
design = design.replace(plainText, ""); | |||
grid = read(design); | |||
actualHeader = grid.getHeaderRow(0).getCell(id).getHtml(); | |||
actualFooter = grid.getFooterRow(0).getCell(id).getHtml(); | |||
expected = "> Test"; | |||
Assert.assertEquals(expected, actualHeader); | |||
Assert.assertEquals(expected, actualFooter); | |||
grid = new Grid<>(); | |||
Column<Person, String> column = grid.addColumn(id, | |||
Person::getFirstName); | |||
HeaderRow header = grid.addHeaderRowAt(0); | |||
FooterRow footer = grid.addFooterRowAt(0); | |||
grid.removeHeaderRow(grid.getDefaultHeaderRow()); | |||
// entities should be encoded when writing back, not interpreted as HTML | |||
header.getCell(column).setText("& Test"); | |||
footer.getCell(column).setText("& Test"); | |||
Element root = new Element(Tag.valueOf(getComponentTag()), ""); | |||
grid.writeDesign(root, new DesignContext()); | |||
Assert.assertEquals("&amp; Test", | |||
root.getElementsByTag("th").get(0).html()); | |||
Assert.assertEquals("&amp; Test", | |||
root.getElementsByTag("td").get(0).html()); | |||
header = grid.addHeaderRowAt(0); | |||
footer = grid.addFooterRowAt(0); | |||
// entities should not be encoded, this is already given as HTML | |||
header.getCell(id).setHtml("& Test"); | |||
footer.getCell(id).setHtml("& Test"); | |||
root = new Element(Tag.valueOf(getComponentTag()), ""); | |||
grid.writeDesign(root, new DesignContext()); | |||
Assert.assertEquals("& Test", | |||
root.getElementsByTag("th").get(0).html()); | |||
Assert.assertEquals("& Test", | |||
root.getElementsByTag("td").get(0).html()); | |||
} | |||
@SuppressWarnings("rawtypes") | |||
@Override | |||
public Grid<?> testRead(String design, Grid expected) { | |||
return testRead(design, expected, false); | |||
} | |||
@SuppressWarnings("rawtypes") | |||
public Grid<?> testRead(String design, Grid expected, boolean retestWrite) { | |||
return testRead(design, expected, retestWrite, false); | |||
} | |||
@SuppressWarnings("rawtypes") | |||
public Grid<?> testRead(String design, Grid expected, boolean retestWrite, | |||
boolean writeData) { | |||
Grid<?> actual = super.testRead(design, expected); | |||
compareGridColumns(expected, actual); | |||
compareHeaders(expected, actual); | |||
compareFooters(expected, actual); | |||
if (retestWrite) { | |||
testWrite(design, actual, writeData); | |||
} | |||
return actual; | |||
} | |||
private void compareHeaders(Grid<?> expected, Grid<?> actual) { | |||
Assert.assertEquals("Different header row count", | |||
expected.getHeaderRowCount(), actual.getHeaderRowCount()); | |||
for (int i = 0; i < expected.getHeaderRowCount(); ++i) { | |||
HeaderRow expectedRow = expected.getHeaderRow(i); | |||
HeaderRow actualRow = actual.getHeaderRow(i); | |||
if (expectedRow.equals(expected.getDefaultHeaderRow())) { | |||
Assert.assertEquals("Different index for default header row", | |||
actual.getDefaultHeaderRow(), actualRow); | |||
} | |||
for (Column<?, ?> column : expected.getColumns()) { | |||
String baseError = "Difference when comparing cell for " | |||
+ column.toString() + " on header row " + i + ": "; | |||
HeaderCell expectedCell = expectedRow.getCell(column); | |||
HeaderCell actualCell = actualRow.getCell(column); | |||
switch (expectedCell.getCellType()) { | |||
case TEXT: | |||
Assert.assertEquals(baseError + "Text content", | |||
expectedCell.getText(), actualCell.getText()); | |||
break; | |||
case HTML: | |||
Assert.assertEquals(baseError + "HTML content", | |||
expectedCell.getHtml(), actualCell.getHtml()); | |||
break; | |||
case WIDGET: | |||
assertEquals(baseError + "Component content", | |||
expectedCell.getComponent(), | |||
actualCell.getComponent()); | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
private void compareFooters(Grid<?> expected, Grid<?> actual) { | |||
Assert.assertEquals("Different footer row count", | |||
expected.getFooterRowCount(), actual.getFooterRowCount()); | |||
for (int i = 0; i < expected.getFooterRowCount(); ++i) { | |||
FooterRow expectedRow = expected.getFooterRow(i); | |||
FooterRow actualRow = actual.getFooterRow(i); | |||
for (Column<?, ?> column : expected.getColumns()) { | |||
String baseError = "Difference when comparing cell for " | |||
+ column.toString() + " on footer row " + i + ": "; | |||
FooterCell expectedCell = expectedRow.getCell(column); | |||
FooterCell actualCell = actualRow.getCell(column); | |||
switch (expectedCell.getCellType()) { | |||
case TEXT: | |||
Assert.assertEquals(baseError + "Text content", | |||
expectedCell.getText(), actualCell.getText()); | |||
break; | |||
case HTML: | |||
Assert.assertEquals(baseError + "HTML content", | |||
expectedCell.getHtml(), actualCell.getHtml()); | |||
break; | |||
case WIDGET: | |||
assertEquals(baseError + "Component content", | |||
expectedCell.getComponent(), | |||
actualCell.getComponent()); | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
private void compareGridColumns(Grid<?> expected, Grid<?> actual) { | |||
List<?> columns = expected.getColumns(); | |||
List<?> actualColumns = actual.getColumns(); | |||
Assert.assertEquals("Different amount of columns", columns.size(), | |||
actualColumns.size()); | |||
for (int i = 0; i < columns.size(); ++i) { | |||
Column<?, ?> col1 = (Column<?, ?>) columns.get(i); | |||
Column<?, ?> col2 = (Column<?, ?>) actualColumns.get(i); | |||
String baseError = "Error when comparing columns for property " | |||
+ col1.getId() + ": "; | |||
assertEquals(baseError + "Width", col1.getWidth(), col2.getWidth()); | |||
assertEquals(baseError + "Maximum width", col1.getMaximumWidth(), | |||
col2.getMaximumWidth()); | |||
assertEquals(baseError + "Minimum width", col1.getMinimumWidth(), | |||
col2.getMinimumWidth()); | |||
assertEquals(baseError + "Expand ratio", col1.getExpandRatio(), | |||
col2.getExpandRatio()); | |||
assertEquals(baseError + "Sortable", col1.isSortable(), | |||
col2.isSortable()); | |||
assertEquals(baseError + "Editable", col1.isEditable(), | |||
col2.isEditable()); | |||
assertEquals(baseError + "Hidable", col1.isHidable(), | |||
col2.isHidable()); | |||
assertEquals(baseError + "Hidden", col1.isHidden(), | |||
col2.isHidden()); | |||
assertEquals(baseError + "HidingToggleCaption", | |||
col1.getHidingToggleCaption(), | |||
col2.getHidingToggleCaption()); | |||
} | |||
} | |||
@Override | |||
protected String getComponentTag() { | |||
return "vaadin-grid"; | |||
} | |||
@Override | |||
protected Class<? extends Grid> getComponentClass() { | |||
return Grid.class; | |||
} | |||
@Override | |||
protected boolean acceptProperty(Class<?> clazz, Method readMethod, | |||
Method writeMethod) { | |||
if (readMethod != null) { | |||
Class<?> returnType = readMethod.getReturnType(); | |||
if (HeaderRow.class.equals(returnType) | |||
|| DataProvider.class.equals(returnType)) { | |||
return false; | |||
} | |||
} | |||
return super.acceptProperty(clazz, readMethod, writeMethod); | |||
} | |||
private Person createPerson(String name, String lastName) { | |||
Person person = new Person() { | |||
@Override | |||
public String toString() { | |||
return getFirstName() + " " + getLastName(); | |||
} | |||
}; | |||
person.setFirstName(name); | |||
person.setLastName(lastName); | |||
return person; | |||
} | |||
} |
@@ -66,7 +66,7 @@ public class GridBasics extends AbstractTestUIWithLog { | |||
public static final String CELL_STYLE_GENERATOR_EMPTY = "Empty string"; | |||
public static final String CELL_STYLE_GENERATOR_NULL = "Null"; | |||
private boolean isUserSelectionAllowed = true; | |||
private boolean isUserSelectionDisallowed; | |||
public static final String[] COLUMN_CAPTIONS = { "Column 0", "Column 1", | |||
"Column 2", "Row Number", "Date", "HTML String", "Big Random", | |||
@@ -408,20 +408,20 @@ public class GridBasics extends AbstractTestUIWithLog { | |||
}).setCheckable(true); | |||
MenuItem selectionAllowedItem = stateMenu | |||
.addItem("Allow user selection", item -> { | |||
isUserSelectionAllowed = !isUserSelectionAllowed; | |||
.addItem("Disallow user selection", item -> { | |||
isUserSelectionDisallowed = !isUserSelectionDisallowed; | |||
if (grid.getSelectionModel() instanceof MultiSelectionModelImpl) { | |||
MultiSelect<DataObject> multiSelect = grid | |||
.asMultiSelect(); | |||
multiSelect.setReadOnly(isUserSelectionAllowed); | |||
multiSelect.setReadOnly(isUserSelectionDisallowed); | |||
} | |||
if (grid.getSelectionModel() instanceof SingleSelectionModelImpl) { | |||
SingleSelect<DataObject> singleSelect = grid | |||
.asSingleSelect(); | |||
singleSelect.setReadOnly(isUserSelectionAllowed); | |||
singleSelect.setReadOnly(isUserSelectionDisallowed); | |||
} | |||
}); | |||
selectionAllowedItem.setChecked(true); | |||
selectionAllowedItem.setChecked(false); | |||
selectionAllowedItem.setCheckable(true); | |||
stateMenu.addItem("Column reorder listener", | |||
@@ -513,7 +513,7 @@ public class GridBasics extends AbstractTestUIWithLog { | |||
selectionListenerRegistration = ((SingleSelectionModelImpl<DataObject>) grid | |||
.getSelectionModel()) | |||
.addSingleSelectionListener(this::onSingleSelect); | |||
grid.asSingleSelect().setReadOnly(isUserSelectionAllowed); | |||
grid.asSingleSelect().setReadOnly(isUserSelectionDisallowed); | |||
}); | |||
selectionModelItem.addItem("multi", menuItem -> { | |||
switchToMultiSelect(); | |||
@@ -559,7 +559,7 @@ public class GridBasics extends AbstractTestUIWithLog { | |||
MultiSelectionModelImpl<DataObject> model = (MultiSelectionModelImpl<DataObject>) grid | |||
.setSelectionMode(SelectionMode.MULTI); | |||
model.addMultiSelectionListener(this::onMultiSelect); | |||
grid.asMultiSelect().setReadOnly(isUserSelectionAllowed); | |||
grid.asMultiSelect().setReadOnly(isUserSelectionDisallowed); | |||
selectionListenerRegistration = model | |||
.addMultiSelectionListener(this::onMultiSelect); | |||
} |
@@ -404,7 +404,7 @@ public class GridSelectionTest extends GridBasicsTest { | |||
} | |||
private void toggleUserSelectionAllowed() { | |||
selectMenuPath("Component", "State", "Allow user selection"); | |||
selectMenuPath("Component", "State", "Disallow user selection"); | |||
} | |||
private WebElement getSelectionCheckbox(int row) { |