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

Initial version of date column #1363

Merged
merged 15 commits into from
Jul 24, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Initial version of date column",
m-akinc marked this conversation as resolved.
Show resolved Hide resolved
"packageName": "@ni/nimble-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { DesignSystem } from '@microsoft/fast-foundation';
import { template } from '../../text-base/cell-view/template';
import type {
TableColumnDateTextCellRecord,
TableColumnDateTextColumnConfig
} from '..';
import { styles } from '../../text-base/cell-view/styles';
import { TableColumnTextCellViewBase } from '../../text-base/cell-view';

declare global {
interface HTMLElementTagNameMap {
'nimble-table-column-date-text-cell-view': TableColumnDateTextCellView;
}
}

/**
* A cell view for displaying date/time fields as text
*/
export class TableColumnDateTextCellView extends TableColumnTextCellViewBase<
TableColumnDateTextCellRecord,
TableColumnDateTextColumnConfig
> {
private columnConfigChanged(): void {
this.placeholder = this.columnConfig.placeholder;
m-akinc marked this conversation as resolved.
Show resolved Hide resolved
}

private cellRecordChanged(): void {
if (typeof this.cellRecord.value === 'number') {
const formatter = new Intl.DateTimeFormat(undefined, {
dateStyle: 'medium',
timeStyle: 'medium'
});
m-akinc marked this conversation as resolved.
Show resolved Hide resolved
try {
this.text = formatter
.format(this.cellRecord.value)
.replace('\u202f', ' '); // on Chrome, the space before AM/PM is a narrow non-breaking space. For testing consistency across browsers, replace it with a regular space.
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure this is a buggy behavior worth overriding. Did you check from that perspective?

If the goal is just making tests easier it should be a part of the page object. I'm not sure there is a clear benefit in trying to override the details of the native date formatter to get the exact same rendered text across browsers. Is there a specific argument for that?

One argument against could be that many apps are likely to use the same formatter. So our date text will render similarly to most other web apps in Chrome.

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 think it's a bug--just a mostly trivial difference between how each browser formats the date. It seems more targeted to doing the replacement here than in the page object. If I do it in the page object, I either have to do this substitution for all text rendered in cells, or add an esoteric new function to fetch a rendered date from a cell. I don't see any practical downsides to replacing one narrow non-breaking space with a regular space. That was my thought process, at least.

Copy link
Member

Choose a reason for hiding this comment

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

Seems like the date table column could have it's own page object

Copy link
Member

@rajsite rajsite Jul 19, 2023

Choose a reason for hiding this comment

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

edit: removed, wasn't very productive

this.shouldUsePlaceholder = false;
} catch (e) {
this.text = '';
this.shouldUsePlaceholder = true;
}
} else {
this.text = '';
this.shouldUsePlaceholder = true;
}
}
}

