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

va-table: Adds ability to arbitrarily right-align columns #1486

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
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
11 changes: 10 additions & 1 deletion packages/storybook/stories/va-table-uswds.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const Template = args => {
scrollable,
striped,
'full-width': fullWidth,
'right-align-cols': rightAlignCols,
} = args;

return (
Expand All @@ -60,6 +61,7 @@ const Template = args => {
sortable={!!sortable}
striped={striped}
full-width={fullWidth}
right-align-cols={rightAlignCols}
>
<va-table-row>
{columns.map((col, i) => (
Expand Down Expand Up @@ -460,4 +462,11 @@ FullWidth.args = {
'full-width': true,
};

Default.argTypes = propStructure(vaTableDocs);
export const RightAlignedColumns = Template.bind(null);
RightAlignedColumns.args = {
'table-title':
'This is a regular table with the second and third column right-aligned.',
'rows': FullWidthRows,
'columns': FullWidthColumns,
'right-align-cols': '1,2',
};
16 changes: 16 additions & 0 deletions packages/web-components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,10 @@ export namespace Components {
* When active, forces the table to expand to the full-width of its container
*/
"fullWidth"?: boolean;
/**
* A comma-separated, zero-indexed string of which columns, if any, should be right-aligned
*/
"rightAlignCols"?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to do this without having a "complex" data string to parse the column values. Like could they just add a prop (boolean) to va-table-row for each column they want to be right aligned?

<va-table-row right-aligned>

Screenshot 2025-02-07 at 10 26 45 AM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was my belief, shared by Micah, that having a single prop at the va-table component level was going to be preferable to asking developers to add a prop to every row or every desired span. If we did it at the row level, it would still need to be a complex prop to define which spans within the row needed to be right-aligned. If we did it at the span level, devs would either need to manually or programmatically add the .vads-u-text-align--right class to just the spans they wanted right-aligned, increasing the chances of error and just generally making for an unpleasant developer experience.

Copy link
Contributor

@jamigibbs jamigibbs Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I feel like I would want more flexibility to be able to add a styling class where I needed it.

Is it always the case that all cells in a column must be right aligned? Is there a situation that some cells would not be right aligned (mixed alignment)?

Sorry for all the questions if there was already conversation about this. Less code is less bugs for us. 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify, I'm ultimately asking if this can just be accomplished with guidelines especially if it's going to get complicated with mixed cell alignments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know about mixed cell alignments, though I would suspect that'd make for a very unattractive-looking table. The ticket just called for alignment options for columns.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If anyone wanted to manually align individual cells, they'd still have the option to add whatever classes they wanted to the individual spans. This just gives a convenient way to right-align entire columns.

/**
* When active, the table can be horizontally scrolled and is focusable
*/
Expand Down Expand Up @@ -1634,6 +1638,10 @@ export namespace Components {
* When active, the table will expand to the full width of its container
*/
"fullWidth": boolean;
/**
* A comma-separated, zero-indexed string of which columns, if any, should be right-aligned
*/
"rightAlignCols"?: string;
"rows"?: number;
/**
* When active, the table can be horizontally scrolled and is focusable
Expand Down Expand Up @@ -5070,6 +5078,10 @@ declare namespace LocalJSX {
* When active, forces the table to expand to the full-width of its container
*/
"fullWidth"?: boolean;
/**
* A comma-separated, zero-indexed string of which columns, if any, should be right-aligned
*/
"rightAlignCols"?: string;
/**
* When active, the table can be horizontally scrolled and is focusable
*/
Expand Down Expand Up @@ -5113,6 +5125,10 @@ declare namespace LocalJSX {
* Fires when the component is closed by clicking on the close icon. This fires only when closeable is true.
*/
"onSortTable"?: (event: VaTableInnerCustomEvent<any>) => void;
/**
* A comma-separated, zero-indexed string of which columns, if any, should be right-aligned
*/
"rightAlignCols"?: string;
"rows"?: number;
/**
* When active, the table can be horizontally scrolled and is focusable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,15 @@ function getTableMarkup(props = {}): string {
</va-table>`;
}

describe('renders header row', () => {
describe('va-table', () => {
it('renders a va-table-inner with va-table-rows inside', async () => {
const page = await newE2EPage();
await page.setContent(getTableMarkup());

const headerRow = await page.find('va-table-inner >>> va-table-row');
expect(headerRow).toBeDefined();
});
});

describe('renders table element', () => {
it('renders a va-table-inner with an HTML table inside', async () => {
const page = await newE2EPage();
await page.setContent(getTableMarkup());
Expand All @@ -84,7 +82,7 @@ describe('renders table element', () => {

it('is not stacked by when attribute is set to false', async () => {
const page = await newE2EPage();
await page.setContent(getTableMarkup({stacked: 'false'}));
await page.setContent(getTableMarkup({ stacked: 'false' }));
const element = await page.find('va-table-inner >>> table');

expect(element).not.toHaveClass('usa-table--stacked');
Expand All @@ -97,4 +95,4 @@ describe('renders table element', () => {

expect(element).toHaveClass('usa-table--stacked');
});
})
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { numSort } from '../../sort/numerical';
import { dateSort } from '../../sort/date';
import { _getCompareFunc } from '../../sort/utils';

describe('va-table', () => {
describe('va-table-inner', () => {
function makeTable(props = {}) {
const defaultProps = {...props, 'table-title': 'this is a caption'};
const defaultProps = { ...props, 'table-title': 'this is a caption' };
return `<va-table ${Object.entries(defaultProps)
.map(([key, value]) => `${key}="${value}"`)
.join(' ')}>
Expand Down Expand Up @@ -64,15 +64,15 @@ describe('va-table', () => {

it('is scrollable when attribute is set to true', async () => {
const page = await newE2EPage();
await page.setContent(makeTable({scrollable: 'true'}));
await page.setContent(makeTable({ scrollable: 'true' }));
const table = await page.find('va-table-inner >>> div');
expect(table.getAttribute('tabindex')).toEqual('0');
expect(table).toHaveClass('usa-table-container--scrollable');
});

it('is not stacked by when attribute is set to false', async () => {
const page = await newE2EPage();
await page.setContent(makeTable({stacked: 'false'}));
await page.setContent(makeTable({ stacked: 'false' }));
const table = await page.find('va-table-inner >>> table');
expect(table).not.toHaveClass('usa-table--stacked');
});
Expand Down Expand Up @@ -112,7 +112,7 @@ describe('va-table', () => {

it('has the USWDS striped class when striped is true', async () => {
const page = await newE2EPage();
await page.setContent(makeTable({striped: 'true'}));
await page.setContent(makeTable({ striped: 'true' }));
const table = await page.find('va-table-inner >>> .usa-table');
expect(table).toHaveClass('usa-table--striped');
});
Expand All @@ -130,6 +130,24 @@ describe('va-table', () => {
const divEl = await page.find('va-table-inner >>> div');
expect(divEl).toHaveClass('va-table--full-width');
});

it('does not have any right-aligned columns by default', async () => {
const page = await newE2EPage();
await page.setContent(makeTable());
const rightEl = await page.find(
'va-table-inner >>> .vads-u-text-align--right',
);
expect(rightEl).toBeNull();
});

it('has the text-right-align class when right-align-cols is set', async () => {
const page = await newE2EPage();
await page.setContent(makeTable({ 'right-align-cols': '2' }));
const rightEl = await page.find(
'va-table-inner >>> .vads-u-text-align--right',
);
expect(rightEl).toBeDefined();
});
});

describe('sorted va-table ', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@forward 'settings';
@use 'usa-table/src/styles/usa-table';
@use 'uswds-helpers/src/styles/usa-sr-only';
@import '~@department-of-veterans-affairs/css-library/dist/stylesheets/utilities.css';

:host {
td slot::slotted(span:empty)::before,
Expand Down Expand Up @@ -47,6 +48,10 @@
}
}

:host .usa-table th[data-sortable="false"] {
padding-right: 0;
}

::slotted([slot='headers']) {
background: var(--vads-color-base-lightest);
font-weight: 700;
Expand Down Expand Up @@ -74,5 +79,10 @@ caption {
.usa-table--stacked th,
.usa-table--stacked td {
width: auto;

// Ensures table headers stay left-aligned even when stacked
&::before {
text-align: left;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ export class VaTableInner {
*/
@Prop() fullWidth: boolean = false;

/**
* A comma-separated, zero-indexed string of which columns, if any, should be right-aligned
*/
@Prop() rightAlignCols?: string;

// Internal 'holder' for the array of columns to right-align, updated in componentWillRender
colsToAlign: Array<number>;

/**
* If sortable is true, the direction of next sort for the column that was just sorted
*/
Expand Down Expand Up @@ -172,11 +180,16 @@ export class VaTableInner {
const header = this.parseHTMLToString(
this.el.querySelector(`[slot="va-table-slot-${i}"]`).innerHTML,
);
let rightAlignClass: string;
// Checks if this cell should be right-aligned and adds a class to make it so
if (this.colsToAlign !== undefined && this.colsToAlign.includes(i)) {
rightAlignClass = 'vads-u-text-align--right';
}
const dataSortActive = row > 0 && this.sortindex === i ? true : false;
return i === 0 || row === 0 ? (
<th
scope={scopeDimension}
data-sortable
data-sortable={this.sortable}
data-sort-active={dataSortActive}
data-label={header}
data-rowindex={i}
Expand All @@ -186,7 +199,11 @@ export class VaTableInner {
{this.getSortIcon(i, row)}
</th>
) : (
<td data-label={header} data-sort-active={dataSortActive}>
<td
class={rightAlignClass}
data-label={header}
data-sort-active={dataSortActive}
>
{slot}
</td>
);
Expand Down Expand Up @@ -298,6 +315,20 @@ export class VaTableInner {
}
}

componentWillRender() {
// If there are right-aligned columns, split and format them and save in memory for performance reasons
if (this.rightAlignCols !== undefined) {
const cols = this.rightAlignCols.split(',').map(val => Number(val));
if (cols.some(col => isNaN(col))) {
console.warn(
'There was a non-number detected in the right-align-cols/rightAlignCols prop, please ensure that this value is a comma-separated string of zero-indexed numbers, where each number represents a column you wish to right-align.',
);
} else {
this.colsToAlign = cols;
}
}
}

/**
* we must update the table after render due to content being projected into slots
* 1. add aria-labels to the th elements in the theader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ va-table > va-table-row[slot] button svg {

@media screen and (min-width: 768px) {
va-table-row span.text-align-right {
text-align: right!important;
text-align: right !important;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ export class VaTable {
*/
@Prop() fullWidth?: boolean = false;

/**
* A comma-separated, zero-indexed string of which columns, if any, should be right-aligned
*/
@Prop() rightAlignCols?: string;

// The number of va-table-rows
@State() rows: number;

Expand Down Expand Up @@ -162,6 +167,10 @@ export class VaTable {
vaTable.setAttribute('full-width', String(this.fullWidth));
}

if (this.rightAlignCols) {
vaTable.setAttribute('right-align-cols', this.rightAlignCols);
}

//make a fragment containing all the cells, one for each slot
const frag = this.makeFragment();
vaTable.appendChild(frag);
Expand Down
Loading