Implement dynamic header/footer height

- add Grid#setAutoHeight and Grid#isAutoHeight
- render "autoHeight" Grid property in GridLCA
- use TSD to calculate header/footer height
- respect column headerWordWrap and grid autoHeight properties
- respect footer spanning

Limitations:
- when resizing a column TSD requests are sent for every new wrap width
- there is some flickering when resizing a column if estimated height is
different than the measured

Change-Id: I195c475ae3a1369be3c1f5032850c9a9d20f83cf
Signed-off-by: Ivan Furnadjiev <ivan@eclipsesource.com>
diff --git a/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/Grid.java b/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/Grid.java
index aeab3e4..3b2626f 100644
--- a/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/Grid.java
+++ b/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/Grid.java
@@ -79,6 +79,7 @@
   private boolean columnHeadersVisible;
   private boolean columnFootersVisible;
   private boolean linesVisible = true;
+  private boolean autoHeight;
   private int currentVisibleItems;
   private int selectionType = SWT.SINGLE;
   private boolean selectionEnabled = true;
@@ -2203,6 +2204,36 @@
     }
   }
 
+  /**
+   * Sets the value of the auto-height feature. When enabled, this feature resizes the height of
+   * rows to reflect the content of cells with word-wrapping enabled. Cell word-wrapping is enabled
+   * via the GridColumn.setWordWrap(boolean) method. If column headers have word-wrapping enabled,
+   * this feature will also resize the height of the column headers as necessary.
+   *
+   * @param autoHeight Set to true to enable this feature, false (default) otherwise.
+   */
+  public void setAutoHeight( boolean autoHeight ) {
+    checkWidget();
+    if( this.autoHeight != autoHeight ) {
+      this.autoHeight = autoHeight;
+      layoutCache.invalidateHeaderHeight();
+      layoutCache.invalidateFooterHeight();
+      scheduleRedraw();
+    }
+  }
+
+  /**
+   * Returns the value of the auto-height feature, which resizes row heights and column header
+   * heights based on word-wrapped content.
+   *
+   * @return Returns whether or not the auto-height feature is enabled.
+   * @see #setAutoHeight(boolean)
+   */
+  public boolean isAutoHeight() {
+    checkWidget();
+    return autoHeight;
+  }
+
   @Override
   @SuppressWarnings("unchecked")
   public <T> T getAdapter( Class<T> adapter ) {
@@ -2701,19 +2732,21 @@
       int columnHeaderHeight = 0;
       for( int i = 0; i < getColumnCount(); i++ ) {
         GridColumn column = columns.get( i );
-        Font headerFont = column.getHeaderFont();
-        String headerText = column.getText();
-        Image headerImage = column.getImage();
-        int computedHeight = computeColumnHeight( headerFont, headerText, headerImage, 0 );
+        Font font = column.getHeaderFont();
+        String text = column.getText();
+        Image image = column.getImage();
+        int wrapWidth = autoHeight && column.getHeaderWordWrap() ? column.getHeaderWrapWidth() : 0;
+        int computedHeight = computeColumnHeight( font, text, image, 0, wrapWidth );
         columnHeaderHeight = Math.max( columnHeaderHeight, computedHeight );
       }
       for( int i = 0; i < getColumnGroupCount(); i++ ) {
         GridColumnGroup group = columnGroups.get( i );
-        Font groupFont = group.getHeaderFont();
-        String groupText = group.getText();
-        Image groupImage = group.getImage();
+        Font font = group.getHeaderFont();
+        String text = group.getText();
+        Image image = group.getImage();
         int chevronHeight = group.getChevronHeight();
-        int computedHeight = computeColumnHeight( groupFont, groupText, groupImage, chevronHeight );
+        int wrapWidth = autoHeight && group.getHeaderWordWrap() ? group.getHeaderWrapWidth() : 0;
+        int computedHeight = computeColumnHeight( font, text, image, chevronHeight, wrapWidth );
         groupHeaderHeight = Math.max( groupHeaderHeight, computedHeight );
       }
       result = columnHeaderHeight + groupHeaderHeight;
@@ -2727,10 +2760,11 @@
       int columnFooterHeight = 0;
       for( int i = 0; i < getColumnCount(); i++ ) {
         GridColumn column = columns.get( i );
-        Font footerFont = column.getFooterFont();
-        String footerText = column.getFooterText();
-        Image footerImage = column.getFooterImage();
-        int computedHeight = computeColumnHeight( footerFont, footerText, footerImage, 0 );
+        Font font = column.getFooterFont();
+        String text = column.getFooterText();
+        Image image = column.getFooterImage();
+        int wrapWidth = autoHeight && column.getHeaderWordWrap() ? column.getFooterWrapWidth() : 0;
+        int computedHeight = computeColumnHeight( font, text, image, 0, wrapWidth );
         columnFooterHeight= Math.max( columnFooterHeight, computedHeight );
       }
       result = columnFooterHeight;
@@ -2738,11 +2772,16 @@
     return result;
   }
 
