Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix min and maxWidth not being respected when gte-container-slack is used #4

Merged
merged 5 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This fork of ember-table is used by Lighthouse where the following changes have
| -------------------------------------------------- | ------------------------------------------------------------------------- |
| /~https://github.com/OTA-Insight/ember-table/pull/1/ | Add ability to customise the row selection and row collapsing/expanding |
| /~https://github.com/OTA-Insight/ember-table/pull/3/ | Change the order of the collapse/expand button and the selection checkbox |
| /~https://github.com/OTA-Insight/ember-table/pull/4/ | Fix min and maxWidth not being respected when gte-container-slack is used |

# Ember Table

Expand Down
82 changes: 82 additions & 0 deletions addon/-private/column-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -763,8 +763,14 @@ export default EmberObject.extend({
return;
}

let widthConstraint = get(this, 'widthConstraint');
let isSlackModeEnabled = get(this, 'isSlackModeEnabled');

// For gte-container-slack, first try to expand columns to their maxWidth
if (widthConstraint === WIDTH_CONSTRAINT.GTE_CONTAINER_SLACK) {
this.expandColumnsToMax();
}

if (isSlackModeEnabled) {
this.updateSlackColumn();
}
Expand Down Expand Up @@ -1177,4 +1183,80 @@ export default EmberObject.extend({
this.token
);
},

/**
* Expands columns to their maxWidth when possible, while respecting container width
*/
expandColumnsToMax() {
let containerWidth = this.getContainerWidth();
let leaves = get(this, 'root.leaves').filter(node => !get(node, 'isSlack'));

// Calculate total fixed width from columns with explicit width
let flexibleColumns = leaves.filter(node => !get(node, 'column.width'));
let totalFixedWidth = leaves.reduce((sum, node) => {
let columnWidth = get(node, 'column.width');
return sum + (columnWidth || 0);
}, 0);

let remainingWidth = containerWidth - totalFixedWidth;

// First pass: ensure all columns get at least their minWidth
let totalMinWidth = 0;
flexibleColumns.forEach(node => {
let minWidth = get(node, 'minWidth');
totalMinWidth += minWidth;
});

// If we don't have space for all minWidths, set all columns to minWidth
if (remainingWidth <= totalMinWidth) {
flexibleColumns.forEach(node => {
set(node, 'width', get(node, 'minWidth'));
});
return;
}

// Keep track of columns that still need width allocated
let columnsNeedingWidth = [...flexibleColumns];
let loopCount = 0;

// Continue redistributing width until all columns are satisfied or we hit guard
while (columnsNeedingWidth.length > 0 && loopCount < LOOP_COUNT_GUARD) {
let availableWidth = remainingWidth;
columnsNeedingWidth.forEach(node => {
availableWidth -= get(node, 'minWidth');
});

let equalExtraWidth = Math.floor(availableWidth / columnsNeedingWidth.length);
let columnsToRemove = [];

// Try to set each column to minWidth + equalExtraWidth
columnsNeedingWidth.forEach(node => {
let minWidth = get(node, 'minWidth');
let maxWidth = get(node, 'maxWidth');
let targetWidth = minWidth + equalExtraWidth;
let width = Math.min(Math.max(targetWidth, minWidth), maxWidth);

// If column hits a constraint, remove it from future calculations
if (width !== targetWidth) {
columnsToRemove.push(node);
remainingWidth -= width;
}

set(node, 'width', width);
});

// If no columns hit constraints, we're done
if (columnsToRemove.length === 0) {
break;
}

// Remove columns that hit constraints and continue redistributing
columnsToRemove.forEach(node => {
let index = columnsNeedingWidth.indexOf(node);
columnsNeedingWidth.splice(index, 1);
});

loopCount++;
}
},
});
5 changes: 3 additions & 2 deletions tests/helpers/generate-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
export { configureTableGeneration, resetTableGenerationConfig, generateColumns, generateRows };

