Added keyboard navigation support
This commit is contained in:
@@ -372,6 +372,102 @@ class TestDataGridBehaviour:
|
||||
assert col_def.type == ColumnType.Number
|
||||
assert col_def.formula == "{age} + 1"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Keyboard Navigation
|
||||
# ------------------------------------------------------------------
|
||||
# Column layout for datagrid_with_data (enable_edition=True):
|
||||
# _state.columns : [name (idx 0), age (idx 1), active (idx 2)]
|
||||
# _columns : [RowSelection_ (pos 0), name (pos 1), age (pos 2), active (pos 3)]
|
||||
# Navigable positions: [1, 2, 3] — RowSelection_ (pos 0) always excluded.
|
||||
|
||||
def test_i_can_get_navigable_col_positions(self, datagrid_with_data):
|
||||
"""RowSelection_ (pos 0) must be excluded; data columns (1, 2, 3) must be included."""
|
||||
positions = datagrid_with_data._get_navigable_col_positions()
|
||||
assert positions == [1, 2, 3]
|
||||
|
||||
def test_i_can_get_navigable_col_positions_with_hidden_column(self, datagrid_with_data):
|
||||
"""Hidden columns must be excluded from navigable positions.
|
||||
|
||||
_state.columns[1] is 'age' → maps to _columns pos 2.
|
||||
After hiding, navigable positions must be [1, 3].
|
||||
"""
|
||||
datagrid_with_data._state.columns[1].visible = False # hide 'age' (pos 2 in _columns)
|
||||
datagrid_with_data._init_columns()
|
||||
positions = datagrid_with_data._get_navigable_col_positions()
|
||||
assert positions == [1, 3]
|
||||
|
||||
@pytest.mark.parametrize("start_pos, direction, expected_pos", [
|
||||
# Normal navigation — right / left
|
||||
((1, 0), "right", (2, 0)),
|
||||
((2, 0), "right", (3, 0)),
|
||||
((3, 0), "left", (2, 0)),
|
||||
((2, 0), "left", (1, 0)),
|
||||
# Normal navigation — down / up
|
||||
((1, 0), "down", (1, 1)),
|
||||
((1, 1), "down", (1, 2)),
|
||||
((1, 2), "up", (1, 1)),
|
||||
((1, 1), "up", (1, 0)),
|
||||
# Boundaries — must stay in place
|
||||
((3, 0), "right", (3, 0)),
|
||||
((1, 0), "left", (1, 0)),
|
||||
((1, 2), "down", (1, 2)),
|
||||
((1, 0), "up", (1, 0)),
|
||||
])
|
||||
def test_i_can_navigate(self, datagrid_with_data, start_pos, direction, expected_pos):
|
||||
"""Navigation moves to the expected position or stays at boundary."""
|
||||
result = datagrid_with_data._navigate(start_pos, direction)
|
||||
assert result == expected_pos
|
||||
|
||||
def test_i_can_navigate_right_skipping_invisible_column(self, datagrid_with_data):
|
||||
"""→ from col 1 must skip hidden col 2 (age) and land on col 3 (active)."""
|
||||
datagrid_with_data._state.columns[1].visible = False # hide 'age' → pos 2
|
||||
datagrid_with_data._init_columns()
|
||||
result = datagrid_with_data._navigate((1, 0), "right")
|
||||
assert result == (3, 0)
|
||||
|
||||
def test_i_can_navigate_left_skipping_invisible_column(self, datagrid_with_data):
|
||||
"""← from col 3 must skip hidden col 2 (age) and land on col 1 (name)."""
|
||||
datagrid_with_data._state.columns[1].visible = False # hide 'age' → pos 2
|
||||
datagrid_with_data._init_columns()
|
||||
result = datagrid_with_data._navigate((3, 0), "left")
|
||||
assert result == (1, 0)
|
||||
|
||||
def test_i_can_navigate_down_skipping_filtered_row(self, datagrid_with_data):
|
||||
"""↓ from row 0 must skip filtered-out row 1 (Bob) and land on row 2 (Charlie).
|
||||
|
||||
Filter keeps age values "25" and "35" only → row 1 (age=30) is excluded.
|
||||
Visible row indices become [0, 2].
|
||||
"""
|
||||
datagrid_with_data._state.filtered["age"] = ["25", "35"]
|
||||
result = datagrid_with_data._navigate((1, 0), "down")
|
||||
assert result == (1, 2)
|
||||
|
||||
@pytest.mark.parametrize("combination, start_pos, expected_pos", [
|
||||
("arrowright", (1, 0), (2, 0)),
|
||||
("arrowleft", (2, 0), (1, 0)),
|
||||
("arrowdown", (1, 0), (1, 1)),
|
||||
("arrowup", (1, 1), (1, 0)),
|
||||
])
|
||||
def test_i_can_navigate_with_arrow_keys(self, datagrid_with_data, combination, start_pos, expected_pos):
|
||||
"""Arrow key presses update selection.selected to the expected position."""
|
||||
datagrid_with_data._state.selection.selected = start_pos
|
||||
datagrid_with_data.on_key_pressed(combination=combination, has_focus=True, is_inside=True)
|
||||
assert datagrid_with_data._state.selection.selected == expected_pos
|
||||
|
||||
def test_i_can_navigate_from_last_selected_when_no_selection(self, datagrid_with_data):
|
||||
"""When selected is None, navigation starts from last_selected."""
|
||||
datagrid_with_data._state.selection.selected = None
|
||||
datagrid_with_data._state.selection.last_selected = (1, 1)
|
||||
datagrid_with_data.on_key_pressed(combination="arrowright", has_focus=True, is_inside=True)
|
||||
assert datagrid_with_data._state.selection.selected == (2, 1)
|
||||
|
||||
def test_i_can_navigate_from_origin_when_no_selection_and_no_last_selected(self, datagrid_with_data):
|
||||
"""When both selected and last_selected are None, navigation starts from (0, 0)."""
|
||||
datagrid_with_data._state.selection.selected = None
|
||||
datagrid_with_data._state.selection.last_selected = None
|
||||
datagrid_with_data.on_key_pressed(combination="arrowright", has_focus=True, is_inside=True)
|
||||
assert datagrid_with_data._state.selection.selected == (1, 0)
|
||||
|
||||
|
||||
class TestDataGridRender:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user