Skip to content

Commit

Permalink
feat: Flow integration for Grid's cell-focus event (#822)
Browse files Browse the repository at this point in the history
Fixes: vaadin/vaadin-grid#1536

Extended grid connector to handle the client side cell-focus event, integrated the grid event context and added a CellFocusEvent class and respective listener to Grid.

Co-authored-by: Sascha Ißbrücker <[email protected]>
Co-authored-by: Tulio Garcia <[email protected]>
  • Loading branch information
3 people authored Apr 23, 2021
1 parent 0bb8758 commit 0deb0b6
Show file tree
Hide file tree
Showing 5 changed files with 427 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright 2000-2021 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.flow.component.grid.it;

import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.grid.Grid.Column;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.router.Route;

/**
* Page created for testing purposes. Not suitable for demos.
*
* @author Vaadin Ltd.
*/
@Route("vaadin-grid/grid-cell-focus-page")
public class GridCellFocusPage extends Div {

public static final String ID_GRID = "cell-focus-grid";
public static final String ID_ITEM_RESULT = "cell-focus-item-result";
public static final String ID_COLUMN_RESULT = "cell-focus-column-result";
public static final String ID_SECTION_RESULT = "cell-focus-section-result";

public static final String KEY_FIRST_COLUMN = "first-column";
public static final String KEY_SECOND_COLUMN = "second-column";
public static final String KEY_THIRD_COLUMN = "third-column";

public static final String NO_ITEM = "-- no item --";
public static final String NO_COLUMN = "-- no column --";
public static final String NO_SECTION = "-- no section --";

public GridCellFocusPage() {
setSizeFull();

Grid<String> grid = new Grid<>();
grid.setId(ID_GRID);

grid.addColumn(s -> s + "1")
.setKey(KEY_FIRST_COLUMN)
.setHeader("First column header")
.setFooter("First column footer");
grid.addColumn(s -> s + "2")
.setKey(KEY_SECOND_COLUMN)
.setHeader("Second column header")
.setFooter("Second column footer");
grid.addColumn(s -> s + "3")
.setKey(KEY_THIRD_COLUMN)
.setHeader("Third column header")
.setFooter("Third column footer");

grid.setItems("A", "B", "C", "D");

Span itemResult = new Span();
itemResult.setId(ID_ITEM_RESULT);

Span colResult = new Span();
colResult.setId(ID_COLUMN_RESULT);

Span sectionResult = new Span();
sectionResult.setId(ID_SECTION_RESULT);

add(itemResult, colResult, sectionResult, grid);

grid.addCellFocusListener(event -> {
String item = event
.getItem()
.orElse(NO_ITEM);

String column = event
.getColumn()
.map(Column::getKey)
.orElse(NO_COLUMN);

itemResult.setText(item);
colResult.setText(column);
sectionResult.setText(event.getSection().getClientSideName());
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2000-2021 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.flow.component.grid.it;

import org.junit.Assert;
import org.junit.Test;

import com.vaadin.flow.component.grid.CellFocusEvent;
import com.vaadin.flow.component.grid.testbench.GridElement;
import com.vaadin.flow.testutil.TestPath;
import com.vaadin.testbench.TestBenchElement;
import com.vaadin.tests.AbstractComponentIT;

/**
* IT for grid's Flow based cell focus event.
*
* @author Vaadin Ltd
*/
@TestPath("vaadin-grid/grid-cell-focus-page")
public class GridCellFocusPageIT extends AbstractComponentIT {

@Test
public void focusBodyCell() {
open();

getGrid().getCell(0, 0).focus();
assertTextResult(GridCellFocusPage.ID_ITEM_RESULT, "A");
assertTextResult(GridCellFocusPage.ID_COLUMN_RESULT,
GridCellFocusPage.KEY_FIRST_COLUMN);
assertTextResult(GridCellFocusPage.ID_SECTION_RESULT,
CellFocusEvent.GridSection.BODY.getClientSideName());

getGrid().getCell(1, 0).focus();
assertTextResult(GridCellFocusPage.ID_ITEM_RESULT, "B");
assertTextResult(GridCellFocusPage.ID_COLUMN_RESULT,
GridCellFocusPage.KEY_FIRST_COLUMN);
assertTextResult(GridCellFocusPage.ID_SECTION_RESULT,
CellFocusEvent.GridSection.BODY.getClientSideName());

getGrid().getCell(2, 1).focus();
assertTextResult(GridCellFocusPage.ID_ITEM_RESULT, "C");
assertTextResult(GridCellFocusPage.ID_COLUMN_RESULT,
GridCellFocusPage.KEY_SECOND_COLUMN);
assertTextResult(GridCellFocusPage.ID_SECTION_RESULT,
CellFocusEvent.GridSection.BODY.getClientSideName());
}

@Test
public void focusHeaderCell() {
open();

getGrid().getHeaderCell(0).focus();
assertTextResult(GridCellFocusPage.ID_ITEM_RESULT,
GridCellFocusPage.NO_ITEM);
assertTextResult(GridCellFocusPage.ID_COLUMN_RESULT,
GridCellFocusPage.KEY_FIRST_COLUMN);
assertTextResult(GridCellFocusPage.ID_SECTION_RESULT,
CellFocusEvent.GridSection.HEADER.getClientSideName());

getGrid().getHeaderCell(1).focus();
assertTextResult(GridCellFocusPage.ID_ITEM_RESULT,
GridCellFocusPage.NO_ITEM);
assertTextResult(GridCellFocusPage.ID_COLUMN_RESULT,
GridCellFocusPage.KEY_SECOND_COLUMN);
assertTextResult(GridCellFocusPage.ID_SECTION_RESULT,
CellFocusEvent.GridSection.HEADER.getClientSideName());
}

@Test
public void focusFooterCell() {
open();

getGrid().getFooterCell(0).focus();
assertTextResult(GridCellFocusPage.ID_ITEM_RESULT,
GridCellFocusPage.NO_ITEM);
assertTextResult(GridCellFocusPage.ID_COLUMN_RESULT,
GridCellFocusPage.KEY_FIRST_COLUMN);
assertTextResult(GridCellFocusPage.ID_SECTION_RESULT,
CellFocusEvent.GridSection.FOOTER.getClientSideName());

getGrid().getFooterCell(1).focus();
assertTextResult(GridCellFocusPage.ID_ITEM_RESULT,
GridCellFocusPage.NO_ITEM);
assertTextResult(GridCellFocusPage.ID_COLUMN_RESULT,
GridCellFocusPage.KEY_SECOND_COLUMN);
assertTextResult(GridCellFocusPage.ID_SECTION_RESULT,
CellFocusEvent.GridSection.FOOTER.getClientSideName());
}

private GridElement getGrid() {
return $(GridElement.class).id(GridCellFocusPage.ID_GRID);
}

private void assertTextResult(String resultFieldId, String expectedResult) {
String text = $(TestBenchElement.class)
.id(resultFieldId)
.getText();

Assert.assertEquals(expectedResult, text);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* Copyright 2000-2021 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.flow.component.grid;

import java.util.Objects;
import java.util.Optional;

import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.DomEvent;
import com.vaadin.flow.component.EventData;

/**
* Event fired when a cell in the Grid is focused.
*
* @param <T> the grid bean type
* @author Vaadin Ltd
* <p>
* @see Grid#addCellFocusListener(com.vaadin.flow.component.ComponentEventListener)
*/
@DomEvent("grid-cell-focus")
public class CellFocusEvent<T> extends ComponentEvent<Grid<T>> {

private final transient T item;
private final Grid.Column<T> column;
private final GridSection section;

/**
* Creates a new cell focus event.
*
* @param source the source component
* @param fromClient <code>true</code> if the event originated from the
* client
*/
public CellFocusEvent(Grid<T> source, boolean fromClient,
@EventData("event.detail.itemKey") String itemKey,
@EventData("event.detail.internalColumnId")
String internalColumnId,
@EventData("event.detail.section") String sectionName) {
super(source, fromClient);

item = source.getDataCommunicator().getKeyMapper().get(itemKey);
column = source.getColumnByInternalId(internalColumnId);
section = GridSection.ofClientSideName(sectionName);
}

/**
* Indicates, if the clicked cell is part of the grid's body section.
*
* @return is a body cell
*/
public boolean isBodyCell() {
return section == GridSection.BODY;
}

/**
* Indicates, if the clicked cell is part of the grid's header section.
*
* @return is a header cell
*/
public boolean isHeaderCell() {
return section == GridSection.HEADER;
}

/**
* Indicates, if the clicked cell is part of the grid's footer section.
*
* @return is a footer cell
*/
public boolean isFooterCell() {
return section == GridSection.FOOTER;
}

/**
* Returns the grid section, where this cell is located. Never null.
* @return section
*/
public GridSection getSection() {
return section;
}

/**
* Returns the item represented by the focused cell. Is empty, when the
* focused cell is not a body cell.
*
* @return item or empty
*/
public Optional<T> getItem() {
return Optional.ofNullable(item);
}

/**
* Returns the column represented by the focused cell. Is empty, when the
* focused cell is a header group (a
* cell with a cellspan > 1).
*
* @return column or empty
*/
public Optional<Grid.Column<T>> getColumn() {
return Optional.ofNullable(column);
}

/**
* An enum representing the different sections of a grid.
*/
public enum GridSection {
/**
* Header section.
*/
HEADER("header"),

/**
* Body section.
*/
BODY("body"),

/**
* Footer section.
*/
FOOTER("footer");


private final String clientSideName;

GridSection(String clientSideName) {
this.clientSideName = clientSideName;
}

/**
* Returns the matching {@link GridSection} for the given client side name.
* An unknown client side name will lead to an exception.
* @param clientSideName client side name to lookup
* @throws IllegalArgumentException on an unknown client side section name
* @return matching section instance
*/
public static GridSection ofClientSideName(String clientSideName) {
for (GridSection section : values()) {
if (Objects.equals(clientSideName, section.getClientSideName())) {
return section;
}
}

throw new IllegalArgumentException("Unknown section client side section name: " + clientSideName);
}

/**
* Returns the client side name of the section.
* @return client side name
*/
public String getClientSideName() {
return clientSideName;
}
}
}
Loading

0 comments on commit 0deb0b6

Please sign in to comment.