Skip to content

Commit 00f47f8

Browse files
authored
Merge pull request #327 from github/tiny-wins-absolute-time-setting
Experimental feature: Support absolute time format
2 parents a917f04 + 856c888 commit 00f47f8

File tree

3 files changed

+84
-7
lines changed

3 files changed

+84
-7
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
},
1919
"scripts": {
2020
"clean": "rm -rf dist",
21-
"lint": "eslint . --ext .js,.ts && tsc --noEmit",
21+
"lint": "tsc --noEmit && eslint . --ext .js,.ts",
2222
"lint:fix": "npm run lint -- --fix",
2323
"prebuild": "npm run clean && npm run lint && mkdir dist",
2424
"bundle": "esbuild --bundle dist/index.js --keep-names --outfile=dist/bundle.js --format=esm",

src/relative-time-element.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,18 @@ export class RelativeTimeElement extends HTMLElement implements Intl.DateTimeFor
217217
return `${this.prefix} ${formatter.format(date)}`.trim()
218218
}
219219

220+
#getUserPreferredAbsoluteTimeFormat(date: Date): string {
221+
return new Intl.DateTimeFormat(this.#lang, {
222+
day: 'numeric',
223+
month: 'short',
224+
year: 'numeric',
225+
hour: 'numeric',
226+
minute: '2-digit',
227+
timeZoneName: 'short',
228+
timeZone: this.timeZone,
229+
}).format(date)
230+
}
231+
220232
#updateRenderRootContent(content: string | null): void {
221233
if (this.hasAttribute('aria-hidden') && this.getAttribute('aria-hidden') === 'true') {
222234
const span = document.createElement('span')
@@ -228,6 +240,16 @@ export class RelativeTimeElement extends HTMLElement implements Intl.DateTimeFor
228240
}
229241
}
230242

243+
#shouldDisplayUserPreferredAbsoluteTime(format: ResolvedFormat): boolean {
244+
// Never override duration format with absolute format.
245+
if (format === 'duration') return false
246+
247+
return (
248+
this.ownerDocument.documentElement.getAttribute('data-prefers-absolute-time') === 'true' ||
249+
this.ownerDocument.body?.getAttribute('data-prefers-absolute-time') === 'true'
250+
)
251+
}
252+
231253
#onRelativeTimeUpdated: ((event: RelativeTimeUpdatedEvent) => void) | null = null
232254
get onRelativeTimeUpdated() {
233255
return this.#onRelativeTimeUpdated
@@ -477,12 +499,19 @@ export class RelativeTimeElement extends HTMLElement implements Intl.DateTimeFor
477499
const duration = elapsedTime(date, this.precision, now)
478500
const format = this.#resolveFormat(duration)
479501
let newText = oldText
480-
if (format === 'duration') {
481-
newText = this.#getDurationFormat(duration)
482-
} else if (format === 'relative') {
483-
newText = this.#getRelativeFormat(duration)
502+
503+
// Experimental: Enable absolute time if users prefers it, but never for `duration` format
504+
const displayUserPreferredAbsoluteTime = this.#shouldDisplayUserPreferredAbsoluteTime(format)
505+
if (displayUserPreferredAbsoluteTime) {
506+
newText = this.#getUserPreferredAbsoluteTimeFormat(date)
484507
} else {
485-
newText = this.#getDateTimeFormat(date)
508+
if (format === 'duration') {
509+
newText = this.#getDurationFormat(duration)
510+
} else if (format === 'relative') {
511+
newText = this.#getRelativeFormat(duration)
512+
} else {
513+
newText = this.#getDateTimeFormat(date)
514+
}
486515
}
487516

488517
if (newText) {
@@ -496,7 +525,7 @@ export class RelativeTimeElement extends HTMLElement implements Intl.DateTimeFor
496525
this.dispatchEvent(new RelativeTimeUpdatedEvent(oldText, newText, oldTitle, newTitle))
497526
}
498527

499-
if (format === 'relative' || format === 'duration') {
528+
if ((format === 'relative' || format === 'duration') && !displayUserPreferredAbsoluteTime) {
500529
dateObserver.observe(this)
501530
} else {
502531
dateObserver.unobserve(this)

test/relative-time.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ suite('relative-time', function () {
3131
})
3232

3333
teardown(() => {
34+
document.body.removeAttribute('data-prefers-absolute-time')
3435
fixture.innerHTML = ''
3536
if (dateNow) {
3637
// eslint-disable-next-line no-global-assign
@@ -1884,6 +1885,53 @@ suite('relative-time', function () {
18841885
}
18851886
})
18861887

1888+
suite('experimental: [data-prefers-absolute-time]', async () => {
1889+
test('formats with absolute time when data-prefers-absolute-time="true"', async () => {
1890+
document.documentElement.setAttribute('data-prefers-absolute-time', 'true')
1891+
const el = document.createElement('relative-time')
1892+
el.setAttribute('datetime', '2022-01-01T12:00:00.000Z')
1893+
await Promise.resolve()
1894+
1895+
assert.match(el.shadowRoot.textContent, /[A-Z][a-z]{2} \d{1,2}, \d{4}, \d{1,2}:\d{2} (AM|PM)/)
1896+
})
1897+
1898+
test('does not format with absolute time when format is elapsed or duration', async () => {
1899+
document.documentElement.setAttribute('data-prefers-absolute-time', 'true')
1900+
const el = document.createElement('relative-time')
1901+
el.setAttribute('datetime', '2022-01-01T12:00:00.000Z')
1902+
el.setAttribute('format', 'elapsed')
1903+
await Promise.resolve()
1904+
1905+
assert.notMatch(el.shadowRoot.textContent, /[A-Z][a-z]{2} \d{1,2}, \d{4}, \d{1,2}:\d{2} (AM|PM)/)
1906+
})
1907+
1908+
test('does not format with absolute time when data-prefers-absolute-time="false"', async () => {
1909+
document.documentElement.setAttribute('data-prefers-absolute-time', 'false')
1910+
const el = document.createElement('relative-time')
1911+
el.setAttribute('datetime', new Date(Date.now() - 3 * 60 * 60 * 24 * 1000).toISOString())
1912+
await Promise.resolve()
1913+
1914+
assert.equal(el.shadowRoot.textContent, '3 days ago')
1915+
})
1916+
1917+
test('does not format with absolute time when data-prefers-absolute-time attribute is not set', async () => {
1918+
const el = document.createElement('relative-time')
1919+
el.setAttribute('datetime', new Date(Date.now() - 3 * 60 * 60 * 24 * 1000).toISOString())
1920+
await Promise.resolve()
1921+
1922+
assert.equal(el.shadowRoot.textContent, '3 days ago')
1923+
})
1924+
1925+
test('supports data-prefers-absolute-time="true" on body element too', async () => {
1926+
document.body.setAttribute('data-prefers-absolute-time', 'true')
1927+
const el = document.createElement('relative-time')
1928+
el.setAttribute('datetime', '2022-01-01T12:00:00.000Z')
1929+
await Promise.resolve()
1930+
1931+
assert.match(el.shadowRoot.textContent, /[A-Z][a-z]{2} \d{1,2}, \d{4}, \d{1,2}:\d{2} (AM|PM)/)
1932+
})
1933+
})
1934+
18871935
suite('[aria-hidden]', async () => {
18881936
test('[aria-hidden="true"] applies to shadow root', async () => {
18891937
const now = new Date().toISOString()

0 commit comments

Comments
 (0)