Skip to content

Grid Multi Row Layout

Svetoslav Krastev edited this page May 28, 2019 · 8 revisions

TOC

Revision history

Version User Date Notes
0.1 Stefan Ivanov Feb 21, 2019 Spec and design initial creation
0.2 Deyan Kamburov Apr 11, 2019 IgxColumnLayoutComponent info
0.3 Deyan Kamburov Apr 17, 2019 API changes

Overview

Multi-row layouts allow developers to create intricate cell arrangements within the same row by defining a template layout that is applied to header and body cells. This provides flexibility of the arrangement and makes it possible to create responsive web apps where grids would adapt and avoid horizontal scrolling when the available space becomes too narrow.

Objectives

User Stories

As an end user, I want to

  • have a clear understanding of where a record starts and ends even for complex, multi-row layouts of the grid data
  • have both the grid header cells and body cells reflect the provided layout and show other related functionality e.g. filtering icon to show an excel-style filter
  • expect that filtering (excel-style only, no row filter) and sorting work on the individual columns and their cells agnostic of the layout, in the same fashion that they would affect a grid without multi-row layout
  • expect that I can pin, resize, move and hide column groups/blocks e.g. see the group below representing Street, Floor, Apartment, City, State, Postal code
  • expect that I can group by a certain column irrelevant from the column group/block it is in e.g. group by State
  • expect that I can navigate through the grid cells using Arrow Up/Down/Left/Right and Tab/Shift+Tab.

User Interface

Expanded State Expanded state

Developer Stories

As a developer, I want to

  • have both vertical and horizontal virtualization work with the feature
  • be able to define custom layouts for my grid rows by the use of a layout grid and relative sizing (col-span and row-span). Cells can also be vertically stacked on multiple rows within the grid row in order to reduce the total grid width.

Layout grid shown for the headers where the top row headers take 3 relative column widths compared to the bottom row header, where each takes 2 relative column widths i.e. the relative column widths sum of all rows in a layout must be the same The layout grid for defining two columns' widths shown on their headers

  • be able to collapse only the header area of a multi-row layout by calling the API A collapsed header through the API for the multi-row layout

Feature integration scenarios

  • Keyboard Navigation - Navigation should works between groups/blocks and is not limited inside them.
  • Virtualization is fully supported (horizontally and vertically), but it is based on groups/blocks.
  • Excel-style Filtering - Excel-style filtering works on the individual columns and their cells agnostic of the layout.
  • Sorting - Sorting works on the individual columns and their cells agnostic of the layout.
  • Group By - Group by works on the individual column level according to the values in cells agnostic of the layout.
  • Column Pinning - Column pinning works with column groups/blocks rather than individual columns.
  • Column Resizing - Column resizing works with individual columns that result in resizing the whole group/block.
  • Column Moving - Column moving works with column groups/blocks rather than individual columns. When moving a column the user will see the individual column header instead of the group/block, because the grid doesn't know whether the operation is column moving or grouping.
  • Column Hiding - Column hiding works with column groups/blocks rather than individual columns.
  • Paging - Paging works on records, not visual rows.
  • Summaries -
  • Editing - both cell and row editing are supported
  • Selection -
  • Searching -
  • Multi column headers - Multi column headers are not supported. igx-column-group will be used either for MCH or for MRL, they cannot be combined.
  • Export to Excel - Export to excel is not supported.

Column group/block is a collection of columns defining a layout that may or may not span the full grid but have a start and end that match for all header row cells e.g. Street, Floor, Apartment, City, State, Postal code in the images above.

In order to enable Multi Row Layout within IgxGridComponent all of the columns should be defined within specific blocks - IgxColumnLayoutComponent. There should be no columns outside of those blocks and no usage of IgxColumnGroupComponent when using Multi Row Layout. It is not possible to mix IgxColumnLayoutComponent and IgxColumnGroupComponent at a time. Each block should define complete grid layout structure. The virtual directive will use those blocks as a base to determine the virtual chunks. Good practice will be splitting the columns into more blocks(IgxColumnLayoutComponent)

