From c1cc4962a6c9934a71f85f71295b7682775275f1 Mon Sep 17 00:00:00 2001 From: smhigley Date: Thu, 20 Sep 2018 15:30:37 -0700 Subject: [PATCH] update pageChange, add page aria --- src/grid/tests/unit/widgets/Body.ts | 71 +++++++++++++++++++++++++++ src/grid/tests/unit/widgets/Grid.ts | 10 ++-- src/grid/tests/unit/widgets/Header.ts | 20 ++++---- src/grid/tests/unit/widgets/Row.ts | 6 +-- src/grid/widgets/Body.ts | 3 +- src/grid/widgets/Grid.ts | 7 ++- src/grid/widgets/Header.ts | 14 ++++-- src/grid/widgets/Row.ts | 2 +- 8 files changed, 108 insertions(+), 25 deletions(-) diff --git a/src/grid/tests/unit/widgets/Body.ts b/src/grid/tests/unit/widgets/Body.ts index 972c457e08..35ea763d5d 100644 --- a/src/grid/tests/unit/widgets/Body.ts +++ b/src/grid/tests/unit/widgets/Body.ts @@ -275,4 +275,75 @@ describe('Body', () => { ) ); }); + + describe('pageChange', () => { + it('should call pageChange with first page at scroll 0', () => { + const pageChangeStub = stub(); + const page: any[] = []; + for (let i = 0; i < 100; i++) { + const item = { id: 'id' }; + page.push(item); + } + + const h = harness(() => + w(Body, { + totalRows: 1000, + pageSize: 100, + height: 400, + pages: { + 'page-1': page + }, + columnConfig: [] as any, + fetcher: noop, + updater: noop, + pageChange: pageChangeStub, + onScroll: noop + }) + ); + + h.trigger('@root', 'onscroll', { + target: { + scrollTop: 0, + scrollLeft: 0 + } + }); + h.expect(() => h.getRender()); + assert.isTrue(pageChangeStub.calledWith(1)); + }); + + it('should use middle row if start and end pages are different', () => { + const pageChangeStub = stub(); + const page: any[] = []; + for (let i = 0; i < 100; i++) { + const item = { id: 'id' }; + page.push(item); + } + + const h = harness(() => + w(Body, { + totalRows: 1000, + pageSize: 100, + height: 400, + pages: { + 'page-1': page + }, + columnConfig: [] as any, + fetcher: noop, + updater: noop, + pageChange: pageChangeStub, + onScroll: noop + }) + ); + // scroll to row 286 + h.trigger('@root', 'onscroll', { + target: { + scrollTop: 10000, + scrollLeft: 0 + } + }); + // force a render + h.expect(() => h.getRender()); + assert.isTrue(pageChangeStub.calledWith(3)); + }); + }); }); diff --git a/src/grid/tests/unit/widgets/Grid.ts b/src/grid/tests/unit/widgets/Grid.ts index 4680ee7043..e8a5510d27 100644 --- a/src/grid/tests/unit/widgets/Grid.ts +++ b/src/grid/tests/unit/widgets/Grid.ts @@ -65,7 +65,7 @@ describe('Grid', () => { ); h.expect(() => - v('div', { key: 'root', classes: [css.root, fixedCss.rootFixed], role: 'table' }, [ + v('div', { key: 'root', classes: [css.root, fixedCss.rootFixed], role: 'table', 'aria-rowcount': null }, [ v('div', { key: 'header', scrollLeft: 0, @@ -132,7 +132,7 @@ describe('Grid', () => { ); h.expect(() => - v('div', { key: 'root', classes: [css.root, fixedCss.rootFixed], role: 'table' }, [ + v('div', { key: 'root', classes: [css.root, fixedCss.rootFixed], role: 'table', 'aria-rowcount': '100' }, [ v('div', { key: 'header', scrollLeft: 0, @@ -191,7 +191,7 @@ describe('Grid', () => { ); h.expect(() => - v('div', { key: 'root', classes: [css.root, fixedCss.rootFixed], role: 'table' }, [ + v('div', { key: 'root', classes: [css.root, fixedCss.rootFixed], role: 'table', 'aria-rowcount': null }, [ v('div', { key: 'header', scrollLeft: 0, @@ -242,7 +242,7 @@ describe('Grid', () => { ); h.expect(() => - v('div', { key: 'root', classes: [css.root, fixedCss.rootFixed], role: 'table' }, [ + v('div', { key: 'root', classes: [css.root, fixedCss.rootFixed], role: 'table', 'aria-rowcount': null }, [ v('div', { key: 'header', scrollLeft: 0, @@ -284,7 +284,7 @@ describe('Grid', () => { h.trigger('@body', 'onScroll', 10); h.expect(() => - v('div', { key: 'root', classes: [css.root, fixedCss.rootFixed], role: 'table' }, [ + v('div', { key: 'root', classes: [css.root, fixedCss.rootFixed], role: 'table', 'aria-rowcount': null }, [ v('div', { key: 'header', scrollLeft: 10, diff --git a/src/grid/tests/unit/widgets/Header.ts b/src/grid/tests/unit/widgets/Header.ts index ec28a52563..7f84183607 100644 --- a/src/grid/tests/unit/widgets/Header.ts +++ b/src/grid/tests/unit/widgets/Header.ts @@ -51,8 +51,8 @@ describe('Header', () => { ); h.expect(() => v('div', { classes: [css.root, fixedCss.rootFixed], role: 'row' }, [ - v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader' }, [v('div', {}, ['Title'])]), - v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader' }, [v('div', {}, ['First Name'])]) + v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader', 'aria-sort': null }, [v('div', {}, ['Title'])]), + v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader', 'aria-sort': null }, [v('div', {}, ['First Name'])]) ]) ); }); @@ -70,8 +70,8 @@ describe('Header', () => { h.expect(() => v('div', { classes: [css.root, fixedCss.rootFixed], role: 'row' }, [ - v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader' }, [v('div', {}, ['Title'])]), - v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader' }, [ + v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader', 'aria-sort': null }, [v('div', {}, ['Title'])]), + v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader', 'aria-sort': null }, [ v('div', { classes: [css.sortable, null, null, null], onclick: noop @@ -118,8 +118,8 @@ describe('Header', () => { h.expect(() => v('div', { classes: [css.root, fixedCss.rootFixed], role: 'row' }, [ - v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader' }, [v('div', {}, ['Title'])]), - v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader' }, [ + v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader', 'aria-sort': null }, [v('div', {}, ['Title'])]), + v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader', 'aria-sort': 'ascending' }, [ v('div', { classes: [css.sortable, css.sorted, null, css.asc], onclick: noop @@ -166,8 +166,8 @@ describe('Header', () => { h.expect(() => v('div', { classes: [css.root, fixedCss.rootFixed], role: 'row' }, [ - v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader' }, [v('div', {}, ['Title'])]), - v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader' }, [ + v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader', 'aria-sort': null }, [v('div', {}, ['Title'])]), + v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader', 'aria-sort': 'descending' }, [ v('div', { classes: [css.sortable, css.sorted, css.desc, null], onclick: noop @@ -214,8 +214,8 @@ describe('Header', () => { h.expect(() => v('div', { classes: [css.root, fixedCss.rootFixed], role: 'row' }, [ - v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader' }, [v('div', {}, ['Title'])]), - v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader' }, [ + v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader', 'aria-sort': null }, [v('div', {}, ['Title'])]), + v('div', { classes: [css.cell, fixedCss.cellFixed], role: 'columnheader', 'aria-sort': null }, [ v('div', { classes: [css.sortable, null, null, null], onclick: noop diff --git a/src/grid/tests/unit/widgets/Row.ts b/src/grid/tests/unit/widgets/Row.ts index b60bec2ace..ea4a5fb314 100644 --- a/src/grid/tests/unit/widgets/Row.ts +++ b/src/grid/tests/unit/widgets/Row.ts @@ -14,7 +14,7 @@ const noop = () => {}; describe('Row', () => { it('should render without columns', () => { const h = harness(() => w(Row, { id: 1, item: {}, columnConfig: [] as any, updater: noop })); - h.expect(() => v('div', { classes: [css.root, fixedCss.rootFixed], role: 'row' }, [])); + h.expect(() => v('div', { classes: [css.root, fixedCss.rootFixed], role: 'row', 'aria-rowindex': '2' }, [])); }); it('should render items for column config', () => { @@ -24,7 +24,7 @@ describe('Row', () => { }; const h = harness(() => w(Row, { id: 1, item: { id: 'id' }, columnConfig: [columnConfig], updater: noop })); h.expect(() => - v('div', { classes: [css.root, fixedCss.rootFixed], role: 'row' }, [ + v('div', { classes: [css.root, fixedCss.rootFixed], role: 'row', 'aria-rowindex': '2' }, [ w(Cell, { key: 'id', updater: noop, value: 'id', editable: undefined, rawValue: 'id' }) ]) ); @@ -38,7 +38,7 @@ describe('Row', () => { }; const h = harness(() => w(Row, { id: 1, item: { id: 'id' }, columnConfig: [columnConfig], updater: noop })); h.expect(() => - v('div', { classes: [css.root, fixedCss.rootFixed], role: 'row' }, [ + v('div', { classes: [css.root, fixedCss.rootFixed], role: 'row', 'aria-rowindex': '2' }, [ w(Cell, { key: 'id', updater: noop, value: 'transformed', editable: undefined, rawValue: 'id' }) ]) ); diff --git a/src/grid/widgets/Body.ts b/src/grid/widgets/Body.ts index d7d6cbf9af..69f5f3e7c8 100755 --- a/src/grid/widgets/Body.ts +++ b/src/grid/widgets/Body.ts @@ -112,7 +112,8 @@ export default class Body extends ThemedMixin(WidgetBase)> if (!endData.length) { fetcher(endPage, pageSize); } - pageChange(endPage); + const midScreenPage = Math.max(Math.ceil((start + this._rowsInView / 2) / pageSize), 1); + pageChange(midScreenPage); data = [...data, ...endData]; } else { pageChange(startPage); diff --git a/src/grid/widgets/Grid.ts b/src/grid/widgets/Grid.ts index c44544eefd..5e4f2f3483 100644 --- a/src/grid/widgets/Grid.ts +++ b/src/grid/widgets/Grid.ts @@ -127,7 +127,12 @@ export default class Grid extends ThemedMixin(WidgetBase)> return v('div', { key: 'root', classes: [this.theme(css.root), fixedCss.rootFixed], role: 'table' }); } - return v('div', { key: 'root', classes: [this.theme(css.root), fixedCss.rootFixed], role: 'table' }, [ + return v('div', { + key: 'root', + classes: [this.theme(css.root), fixedCss.rootFixed], + role: 'table', + 'aria-rowcount': meta.total ? `${meta.total}` : null + }, [ v('div', { key: 'header', scrollLeft: this._scrollLeft, diff --git a/src/grid/widgets/Header.ts b/src/grid/widgets/Header.ts index cb57d00552..fd07da9323 100755 --- a/src/grid/widgets/Header.ts +++ b/src/grid/widgets/Header.ts @@ -38,13 +38,15 @@ export default class Header extends ThemedMixin(WidgetBase) { title = column.title; } let headerProperties = {}; + const isSorted = sort && sort.columnId === column.id; + const isSortedAsc = sort && sort.columnId === column.id && sort.direction === 'asc'; if (column.sortable) { headerProperties = { classes: [ this.theme(css.sortable), - sort && sort.columnId === column.id ? this.theme(css.sorted) : null, - sort && sort.columnId === column.id && sort.direction === 'desc' ? this.theme(css.desc) : null, - sort && sort.columnId === column.id && sort.direction === 'asc' ? this.theme(css.asc) : null + isSorted ? this.theme(css.sorted) : null, + isSorted && !isSortedAsc ? this.theme(css.desc) : null, + isSortedAsc ? this.theme(css.asc) : null ], onclick: () => { this._sortColumn(column.id); @@ -52,7 +54,11 @@ export default class Header extends ThemedMixin(WidgetBase) { }; } - return v('div', { classes: [this.theme(css.cell), fixedCss.cellFixed], role: 'columnheader' }, [ + return v('div', { + 'aria-sort': isSorted ? isSortedAsc ? 'ascending' : 'descending' : null, + classes: [this.theme(css.cell), fixedCss.cellFixed], + role: 'columnheader' + }, [ v('div', headerProperties, [ title, column.sortable ? v('button', { diff --git a/src/grid/widgets/Row.ts b/src/grid/widgets/Row.ts index a056124cf8..40b2a75f6d 100755 --- a/src/grid/widgets/Row.ts +++ b/src/grid/widgets/Row.ts @@ -38,6 +38,6 @@ export default class Row extends ThemedMixin(WidgetBase) { [] as DNode[] ); - return v('div', { classes: [this.theme(css.root), fixedCss.rootFixed], role: 'row' }, columns); + return v('div', { classes: [this.theme(css.root), fixedCss.rootFixed], role: 'row', 'aria-rowindex': `${id + 1}` }, columns); } }