Repository: handsontable/handsontable
Pull request #11487: Fix problem with rowHeights when the value is lower than the default row height
Author: login: adrianspdev
Labels: name: Status: Released
Created: 2025-03-07T07:33:27Z | Merged: 2025-03-10T13:54:59Z

### Context
This PR includes a fix for the issue when the user sets row heights to a value lower than the minimum height for each theme. Documentation examples and descriptions have been updated.

### How has this been tested?
<!--- Please describe in detail how you tested your changes (doesn't apply to translations). -->

### Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [x] Bug fix (non-breaking change which fixes an issue)

### Related issue(s):
1. https://github.com/handsontable/dev-handsontable/issues/2275

### Affected project(s):
- [x] `handsontable`

### Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [x] I have reviewed the guidelines about [Contributing to Handsontable](https://github.com/handsontable/handsontable/blob/master/CONTRIBUTING.md) and I confirm that my code follows the code style of this project.
- [x] I have signed the [Contributor License Agreement](https://docs.google.com/forms/d/e/1FAIpQLScpMq4swMelvw3-onxC8Jl29m0fVp5hpf7d1yQVklqVjGjWGA/viewform?c=0&w=1)
- [x] My change requires a change to the documentation.



Diff:
diff --git a/.changelogs/11487.json b/.changelogs/11487.json
new file mode 100644
index 00000000000..56cf2b82f05
--- /dev/null
+++ b/.changelogs/11487.json
@@ -0,0 +1,8 @@
+{
+  "issuesOrigin": "private",
+  "title": "Fixed problem with rowHeights when the value is lower than the default/min row height",
+  "type": "fixed",
+  "issueOrPR": 11487,
+  "breaking": false,
+  "framework": "none"
+}
diff --git a/docs/content/guides/cell-features/selection/react/example1.jsx b/docs/content/guides/cell-features/selection/react/example1.jsx
index 9d688feccb2..5aa1699ba95 100644
--- a/docs/content/guides/cell-features/selection/react/example1.jsx
+++ b/docs/content/guides/cell-features/selection/react/example1.jsx
@@ -48,7 +48,6 @@ const ExampleComponent = () => {
         width="auto"
         height="auto"
         colWidths={100}
-        rowHeights={23}
         rowHeaders={true}
         colHeaders={true}
         selectionMode="multiple" // 'single', 'range' or 'multiple',
diff --git a/docs/content/guides/cell-features/selection/react/example1.tsx b/docs/content/guides/cell-features/selection/react/example1.tsx
index 1aa7c8f9727..0017617ed02 100644
--- a/docs/content/guides/cell-features/selection/react/example1.tsx
+++ b/docs/content/guides/cell-features/selection/react/example1.tsx
@@ -53,7 +53,6 @@ const ExampleComponent = () => {
         width="auto"
         height="auto"
         colWidths={100}
-        rowHeights={23}
         rowHeaders={true}
         colHeaders={true}
         selectionMode="multiple" // 'single', 'range' or 'multiple',
diff --git a/docs/content/guides/cell-features/selection/react/example2.jsx b/docs/content/guides/cell-features/selection/react/example2.jsx
index f5c1f1eb5be..dbd214bec00 100644
--- a/docs/content/guides/cell-features/selection/react/example2.jsx
+++ b/docs/content/guides/cell-features/selection/react/example2.jsx
@@ -56,7 +56,6 @@ const ExampleComponent = () => {
         width="auto"
         height="auto"
         colWidths={100}
-        rowHeights={23}
         rowHeaders={true}
         colHeaders={true}
         outsideClickDeselects={false}
diff --git a/docs/content/guides/cell-features/selection/react/example2.tsx b/docs/content/guides/cell-features/selection/react/example2.tsx
index 5a6369b4b7d..39bc77ba2a5 100644
--- a/docs/content/guides/cell-features/selection/react/example2.tsx
+++ b/docs/content/guides/cell-features/selection/react/example2.tsx
@@ -58,7 +58,6 @@ const ExampleComponent = () => {
         width="auto"
         height="auto"
         colWidths={100}
-        rowHeights={23}
         rowHeaders={true}
         colHeaders={true}
         outsideClickDeselects={false}
diff --git a/docs/content/guides/cell-features/selection/react/example3.jsx b/docs/content/guides/cell-features/selection/react/example3.jsx
index 8f2f4adf3d6..b9a06737fc0 100644
--- a/docs/content/guides/cell-features/selection/react/example3.jsx
+++ b/docs/content/guides/cell-features/selection/react/example3.jsx
@@ -61,7 +61,6 @@ const ExampleComponent = () => {
         width="auto"
         height="auto"
         colWidths={100}
-        rowHeights={23}
         rowHeaders={true}
         colHeaders={true}
         outsideClickDeselects={false}
diff --git a/docs/content/guides/cell-features/selection/react/example3.tsx b/docs/content/guides/cell-features/selection/react/example3.tsx
index d98b5ba9323..c7458030129 100644
--- a/docs/content/guides/cell-features/selection/react/example3.tsx
+++ b/docs/content/guides/cell-features/selection/react/example3.tsx
@@ -62,7 +62,6 @@ const ExampleComponent = () => {
         width="auto"
         height="auto"
         colWidths={100}
-        rowHeights={23}
         rowHeaders={true}
         colHeaders={true}
         outsideClickDeselects={false}
diff --git a/docs/content/guides/getting-started/grid-size/javascript/example.js b/docs/content/guides/getting-started/grid-size/javascript/example.js
index a18abd9d444..ea7d9174eee 100644
--- a/docs/content/guides/getting-started/grid-size/javascript/example.js
+++ b/docs/content/guides/getting-started/grid-size/javascript/example.js
@@ -20,7 +20,6 @@ const hot = new Handsontable(example, {
   colHeaders: true,
   width: '100%',
   height: '100%',
-  rowHeights: 23,
   colWidths: 100,
   autoWrapRow: true,
   autoWrapCol: true,
diff --git a/docs/content/guides/getting-started/grid-size/javascript/example.ts b/docs/content/guides/getting-started/grid-size/javascript/example.ts
index 8d2715b742b..f60cb4eedc7 100644
--- a/docs/content/guides/getting-started/grid-size/javascript/example.ts
+++ b/docs/content/guides/getting-started/grid-size/javascript/example.ts
@@ -21,7 +21,6 @@ const hot = new Handsontable(example, {
   colHeaders: true,
   width: '100%',
   height: '100%',
-  rowHeights: 23,
   colWidths: 100,
   autoWrapRow: true,
   autoWrapCol: true,
diff --git a/docs/content/guides/getting-started/grid-size/react/example.jsx b/docs/content/guides/getting-started/grid-size/react/example.jsx
index 0d7f11e7a23..1acb2ef96ba 100644
--- a/docs/content/guides/getting-started/grid-size/react/example.jsx
+++ b/docs/content/guides/getting-started/grid-size/react/example.jsx
@@ -50,7 +50,6 @@ const ExampleComponent = () => {
           colHeaders={true}
           width="100%"
           height="100%"
-          rowHeights={23}
           colWidths={100}
           autoWrapRow={true}
           autoWrapCol={true}
diff --git a/docs/content/guides/getting-started/grid-size/react/example.tsx b/docs/content/guides/getting-started/grid-size/react/example.tsx
index e84d9dcd738..63d8be5fe31 100644
--- a/docs/content/guides/getting-started/grid-size/react/example.tsx
+++ b/docs/content/guides/getting-started/grid-size/react/example.tsx
@@ -51,7 +51,6 @@ const ExampleComponent = () => {
           colHeaders={true}
           width="100%"
           height="100%"
-          rowHeights={23}
           colWidths={100}
           autoWrapRow={true}
           autoWrapCol={true}
diff --git a/docs/content/guides/rows/row-height/row-height.md b/docs/content/guides/rows/row-height/row-height.md
index 8171e22c059..2af4f562a00 100644
--- a/docs/content/guides/rows/row-height/row-height.md
+++ b/docs/content/guides/rows/row-height/row-height.md
@@ -31,7 +31,7 @@ Configure row heights, using a number, an array or a function. Let your users ma
 
 ## Overview
 
-The default (and minimum) row height is calculated based on the used theme's padding and border values (in the classic theme it's 23 px - 22 px + 1 px of the row's bottom border). Unless configured otherwise, Handsontable assumes that your cell contents fit in this default row height.
+The default (and minimum) row height is calculated based on the used theme's line height, vertical padding and cell border values and in the `main` theme it's 29px, in the `horizon` it's 37px and in the `classic` theme it's 23 px (22 px + 1 px of the row's bottom border). Unless configured otherwise, Handsontable assumes that your cell contents fit in this default row height.
 
 If your cell contents require heights greater than default (because you use multiline text, or [custom renderers](@/guides/cell-functions/cell-renderer/cell-renderer.md), or custom styles), use one of the following configurations to avoid potential layout problems:
   - Configure your row heights in advance: set the [`rowHeights`](@/api/options.md#rowheights) option to a [number](#set-row-heights-to-a-number), or an [array](#set-row-heights-with-an-array), or a [function](#set-row-heights-with-a-function). This requires you to know the heights beforehand, but results in the best runtime performance.
diff --git a/handsontable/src/core.js b/handsontable/src/core.js
index 4d2b4134b31..0ef29e4c8e1 100644
--- a/handsontable/src/core.js
+++ b/handsontable/src/core.js
@@ -3870,6 +3870,7 @@ export default function Core(rootElement, userSettings, rootInstanceSymbol = fal
    * @returns {number}
    */
   this._getRowHeightFromSettings = function(row) {
+    const defaultRowHeight = this.view.getDefaultRowHeight();
     let height = tableMeta.rowHeights;
 
     if (height !== undefined && height !== null) {
@@ -3881,15 +3882,17 @@ export default function Core(rootElement, userSettings, rootInstanceSymbol = fal
         case 'function':
           height = height(row);
           break;
+
         default:
           break;
       }
+
       if (typeof height === 'string') {
         height = parseInt(height, 10);
       }
     }
 
-    return height;
+    return (height !== undefined && height !== null && height < defaultRowHeight) ? defaultRowHeight : height;
   };
 
   /**
diff --git a/handsontable/src/dataMap/metaManager/metaSchema.js b/handsontable/src/dataMap/metaManager/metaSchema.js
index 9650e3f1c2f..7af73dc4d65 100644
--- a/handsontable/src/dataMap/metaManager/metaSchema.js
+++ b/handsontable/src/dataMap/metaManager/metaSchema.js
@@ -3967,8 +3967,8 @@ export default () => {
     /**
      * The `rowHeights` option sets rows' heights, in pixels.
      *
-     * In the rendering process, the default row height is 23 px (in the classic theme: 22 px + 1 px of the row's bottom border) or whatever is defined in the used theme (based on the line height, vertical padding and cell borders).
-     * You can change it to equal or greater than the defautl value, by setting the `rowHeights` option to one of the following:
+     * In the rendering process, the default row height is `classic: 23px`, `main: 29px`, `horizon: 37px` (in the classic theme: 22px + 1px of the row's bottom border) or whatever is defined in the used theme (based on the line height, vertical padding and cell borders).
+     * You can change it to equal or greater than the default value, by setting the `rowHeights` option to one of the following:
      *
      * | Setting     | Description                                                                                         | Example                                                      |
      * | ----------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
diff --git a/handsontable/src/plugins/contextMenu/menu/menu.js b/handsontable/src/plugins/contextMenu/menu/menu.js
index 52ed4436dee..600f7243db5 100644
--- a/handsontable/src/plugins/contextMenu/menu/menu.js
+++ b/handsontable/src/plugins/contextMenu/menu/menu.js
@@ -318,7 +318,6 @@ export class Menu {
           this.openSubMenu(coords.row);
         }
       },
-      rowHeights: row => (filteredItems[row].name === SEPARATOR ? 1 : undefined),
       afterOnCellContextMenu: (event) => {
         event.preventDefault();
 
diff --git a/handsontable/test/e2e/core/getRowHeight.spec.js b/handsontable/test/e2e/core/getRowHeight.spec.js
index 7e74ca88b08..6fe5fce7ccc 100644
--- a/handsontable/test/e2e/core/getRowHeight.spec.js
+++ b/handsontable/test/e2e/core/getRowHeight.spec.js
@@ -26,4 +26,37 @@ describe('Core.getRowHeight', () => {
     expect(modifyRowHeight.calls.count()).toBe(1);
     expect(modifyRowHeight).toHaveBeenCalledWith(50, 1);
   });
+
+  it('should return the same value as the `rowHeights` if the value is greater than minimum theme row height', () => {
+    handsontable({
+      data: createSpreadsheetData(5, 5),
+      rowHeights: 50,
+      rowHeaders: false,
+      colHeaders: false,
+    });
+
+    expect(getRowHeight(0)).toBe(50);
+  });
+
+  it('should return the minimum theme row height if the lower value is defined', () => {
+    handsontable({
+      data: createSpreadsheetData(5, 5),
+      rowHeights: 12,
+      rowHeaders: false,
+      colHeaders: false,
+    });
+
+    expect(getRowHeight(0)).toBe(getDefaultRowHeight());
+  });
+
+  it('should return the minimum theme row height if the value is equal to `0`', () => {
+    handsontable({
+      data: createSpreadsheetData(5, 5),
+      rowHeights: 0,
+      rowHeaders: false,
+      colHeaders: false,
+    });
+
+    expect(getRowHeight(0)).toBe(getDefaultRowHeight());
+  });
 });


Reviews:
user: login: budnix
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-03-07T09:55:57Z
user: login: budnix
body: It looks good. However, I would improve the descriptions of tests to be more abstract than those that describe the logic of the method. WDYT?
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-03-07T10:57:59Z
user: login: budnix
body: Cool 👍 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-07T12:12:43Z
user: login: qunabu
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T10:01:28Z

Review comments:
diff_hunk: @@ -3881,15 +3882,17 @@ export default function Core(rootElement, userSettings, rootInstanceSymbol = fal
         case 'function':
           height = height(row);
           break;
+
         default:
           break;
       }
+
       if (typeof height === 'string') {
         height = parseInt(height, 10);
       }
     }
 
-    return height;
+    return (height && height < defaultRowHeight) ? defaultRowHeight : height;
path: handsontable/src/core.js
user: login: budnix
body: Can I ask you to cover the change with tests (`handsontable/test/e2e/core/getRowHeight.spec.js`)?  Also, I have found a bug ... when I declare `rowHeights: 0`, then the method returns `0` instead of the default theme row size.
created_at: 2025-03-07T09:55:53Z
author_association: MEMBER
diff_hunk: @@ -26,4 +26,37 @@ describe('Core.getRowHeight', () => {
     expect(modifyRowHeight.calls.count()).toBe(1);
     expect(modifyRowHeight).toHaveBeenCalledWith(50, 1);
   });
+
+  it('should return `rowHeights` if value is greater than `defaultHeight`', () => {
path: handsontable/test/e2e/core/getRowHeight.spec.js
user: login: budnix
body: WDYT about adding a more precise description? (mainly changing "defaultHeight" to "minimal theme row height")
```suggestion
  it('should return the same value as the `rowHeights` if the value is greater than minimum theme row height', () => {
```
created_at: 2025-03-07T10:51:33Z
author_association: MEMBER
diff_hunk: @@ -26,4 +26,37 @@ describe('Core.getRowHeight', () => {
     expect(modifyRowHeight.calls.count()).toBe(1);
     expect(modifyRowHeight).toHaveBeenCalledWith(50, 1);
   });
+
+  it('should return `rowHeights` if value is greater than `defaultHeight`', () => {
+    handsontable({
+      data: createSpreadsheetData(5, 5),
+      rowHeights: 50,
+      rowHeaders: false,
+      colHeaders: false,
+    });
+
+    expect(getRowHeight(0)).toBe(50);
+  });
+
+  it('should return `defaultRowHeight` if `rowHeights` is less than `defaultRowHeight`', () => {
path: handsontable/test/e2e/core/getRowHeight.spec.js
user: login: budnix
body: ```suggestion
  it('should return the minimum theme row height if the lower value is defined', () => {
```
created_at: 2025-03-07T10:54:12Z
author_association: MEMBER
diff_hunk: @@ -26,4 +26,37 @@ describe('Core.getRowHeight', () => {
     expect(modifyRowHeight.calls.count()).toBe(1);
     expect(modifyRowHeight).toHaveBeenCalledWith(50, 1);
   });
+
+  it('should return `rowHeights` if value is greater than `defaultHeight`', () => {
+    handsontable({
+      data: createSpreadsheetData(5, 5),
+      rowHeights: 50,
+      rowHeaders: false,
+      colHeaders: false,
+    });
+
+    expect(getRowHeight(0)).toBe(50);
+  });
+
+  it('should return `defaultRowHeight` if `rowHeights` is less than `defaultRowHeight`', () => {
+    handsontable({
+      data: createSpreadsheetData(5, 5),
+      rowHeights: 12,
+      rowHeaders: false,
+      colHeaders: false,
+    });
+
+    expect(getRowHeight(0)).toBe(getDefaultRowHeight());
+  });
+
+  it('should return `defaultRowHeight` if `rowHeights` is equal to 0', () => {
path: handsontable/test/e2e/core/getRowHeight.spec.js
user: login: budnix
body: ```suggestion
  it('should return the minimum theme row height if the value is equal to `0`', () => {
```
created_at: 2025-03-07T10:54:48Z
author_association: MEMBER

PR comments:
user: login: codesandbox-ci[bot]
created_at: 2025-03-07T07:43:35Z
body: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/handsontable/handsontable/pr/11487/builds/595566) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit af574831f762937b0756d8128fecb8445d28e47a:

|Sandbox| Source |
|--|--|
|[handsontable-pr-javascript-demo](https://codesandbox.io/s/handsontable-pr-javascript-demo-yqgsdl)| Configuration |
|[handsontable-ts-demo](https://codesandbox.io/s/handsontable-ts-demo-tcg4z3)| Configuration |
|[handsontable-pr-react-demo](https://codesandbox.io/s/handsontable-pr-react-demo-h9vf67)| Configuration |

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-07T07:43:35Z
body: Launch the local version of documentation by running:
```bash
npm run docs:review 2be8c9d745c85c1639147898f0267c013a11502a
```
<!-- Sticky Pull Request Comment -->
author_association: CONTRIBUTOR