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

Table collapse all #1189

Merged
merged 36 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a846d51
Initial changes
atmgrifter00 Apr 14, 2023
2247c27
Merge branch 'main' into table-collapse-all
atmgrifter00 Apr 14, 2023
0c7bd73
Add grouped indicator icon to header.
atmgrifter00 Apr 14, 2023
da65982
Change files
atmgrifter00 Apr 17, 2023
f7755f5
Adding tests.
atmgrifter00 Apr 17, 2023
14dc82f
Fix tests.
atmgrifter00 Apr 17, 2023
b42bd17
Prettier fixes.
atmgrifter00 Apr 17, 2023
7c9e1ce
Remove unused style.
atmgrifter00 Apr 17, 2023
46532dc
Unused styling.
atmgrifter00 Apr 17, 2023
da9d591
Update packages/nimble-components/src/table/components/row/index.ts
atmgrifter00 Apr 18, 2023
4b9a33a
Handling PR feedback.
atmgrifter00 Apr 18, 2023
975013a
Merge branch 'main' into table-collapse-all
atmgrifter00 Apr 18, 2023
b6436a3
Handling PR feedback.
atmgrifter00 Apr 20, 2023
a601399
Merge branch 'main' into table-collapse-all
atmgrifter00 Apr 20, 2023
2e9b0f9
Test fixes.
atmgrifter00 Apr 20, 2023
4447011
Merge branch 'table-collapse-all' of https://github.com/ni/nimble int…
atmgrifter00 Apr 20, 2023
08b7927
Change files
atmgrifter00 Apr 21, 2023
b58f5c7
Add collapse-all button label attribute.
atmgrifter00 Apr 21, 2023
a311180
Change files
atmgrifter00 Apr 21, 2023
df639a2
Handling PR feedback
atmgrifter00 Apr 21, 2023
2f06ae5
Handling PR feedback.
atmgrifter00 Apr 21, 2023
aa1891e
Merge branch 'main' into table-collapse-all
atmgrifter00 Apr 21, 2023
bb16d5c
Comment update.
atmgrifter00 Apr 24, 2023
faf698d
Merge branch 'table-collapse-all' of https://github.com/ni/nimble int…
atmgrifter00 Apr 24, 2023
a93410e
Merge branch 'main' into table-collapse-all
atmgrifter00 Apr 24, 2023
039e6ce
Add collapse all button label to example app
atmgrifter00 Apr 24, 2023
85994c7
Revert "Add collapse-all button label attribute."
atmgrifter00 Apr 24, 2023
1faa735
Revert "Add collapse all button label to example app"
atmgrifter00 Apr 24, 2023
90e426e
Lower accessibility threshold.
atmgrifter00 Apr 24, 2023
af57dc6
Update change files.
atmgrifter00 Apr 25, 2023
c2888e0
Merge branch 'main' into table-collapse-all
atmgrifter00 Apr 25, 2023
4ccff9b
Merge branch 'main' into table-collapse-all
atmgrifter00 Apr 25, 2023
935d8f8
Merge branch 'main' into table-collapse-all
atmgrifter00 Apr 25, 2023
ec9ee95
Handling PR feedback
atmgrifter00 Apr 25, 2023
8239865
Merge branch 'table-collapse-all' of https://github.com/ni/nimble int…
atmgrifter00 Apr 25, 2023
0adc7fd
Slight name change
atmgrifter00 Apr 25, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
jattasNI marked this conversation as resolved.
Show resolved Hide resolved
"type": "patch",
"comment": "Add collapse-all button and grouping indicator to column headers",
"packageName": "@ni/nimble-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ export const TableColumnSortOperation = {
export type TableColumnSortOperation =
(typeof TableColumnSortOperation)[keyof typeof TableColumnSortOperation];

export const defaultMinPixelWidth = 88;
export const defaultMinPixelWidth = 121;
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved

export const defaultFractionalWidth = 1;
12 changes: 6 additions & 6 deletions packages/nimble-components/src/table/components/cell/styles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { css } from '@microsoft/fast-element';
import { display } from '@microsoft/fast-foundation';
import { standardPadding } from '../../../theme-provider/design-tokens';
import {
smallPadding,
standardPadding
} from '../../../theme-provider/design-tokens';