-  private int computeColumnHeight( Font font, String text, Image image, int minHeight ) {
+  private int computeColumnHeight( Font font,
+                                   String text,
+                                   Image image,
+                                   int minHeight,
+                                   int wrapWidth )
+  {
     int result = minHeight;
     int textHeight = 0;
-    if( text.contains( "\n" ) ) {
-      textHeight = TextSizeUtil.textExtent( font, text, 0 ).y;
+    if( text.contains( "\n" ) || wrapWidth > 0 ) {
+      textHeight = TextSizeUtil.textExtent( font, text, wrapWidth ).y;
     } else {
       textHeight = TextSizeUtil.getCharHeight( font );
     }
diff --git a/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/GridColumn.java b/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/GridColumn.java
index 865777b..e571dc1 100644
--- a/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/GridColumn.java
+++ b/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/GridColumn.java
@@ -68,6 +68,7 @@
   private String footerText = "";
   private Image footerImage;
   private Font footerFont;
+  private int footerSpan = 1;
   private boolean packed;
   private boolean wordWrap;
   private boolean headerWordWrap;
@@ -811,7 +812,10 @@
    */
   public void setWordWrap( boolean wordWrap ) {
     checkWidget();
-    this.wordWrap = wordWrap;
+    if( this.wordWrap != wordWrap ) {
+      this.wordWrap = wordWrap;
+      parent.scheduleRedraw();
+    }
   }
 
   /**
@@ -911,9 +915,12 @@
    */
   public void setHeaderWordWrap( boolean wordWrap ) {
     checkWidget();
-    this.headerWordWrap = wordWrap;
-    parent.layoutCache.invalidateHeaderHeight();
-    parent.scheduleRedraw();
+    if( headerWordWrap != wordWrap ) {
+      headerWordWrap = wordWrap;
+      parent.layoutCache.invalidateHeaderHeight();
+      parent.layoutCache.invalidateFooterHeight();
+      parent.scheduleRedraw();
+    }
   }
 
   /**
@@ -1089,17 +1096,22 @@
 
   @Override
   public void setData( String key, Object value ) {
-    checkFooterSpan( key, value );
+    handleFooterSpan( key, value );
     if( !RWT.TOOLTIP_MARKUP_ENABLED.equals( key ) || !isToolTipMarkupEnabledFor( this ) ) {
       super.setData( key, value );
     }
   }
 
-  private static void checkFooterSpan( String key, Object value ) {
+  private void handleFooterSpan( String key, Object value ) {
     if( FOOTER_SPAN.equals( key ) ) {
       if( !( value instanceof Integer ) || ( ( Integer )value ).intValue() < 1 ) {
         SWT.error( SWT.ERROR_INVALID_ARGUMENT );
       }
+      footerSpan = ( ( Integer )value ).intValue();
+      if( getHeaderWordWrap() ) {
+        parent.layoutCache.invalidateFooterHeight();
+        parent.scheduleRedraw();
+      }
     }
   }
 
@@ -1111,8 +1123,8 @@
 
   int getLeft() {
     int result = 0;
-    int[] columnOrder = parent.getColumnOrder();
     boolean found = false;
+    int[] columnOrder = parent.getColumnOrder();
     for( int i = 0; i < columnOrder.length && !found; i++ ) {
       GridColumn currentColumn = parent.getColumn( columnOrder[ i ] );
       if( currentColumn == this ) {
@@ -1124,6 +1136,47 @@
     return result;
   }
 
+  int getHeaderWrapWidth() {
+    int result = width - parent.getHeaderPadding().width;
+    Image headerImage = getImage();
+    if( headerImage != null ) {
+      result -= headerImage.getBounds().width;
+      result -= MARGIN_IMAGE;
+    }
+    if( sortStyle != SWT.NONE ) {
+      result -= SORT_INDICATOR_WIDTH;
+      result -= MARGIN_IMAGE;
+    }
+    return result;
+  }
+
+  int getFooterWrapWidth() {
+    int result = getFooterWidth() - parent.getHeaderPadding().width;
+    if( footerImage != null ) {
+      result -= footerImage.getBounds().width;
+      result -= MARGIN_IMAGE;
+    }
+    return result;
+  }
+
+  private int getFooterWidth() {
+    int result = width;
+    if( footerSpan != 1 ) {
+      boolean found = false;
+      int nextColumns = 0;
+      int[] columnOrder = parent.getColumnOrder();
+      for( int i = 0; i < columnOrder.length; i++ ) {
+        GridColumn currentColumn = parent.getColumn( columnOrder[ i ] );
+        if( currentColumn == this ) {
+          found = true;
+        } else if( found && currentColumn.isVisible() && footerSpan > ++nextColumns ) {
+          result += currentColumn.getWidth();
+        }
+      }
+    }
+    return result;
+  }
+
   private int getPreferredWidth() {
     int headerWidth = 0;
     String headerText = getText();
@@ -1163,8 +1216,11 @@
       this.width = newWidth;
       packed = false;
       processControlEvents();
-      parent.invalidateScrollBars();
-      parent.redraw();
+      if( parent.isAutoHeight() && getHeaderWordWrap() ) {
+        parent.layoutCache.invalidateHeaderHeight();
+        parent.layoutCache.invalidateFooterHeight();
+      }
+      parent.scheduleRedraw();
     }
   }
 
diff --git a/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/GridColumnGroup.java b/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/GridColumnGroup.java
index 43f29d4..eefc91b 100644
--- a/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/GridColumnGroup.java
+++ b/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/GridColumnGroup.java
@@ -42,6 +42,8 @@
 public class GridColumnGroup extends Item {
 
   private static final int CHEVRON_HEIGHT = 16;
+  private static final int CHEVRON_WIDTH = 12;
+  private static final int MARGIN_IMAGE = 3;
 
   private Grid parent;
   private List<GridColumn> columns = new ArrayList<GridColumn>();
@@ -313,4 +315,26 @@
   int getChevronHeight() {
     return ( getStyle() & SWT.TOGGLE ) != 0 ? CHEVRON_HEIGHT : 0;
   }
+
+  int getHeaderWrapWidth() {
+    int result = getGroupWidth() - parent.getHeaderPadding().width;
+    Image headerImage = getImage();
+    if( headerImage != null ) {
+      result -= headerImage.getBounds().width;
+      result -= MARGIN_IMAGE;
+    }
+    result -= ( getStyle() & SWT.TOGGLE ) != 0 ? CHEVRON_WIDTH : 0;
+    return result;
+  }
+
+  private int getGroupWidth() {
+    int width = 0;
+    for( GridColumn column : columns ) {
+      if( column.isVisible() ) {
+        width += column.getWidth();
+      }
+    }
+    return width;
+  }
+
 }
diff --git a/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/internal/gridkit/GridLCA.java b/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/internal/gridkit/GridLCA.java
index 01466b2..74d0811 100644
--- a/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/internal/gridkit/GridLCA.java
+++ b/bundles/org.eclipse.rap.nebula.widgets.grid/src/org/eclipse/nebula/widgets/grid/internal/gridkit/GridLCA.java
@@ -70,6 +70,7 @@
   private static final String PROP_FOCUS_ITEM = "focusItem";
   private static final String PROP_SCROLL_LEFT = "scrollLeft";
   private static final String PROP_SELECTION = "selection";
+  private static final String PROP_AUTO_HEIGHT = "autoHeight";
   // TODO: [if] Sync sortDirection and sortColumn in GridColumnLCA when multiple sort columns are
   // possible on the client
   private static final String PROP_SORT_DIRECTION = "sortDirection";
@@ -129,6 +130,7 @@
     preserveProperty( grid, PROP_FOCUS_ITEM, grid.getFocusItem() );
     preserveProperty( grid, PROP_SCROLL_LEFT, getScrollLeft( grid ) );
     preserveProperty( grid, PROP_SELECTION, getSelection( grid ) );
+    preserveProperty( grid, PROP_AUTO_HEIGHT, grid.isAutoHeight() );
     preserveProperty( grid, PROP_SORT_DIRECTION, getSortDirection( grid ) );
     preserveProperty( grid, PROP_SORT_COLUMN, getSortColumn( grid ) );
     preserveListener( grid, PROP_SELECTION_LISTENER, isListening( grid, SWT.Selection ) );
@@ -163,6 +165,7 @@
     renderProperty( grid, PROP_FOCUS_ITEM, grid.getFocusItem(), null );
     renderProperty( grid, PROP_SCROLL_LEFT, getScrollLeft( grid ), ZERO );
     renderProperty( grid, PROP_SELECTION, getSelection( grid ), DEFAULT_SELECTION );
+    renderProperty( grid, PROP_AUTO_HEIGHT, grid.isAutoHeight(), false );
     renderProperty( grid, PROP_SORT_DIRECTION, getSortDirection( grid ), DEFAULT_SORT_DIRECTION );
     renderProperty( grid, PROP_SORT_COLUMN, getSortColumn( grid ), null );
     renderListener( grid, PROP_SELECTION_LISTENER, isListening( grid, SWT.Selection ), false );
diff --git a/examples/org.eclipse.rap.nebula.widgets.grid.demo.standalone/src/org/eclipse/rap/nebula/widgets/grid/snippets/GridSnippet.java b/examples/org.eclipse.rap.nebula.widgets.grid.demo.standalone/src/org/eclipse/rap/nebula/widgets/grid/snippets/GridSnippet.java
index 1451773..9ec66e0 100644
--- a/examples/org.eclipse.rap.nebula.widgets.grid.demo.standalone/src/org/eclipse/rap/nebula/widgets/grid/snippets/GridSnippet.java
+++ b/examples/org.eclipse.rap.nebula.widgets.grid.demo.standalone/src/org/eclipse/rap/nebula/widgets/grid/snippets/GridSnippet.java
@@ -59,6 +59,7 @@
     createSetFooterSpanGroup( parent );
     createShowHeaderButton( parent );
     createShowFooterButton( parent );
+    createAutoHeightButton( parent );
     createWordWrapButton( parent );
     createHeaderWordWrapButton( parent );
     createQueryFocusItem( parent );
@@ -384,6 +385,18 @@
     } );
   }
 
+  private void createAutoHeightButton( Composite parent ) {
+    final Button button = new Button( parent, SWT.CHECK );
+    button.setText( "Item/Header/Footer auto height" );
+    button.setSelection( false );
+    button.addSelectionListener( new SelectionAdapter() {
+      @Override
+      public void widgetSelected( SelectionEvent event ) {
+        grid.setAutoHeight( button.getSelection() );
+      }
+    } );
+  }
+
   private void createWordWrapButton( Composite parent ) {
     final Button button = new Button( parent, SWT.CHECK );
     button.setText( "Word wrap cells text" );
diff --git a/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/GridColumn_Test.java b/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/GridColumn_Test.java
index e514d27..2c99e21 100644
--- a/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/GridColumn_Test.java
+++ b/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/GridColumn_Test.java
@@ -771,12 +771,13 @@
   }
 
   @Test
-  public void testGetHeaderWordWrap_invalidatesCachedHeaderHeight() {
+  public void testGetHeaderWordWrap_invalidatesCachedHeights() {
     grid.getHeaderHeight();
 
     column.setHeaderWordWrap( true );
 
     assertFalse( grid.layoutCache.hasHeaderHeight() );
+    assertFalse( grid.layoutCache.hasFooterHeight() );
   }
 
   @Test
diff --git a/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/Grid_Test.java b/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/Grid_Test.java
index 5c94f7d..770381c 100644
--- a/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/Grid_Test.java
+++ b/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/Grid_Test.java
@@ -1899,6 +1899,28 @@
   }
 
   @Test
+  public void testGetHeaderHeight_withWordWrap_withAutoHeight() {
+    grid.setHeaderVisible( true );
+    grid.setAutoHeight( true );
+    GridColumn[] columns = createGridColumns( grid, 3, SWT.NONE );
+    columns[ 1 ].setHeaderWordWrap( true );
+    columns[ 1 ].setText( "foo bar" );
+
+    assertEquals( 52, grid.getHeaderHeight() );
+  }
+
+  @Test
+  public void testGetHeaderHeight_withWordWrap_withoutAutoHeight() {
+    grid.setHeaderVisible( true );
+    grid.setAutoHeight( false );
+    GridColumn[] columns = createGridColumns( grid, 3, SWT.NONE );
+    columns[ 1 ].setHeaderWordWrap( true );
+    columns[ 1 ].setText( "foo bar" );
+
+    assertEquals( 31, grid.getHeaderHeight() );
+  }
+
+  @Test
   public void testGetFooterHeight_Initial() {
     createGridColumns( grid, 3, SWT.NONE );
 
@@ -1983,6 +2005,60 @@
   }
 
   @Test
+  public void testGetFooterHeight_wrapsText_withAutoHeight() {
+    grid.setFooterVisible( true );
+    grid.setAutoHeight( true );
+    GridColumn column = new GridColumn( grid, SWT.NONE );
+    column.setWidth( 40 );
+    column.setHeaderWordWrap( true );
+    column.setFooterText( "foo bar" );
+
+    assertEquals( 52, grid.getFooterHeight() );
+  }
+
+  @Test
+  public void testGetFooterHeight_doesNotWrapText_withoutAutoHeight() {
+    grid.setFooterVisible( true );
+    grid.setAutoHeight( false );
+    GridColumn column = new GridColumn( grid, SWT.NONE );
+    column.setWidth( 40 );
+    column.setHeaderWordWrap( true );
+    column.setFooterText( "foo bar" );
+
+    assertEquals( 31, grid.getFooterHeight() );
+  }
+
+  @Test
+  public void testGetFooterHeight_wrapsText_withFooterSpan() {
+    grid.setFooterVisible( true );
+    grid.setAutoHeight( true );
+    GridColumn column1 = new GridColumn( grid, SWT.NONE );
+    column1.setWidth( 20 );
+    GridColumn column2 = new GridColumn( grid, SWT.NONE );
+    column2.setWidth( 20 );
+    column1.setHeaderWordWrap( true );
+    column1.setFooterText( "foo bar" );
+    column1.setData( "footerSpan", Integer.valueOf( 2 ) );
+
+    assertEquals( 52, grid.getFooterHeight() );
+  }
+
+  @Test
+  public void testGetFooterHeight_doesNotWrapText_withFooterSpanAndEnoughSpace() {
+    grid.setFooterVisible( true );
+    grid.setAutoHeight( true );
+    GridColumn column1 = new GridColumn( grid, SWT.NONE );
+    column1.setWidth( 20 );
+    GridColumn column2 = new GridColumn( grid, SWT.NONE );
+    column2.setWidth( 100 );
+    column1.setHeaderWordWrap( true );
+    column1.setFooterText( "foofoo bar" );
+    column1.setData( "footerSpan", Integer.valueOf( 2 ) );
+
+    assertEquals( 35, grid.getFooterHeight() );
+  }
+
+  @Test
   public void testGetGroupHeaderHeight_Initial() {
     createGridColumns( grid, 1, SWT.NONE );
     GridColumnGroup group = new GridColumnGroup( grid, SWT.NONE );
@@ -2005,6 +2081,36 @@
   }
 
   @Test
+  public void testGetGroupHeaderHeight_wrapsText_withAutoHeight() {
+    grid.setHeaderVisible( true );
+    grid.setAutoHeight( true );
+    GridColumnGroup group = new GridColumnGroup( grid, SWT.NONE );
+    GridColumn column1 = new GridColumn( group, SWT.NONE );
+    column1.setWidth( 20 );
+    GridColumn column2 = new GridColumn( group, SWT.NONE );
+    column2.setWidth( 20 );
+    group.setHeaderWordWrap( true );
+    group.setText( "foo bar" );
+
+    assertEquals( 52, grid.getGroupHeaderHeight() );
+  }
+
+  @Test
+  public void testGetGroupHeaderHeight_doesNotWrapText_withoutAutoHeight() {
+    grid.setHeaderVisible( true );
+    grid.setAutoHeight( false );
+    GridColumnGroup group = new GridColumnGroup( grid, SWT.NONE );
+    GridColumn column1 = new GridColumn( group, SWT.NONE );
+    column1.setWidth( 20 );
+    GridColumn column2 = new GridColumn( group, SWT.NONE );
+    column2.setWidth( 20 );
+    group.setHeaderWordWrap( true );
+    group.setText( "foo bar" );
+
+    assertEquals( 31, grid.getGroupHeaderHeight() );
+  }
+
+  @Test
   public void testComputeSize() {
     grid = new Grid( shell, SWT.NONE );
     createGridColumns( grid, 3, SWT.NONE );
@@ -2815,6 +2921,29 @@
     assertEquals( 4, countResolvedGridItems() );
   }
 
+  @Test
+  public void testIsAutoHeght_Initial() {
+    assertFalse( grid.isAutoHeight() );
+  }
+
+  @Test
+  public void testSetAutoHeght() {
+    grid.setAutoHeight( true );
+
+    assertTrue( grid.isAutoHeight() );
+  }
+
+  @Test
+  public void testSetAutoHeght_invalidatesHeaderFooterHeight() {
+    grid.getHeaderHeight();
+    grid.getFooterHeight();
+
+    grid.setAutoHeight( true );
+
+    assertFalse( grid.layoutCache.hasHeaderHeight() );
+    assertFalse( grid.layoutCache.hasFooterHeight() );
+  }
+
   //////////////////
   // Helping methods
 
diff --git a/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/internal/gridkit/GridLCA_Test.java b/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/internal/gridkit/GridLCA_Test.java
index d84ac5a..49121cc 100644
--- a/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/internal/gridkit/GridLCA_Test.java
+++ b/tests/org.eclipse.rap.nebula.widgets.grid.test/src/org/eclipse/nebula/widgets/grid/internal/gridkit/GridLCA_Test.java
@@ -535,6 +535,38 @@
   }
 
   @Test
+  public void testRenderInitialAutoHeight() throws IOException {
+    lca.render( grid );
+
+    TestMessage message = Fixture.getProtocolMessage();
+    CreateOperation operation = message.findCreateOperation( grid );
+    assertTrue( operation.getProperties().names().indexOf( "autoHeight" ) == -1 );
+  }
+
+  @Test
+  public void testRenderAutoHeight() throws IOException {
+    Fixture.markInitialized( grid );
+    grid.setAutoHeight( true );
+    lca.renderChanges( grid );
+
+    TestMessage message = Fixture.getProtocolMessage();
+    assertEquals( JsonValue.TRUE, message.findSetProperty( grid, "autoHeight" ) );
+  }
+
+  @Test
+  public void testRenderAutoHeightUnchanged() throws IOException {
+    Fixture.markInitialized( display );
+    Fixture.markInitialized( grid );
+
+    grid.setAutoHeight( true );
+    Fixture.preserveWidgets();
+    lca.renderChanges( grid );
+
+    TestMessage message = Fixture.getProtocolMessage();
+    assertNull( message.findSetOperation( grid, "autoHeight" ) );
+  }
+
+  @Test
   public void testRenderInitialTopItemIndex() throws IOException {
     grid.setSize( 100, 100 );