IgxColumnComponent will expose 4 @Input properties to determine the location and size of each cell:

  • colEnd - column index where the current field should end. The amount of columns between colStart and colEnd will determine the amount of spanning columns to that field
  • rowEnd - row index where the current field should end. The amount of rows between rowStart and rowEnd will determine the amount of spanning rows to that field
  • colStart - column index from which the field is starting
  • rowStart- row index from which the field is starting
<igx-column-layout>
	<igx-column [rowStart]="1" [colStart]="1" [colEnd]="5" field="ContactName"></igx-column>
	<igx-column [rowStart]="1" [colStart]="5" field="ContactTitle"></igx-column>
	<igx-column [rowStart]="1" [colStart]="6" field="Country"></igx-column>
	<igx-column [rowStart]="2" [colStart]="1" [colEnd]="3" field="Phone"></igx-column>
	<igx-column [rowStart]="2" [colStart]="3" [colEnd]="5" field="City"></igx-column>
	<igx-column [rowStart]="2" [colStart]="5" [colEnd]="7" field="Address"></igx-column>
	<igx-column [rowStart]="3" [colStart]="1" sortable="true" resizable="true"  field="CompanyName"></igx-column>
	<igx-column [rowStart]="3" [colStart]="2"  field="PostalCode"></igx-column>
	<igx-column [rowStart]="3" [colStart]="3" [colEnd]="7" field="Fax"></igx-column>
</igx-column-layout>

Column widths

When rendering multi-row layout block the amount of tracks rendered is determined based on the biggest 'colEnd' and the biggest 'rowEnd' specified in the template. The height of each track in the block's structure is determined by the grid's rowHeight. The width of each track in this structure is determined by a few criteria based on which columns have width specified and if it is in pixels or percentages, but the main logic is as it follows:

  • For each track the width is taken from the column that corresponds to it and spans the least. (If a columns spans on two or more tracks it has respectively two or more tracks that it corresponds to.)

  • If a column span on two or more tracks and no smaller one is found for these tracks, the width for each track it corresponds to is taken from the width of the column divided by its colSpan. (track width = col width / col span).

  • If a column with the same span is found compared to the one found for a track it is skipped and only the first match is saved.

Default width

By default when columns inside a column layout does not have width set, their tracks size automatically so all of them could fit the view area of the igxGrid. Auto sized columns have minimum width of "136px" per track. This means that if all tracks for all layouts cannot be fit into the view area they will have width "136px" and horizontal scrollbar will be rendered. That does not limit the tracks to increase their size when the grid width is increased and there is enough space for them to size and it will increase the size of the corresponding columns.

When deciding which columns to take in order to calculate the layout track sizes, since the width of columns is not set, only the 'colSpan' is taken into consideration. If a column that has width is found and the current taken column for the track does not have width, the one with the width is taken then despite the current one having smaller 'colSpan'.

For actually sizing the tracks in this scenario, the grid takes the flattened array of sizes for the tracks and takes those that don't have width set. Then from the grid visible area width is subtracted the combined width of all tracks that have width set. The remaining width is spread equally across all tracks that do not have width equally. The width set to those tracks then is set as default width to those columns that don't have it set by the user.

Example:

In this case the grid has a width set to "1300px", a single layout set for demonstration and no columns have width set. The tracks in this case are 6 and the visible area of the grid is "1278px". Here the width of the grid is enough so all would fit and that is why the width of each track is "213px".

In the picture bellow the grid width is not big enough to handle 6 * 136px columns and that is why there is scrollbar and the tracks have the minimum width of "136px".

Width in pixels

