diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4acf4a76..56b6aff24 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](http://keepachangelog.com/)
 and this project adheres to [Semantic Versioning](http://semver.org/).
 
+## Unreleased
+
+### Changed
+
+- The DataTable cursor is now scrolled into view when the cursor coordinate is changed programmatically https://github.com/Textualize/textual/issues/2459
+
 ## [0.23.0] - 2023-05-03
 
 ### Fixed
diff --git a/src/textual/widgets/_data_table.py b/src/textual/widgets/_data_table.py
index 0681c3993..5991c98db 100644
--- a/src/textual/widgets/_data_table.py
+++ b/src/textual/widgets/_data_table.py
@@ -950,6 +950,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
             elif self.cursor_type == "column":
                 self.refresh_column(old_coordinate.column)
                 self._highlight_column(new_coordinate.column)
+            self._scroll_cursor_into_view()
 
     def _highlight_coordinate(self, coordinate: Coordinate) -> None:
         """Apply highlighting to the cell at the coordinate, and post event."""

diff --git a/tests/test_data_table.py b/tests/test_data_table.py
index 56b148d56..0bba677cf 100644
--- a/tests/test_data_table.py
+++ b/tests/test_data_table.py
@@ -991,3 +991,28 @@ def test_key_string_lookup():
     assert dictionary[RowKey("foo")] == "bar"
     assert dictionary["hello"] == "world"
     assert dictionary[RowKey("hello")] == "world"
+
+
+async def test_scrolling_cursor_into_view():
+    """Regression test for https://github.com/Textualize/textual/issues/2459"""
+
+    class TableApp(App):
+        CSS = "DataTable { height: 100%; }"
+
+        def compose(self):
+            yield DataTable()
+
+        def on_mount(self) -> None:
+            table = self.query_one(DataTable)
+            table.add_column("n")
+            table.add_rows([(n,) for n in range(300)])
+
+        def key_c(self):
+            self.query_one(DataTable).cursor_coordinate = Coordinate(200, 0)
+
+    app = TableApp()
+
+    async with app.run_test() as pilot:
+        await pilot.press("c")
+        await pilot.pause()
+        assert app.query_one(DataTable).scroll_y > 100

DataTable - When cursor_coordinate updates, we should scroll_cursor_visible
When the cursor moves out of the screen, we should scroll the cursor into visibility.

It seems like there is already code to do this, but because of #2458 the scrolling doesn't work.
tests/test_data_table.py::test_scrolling_cursor_into_view
tests/test_data_table.py::test_datatable_message_emission tests/test_data_table.py::test_empty_table_interactions tests/test_data_table.py::test_cursor_movement_with_home_pagedown_etc tests/test_data_table.py::test_add_rows tests/test_data_table.py::test_add_rows_user_defined_keys tests/test_data_table.py::test_add_row_duplicate_key tests/test_data_table.py::test_add_column_duplicate_key tests/test_data_table.py::test_add_column_with_width tests/test_data_table.py::test_add_columns tests/test_data_table.py::test_add_columns_user_defined_keys tests/test_data_table.py::test_remove_row tests/test_data_table.py::test_clear tests/test_data_table.py::test_column_labels tests/test_data_table.py::test_initial_column_widths tests/test_data_table.py::test_get_cell_returns_value_at_cell tests/test_data_table.py::test_get_cell_invalid_row_key tests/test_data_table.py::test_get_cell_invalid_column_key tests/test_data_table.py::test_get_cell_at_returns_value_at_cell tests/test_data_table.py::test_get_cell_at_exception tests/test_data_table.py::test_get_row tests/test_data_table.py::test_get_row_invalid_row_key tests/test_data_table.py::test_get_row_at tests/test_data_table.py::test_get_row_at_invalid_index[-1] tests/test_data_table.py::test_get_row_at_invalid_index[2] tests/test_data_table.py::test_get_column tests/test_data_table.py::test_get_column_invalid_key tests/test_data_table.py::test_get_column_at tests/test_data_table.py::test_get_column_at_invalid_index[-1] tests/test_data_table.py::test_get_column_at_invalid_index[5] tests/test_data_table.py::test_update_cell_cell_exists tests/test_data_table.py::test_update_cell_cell_doesnt_exist tests/test_data_table.py::test_update_cell_at_coordinate_exists tests/test_data_table.py::test_update_cell_at_coordinate_doesnt_exist tests/test_data_table.py::test_update_cell_at_column_width[A-BB-3] tests/test_data_table.py::test_update_cell_at_column_width[1234567-1234-7] tests/test_data_table.py::test_update_cell_at_column_width[12345-123-5] tests/test_data_table.py::test_update_cell_at_column_width[12345-123456789-9] tests/test_data_table.py::test_coordinate_to_cell_key tests/test_data_table.py::test_coordinate_to_cell_key_invalid_coordinate tests/test_data_table.py::test_datatable_click_cell_cursor tests/test_data_table.py::test_click_row_cursor tests/test_data_table.py::test_click_column_cursor tests/test_data_table.py::test_hover_coordinate tests/test_data_table.py::test_hover_mouse_leave tests/test_data_table.py::test_header_selected tests/test_data_table.py::test_row_label_selected tests/test_data_table.py::test_sort_coordinate_and_key_access tests/test_data_table.py::test_sort_reverse_coordinate_and_key_access tests/test_data_table.py::test_cell_cursor_highlight_events tests/test_data_table.py::test_row_cursor_highlight_events tests/test_data_table.py::test_column_cursor_highlight_events tests/test_data_table.py::test_reuse_row_key_after_clear tests/test_data_table.py::test_reuse_column_key_after_clear tests/test_data_table.py::test_key_equals_equivalent_string tests/test_data_table.py::test_key_doesnt_match_non_equal_string tests/test_data_table.py::test_key_equals_self tests/test_data_table.py::test_key_string_lookup