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

HLD: API for number-text and date-text table columns #1268

Merged
merged 30 commits into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f71e923
Fix link format
jattasNI May 15, 2023
e8065d4
Number column proposal and other column ideas
jattasNI May 15, 2023
d1a3eb4
no toString
jattasNI May 15, 2023
b86f82d
Date column
jattasNI May 15, 2023
8ac8324
Change files
jattasNI May 15, 2023
2899f23
More alignment and format proposals
jattasNI May 23, 2023
15d4dc2
Merge branch 'main' into users/jattas/intl-table-columns-hld
jattasNI May 23, 2023
6876a59
Updates after self review
jattasNI May 23, 2023
c0c359b
Merge branch 'main' into users/jattas/intl-table-columns-hld
jattasNI May 23, 2023
8eaad0c
Link to docs about locales
jattasNI May 25, 2023
099ef73
undefined for default attributes
jattasNI May 25, 2023
497ed64
Mention RelativeTimeFormat
jattasNI Jun 6, 2023
fb8b65d
Prefer no prefix/suffix
jattasNI Jun 6, 2023
1e44e89
*-field-name attributes as alternative
jattasNI Jun 6, 2023
6e44394
Data type for dates
jattasNI Jun 6, 2023
4a30a1c
Merge branch 'main' into users/jattas/intl-table-columns-hld
jattasNI Jun 6, 2023
c2d2811
Mapping column link
jattasNI Jun 6, 2023
a58e3c3
Validity
jattasNI Jun 6, 2023
8d56363
Experimental config
jattasNI Jun 6, 2023
26c896e
Update experimental guidance
jattasNI Jun 6, 2023
f4b4be5
Fix format type names
jattasNI Jun 12, 2023
1715c87
locale deisgn token
jattasNI Jun 12, 2023
810ec6f
null date
jattasNI Jun 12, 2023
4da9db4
Merge branch 'main' into users/jattas/intl-table-columns-hld
jattasNI Jun 12, 2023
d744149
Better plan for custom columns
jattasNI Jun 13, 2023
a9c49e4
attribute lang instead of locale
jattasNI Jun 13, 2023
c02c961
Merge branch 'main' into users/jattas/intl-table-columns-hld
jattasNI Jun 13, 2023
1bb063e
Merge branch 'main' into users/jattas/intl-table-columns-hld
jattasNI Jun 13, 2023
42a8d02
RTL notes
jattasNI Jun 15, 2023
3605283
Merge branch 'main' into users/jattas/intl-table-columns-hld
jattasNI Jun 15, 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 @@
{
"type": "none",
"comment": "HLD updates for number and date table columns",
"packageName": "@ni/nimble-components",
"email": "[email protected]",
"dependentChangeType": "none"
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,13 @@ This is prototyped in the [formatted-text-column branch](https://github.com/ni/n

jattasNI marked this conversation as resolved.
Show resolved Hide resolved
Nimble will provide several columns that derive from the above base classes and provide higher level text formatting APIs for specific data types.

All columns will include a `field-name` string attribute to select the field to display and a `placeholder` string attribute to specify the text to display if the field value isn't in the expected format (more details about expected formats for each column type).

All columns will offer standard Angular and Blazor integration.

#### Column naming

Follows the [Column Type Philosophy](/packages/nimble-components/src/table/specs
/table-columns-hld.md#column-type-philosophy) where:
Follows the [Column Type Philosophy](/packages/nimble-components/src/table/specs/table-columns-hld.md#column-type-philosophy) where:

- category: `table-column`
- presentation: `text`
Expand All @@ -176,60 +179,101 @@ Follows the [Column Type Philosophy](/packages/nimble-components/src/table/specs

`nimble-table-column-text` will continue to present the same API it does today, but will derive from the base classes described above.

#### Numeric column
#### Number column

Nimble will introduce `nimble-table-column-number-text` which formats a numeric field value and displays it as text. It will offer sufficient configuration to support use cases 1-4 above.

##### API
jattasNI marked this conversation as resolved.
Show resolved Hide resolved

Nimble could introduce `nimble-table-column-numeric-text` which formats a numeric field value and displays it as text. It will offer sufficient configuration to support use cases 1-4 above.
- `prefix` - a string which will be appended before each value (e.g. `'$'`). Defaults to `''`.
- `suffix` - a string which will be appended after each value (e.g. `'%'` or `' V'`). Defaults to `''`. Spacing will be at the discretion of clients, but Nimble will recommend including a space before the unit except for symbol units like `%`, `"`, `Β°`, and `Β°C`. This [matches the Chicago Manual of Style](https://www.chicagomanualofstyle.org/book/ed17/part2/ch09/psec016.html) (requires VPN) which is NI's documentation style guide.
jattasNI marked this conversation as resolved.
Show resolved Hide resolved
- `alignment` - a string value matching `"left"`, `"right"`, or `undefined` (the default, meaning `"automatic"`) which controls whether values and column headers are left or right aligned within the column. If set to `undefined` Nimble will choose left or right based on the value of `format`. Clients should select `right` if it is known that the decimal separators of all values in the column will align in the given the `format`.
jattasNI marked this conversation as resolved.
Show resolved Hide resolved
- `format` - a string which controls how the number is formatted for display. It can take one of the following values:
- `undefined` - use a default formatter, which will format similarly to [`Number.toString()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString#description). This displays integers with no trailing zeros, limits to about 16 significant digits, and switches to exponential notation for very large and small numbers. Instead of using `toString()` it will be implemented using `Intl.NumberFormat` to achieve more consistent i18n (`toString` always uses a `.` separator and displays the English word for "Infinity"). Will be displayed left-aligned by default (since numbers will display an inconsistent number of fractional digits).
- `'integer'` - format all values as integers, rounding to nearest if the value isn't an integer and never displaying exponential notation. Will be displayed right-aligned by default.
- `'decimal'` - format all values as decimal values (e.g. 123.45), always displaying `decimal-digits` digits after the separator and never displaying exponential notation. Will be displayed right-aligned by default.
- `'custom'` - use [`Intl.NumberFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat) configured with values specified in other attributes. Will be displayed left-aligned by default (since Nimble can't know if the provided configuration will produce consistent decimal separators).
jattasNI marked this conversation as resolved.
Show resolved Hide resolved
jattasNI marked this conversation as resolved.
Show resolved Hide resolved
- This could be extended to other pre-configured formats in future. Their configuration attributes would similarly be prefixed with the name of the format mode.
- **Note:** all of the above will be implemented using a `Intl.NumberFormat` formatter. For all modes besides `custom` Nimble will configure the formatter with defaults to match the [visual design spec](https://github.com/ni/nimble/issues/887) (e.g. `useGrouping: false` to achieve `1000` rather than `1,000` and `signDisplay: auto` to achieve `1` rather than `+1`).
- `locales` - a string containing a comma-separated list of locales to pass to the [`locales` parameter of the `Intl.NumberFormat` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locale_identification_and_negotiation) which is used by all format modes. This means numbers will be formatted with locale-specific decimal separators.
jattasNI marked this conversation as resolved.
Show resolved Hide resolved
jattasNI marked this conversation as resolved.
Show resolved Hide resolved
jattasNI marked this conversation as resolved.
Show resolved Hide resolved
- `decimal-digits` - when format is `decimal`, a number that controls how many digits are shown to the right of the decimal separator. Defaults to 2.
- `custom-*` - when format is `custom`, these attribute-cased values will be passed to the equivalent camelCased fields of the `options` parameter of the `NumberFormat` constructor. For example, `options.maximumFractionDigits` will be set to the value of `custom-maximum-fraction-digits`. These fields are all string, boolean, or number and their property equivalents will be strictly typed.

The API will be specified in a future update to this document. Below is **an example API** that leverages the native browser [`Intl.NumberFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat): this is intended to help visualize what the API _could_ look like, but isn't yet finalized.
This column will display the `placeholder` when `typeof` the value is not `"number"` (i.e. if the value is `null`, `undefined`, not present, or has a different runtime data type). Note that IEE 754 numbers like Infinity, NaN, and -0 are type `"number"` so will be displayed how each formatter converts them. This will preserve values like `"∞"`, `"NaN"` and `"-0"`.
jattasNI marked this conversation as resolved.
Show resolved Hide resolved

##### Examples

```html
<nimble-table>
<nimble-table-column-number
<nimble-table-column-number-text
field-name="numericTag"
placeholder="Not set"
>
Tag
</nimble-table-column-number-text>

<nimble-table-column-number-text
field-name="count"
number-format-locales="en-US"
number-format-options-style="decimal"
number-format-options-use-grouping="false"
number-format-options-maximum-fraction-digits="0"
number-format-options-rounding-mode="trunc"
format="integer"
placeholder="Not found"
>
Count
</nimble-table-column-number>
</nimble-table-column-number-text>

<nimble-table-column-number
<nimble-table-column-number-text
field-name="voltage"
number-format-locales="en-US"
number-format-options-style="decimal"
number-format-options-use-grouping="false"
format="custom"
locales="en-US"
custom-style="decimal"
custom-use-grouping="false"
suffix=" V"
>
Voltage
</nimble-table-column-number>
</nimble-table-column-number-text>
</nimble-table>
```

##### Date Column
#### Date Column

Nimble could introduce `nimble-table-column-date-text` which maps date-time values to localized strings. The API will be specified in a future update to this document. It will need to consider cases like date formatting (both for locale and other reasons) and how to provide localized strings, possibly by exposing [the native browser `Intl.DateTimeFormat` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat).
Nimble will introduce `nimble-table-column-date-text` which maps [JavaScript `Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) values to localized strings.

---

_These sections will be populated in a future update_
Note that mutating a `Date` object does not cause the value to be rendered again.

### API
**Open question**: Should we represent dates using a numeric value (milliseconds since the Unix epoch) to avoid mutability concerns?
jattasNI marked this conversation as resolved.
Show resolved Hide resolved
jattasNI marked this conversation as resolved.
Show resolved Hide resolved

_Component Name_
##### API

_*Props/Attrs*_
- `format` - a string which can take one of the following values
- `undefined` - use the default formatter, which will display values similar to `Dec 19, 2020, 9:23:16 PM` and include support for localization. It will be implemented using `Intl.DateTimeFormat` with `options.dateStyle` and `options.timeStyle` set to `"medium"`.
- `'custom'` - use [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat) configured with values specified in other attributes.
jattasNI marked this conversation as resolved.
Show resolved Hide resolved
- This could be extended to other pre-configured formats in future. Their configuration attributes would similarly be prefixed with the name of the format mode.
- `locales` - a string containing a comma-separated list of locales to pass to the [`locales` parameter of the `DateTimeFormat` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locale_identification_and_negotiation) which is used by all format modes.
- `custom-*` - when format is `custom`, these attribute-cased values will be passed to the equivalent camelCased fields of the `options` parameter of the `DateTimeFormat` constructor. For example, `options.dateStyle` will be set to the value of `custom-date-style`. These fields are all string, boolean, or number and their property equivalents will be strictly typed.

_Type Reference_
This column will display the `placeholder` for a date when it's not `instanceof Date` or when `isNaN(date)` returns `true` (i.e. if the value is `null`, `undefined`, not present, has a non-`Date` runtime data type, or is an invalid `Date`) (see ["Detecting an invalid Date instance"](https://stackoverflow.com/a/1353711)).

### Anatomy
##### Examples

### Angular integration
```html
<nimble-table>
<nimble-table-column-date-text field-name="start" placeholder="Not started">
Start
</nimble-table-column-date-text>

<nimble-table-column-date-text
field-name="dueDate"
format="custom"
locales="en-US"
custom-date-style="full"
>
Due Date
</nimble-table-column-date-text>
</nimble-table>
```

### Blazor integration
#### Elapsed Time Column

### Visual Appearance
Nimble could introduce a column type that maps time duration to localized strings (e.g. "1 minute, 10 seconds"). Its API will be discussed in a future update to this document. Ideally it might use [Temporal duration values](https://tc39.es/proposal-temporal/docs/duration.html) formatted with [`Intl.DurationFormat`](https://tc39.es/proposal-intl-duration-format/) but those APIs are not yet approved for implementation in browsers.
jattasNI marked this conversation as resolved.
Show resolved Hide resolved

---

Expand Down Expand Up @@ -301,35 +345,65 @@ Nimble already has a mechanism for clients to provide custom columns by deriving
- Difficult to enforce styling differences between string and numeric columns (e.g. right vs left text alignment)
- Potential cross-app inconsistency if formatting code isn't shared

---
### Other number/date/time formatting APIs

1. Existing SLE applications use the [Angular DatePipe](https://angular.io/api/common/DatePipe). This offers similar options to the `Intl.DateTimeFormat` (including locale-specific formatting) but would be a challenge to support in non-Angular applications.
2. The TC39 ECMAScript standard is working on [a proposal for Temporal](https://github.com/tc39/proposal-temporal), which includes a new [immutable format for representing time](https://tc39.es/proposal-temporal/docs/index.html) and namespace for manipulating it. It's in Stage 3 meaning the API isn't expected to change and [polyfills](https://github.com/tc39/proposal-temporal#polyfills) are available. Since the formatting APIs that browsers support today are sufficient, we propose to use them instead.
3. There are numerous libraries for formatting including the standards-based [Format.js](https://formatjs.io/), the opinionated [moment.js](https://momentjs.com/docs/), and [various successors](https://momentjs.com/docs/#/-project-status/recommendations/). We may choose to depend on one in the future to expose additional formatting options but will start with browser APIs as they require no additional dependencies.

_These sections will be populated in a future update_
---

### States

N/A

### Accessibility

No unique considerations

### Globalization

Formatting APIs support locale-specific formatting and include user-visible strings in the browser.

### Security

No unique considerations

### Performance

We will do manual testing to ensure the browser formatting functions don't impact scroll performance.

### Dependencies

None

### Test Plan

This will be filled out in more detail, but some reminders:
We will add standard unit tests, Blazor/Angular tests, and Chromatic tests for new column types. The unit tests will exercise formatting corner cases including:

#### Number column

- number edge cases (-Inf, Inf, -0, +0, NaN, Number.MAX_SAFE_INTEGER + n, Number.MIN_SAFE_INTEGER -n) should render as numbers
- non-number edge cases (e.g. strings containing numbers, undefined, null) should display the placeholder
- formatting should change with different locales

- handle number edge cases (-Inf, Inf, -0, +0, NaN, Number.MAX_SAFE_INTEGER + n, Number.MIN_SAFE_INTEGER -n) as numbers
- handle non-number edge cases (strings containing numbers, undefined, null)
#### Date column

- date edge cases (min and max date, different time zones)
- non-date edge cases (e.g. non-dates, invalid dates) should display the placeholder
- formatting should change with different locales

### Tooling

N/A

### Documentation

Standard Storybook documentation for column APIs.

---

## Open Issues

1. API to configure text alignment of column content, placeholder text, and column headers (e.g. right align numeric columns but left align string columns and numeric columns with non-uniform formatting; [more discussion here](https://github.com/ni/nimble/issues/887)). We'll update the HLD with a recommendation when we start working on columns that need it (you're welcome to comment with ideas now though).
1. Should we represent dates using a numeric value (milliseconds since the Unix epoch) to avoid mutability concerns?
2. Visual design recommends that column header text alignment match data alignment. Once we prototype this we may hit implementation concerns (e.g. clash with proposed header menu button).
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Some potential column names following this convention are listed below.
```
nimble-table-column-anchor
nimble-table-column-text
nimble-table-column-numeric-text
nimble-table-column-number-text
nimble-table-column-date-text
nimble-table-column-progress
nimble-table-column-text-field
Expand Down