When columns have width set it pixels it takes the widths of those which col span the less. Same as described earlier on how widths are assigned. For those columns that have bigger span than some and are not taken into account their width is ignored.

  • If all columns have width set, the smallest available are taken.

  • If width of the grid is greater than the calculated grid widths there will be empty space.

  • When the columns have set width by the user the default minimum width of "80px" is not taken into account. This is not to be mistaken with the minimum width of "136px" since it is only applicable for auto sized columns.

  • If a suitable column is found for a track that spans for example 2 column but the next track is already assigned to a more suitable column, the current track receives the width of the column divided by 2.

    Example:

    In the picture above the columns in green have been used in setting each track width. For those that span more tracks they are divided respectively based on how much they span and which tracks they can populate. The grayed out columns represent those columns that their width is ignored.

  • If the column with the smallest span available for a track does not have it's width set by a user and there is a column with bigger span but with width set, the column with the widths set will be taken into consideration instead. Since that bigger column would span on more than one tracks, the width for that track will equal to the "width of the column / column span".

    Example:

    In this case the "Contact Name" column does not have width set and the track with index 3 takes width from the "Company Name" column. Since the "Company Name" column span on 3 tracks the width for a single track would be "300px" and this is how much will be assigned.

  • If the column with the smallest span available for a track does not have width and there are not other columns that have with, no matter their span, that specific track that is left 'undefined' will auto size to the remaining width of the grid.

    Example:

    In this case since there's nowhere we can get the width for the 3rd track, it is auto sized based on the remaining width of the view area.

Width in percentages

When the columns have widths in percentages the widths for each track should be taken in a similar manner compared to when the widths are set in pixels. The difference would be that this time the each for this track will be based on the width of the grid and when the grid is resized the tracks/columns should also resize.

  • Hiding should work like the default column hiding but only on column layout level.
  • Hiding should be possible through the grid toolbar when enabled, again only by hiding column layouts.
  • If a column is hidden the whole column layout it is part of should hide accordingly.
  • Pinning should work like the regular pinning but only a column layout level.
  • Pinning should be possible through the grid toolbar when enabled, again only by hiding column layouts.
  • If a single column is pinned the whole column layout it is part of should pin accordingly.
  • If a maximum pinned area will be exceeded if column layout it pinned, that column layout should not be possible to be pinned.
  • If column layout is not complete for a pinned column layout, there should be not visible cells bellow the pinned cells.

Resizing column with span 1

  • Since for grid column template sizing the first column with the least span is taken, if this column is used for it only that column will be resized.
  • Any other columns ,no matter their span, that use the same column for their size will be resized as well.
  • The whole column layout will resize also the same amount of pixels the column is resized.
  • If a column reaches its min/max width when resizing, it should not be allowed to exceed these limits.
  • By default the min width of a column remains '80px' if it has span 1.
  • When resizing a column that is part of a pinned column layout, the max pinned width should not be able to be exceeded.

Resizing column with span bigger than 1

  • Resizing a column that has more than 1 span should resize it to where the resizing indicator ends.
  • Since widths are determined by the smallest columns available for the grid column template, if the width of the resized column is determined by smaller columns, the widths of these smaller columns should resize equally. Ex:
  • If the width of the resized column is determined by smaller columns and at the start/end the smaller column is half intersecting it, that column should also resize.
  • When a columns has a span 2 for example, its default min width is 2 * '80px'. Similarly for span 3 it is 3 * '80px'.
  • When resizing a column that is part of a pinned column layout, the max pinned width should not be able to be exceeded.

Tab Navigation

  • TAB and SHIFT + TAB move to the next/previous cell from left to right row after row unaffected by the column groups/blocks that are defined.
  • TAB and SHIFT + TAB should move through cells once for each row, meaning that it should skip cell if doesn't have the same rowStart as the currently selected cell.

Horizontal Navigation

  • LEFT and RIGHT move to the adjacent cell on the left/right within the current row unaffected by the column groups/blocks that are defined.
  • If the current cell spans on more than one row, LEFT and RIGHT should navigate to the first cell on the left and right with the same rowStart.
  • The horizontal navigation does not wrap around to the next/previous row by default when reaching end/start and continuing to navigate right/left.

  • CTRL + LEFT and HOME should navigate to the start of the row and select the cell with the same rowStart as the currently selected cell.
  • CTRL + RIGHT and END should navigate to the end of the row and select the cell with the same rowStart as the currently selected cell.