const dateTextCellView = TableColumnDateTextCellView.compose({
baseName: 'table-column-date-text-cell-view',
template,
styles
});
DesignSystem.getOrCreate().withPrefix('nimble').register(dateTextCellView());
export const tableColumnDateTextCellViewTag = DesignSystem.tagFor(
TableColumnDateTextCellView
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { DesignSystem } from '@microsoft/fast-foundation';
import type { TableNumberFieldValue } from '../../../table/types';
import { TableColumnTextGroupHeaderViewBase } from '../../text-base/group-header-view';
import { template } from '../../text-base/group-header-view/template';
import { styles } from '../../text-base/group-header-view/styles';
import type { TableColumnDateTextColumnConfig } from '..';

declare global {
interface HTMLElementTagNameMap {
'nimble-table-column-date-text-group-header': TableColumnDateTextGroupHeaderView;
}
}
/**
* The group header view for displaying date/time fields as text.
*/
export class TableColumnDateTextGroupHeaderView extends TableColumnTextGroupHeaderViewBase<
TableNumberFieldValue,
TableColumnDateTextColumnConfig
> {
private columnConfigChanged(): void {
this.placeholder = this.columnConfig?.placeholder ?? '';
}

private groupHeaderValueChanged(): void {
if (typeof this.groupHeaderValue === 'number') {
const formatter = new Intl.DateTimeFormat(undefined, {
m-akinc marked this conversation as resolved.
Show resolved Hide resolved
dateStyle: 'medium',
timeStyle: 'medium'
});
try {
this.text = formatter
.format(this.groupHeaderValue)
.replace('\u202f', ' '); // on Chrome, the space before AM/PM is a narrow non-breaking space. For testing consistency across browsers, replace it with a regular space.
this.shouldUsePlaceholder = false;
} catch (e) {
this.text = '';
this.shouldUsePlaceholder = true;
}
} else {
this.text = '';
this.shouldUsePlaceholder = true;
}
}
}

const tableColumnDateTextGroupHeaderView = TableColumnDateTextGroupHeaderView.compose({
baseName: 'table-column-date-text-group-header',
template,
styles
});
DesignSystem.getOrCreate()
.withPrefix('nimble')
.register(tableColumnDateTextGroupHeaderView());
export const tableColumnDateTextGroupHeaderTag = DesignSystem.tagFor(
TableColumnDateTextGroupHeaderView
);
49 changes: 49 additions & 0 deletions packages/nimble-components/src/table-column/date-text/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { DesignSystem } from '@microsoft/fast-foundation';
import { styles } from '../base/styles';
import { template } from '../base/template';
import type { TableNumberField } from '../../table/types';
import { TableColumnTextBase } from '../text-base';
import {
TableColumnWithPlaceholderColumnConfig,
TableColumnSortOperation
} from '../base/types';
import { tableColumnDateTextGroupHeaderTag } from './group-header-view';
import { tableColumnDateTextCellViewTag } from './cell-view';
import type { ColumnInternalsOptions } from '../base/models/column-internals';

export type TableColumnDateTextCellRecord = TableNumberField<'value'>;
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface TableColumnDateTextColumnConfig
extends TableColumnWithPlaceholderColumnConfig {}

declare global {
interface HTMLElementTagNameMap {
'nimble-table-column-date-text': TableColumnDateText;
}
}

/**
* The table column for displaying dates/times as text.
*/
export class TableColumnDateText extends TableColumnTextBase {
protected override getColumnInternalsOptions(): ColumnInternalsOptions {
return {
cellRecordFieldNames: ['value'],
cellViewTag: tableColumnDateTextCellViewTag,
groupHeaderViewTag: tableColumnDateTextGroupHeaderTag,
delegatedEvents: [],
sortOperation: TableColumnSortOperation.basic
};
}
}

const nimbleTableColumnDateText = TableColumnDateText.compose({
baseName: 'table-column-date-text',
template,
styles
});

DesignSystem.getOrCreate()
.withPrefix('nimble')
.register(nimbleTableColumnDateText());
export const tableColumnDateTextTag = DesignSystem.tagFor(TableColumnDateText);
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { StoryFn, Meta } from '@storybook/html';
import { html, ViewTemplate } from '@microsoft/fast-element';
import { createMatrixThemeStory } from '../../../utilities/tests/storybook';
import {
createMatrix,
sharedMatrixParameters
} from '../../../utilities/tests/matrix';
import { tableColumnDateTextTag } from '..';
import { iconUserTag } from '../../../icons/user';
import { Table, tableTag } from '../../../table';

const metadata: Meta = {
title: 'Tests/Table Column Types',
parameters: {
...sharedMatrixParameters()
}
};

export default metadata;

const data = [
{
id: '0',
date: new Date('Dec 21, 2020, 3:45:03 PM').valueOf()
},
{
id: '1',
date: new Date('Dec 21, 2020, 3:45:03 PM').valueOf()
},
{
id: '2'
}
] as const;

// prettier-ignore
const component = (): ViewTemplate => html`
<label style="color: var(--ni-nimble-control-label-font-color); font: var(--ni-nimble-control-label-font)">Date Text Table Column</label>
m-akinc marked this conversation as resolved.
Show resolved Hide resolved
<${tableTag} id-field-name="id" style="height: 250px">
<${tableColumnDateTextTag}
field-name="date"
placeholder="no value"
group-index="0"
>
<${iconUserTag}></${iconUserTag}>
</${tableColumnDateTextTag}>
</${tableTag}>
`;

export const tableColumnDateTextThemeMatrix: StoryFn = createMatrixThemeStory(
createMatrix(component)
);

tableColumnDateTextThemeMatrix.play = async (): Promise<void> => {
await Promise.all(
Array.from(document.querySelectorAll<Table>('nimble-table')).map(
async table => {
await table.setData(data);
}
)
);
};
Loading