Skip to content
Open
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
2 changes: 1 addition & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const config = defineConfig({
actionTimeout: 15 * 1000,

/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: 'https://app.qa.cboard.io',
baseURL: process.env.BASE_URL || 'https://app.qa.cboard.io',

/* Longer navigation timeout for slow environments */
navigationTimeout: 60 * 1000,
Expand Down
78 changes: 76 additions & 2 deletions src/components/Board/Board.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ export class Board extends Component {

this.state = {
openTitleDialog: false,
titleDialogValue: props.board && props.board.name ? props.board.name : ''
titleDialogValue: props.board && props.board.name ? props.board.name : '',
currentPage: 1
};

this.boardContainerRef = React.createRef();
Expand Down Expand Up @@ -193,6 +194,27 @@ export class Board extends Component {
});
};

// Pagination helpers (for non-fixed boards)
pageSize = 20; // symbols per page (can be adjusted or made configurable)

getTotalPages = totalItems => {
return Math.max(1, Math.ceil(totalItems / this.pageSize));
};

goToPrevPage = totalItems => {
this.setState(prev => ({
currentPage: prev.currentPage > 1 ? prev.currentPage - 1 : 1
}));
};

goToNextPage = totalItems => {
const totalPages = this.getTotalPages(totalItems);
this.setState(prev => ({
currentPage:
prev.currentPage < totalPages ? prev.currentPage + 1 : totalPages
}));
};

renderTiles(tiles) {
const {
isSelecting,
Expand Down Expand Up @@ -333,7 +355,17 @@ export class Board extends Component {
speak
} = this.props;

const tiles = this.renderTiles(board.tiles);
// Pagination (non-fixed boards): slice tiles per page
const totalTiles = board.tiles.length;
const totalPages = this.getTotalPages(totalTiles);
const safeCurrentPage = Math.min(this.state.currentPage, totalPages);
const startIdx = (safeCurrentPage - 1) * this.pageSize;
const endIdx = startIdx + this.pageSize;
const pagedTilesSrc = board.isFixed
? board.tiles
: board.tiles.slice(startIdx, endIdx);

const tiles = this.renderTiles(pagedTilesSrc);
const cols = DISPLAY_SIZE_GRID_COLS[this.props.displaySettings.uiSize];
const isLoggedIn = !!userData.email;
const isNavigationButtonsOnTheSide =
Expand Down Expand Up @@ -515,6 +547,48 @@ export class Board extends Component {
isNavigationButtonsOnTheSide
}
/>
{!board.isFixed && totalPages > 1 && (
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
padding: '8px 0'
}}
>
<Button
size="small"
onClick={() => this.goToPrevPage(totalTiles)}
disabled={safeCurrentPage === 1}
style={{ marginRight: 8 }}
>
{intl.formatMessage({
id: 'pagination.prev',
defaultMessage: 'Previous'
})}
</Button>
<span style={{ fontSize: 12 }}>
{intl.formatMessage(
{
id: 'pagination.pageOf',
defaultMessage: 'Page {page} of {pages}'
},
{ page: safeCurrentPage, pages: totalPages }
)}
</span>
<Button
size="small"
onClick={() => this.goToNextPage(totalTiles)}
disabled={safeCurrentPage === totalPages}
style={{ marginLeft: 8 }}
>
{intl.formatMessage({
id: 'pagination.next',
defaultMessage: 'Next'
})}
</Button>
</div>
)}
</div>
</Scannable>

Expand Down
2 changes: 1 addition & 1 deletion src/components/Board/Board.css
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
background: #fff;
direction: ltr; /* for grid */
overflow-x: hidden;
overflow-y: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/FixedGrid/Grid.module.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.root {
width: 100%;
height: 100%;
overflow: auto;
overflow: hidden;
display: flex;
flex-direction: column;
/* scroll-snap-type: y mandatory; */
Expand Down
59 changes: 59 additions & 0 deletions tests/scroll.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { test, expect } from '@playwright/test';
import { createCboard } from './page-objects/cboard.js';

test.describe('Board scroll behavior', () => {
test('tiles container should not be scrollable (overflow-y hidden)', async ({
page
}) => {
const cboard = createCboard(page);
await cboard.goto('/board/root');

// Find Board tiles container
const tiles = page.locator('#BoardTilesContainer');
await expect(tiles).toBeVisible();

// Check computed CSS
const overflowY = await tiles.evaluate(
el => getComputedStyle(el).overflowY
);
expect(overflowY).toBe('hidden');

// Try to scroll and verify position stays at 0
const before = await tiles.evaluate(el => el.scrollTop);
await tiles.evaluate(el => el.scrollBy(0, 200));
const after = await tiles.evaluate(el => el.scrollTop);
expect(before).toBe(after);
});

test('fixed grid root should not be scrollable (overflow hidden)', async ({
page
}) => {
const cboard = createCboard(page);
await cboard.goto('/board/root');

// Heuristic: locate the FixedGrid container by CSS module root class substring
const fixedGridRoot = page.locator('[class*="Grid_root"]');
const exists = await fixedGridRoot.count();

if (exists > 0) {
const first = fixedGridRoot.first();
const overflow = await first.evaluate(
el => getComputedStyle(el).overflow
);
expect(overflow).toBe('hidden');

const before = await first.evaluate(el => el.scrollTop);
await first.evaluate(el => el.scrollBy(0, 200));
const after = await first.evaluate(el => el.scrollTop);
expect(before).toBe(after);
} else {
test
.info()
.annotations.push({
type: 'note',
description:
'Fixed grid root not present on this board; skipping overflow assertion.'
});
}
});
});
Loading