Skip to content

Commit

Permalink
✨ Supports locale options for localeString in amp-date-display.
Browse files Browse the repository at this point in the history
* Allow user to customize locale options for localeString, the options setting is same as [Date.toLocaleString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString#syntax) parameter.
  • Loading branch information
jingfei committed May 18, 2021
1 parent 2179341 commit 14bb644
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 9 deletions.
8 changes: 8 additions & 0 deletions examples/amp-date-display.amp.html
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ <h2>Locales</h2>
en-GB: {{dayName}} {{day}} {{monthName}} {{year}}
</template>
</amp-date-display>
<amp-date-display datetime="now" locale="zh-TW" layout="fixed" width="360" height="20">
<script type="application/json">
{ "localeOptions": { "timeStyle": "short" } }
</script>
<template type="amp-mustache">
zh-TW: {{localeString}}
</template>
</amp-date-display>
</div>
</body>
</html>
11 changes: 11 additions & 0 deletions extensions/amp-date-display/1.0/amp-date-display.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import {BaseElement} from './base-element';
import {Services} from '../../../src/services';
import {childElementsByTag, isJsonScriptTag} from '../../../src/dom';
import {dev, userAssert} from '../../../src/log';
import {dict} from '../../../src/core/types/object';
import {isExperimentOn} from '../../../src/experiments';
Expand All @@ -35,6 +36,16 @@ class AmpDateDisplay extends BaseElement {
this.template_ = null;
}

/** @override */
buildCallback() {
super.buildCallback();
// Handle additional JSON
const scriptElement = childElementsByTag(this.element, 'SCRIPT')[0];
if (scriptElement && isJsonScriptTag(scriptElement)) {
this.element.setAttribute('json', scriptElement.textContent);
}
}

/** @override */
isLayoutSupported(layout) {
userAssert(
Expand Down
20 changes: 20 additions & 0 deletions extensions/amp-date-display/1.0/base-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import {DateDisplay} from './component';
import {PreactBaseElement} from '../../../src/preact/base-element';
import {parseDateAttrs as parseDateAttrsBase} from '../../../src/utils/date';
import {tryParseJson} from '../../../src/json';

export class BaseElement extends PreactBaseElement {}

Expand All @@ -31,6 +32,10 @@ BaseElement['props'] = {
},
'displayIn': {attr: 'display-in'},
'locale': {attr: 'locale'},
'additionalOptions': {
attrs: ['json'],
parseAttrs: parseJsonStr,
},
};

/** @override */
Expand All @@ -55,3 +60,18 @@ export function parseDateAttrs(element) {
'timestamp-seconds',
]);
}