export const styles = css`
${display('grid')}
Expand All @@ -9,11 +12,8 @@ export const styles = css`
--ni-private-table-cell-nesting-level: 0;
padding: 0px calc(${standardPadding} / 2);
padding-left: calc(
${standardPadding} / 2 +
(
var(--ni-private-table-cell-nesting-level) *
${standardPadding} * 2
)
${smallPadding} * 2 + ${standardPadding} * 2 *
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved
var(--ni-private-table-cell-nesting-level)
);
align-self: center;
height: 100%;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { attr } from '@microsoft/fast-element';
import { attr, observable } from '@microsoft/fast-element';
import { DesignSystem, FoundationElement } from '@microsoft/fast-foundation';
import { TableColumnSortDirection } from '../../types';
import { styles } from './styles';
Expand All @@ -21,6 +21,9 @@ export class TableHeader extends FoundationElement {
@attr({ attribute: 'first-sorted-column', mode: 'boolean' })
public firstSortedColumn = false;

@observable
public isGrouped = false;

protected sortDirectionChanged(
_prev: TableColumnSortDirection | undefined,
_next: TableColumnSortDirection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ export const styles = css`
color: ${tableHeaderFontColor};
${iconColor.cssCustomProperty}: ${tableHeaderFontColor};
text-transform: uppercase;
gap: calc(${standardPadding} / 2);
}

.sort-indicator {
padding: 0px calc(${standardPadding} / 2);
.sort-indicator,
.grouped-indicator {
flex: 0 0 auto;
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { html, when } from '@microsoft/fast-element';
import type { TableHeader } from '.';
import { iconArrowDownTag } from '../../../icons/arrow-down';
import { iconArrowUpTag } from '../../../icons/arrow-up';
import { iconTwoSquaresInBracketsTag } from '../../../icons/two-squares-in-brackets';
import { TableColumnSortDirection } from '../../types';

// prettier-ignore
Expand All @@ -15,5 +16,8 @@ export const template = html<TableHeader>`
${when(x => x.sortDirection === TableColumnSortDirection.descending, html`
<${iconArrowDownTag} class="sort-indicator" aria-hidden="true"></${iconArrowDownTag}>
`)}
${when(x => x.isGrouped, html`
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved
<${iconTwoSquaresInBracketsTag} class="grouped-indicator"></${iconTwoSquaresInBracketsTag}>
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved
`)}
</template>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { TableHeader } from '..';
import { iconArrowDownTag } from '../../../../icons/arrow-down';
import { iconArrowUpTag } from '../../../../icons/arrow-up';

/**
* Page object for the `nimble-table-header` component to provide consistent ways
* of querying and interacting with the component during tests.
*/
export class TableHeaderPageObject {
public constructor(private readonly tableElement: TableHeader) {}

public isSortAscendingIconVisible(): boolean {
return this.getSortAscendingIcon() !== null;
}

public isSortDescendingIconVisible(): boolean {
return this.getSortDescendingIcon() !== null;
}

public isGroupIndicatorIconVisible(): boolean {
return this.getGroupIndicatorIcon() !== null;
}

private getSortAscendingIcon(): HTMLElement | null {
return this.tableElement.shadowRoot!.querySelector(
`${iconArrowUpTag}.sort-indicator`
);
}

private getSortDescendingIcon(): HTMLElement | null {
return this.tableElement.shadowRoot!.querySelector(
`${iconArrowDownTag}.sort-indicator`
);
}

private getGroupIndicatorIcon(): HTMLElement | null {
return this.tableElement.shadowRoot!.querySelector(
'.grouped-indicator'
);
}
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,25 @@
import { html } from '@microsoft/fast-element';
import { TableHeader } from '..';
import { iconArrowDownTag } from '../../../../icons/arrow-down';
import { iconArrowUpTag } from '../../../../icons/arrow-up';
import { waitForUpdatesAsync } from '../../../../testing/async-helpers';
import { type Fixture, fixture } from '../../../../utilities/tests/fixture';
import { TableColumnSortDirection } from '../../../types';
import { TableHeaderPageObject } from './table-header-pageobject';

async function setup(): Promise<Fixture<TableHeader>> {
return fixture<TableHeader>(
html`<nimble-table-header> </nimble-table-header>`
);
}

function getSortIcons(element: TableHeader): {
ascendingIcon: HTMLElement | null,
descendingIcon: HTMLElement | null
} {
return {
ascendingIcon: element.shadowRoot!.querySelector(
`${iconArrowUpTag}.sort-indicator`
),
descendingIcon: element.shadowRoot!.querySelector(
`${iconArrowDownTag}.sort-indicator`
)
};
}

describe('TableHeader', () => {
let element: TableHeader;
let pageObject: TableHeaderPageObject;
let connect: () => Promise<void>;
let disconnect: () => Promise<void>;

beforeEach(async () => {
({ element, connect, disconnect } = await setup());
pageObject = new TableHeaderPageObject(element);
await connect();
});

Expand All @@ -55,9 +42,8 @@ describe('TableHeader', () => {

it('has correct state when not sorted', () => {
expect(element.hasAttribute('aria-sort')).toBeFalse();
const sortIcons = getSortIcons(element);
expect(sortIcons.ascendingIcon).toBeFalsy();
expect(sortIcons.descendingIcon).toBeFalsy();
expect(pageObject.isSortAscendingIconVisible()).toBeFalse();
expect(pageObject.isSortDescendingIconVisible()).toBeFalse();
});

// Firefox skipped, see: https://github.com/ni/nimble/issues/1075
Expand All @@ -67,9 +53,8 @@ describe('TableHeader', () => {
await waitForUpdatesAsync();

expect(element.getAttribute('aria-sort')).toEqual('ascending');
const sortIcons = getSortIcons(element);
expect(sortIcons.ascendingIcon).toBeTruthy();
expect(sortIcons.descendingIcon).toBeFalsy();
expect(pageObject.isSortAscendingIconVisible()).toBeTrue();
expect(pageObject.isSortDescendingIconVisible()).toBeFalse();
});

// Firefox skipped, see: https://github.com/ni/nimble/issues/1075
Expand All @@ -79,9 +64,8 @@ describe('TableHeader', () => {
await waitForUpdatesAsync();

expect(element.getAttribute('aria-sort')).toEqual('descending');
const sortIcons = getSortIcons(element);
expect(sortIcons.ascendingIcon).toBeFalsy();
expect(sortIcons.descendingIcon).toBeTruthy();
expect(pageObject.isSortAscendingIconVisible()).toBeFalse();
expect(pageObject.isSortDescendingIconVisible()).toBeTrue();
});

it('does not configure aria-sort if it is not the first sorted column', async () => {
Expand All @@ -90,8 +74,19 @@ describe('TableHeader', () => {
await waitForUpdatesAsync();

expect(element.hasAttribute('aria-sort')).toBeFalse();
const sortIcons = getSortIcons(element);
expect(sortIcons.ascendingIcon).toBeFalsy();
expect(sortIcons.descendingIcon).toBeTruthy();
expect(pageObject.isSortAscendingIconVisible()).toBeFalse();
expect(pageObject.isSortDescendingIconVisible()).toBeTrue();
});

it('displays grouping indicator icon when grouped', async () => {
element.isGrouped = true;
await waitForUpdatesAsync();

expect(pageObject.isGroupIndicatorIconVisible()).toBeTrue();
});

it('grouping indicator icon is not shown when not grouped', () => {
expect(element.isGrouped).toBeFalse();
expect(pageObject.isGroupIndicatorIconVisible()).toBeFalse();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export class TableRow<
columnConfig: {}
};
}
const cellIndentLevel = i === 0 ? this.nestingLevel : 0;
const cellIndentLevel = i === 0 && this.nestingLevel > 0 ? this.nestingLevel - 1 : 0;
return { column, cellState, cellIndentLevel };
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export const styles = css`
padding-left: 0px;
}

.row-front-spacer {
width: ${controlHeight};
flex: 0 0 auto;
}

.cell-container {
display: grid;
width: 100%;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export const template = html<TableRow>`
</${checkboxTag}>
</span>
`)}
${'' /* This is needed to help align the cell widths exactly with the column headers, due to the space reserved for
the collapse-all button in the header. */}
<span class="row-front-spacer"></span>
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved

<span ${ref('cellContainer')} class="cell-container">
${repeat(x => x.columnStates, html<ColumnState, TableRow>`
Expand Down
22 changes: 21 additions & 1 deletion packages/nimble-components/src/table/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ interface TableRowState<TData extends TableRecord = TableRecord> {
isGrouped: boolean;
groupRowValue?: unknown;
isExpanded: boolean;
nestingLevel?: number;
nestingLevel: number;
leafItemCount?: number;
groupColumn?: TableColumn;
}
Expand Down Expand Up @@ -155,6 +155,12 @@ export class Table<
@observable
public readonly selectionCheckbox?: Checkbox;

/**
* @internal
*/
@observable
public showCollapseAll = false;

/**
* @internal
*/
Expand Down Expand Up @@ -394,6 +400,16 @@ export class Table<
}
}

/** @internal */
public handleCollapseAllGroupRows(): void {
this.collapsedRows.clear();
this.table
.getRowModel()
.flatRows.filter(row => row.getIsGrouped())
.forEach(row => this.collapsedRows.add(row.id));
this.table.toggleAllRowsExpanded(false);
}

/** @internal */
public handleGroupRowExpanded(rowIndex: number, event: Event): void {
this.toggleGroupExpanded(rowIndex);
Expand All @@ -416,6 +432,10 @@ export class Table<
if (this.updateTracker.updateColumnWidths) {
this.updateRowGridColumns();
}

if (this.updateTracker.updateGroupRows) {
this.showCollapseAll = this.getColumnsParticipatingInGrouping().length > 0;
}
}

public override get ariaMultiSelectable(): 'true' | 'false' | null {
Expand Down
14 changes: 14 additions & 0 deletions packages/nimble-components/src/table/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
applicationBackgroundColor,
bodyFont,
bodyFontColor,
controlSlimHeight,
fillHoverColor,
fillHoverSelectedColor,
fillSelectedColor,
smallPadding,
standardPadding
} from '../theme-provider/design-tokens';
import { Theme } from '../theme-provider/types';
Expand Down Expand Up @@ -63,6 +65,7 @@ export const styles = css`
width: fit-content;
min-width: 100%;
left: var(--ni-private-table-scroll-x);
align-items: center;
}

.column-header-container {
Expand All @@ -71,6 +74,17 @@ export const styles = css`
grid-template-columns: var(--ni-private-table-row-grid-columns) auto;
}

.collapse-all-button {
width: ${controlSlimHeight};
height: ${controlSlimHeight};
margin-left: calc(${smallPadding} * 2);
visibility: hidden;
}

.collapse-all-button.visible {
visibility: visible;
}

.header-scrollbar-spacer {
width: var(--ni-private-table-header-scrollbar-spacer-width);
}
Expand Down
18 changes: 16 additions & 2 deletions packages/nimble-components/src/table/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import {
TableRowSelectionToggleEventDetail
} from './types';
import { tableGroupRowTag } from './components/group-row';
import { buttonTag } from '../button';
import { ButtonAppearance } from '../button/types';
import { iconTriangleTwoLinesHorizontalTag } from '../icons/triangle-two-lines-horizontal';
import { checkboxTag } from '../checkbox';

// prettier-ignore
Expand All @@ -33,7 +36,8 @@ export const template = html<Table>`
--ni-private-table-header-scrollbar-spacer-width: ${x => x.virtualizer.headerContainerMarginRight}px;
--ni-private-table-scroll-height: ${x => x.virtualizer.allRowsHeight}px;
--ni-private-table-row-container-top: ${x => x.virtualizer.rowContainerYOffset}px;
--ni-private-table-row-grid-columns: ${x => x.rowGridColumns ?? ''}
--ni-private-table-row-grid-columns: ${x => x.rowGridColumns ?? ''};
--ni-private-collapse-button-visibility: ${x => (x.showCollapseAll ? 'visible' : 'hidden')};
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved
">
<div role="rowgroup" class="header-container">
<div class="header-row" role="row">
Expand All @@ -47,14 +51,24 @@ export const template = html<Table>`
</${checkboxTag}>
</span>
`)}

<span role="gridcell">
<${buttonTag}
class="${x => `collapse-all-button ${(x.showCollapseAll ? 'visible' : '')}`}"
atmgrifter00 marked this conversation as resolved.
Show resolved Hide resolved
content-hidden
appearance="${ButtonAppearance.ghost}"
@click="${x => x.handleCollapseAllGroupRows()}"
>
<${iconTriangleTwoLinesHorizontalTag} slot="start"></${iconTriangleTwoLinesHorizontalTag}>
</${buttonTag}>
</span>
<span class="column-header-container">
${repeat(x => x.columns, html<TableColumn>`
${when(x => !x.columnHidden, html<TableColumn, Table>`
<${tableHeaderTag}
class="header"
sort-direction="${x => (typeof x.sortIndex === 'number' ? x.sortDirection : TableColumnSortDirection.none)}"
?first-sorted-column="${(x, c) => x === c.parent.firstSortedColumn}"
:isGrouped=${x => (typeof x.columnInternals.groupIndex === 'number' && !x.columnInternals.groupingDisabled)}
>
<slot name="${x => x.slot}"></slot>
</${tableHeaderTag}>
Expand Down
Loading