const fullTable = hbs`
<div style="height: 500px;">
<div style="height: 500px; {{if this.containerWidth (concat 'width: ' this.containerWidth 'px;')}}">
<EmberTable data-test-main-table as |t|>
<EmberThead
@api={{t}}
Expand Down Expand Up @@ -127,7 +127,7 @@ export function generateTableValues(
footerRowCount = 0,
columnCount = 10,
columnOptions,

containerWidth,
rowComponent = 'ember-tr',

...options
Expand All @@ -137,6 +137,7 @@ export function generateTableValues(
testContext.set(property, options[property]);
}
testContext.set('rowComponent', rowComponent);
testContext.set('containerWidth', containerWidth);

columns = columns || generateColumns(columnCount, columnOptions);

Expand Down
88 changes: 88 additions & 0 deletions tests/integration/components/headers/main-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,94 @@ module('Integration | header | main', function() {
assert.equal(table.logicalWidth, containerWidth + 100, 'table extends beyond container');
assert.notOk(slackHeader.isRendered, 'slack column is not rendered');
});

test('expandColumnsToMax behavior in gte-container-slack mode', async function(assert) {
await generateTable(this, {
widthConstraint: 'gte-container-slack',
columnCount: 3,
columnOptions: {
minWidth: 50,
maxWidth: 200,
},
containerWidth: 600,
});

let containerWidth = table.logicalContainerWidth;
let header1 = table.headers.objectAt(0);
let header2 = table.headers.objectAt(1);
let header3 = table.headers.objectAt(2);
let slackHeader = table.slackHeaders.objectAt(0);

// Initially, columns should expand to fill container equally to 600
assert.equal(
header1.logicalWidth + header2.logicalWidth + header3.logicalWidth,
containerWidth,
'columns fill container width initially'
);

await generateTable(this, {
widthConstraint: 'gte-container-slack',
columnCount: 3,
columnOptions: {
minWidth: 50,
},
columns: [
{ name: 'A', maxWidth: 50 },
{ name: 'B', maxWidth: 200 }, // flexible
{ name: 'C', maxWidth: 200 }, // flexible
],
containerWidth: 600,
});

assert.equal(header1.logicalWidth, 50, 'first column respects maxWidth');

// Remaining space should be distributed between other columns up to maxWidth
let remainingWidth = containerWidth - 50;
let expectedWidth = Math.min(remainingWidth / 2, 200);
assert.equal(header2.logicalWidth, expectedWidth, 'second column expands within maxWidth');
assert.equal(header3.logicalWidth, expectedWidth, 'third column expands within maxWidth');

// If there's still space after maxWidth, it should go to slack
let totalColumnWidth = 50 + expectedWidth * 2;
if (totalColumnWidth < containerWidth) {
assert.ok(slackHeader.isRendered, 'slack column appears when space available');
assert.equal(
slackHeader.logicalWidth,
containerWidth - totalColumnWidth,
'slack column fills remaining space'
);
} else {
assert.notOk(slackHeader.isRendered, 'no slack column when columns fill width');
}

// Test flexible columns without explicit width
await generateTable(this, {
widthConstraint: 'gte-container-slack',
columnCount: 3,
columnOptions: {
minWidth: 50,
},
columns: [
{ name: 'A', width: 100 },
{ name: 'B', maxWidth: 200 },
{ name: 'C', maxWidth: 200 },
],
containerWidth: 600,
});

header1 = table.headers.objectAt(0);
header2 = table.headers.objectAt(1);
header3 = table.headers.objectAt(2);

// Fixed width column should maintain its width
assert.equal(header1.logicalWidth, 100, 'fixed width column maintains width');

// Flexible columns should share remaining space equally up to maxWidth
remainingWidth = containerWidth - 100;
expectedWidth = Math.min(remainingWidth / 2, 200);
assert.equal(header2.logicalWidth, expectedWidth, 'flexible column expands within maxWidth');
assert.equal(header3.logicalWidth, expectedWidth, 'flexible column expands within maxWidth');
});
});

componentModule('fillMode', function() {
Expand Down