summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--WebContent/VAADIN/themes/base/grid/grid.scss48
-rw-r--r--WebContent/VAADIN/themes/valo/components/_grid.scss13
-rw-r--r--client/src/com/vaadin/client/widgets/Escalator.java1
-rw-r--r--client/src/com/vaadin/client/widgets/Grid.java205
-rw-r--r--uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java3
-rw-r--r--uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java18
-rw-r--r--uitest/src/com/vaadin/tests/components/grid/basicfeatures/element/CustomGridElement.java10
7 files changed, 250 insertions, 48 deletions
diff --git a/WebContent/VAADIN/themes/base/grid/grid.scss b/WebContent/VAADIN/themes/base/grid/grid.scss
index ccb7043c50..7dc877dca5 100644
--- a/WebContent/VAADIN/themes/base/grid/grid.scss
+++ b/WebContent/VAADIN/themes/base/grid/grid.scss
@@ -24,6 +24,12 @@ $v-grid-cell-padding-horizontal: 5px !default;
$v-grid-editor-background-color: $v-grid-row-background-color !default;
+$v-grid-details-marker-width: 2px !default;
+$v-grid-details-marker-color: $v-grid-row-selected-background-color !default;
+$v-grid-details-border-top: $v-grid-cell-horizontal-border !default;
+$v-grid-details-border-top-stripe: $v-grid-cell-horizontal-border !default;
+$v-grid-details-border-bottom: 1px solid darken($v-grid-row-stripe-background-color, 10%) !default;
+$v-grid-details-border-bottom-stripe: 1px solid darken($v-grid-row-background-color, 10%) !default;
@import "../escalator/escalator";
@@ -392,9 +398,45 @@ $v-grid-editor-background-color: $v-grid-row-background-color !default;
.#{$primaryStyleName}-editor-save {
margin-right: 4px;
}
-
- .#{$primaryStyleName}-spacer {
- border: $v-grid-border;
+
+ .#{$primaryStyleName}-spacer > td {
+ display: block;
+ padding: 0;
+
+ background-color: $v-grid-row-background-color;
+ border-top: $v-grid-details-border-top;
+ border-bottom: $v-grid-details-border-bottom;
+ }
+
+ .#{$primaryStyleName}-spacer.stripe > td {
+ background-color: $v-grid-row-stripe-background-color;
+ border-top: $v-grid-details-border-top-stripe;
+ border-bottom: $v-grid-details-border-bottom-stripe;
+ }
+
+ .#{$primaryStyleName}-spacer .deco {
+ top: 0; // this will be overridden by code, but it's a good default.
+ left: 0;
+ width: $v-grid-details-marker-width;
+ background-color: $v-grid-details-marker-color;
+ position: absolute;
+ height: 100%; // this will be overridden by code, but it's a good default.
+ pointer-events: none;
+
+ // IE 8-10 apply "pointer-events" only to SVG elements.
+ // Using an empty SVG instead of an empty text node makes IE
+ // obey the "pointer-events: none" and forwards click events
+ // to the underlying element. The data decodes to:
+ // <svg xmlns="http://www.w3.org/2000/svg"></svg>
+ .ie8 &:before,
+ .ie9 &:before,
+ .ie10 &:before {
+ content: url(data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==);
+ }
+ }
+
+ .#{$primaryStyleName}-spacer .content {
+ padding-left: $v-grid-details-marker-width;
}
// Renderers
diff --git a/WebContent/VAADIN/themes/valo/components/_grid.scss b/WebContent/VAADIN/themes/valo/components/_grid.scss
index 0d6d2ff0a6..0adfdd27ab 100644
--- a/WebContent/VAADIN/themes/valo/components/_grid.scss
+++ b/WebContent/VAADIN/themes/valo/components/_grid.scss
@@ -3,7 +3,8 @@
$v-grid-row-background-color: valo-table-background-color() !default;
$v-grid-row-stripe-background-color: scale-color($v-grid-row-background-color, $lightness: if(color-luminance($v-grid-row-background-color) < 10, 4%, -4%)) !default;
-$v-grid-border: flatten-list(valo-border($color: $v-grid-row-background-color, $strength: 0.8)) !default;
+$v-grid-border-color-source: $v-grid-row-background-color !default;
+$v-grid-border: flatten-list(valo-border($color: $v-grid-border-color-source, $strength: 0.8)) !default;
$v-grid-cell-focused-border: max(2px, first-number($v-border)) solid $v-selection-color !default;
$v-grid-row-height: $v-table-row-height !default;
@@ -16,6 +17,12 @@ $v-grid-cell-padding-horizontal: $v-table-cell-padding-horizontal !default;
$v-grid-animations-enabled: $v-animations-enabled !default;
+$v-grid-details-marker-width: first-number($v-grid-border) * 2 !default;
+$v-grid-details-marker-color: $v-selection-color !default;
+$v-grid-details-border-top: valo-border($color: $v-grid-border-color-source, $strength: 0.3) !default;
+$v-grid-details-border-top-stripe: valo-border($color: $v-grid-row-stripe-background-color, $strength: 0.3) !default;
+$v-grid-details-border-bottom: $v-grid-cell-horizontal-border !default;
+$v-grid-details-border-bottom-stripe: $v-grid-cell-horizontal-border !default;
@import "../../base/grid/grid";
@@ -177,6 +184,10 @@ $v-grid-animations-enabled: $v-animations-enabled !default;
outline: none;
}
+ .#{$primary-stylename}-spacer {
+ margin-top: first-number($v-grid-border) * -1;
+ }
+
// Customize scrollbars
.#{$primary-stylename}-scroller {
&::-webkit-scrollbar {
diff --git a/client/src/com/vaadin/client/widgets/Escalator.java b/client/src/com/vaadin/client/widgets/Escalator.java
index 88ed9295e4..0ce5aff74e 100644
--- a/client/src/com/vaadin/client/widgets/Escalator.java
+++ b/client/src/com/vaadin/client/widgets/Escalator.java
@@ -4597,7 +4597,6 @@ public class Escalator extends Widget implements RequiresResize,
getRootElement().getStyle().setWidth(getInnerWidth(), Unit.PX);
setHeight(height);
- spacerElement.getStyle().setWidth(100, Unit.PCT);
spacerElement.setColSpan(getColumnConfiguration()
.getColumnCount());
diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java
index 73dcccfd1e..ed7c0d1800 100644
--- a/client/src/com/vaadin/client/widgets/Grid.java
+++ b/client/src/com/vaadin/client/widgets/Grid.java
@@ -2843,15 +2843,21 @@ public class Grid<T> extends ResizeComposite implements
private class GridSpacerUpdater implements SpacerUpdater {
+ private static final String DECO_CLASSNAME = "deco";
+ private static final String CONTENT_CLASSNAME = "content";
+ private static final String STRIPE_CLASSNAME = "stripe";
+
private final Map<Element, Widget> elementToWidgetMap = new HashMap<Element, Widget>();
@Override
public void init(Spacer spacer) {
+ initStructure(spacer);
+ Element root = getDetailsRoot(spacer);
- assert spacer.getElement().getFirstChild() == null : "The spacer's"
+ assert root.getFirstChild() == null : "The spacer's"
+ " element should be empty at this point. (row: "
- + spacer.getRow() + ", child: "
- + spacer.getElement().getFirstChild() + ")";
+ + spacer.getRow() + ", child: " + root.getFirstChild()
+ + ")";
int rowIndex = spacer.getRow();
@@ -2865,43 +2871,45 @@ public class Grid<T> extends ResizeComposite implements
+ rowIndex, e);
}
+ final double spacerHeight;
if (detailsWidget == null) {
- spacer.getElement().removeAllChildren();
- escalator.getBody().setSpacer(rowIndex,
- DETAILS_ROW_INITIAL_HEIGHT);
- return;
- }
+ root.removeAllChildren();
+ spacerHeight = DETAILS_ROW_INITIAL_HEIGHT;
+ } else {
+ Element element = detailsWidget.getElement();
+ root.appendChild(element);
+ setParent(detailsWidget, Grid.this);
+ Widget previousWidget = elementToWidgetMap.put(element,
+ detailsWidget);
- Element element = detailsWidget.getElement();
- spacer.getElement().appendChild(element);
- setParent(detailsWidget, Grid.this);
- Widget previousWidget = elementToWidgetMap.put(element,
- detailsWidget);
+ assert previousWidget == null : "Overwrote a pre-existing widget on row "
+ + rowIndex + " without proper removal first.";
- assert previousWidget == null : "Overwrote a pre-existing widget on row "
- + rowIndex + " without proper removal first.";
+ /*
+ * Once we have the content properly inside the DOM, we should
+ * re-measure it to make sure that it's the correct height.
+ */
+ double measuredHeight = WidgetUtil
+ .getRequiredHeightBoundingClientRectDouble(root);
+ assert getElement().isOrHasChild(root) : "The spacer element wasn't in the DOM during measurement, but was assumed to be.";
+ spacerHeight = measuredHeight;
+ }
- /*
- * Once we have the content properly inside the DOM, we should
- * re-measure it to make sure that it's the correct height.
- */
- double measuredHeight = WidgetUtil
- .getRequiredHeightBoundingClientRectDouble(spacer
- .getElement());
- assert getElement().isOrHasChild(spacer.getElement()) : "The spacer element wasn't in the DOM during measurement, but was assumed to be.";
- escalator.getBody().setSpacer(rowIndex, measuredHeight);
+ escalator.getBody().setSpacer(rowIndex, spacerHeight);
+ updateDecoratorGeometry(spacerHeight, spacer);
}
@Override
public void destroy(Spacer spacer) {
+ Element root = getDetailsRoot(spacer);
- assert getElement().isOrHasChild(spacer.getElement()) : "Trying "
+ assert getElement().isOrHasChild(root) : "Trying "
+ "to destroy a spacer that is not connected to this "
+ "Grid's DOM. (row: " + spacer.getRow() + ", element: "
- + spacer.getElement() + ")";
+ + root + ")";
- Widget detailsWidget = elementToWidgetMap.remove(spacer
- .getElement().getFirstChildElement());
+ Widget detailsWidget = elementToWidgetMap.remove(root
+ .getFirstChildElement());
if (detailsWidget != null) {
/*
@@ -2909,16 +2917,107 @@ public class Grid<T> extends ResizeComposite implements
* returned a null widget.
*/
- assert spacer.getElement().getFirstChild() != null : "The "
+ assert root.getFirstChild() != null : "The "
+ "details row to destroy did not contain a widget - "
+ "probably removed by something else without "
+ "permission? (row: " + spacer.getRow()
- + ", element: " + spacer.getElement() + ")";
+ + ", element: " + root + ")";
setParent(detailsWidget, null);
- spacer.getElement().removeAllChildren();
+ root.removeAllChildren();
+ }
+ }
+
+ /**
+ * Initializes the spacer element into a details structure, containing a
+ * decorator and a slot for the details widget.
+ */
+ private void initStructure(Spacer spacer) {
+ Element spacerRoot = spacer.getElement();
+
+ if (spacerRoot.getChildCount() == 0) {
+ Element deco = DOM.createDiv();
+ deco.setClassName(DECO_CLASSNAME);
+
+ Element detailsContent = DOM.createDiv();
+ detailsContent.setClassName(CONTENT_CLASSNAME);
+
+ if (spacer.getRow() % 2 == 1) {
+ spacerRoot.getParentElement()
+ .addClassName(STRIPE_CLASSNAME);
+ }
+
+ spacerRoot.appendChild(deco);
+ spacerRoot.appendChild(detailsContent);
+ }
+
+ else {
+ if (spacer.getRow() % 2 == 1) {
+ spacerRoot.getParentElement()
+ .addClassName(STRIPE_CLASSNAME);
+ } else {
+ spacerRoot.getParentElement().removeClassName(
+ STRIPE_CLASSNAME);
+ }
+
+ /*
+ * The only case when we get into this else branch is when the
+ * previous generated details element was a null Widget. In
+ * those situations, we don't call destroy on the content, but
+ * simply reuse it as-is.
+ */
+ assert getDetailsRoot(spacer).getChildCount() == 0 : "This "
+ + "code should never be triggered unless the details "
+ + "root already was empty";
}
}
+
+ /** Gets the decorator element from the DOM structure. */
+ private Element getDecorator(Spacer spacer) {
+ TableCellElement td = TableCellElement.as(spacer.getElement());
+ Element decorator = td.getFirstChildElement();
+ return decorator;
+ }
+
+ /** Gets the element for the details widget from the DOM structure. */
+ private Element getDetailsRoot(Spacer spacer) {
+ Element detailsRoot = getDecorator(spacer).getNextSiblingElement();
+ return detailsRoot;
+ }
+
+ /** Resizes and places the decorator. */
+ private void updateDecoratorGeometry(double detailsHeight, Spacer spacer) {
+ Element decorator = getDecorator(spacer);
+ Style style = decorator.getStyle();
+ double rowHeight = escalator.getBody().getDefaultRowHeight();
+ double borderHeight = getBorderHeight(spacer);
+
+ style.setTop(-(rowHeight - borderHeight), Unit.PX);
+ style.setHeight(detailsHeight + rowHeight, Unit.PX);
+ }
+
+ private native double getBorderHeight(Spacer spacer)
+ /*-{
+ var spacerCell = spacer.@com.vaadin.client.widget.escalator.Spacer::getElement()();
+ if (typeof $wnd.getComputedStyle === 'function') {
+ var computedStyle = $wnd.getComputedStyle(spacerCell);
+ var borderTopWidth = computedStyle['borderTopWidth'];
+ var width = parseFloat(borderTopWidth);
+ return width;
+ } else {
+ var spacerRow = spacerCell.offsetParent;
+ var cloneCell = spacerCell.cloneNode(false);
+ spacerRow.appendChild(cloneCell);
+ cloneCell.style.height = "10px"; // IE8 wants the height to be set to something...
+ var heightWithBorder = cloneCell.offsetHeight;
+ cloneCell.style.borderTopWidth = "0";
+ var heightWithoutBorder = cloneCell.offsetHeight;
+ spacerRow.removeChild(cloneCell);
+
+ console.log(heightWithBorder+" - "+heightWithoutBorder);
+ return heightWithBorder - heightWithoutBorder;
+ }
+ }-*/;
}
/**
@@ -4829,6 +4928,8 @@ public class Grid<T> extends ResizeComposite implements
setSelectionMode(SelectionMode.SINGLE);
+ escalator.getBody().setSpacerUpdater(gridSpacerUpdater);
+
escalator.addScrollHandler(new ScrollHandler() {
@Override
public void onScroll(ScrollEvent event) {
@@ -6053,10 +6154,12 @@ public class Grid<T> extends ResizeComposite implements
private boolean isOrContainsInSpacer(Node node) {
Node n = node;
while (n != null && n != getElement()) {
- if (Element.is(n)
- && Element.as(n).getClassName()
- .equals(getStylePrimaryName() + "-spacer")) {
- return true;
+ boolean isElement = Element.is(n);
+ if (isElement) {
+ String className = Element.as(n).getClassName();
+ if (className.contains(getStylePrimaryName() + "-spacer")) {
+ return true;
+ }
}
n = n.getParentNode();
}
@@ -6318,10 +6421,19 @@ public class Grid<T> extends ResizeComposite implements
@SuppressWarnings("deprecation")
public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
- Element subPartElement = escalator.getSubPartElement(subPart
+ /*
+ * gandles details[] (translated to spacer[] for Escalator), cell[],
+ * header[] and footer[]
+ */
+ Element escalatorElement = escalator.getSubPartElement(subPart
.replaceFirst("^details\\[", "spacer["));
- if (subPartElement != null) {
- return DOM.asOld(subPartElement);
+
+ if (escalatorElement != null) {
+ if (subPart.startsWith("details[")) {
+ return DOM.asOld(parseDetails(escalatorElement));
+ } else {
+ return DOM.asOld(escalatorElement);
+ }
}
SubPartArguments args = Escalator.parseSubPartArguments(subPart);
@@ -6334,6 +6446,23 @@ public class Grid<T> extends ResizeComposite implements
return null;
}
+ @SuppressWarnings("static-method")
+ private Element parseDetails(Element spacer) {
+ assert spacer.getChildCount() == 2 : "Unexpected structure for details ";
+
+ Element decorator = spacer.getFirstChildElement();
+ assert decorator != null : "unexpected spacer DOM structure";
+ assert decorator.getClassName()
+ .equals(GridSpacerUpdater.DECO_CLASSNAME) : "unexpected first details element";
+
+ Element spacerRoot = decorator.getNextSiblingElement();
+ assert spacerRoot != null : "unexpected spacer DOM structure";
+ assert spacerRoot.getClassName().equals(
+ GridSpacerUpdater.CONTENT_CLASSNAME) : "unexpected second details element";
+
+ return DOM.asOld(spacerRoot);
+ }
+
private Element getSubPartElementEditor(SubPartArguments args) {
if (!args.getType().equalsIgnoreCase("editor")
diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java
index 6f4c7df38c..94620f34bd 100644
--- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java
+++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java
@@ -1301,6 +1301,9 @@ public class GridBasicFeatures extends AbstractComponentTest<Grid> {
createBooleanAction("Open firstItemId", "Details", false,
openOrCloseItemId, ds.firstItemId());
+ createBooleanAction("Open 1", "Details", false, openOrCloseItemId,
+ ds.getIdByIndex(1));
+
createBooleanAction("Open 995", "Details", false, openOrCloseItemId,
ds.getIdByIndex(995));
}
diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java
index 5ab0c238e5..619033226c 100644
--- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java
+++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java
@@ -23,6 +23,8 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.util.List;
+
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.NoSuchElementException;
@@ -33,12 +35,9 @@ import com.vaadin.shared.ui.grid.ScrollDestination;
import com.vaadin.testbench.By;
import com.vaadin.testbench.ElementQuery;
import com.vaadin.testbench.TestBenchElement;
-import com.vaadin.testbench.annotations.RunLocally;
import com.vaadin.testbench.elements.NotificationElement;
-import com.vaadin.testbench.parallel.Browser;
import com.vaadin.tests.components.grid.basicfeatures.GridBasicClientFeaturesTest;
-@RunLocally(Browser.PHANTOMJS)
public class GridDetailsClientTest extends GridBasicClientFeaturesTest {
private static final String[] SET_GENERATOR = new String[] { "Component",
@@ -183,6 +182,19 @@ public class GridDetailsClientTest extends GridBasicClientFeaturesTest {
getGridElement().getDetails(1);
}
+
+ @Test
+ public void rowElementClassNames() {
+ toggleDetailsFor(0);
+ toggleDetailsFor(1);
+
+ List<WebElement> elements = getGridElement().findElements(
+ By.className("v-grid-spacer"));
+ assertEquals("v-grid-spacer", elements.get(0).getAttribute("class"));
+ assertEquals("v-grid-spacer stripe",
+ elements.get(1).getAttribute("class"));
+ }
+
@Test
public void scrollDownToRowWithDetails() {
toggleDetailsFor(100);
diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/element/CustomGridElement.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/element/CustomGridElement.java
index dc9b8722f2..e1934d4f2b 100644
--- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/element/CustomGridElement.java
+++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/element/CustomGridElement.java
@@ -15,6 +15,8 @@
*/
package com.vaadin.tests.components.grid.basicfeatures.element;
+import org.openqa.selenium.NoSuchElementException;
+
import com.vaadin.testbench.By;
import com.vaadin.testbench.TestBenchElement;
import com.vaadin.testbench.elements.GridElement;
@@ -28,9 +30,13 @@ public class CustomGridElement extends GridElement {
* @since
* @param rowIndex
* the index of the row for the details
- * @return the element that contains the details of a row
+ * @return the element that contains the details of a row. <code>null</code>
+ * if no widget is defined for the detials row
+ * @throws NoSuchElementException
+ * if the given details row is currently not open
*/
- public TestBenchElement getDetails(int rowIndex) {
+ public TestBenchElement getDetails(int rowIndex)
+ throws NoSuchElementException {
return getSubPart("#details[" + rowIndex + "]");
}