/**
* @param {!Element} element
* @return {?JsonObject} May be extend to parse arrays.
* @throws {Error} Parsing fails error.
*/
function parseJsonStr(element) {
const jsonStr = element.getAttribute('json');
if (!jsonStr) {
return;
}
return tryParseJson(jsonStr, (error) => {
throw error;
});
}
35 changes: 26 additions & 9 deletions extensions/amp-date-display/1.0/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,15 @@ export function DateDisplay({
datetime,
displayIn = DEFAULT_DISPLAY_IN,
locale = DEFAULT_LOCALE,
additionalOptions = {},
render = DEFAULT_RENDER,
...rest
}) {
const date = getDate(datetime);
const {localeOptions} = additionalOptions;
const data = useMemo(
() => getDataForTemplate(new Date(date), displayIn, locale),
[date, displayIn, locale]
() => getDataForTemplate(new Date(date), displayIn, locale, localeOptions),
[date, displayIn, locale, localeOptions]
);

const rendered = useRenderer(render, data);
Expand All @@ -126,13 +128,14 @@ export function DateDisplay({
* @param {!Date} date
* @param {string} displayIn
* @param {string} locale
* @param {Object<string, *>} localeOptions
* @return {!EnhancedVariablesV2Def}
*/
function getDataForTemplate(date, displayIn, locale) {
function getDataForTemplate(date, displayIn, locale, localeOptions) {
const basicData =
displayIn.toLowerCase() === 'utc'
? getVariablesInUTC(date, locale)
: getVariablesInLocal(date, locale);
? getVariablesInUTC(date, locale, localeOptions)
: getVariablesInLocal(date, locale, localeOptions);

return enhanceBasicVariables(basicData);
}
Expand Down Expand Up @@ -174,9 +177,14 @@ function enhanceBasicVariables(data) {
/**
* @param {!Date} date
* @param {string} locale
* @param {?Object<string, *>} localeOptions
* @return {!VariablesV2Def}
*/
function getVariablesInLocal(date, locale) {
function getVariablesInLocal(
date,
locale,
localeOptions = DEFAULT_DATETIME_OPTIONS
) {
return {
'year': date.getFullYear(),
'month': date.getMonth() + 1,
Expand All @@ -193,16 +201,25 @@ function getVariablesInLocal(date, locale) {
'minute': date.getMinutes(),
'second': date.getSeconds(),
'iso': date.toISOString(),
'localeString': date.toLocaleString(locale, DEFAULT_DATETIME_OPTIONS),
'localeString': date.toLocaleString(locale, localeOptions),
};
}

/**
* @param {!Date} date
* @param {string} locale
* @param {?Object<string, *>} localeOptions
* @return {!VariablesV2Def}
*/
function getVariablesInUTC(date, locale) {
function getVariablesInUTC(
date,
locale,
localeOptions = DEFAULT_DATETIME_OPTIONS_UTC
) {
const localeOptionsInUTC = {
...localeOptions,
timeZone: 'UTC',
};
return {
'year': date.getUTCFullYear(),
'month': date.getUTCMonth() + 1,
Expand All @@ -227,6 +244,6 @@ function getVariablesInUTC(date, locale) {
'minute': date.getUTCMinutes(),
'second': date.getUTCSeconds(),
'iso': date.toISOString(),
'localeString': date.toLocaleString(locale, DEFAULT_DATETIME_OPTIONS_UTC),
'localeString': date.toLocaleString(locale, localeOptionsInUTC),
};
}
1 change: 1 addition & 0 deletions extensions/amp-date-display/1.0/component.type.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var DateDisplayDef = {};
* datetime: (!Date|number|string),
* displayIn: (string|undefined),
* locale: (string|undefined),
* additionalOptions: (?Object<string, *>|undefined),
* render: (?RendererFunctionType|undefined),
* }}
*/
Expand Down
21 changes: 21 additions & 0 deletions extensions/amp-date-display/1.0/test/test-amp-date-display.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ describes.realWin(
secondTwoDigit: '{{secondTwoDigit}}',
dayPeriod: '{{dayPeriod}}',
iso: '{{iso}}',
localeString: '{{localeString}}',
});
element.appendChild(template);
element.setAttribute('layout', 'nodisplay');
Expand Down Expand Up @@ -104,6 +105,7 @@ describes.realWin(
expect(data.second).to.equal('6');
expect(data.secondTwoDigit).to.equal('06');
expect(data.dayPeriod).to.equal('am');
expect(data.localeString).to.equal('Feb 3, 2001, 4:05 AM');
});

it('renders mustache template with "timestamp-ms"', async () => {
Expand Down Expand Up @@ -135,6 +137,25 @@ describes.realWin(
expect(data.second).to.equal('6');
expect(data.secondTwoDigit).to.equal('06');
expect(data.dayPeriod).to.equal('am');
expect(data.localeString).to.equal('Feb 3, 2001, 4:05 AM');
});

it('render localeString with "localeOptions" from json script', async () => {
const options = {localeOptions: {timeStyle: 'short'}};
const script = win.document.createElement('script');
script.setAttribute('type', 'application/json');
script.textContent = JSON.stringify(options);

element.setAttribute('datetime', '2001-02-03T04:05:06.007Z');
element.setAttribute('display-in', 'UTC');
element.setAttribute('locale', 'zh-TW');
element.appendChild(script);

win.document.body.appendChild(element);

const data = await getRenderedData();

expect(data.localeString).to.equal('上午4:05');
});

it('renders default template into element', async () => {
Expand Down
17 changes: 17 additions & 0 deletions extensions/amp-date-display/1.0/test/test-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,21 @@ describes.sandboxed('DateDisplay 1.0 preact component', {}, (env) => {
expect(data.dayName).to.equal('sobota');
expect(data.dayNameShort).to.equal('so');
});

it('shows custom locale string when localeOptions is passed', () => {
const additionalOptions = {localeOptions: {timeStyle: 'short'}};
const props = {
render,
datetime: Date.parse('2001-02-03T04:05:06.007Z'),
displayIn: 'UTC',
locale: 'zh-TW',
additionalOptions,
};
const jsx = <DateDisplay {...props} />;

const wrapper = mount(jsx);
const data = JSON.parse(wrapper.text());

expect(data.localeString).to.equal('上午4:05');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,24 @@
<amp-date-display datetime="2017-08-02T15:05:05.+04:00" layout="fixed" width="360" height="20">
<template type="amp-mustache">{{iso}}</template>
</amp-date-display>

<!-- valid, with localeOptions json -->
<amp-date-display datetime="now" layout="fixed" width="360" height="20">
<script type="application/json">
{ "localeOptions": { "timeStyle": "short" } }
</script>
<template type="amp-mustache">{{localeString}}</template>
</amp-date-display>

<!-- invalid: AMP Runtime can't parse json with comments. -->
<amp-date-display datetime="now" layout="fixed" width="360" height="20">
<script type="application/json">
{
// Comment, should break parsing and result in a warning.
}
</script>
<template type="amp-mustache">{{localeString}}</template>
</amp-date-display>
</body>

</html>
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,27 @@ amp-date-display/1.0/test/validator-amp-date-display.html:168:2 The attribute 'd
amp-date-display/1.0/test/validator-amp-date-display.html:173:2 The attribute 'datetime' in tag 'amp-date-display' is set to the invalid value '2017-08-02T15:05:05.+04:00'. (see https://amp.dev/documentation/components/amp-date-display)
| <template type="amp-mustache">{{iso}}</template>
| </amp-date-display>
|
| <!-- valid, with localeOptions json -->
| <amp-date-display datetime="now" layout="fixed" width="360" height="20">
| <script type="application/json">
| { "localeOptions": { "timeStyle": "short" } }
| </script>
| <template type="amp-mustache">{{localeString}}</template>
| </amp-date-display>
|
| <!-- invalid: AMP Runtime can't parse json with comments. -->
| <amp-date-display datetime="now" layout="fixed" width="360" height="20">
| <script type="application/json">
>> ^~~~~~~~~
amp-date-display/1.0/test/validator-amp-date-display.html:187:4 The script tag contains invalid JSON that cannot be parsed.
| {
| // Comment, should break parsing and result in a warning.
| }
| </script>
| <template type="amp-mustache">{{localeString}}</template>
| </amp-date-display>
| </body>
|
| </html>

20 changes: 20 additions & 0 deletions extensions/amp-date-display/validator-amp-date-display.protoascii
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,23 @@ tags: { # <amp-date-display>
supported_layouts: RESPONSIVE
}
}
tags: { # amp-date-display (json)
html_format: AMP
tag_name: "SCRIPT"
spec_name: "amp-date-display extension .json script"
requires_extension: "amp-date-display"
mandatory_parent: "AMP-DATE-DISPLAY"
attrs: {
name: "type"
mandatory: true
value_casei: "application/json"
dispatch_key: NAME_VALUE_PARENT_DISPATCH
}
attr_lists: "nonce-attr"
cdata: {
disallowed_cdata_regex: {
regex: "<!--"
error_message: "html comments"
}
}
}

0 comments on commit 14bb644

Please sign in to comment.