Vertical Navigation

  • UP and DOWN move to the cell above/below in relation to a starting position (see the green dot below) inside the active cell before these two keys have been pressed and is unaffected by the rows.
  • If the current cell spans on more than one column, UP and DOWN should move to the first cell bellow or above.
  • The vertical navigation does not wrap around to the top/bottom of the grid if reaching the bottom/top and continuing to navigate down/up.

  • CTRL + UP should navigate to the first row and select the cell with the same colStart and rowStart as the currently selected cell.
  • CTRL + DOWN should navigate to the last row and select the cell with the same colStart and rowStart as the currently selected cell.
  • CTRL + HOME should navigate to the first row and first cell.
  • CTRL + END should navigate to the last row and last cell.

ARIA support

Assumptions and Limitations

  • Multi column headers are not supported
  • Hierarchical Grid is not supported
  • Tree Grid - is not supported
  • Filtering (quick filtering) is not supported
  • Export to Excel is not supported

Scenarios not covered

Test Scenarios

Automation

Basic

  • Should allow defining a column group that contains a complete MRL layout.
  • Should be no misalignment between header and content cells.
  • Should allow defining multiple column groups, each one containing a complete MRL layout.
  • Should not throw errors if layout inside column group is incomplete.
  • In case columns do not have widths set should calculate default column widths based on the grid total column spans in all available column groups and apply correct value for the related column groups.
  • The columns with the smallest col spans should take precedent when determining the column group’s widths.In case parent has width that does not match the related children then parent width will be disregarded.
  • Should allow defining a MRL layout where columns have width in px.
  • Should allow defining a MRL layout where columns have width in %.
  • Should allow navigating between cells via Tab/Shift+Tab based on the column’s order in the columns collection.

Virtualization

  • Virtualization should work on the column groups (not on individual columns).
  • Should virtualize content correctly when there are multiple complex column groups with/without widths defined.

Pinning

  • Should allow pinning/unpinning a whole group.
  • Should pin/unpin whole group if a single child column is pinned/unpinned.
  • Should not allow pinning if group width exceeds max allowed.
  • Should emit onColumnPinning event with correct parameters.
  • Should work with horizontal virtualization on the unpinned groups.
  • Pinned columns count and drop-down items text in Pinning Toolbar should be correct when group is pinned.
  • Toggling column checkbox in Pinning Toolbar checked state successfully changes the column's pinned state.

Hiding

  • Should allow setting a whole group as hidden/shown.
  • Should hide/show whole group if a single child column is hidden/shown.
  • Should work with horizontal virtualization when some groups are hidden/shown.
  • Hidden columns count and drop-down items text in hiding toolbar should be correct when group is hidden/shown.
  • Toggling column checkbox in Hiding Toolbar checked state successfully changes the column's hiding state.
  • Should emit onColumnVisibilityChanged event on toggling checkboxes in Hiding Toolbar.

Resizing

  • Should correctly resize column on upper level with 3 spans and the two cols below it with span 1 that have width.
  • Should correctly resize column with span 2 and the ones below it that have span 1 with width set.
  • Should correctly resize column that spans 1 column that is used to size the column templates.
  • Should correctly resize column with span 1 and bigger columns that start with same colStart with bigger span.
  • Should correctly resize column while there is another column that does not have width set.
  • Should correctly resize column that does not have width set, but is intersected by a column with width set.

Keyboard Navigation

  • Should navigate through a single layout with right and left arrow keys.
  • Should navigate between column layouts with right arrow key.
  • Should navigate between column layouts with right left key.
  • Should navigate down and up to a cell from the same column layout from a cell with bigger col span.
  • Should navigate down and up to a cell from the same column layout to a cell with bigger col span.
  • Should navigate down and up to a cell from the same column layout according to its starting location.
  • Should allow navigating down to a cell from the next row.
  • Should allow navigating down to a cell from the next row with hidden column layout.
  • Should allow navigating down with scrolling.
  • Should retain the focus when the first cell is reached.
  • Should navigate up correctly.
  • Should navigate to right and left with hidden columns.
  • Should navigate to the last cell from the layout by pressing Home/End or Ctrl + ArrowLeft/ArrowRight key.

Manual

  • Complex column groups with/without widths defined should be tested for correct rendering in IE.
  • Grid is rendered with display density "compact"
  • Grid is rendered with display density "cosy"
  • Grid is rendered with display density "comfort"
  • Changing display density at run-time works
Clone this wiki locally