From 22107e0af2b58b410811701e87afc67e824bfeea Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Fri, 17 Dec 2021 14:44:25 +0100 Subject: [PATCH 1/6] add disabler elative dates setting Signed-off-by: Kerry Archibald --- src/settings/Settings.tsx | 4 ++++ src/settings/UIFeature.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 4edc4884d5f..c4d4335bf91 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -965,4 +965,8 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_UI_FEATURE, default: true, }, + [UIFeature.TimelineDisableRelativeDates]: { + supportedLevels: LEVELS_UI_FEATURE, + default: false, + }, }; diff --git a/src/settings/UIFeature.ts b/src/settings/UIFeature.ts index 847da2537ee..9c81dbe871c 100644 --- a/src/settings/UIFeature.ts +++ b/src/settings/UIFeature.ts @@ -32,6 +32,7 @@ export enum UIFeature { Communities = "UIFeature.communities", AdvancedSettings = "UIFeature.advancedSettings", RoomHistorySettings = "UIFeature.roomHistorySettings", + TimelineDisableRelativeDates = "UIFeature.timelineDisableRelativeDates" } export enum UIComponent { From c1ebc487074fcc08657d4892e995444494315f40 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Fri, 17 Dec 2021 16:24:02 +0100 Subject: [PATCH 2/6] test existing DateSeparator Signed-off-by: Kerry Archibald --- .../views/messages/DateSeparator.tsx | 14 ++-- .../views/messages/DateSeparator-test.tsx | 67 +++++++++++++++++++ .../__snapshots__/DateSeparator-test.tsx.snap | 32 +++++++++ test/skinned-sdk.js | 2 +- 4 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 test/components/views/messages/DateSeparator-test.tsx create mode 100644 test/components/views/messages/__snapshots__/DateSeparator-test.tsx.snap diff --git a/src/components/views/messages/DateSeparator.tsx b/src/components/views/messages/DateSeparator.tsx index b20319e800e..b737b278aad 100644 --- a/src/components/views/messages/DateSeparator.tsx +++ b/src/components/views/messages/DateSeparator.tsx @@ -36,18 +36,23 @@ function getDaysArray(): string[] { interface IProps { ts: number; forExport?: boolean; + now?: Date; } @replaceableComponent("views.messages.DateSeparator") export default class DateSeparator extends React.Component { + public static defaultProps = { + now: new Date(), + }; + private getLabel() { const date = new Date(this.props.ts); // During the time the archive is being viewed, a specific day might not make sense, so we return the full date if (this.props.forExport) return formatFullDateNoTime(date); - const today = new Date(); - const yesterday = new Date(); + const today = this.props.now; + const yesterday = new Date(this.props.now); const days = getDaysArray(); yesterday.setDate(today.getDate() - 1); @@ -63,11 +68,12 @@ export default class DateSeparator extends React.Component { } render() { + const label = this.getLabel(); // ARIA treats
s as separators, here we abuse them slightly so manually treat this entire thing as one // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers - return

+ return


- +

; } diff --git a/test/components/views/messages/DateSeparator-test.tsx b/test/components/views/messages/DateSeparator-test.tsx new file mode 100644 index 00000000000..b881ac17768 --- /dev/null +++ b/test/components/views/messages/DateSeparator-test.tsx @@ -0,0 +1,67 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; +import { mount } from "enzyme"; + +import sdk from "../../../skinned-sdk"; +import * as TestUtils from "../../../test-utils"; +import { formatFullDateNoTime } from "../../../../src/DateUtils"; + +const _DateSeparator = sdk.getComponent("views.messages.DateSeparator"); +const DateSeparator = TestUtils.wrapInMatrixClientContext(_DateSeparator); + +describe("DateSeparator", () => { + const HOUR_MS = 3600000; + const DAY_MS = HOUR_MS * 24; + // Friday Dec 17 2021, 9:09am + const now = new Date('Fri Dec 17 2021 09:09:00 GMT+0100 (Central European Standard Time)'); + const defaultProps = { + ts: now.getTime(), + now, + }; + const getComponent = (props = {}) => + mount(); + + type TestCase = [string, number, string]; + const testCases: TestCase[] = [ + ['the exact same moment', now.getTime(), 'Today'], + ['same day as current day', now.getTime() - HOUR_MS, 'Today'], + ['day before the current day', now.getTime() - (HOUR_MS * 12), 'Yesterday'], + ['2 days ago', now.getTime() - DAY_MS * 2, 'Wednesday'], + ['144 hours ago', now.getTime() - HOUR_MS * 144, 'Sat, Dec 11 2021'], + [ + '6 days ago, but less than 144h', + new Date('Saturday Dec 11 2021 23:59:00 GMT+0100 (Central European Standard Time)').getTime(), + 'Saturday', + ], + ]; + + it('renders the date separator correctly', () => { + const component = getComponent(); + expect(component).toMatchSnapshot(); + }); + + it.each(testCases)('formats date correctly when current time is %s', (_d, ts, result) => { + expect(getComponent({ ts, forExport: false }).text()).toEqual(result); + }); + + describe('when forExport is true', () => { + it.each(testCases)('formats date in full when current time is %s', (_d, ts) => { + expect(getComponent({ ts, forExport: true }).text()).toEqual(formatFullDateNoTime(new Date(ts))); + }); + }); +}); diff --git a/test/components/views/messages/__snapshots__/DateSeparator-test.tsx.snap b/test/components/views/messages/__snapshots__/DateSeparator-test.tsx.snap new file mode 100644 index 00000000000..55ca7ad2cc2 --- /dev/null +++ b/test/components/views/messages/__snapshots__/DateSeparator-test.tsx.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DateSeparator renders the date separator correctly 1`] = ` + + +

+
+ +
+

+
+
+`; diff --git a/test/skinned-sdk.js b/test/skinned-sdk.js index 9de13d20a1d..87c5b40d2c1 100644 --- a/test/skinned-sdk.js +++ b/test/skinned-sdk.js @@ -19,7 +19,7 @@ components['structures.RoomDirectory'] = stubComponent(); components['views.globals.GuestWarningBar'] = stubComponent(); components['views.globals.NewVersionBar'] = stubComponent(); components['views.elements.Spinner'] = stubComponent({ displayName: 'Spinner' }); -components['views.messages.DateSeparator'] = stubComponent({ displayName: 'DateSeparator' }); +// components['views.messages.DateSeparator'] = stubComponent({ displayName: 'DateSeparator' }); components['views.messages.MessageTimestamp'] = stubComponent({ displayName: 'MessageTimestamp' }); components['views.messages.SenderProfile'] = stubComponent({ displayName: 'SenderProfile' }); components['views.rooms.SearchBar'] = stubComponent(); From 09c65fc60707d4864c31f45102411645ffca61dc Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Fri, 17 Dec 2021 16:37:42 +0100 Subject: [PATCH 3/6] use full date format when TimelineDisableRelativeDates is truthy Signed-off-by: Kerry Archibald --- src/components/views/messages/DateSeparator.tsx | 5 ++++- .../views/messages/DateSeparator-test.tsx | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/views/messages/DateSeparator.tsx b/src/components/views/messages/DateSeparator.tsx index b737b278aad..e2c962ef521 100644 --- a/src/components/views/messages/DateSeparator.tsx +++ b/src/components/views/messages/DateSeparator.tsx @@ -20,6 +20,8 @@ import React from 'react'; import { _t } from '../../../languageHandler'; import { formatFullDateNoTime } from '../../../DateUtils'; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import SettingsStore from '../../../settings/SettingsStore'; +import { UIFeature } from '../../../settings/UIFeature'; function getDaysArray(): string[] { return [ @@ -47,9 +49,10 @@ export default class DateSeparator extends React.Component { private getLabel() { const date = new Date(this.props.ts); + const disableRelativeTimestamps = SettingsStore.getValue(UIFeature.TimelineDisableRelativeDates); // During the time the archive is being viewed, a specific day might not make sense, so we return the full date - if (this.props.forExport) return formatFullDateNoTime(date); + if (this.props.forExport || disableRelativeTimestamps) return formatFullDateNoTime(date); const today = this.props.now; const yesterday = new Date(this.props.now); diff --git a/test/components/views/messages/DateSeparator-test.tsx b/test/components/views/messages/DateSeparator-test.tsx index b881ac17768..13bcc776660 100644 --- a/test/components/views/messages/DateSeparator-test.tsx +++ b/test/components/views/messages/DateSeparator-test.tsx @@ -20,6 +20,10 @@ import { mount } from "enzyme"; import sdk from "../../../skinned-sdk"; import * as TestUtils from "../../../test-utils"; import { formatFullDateNoTime } from "../../../../src/DateUtils"; +import SettingsStore from "../../../../src/settings/SettingsStore"; +import { UIFeature } from "../../../../src/settings/UIFeature"; + +jest.mock("../../../../src/settings/SettingsStore"); const _DateSeparator = sdk.getComponent("views.messages.DateSeparator"); const DateSeparator = TestUtils.wrapInMatrixClientContext(_DateSeparator); @@ -53,6 +57,7 @@ describe("DateSeparator", () => { it('renders the date separator correctly', () => { const component = getComponent(); expect(component).toMatchSnapshot(); + expect(SettingsStore.getValue).toHaveBeenCalledWith(UIFeature.TimelineDisableRelativeDates); }); it.each(testCases)('formats date correctly when current time is %s', (_d, ts, result) => { @@ -64,4 +69,13 @@ describe("DateSeparator", () => { expect(getComponent({ ts, forExport: true }).text()).toEqual(formatFullDateNoTime(new Date(ts))); }); }); + + describe('when Settings.TimelineDisableRelativeDates is truthy', () => { + beforeEach(() => { + (SettingsStore.getValue as jest.Mock).mockReturnValue(true); + }); + it.each(testCases)('formats date in full when current time is %s', (_d, ts) => { + expect(getComponent({ ts, forExport: false }).text()).toEqual(formatFullDateNoTime(new Date(ts))); + }); + }); }); From ad78a941a057ec0d0b03352aa88fe72b477674b3 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Fri, 17 Dec 2021 16:42:27 +0100 Subject: [PATCH 4/6] add comment Signed-off-by: Kerry Archibald --- src/components/views/messages/DateSeparator.tsx | 1 + test/skinned-sdk.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/messages/DateSeparator.tsx b/src/components/views/messages/DateSeparator.tsx index e2c962ef521..d6800d985ee 100644 --- a/src/components/views/messages/DateSeparator.tsx +++ b/src/components/views/messages/DateSeparator.tsx @@ -43,6 +43,7 @@ interface IProps { @replaceableComponent("views.messages.DateSeparator") export default class DateSeparator extends React.Component { + // use defaultProp for now to make testing easier public static defaultProps = { now: new Date(), }; diff --git a/test/skinned-sdk.js b/test/skinned-sdk.js index 87c5b40d2c1..0a3cc85a3b5 100644 --- a/test/skinned-sdk.js +++ b/test/skinned-sdk.js @@ -19,7 +19,6 @@ components['structures.RoomDirectory'] = stubComponent(); components['views.globals.GuestWarningBar'] = stubComponent(); components['views.globals.NewVersionBar'] = stubComponent(); components['views.elements.Spinner'] = stubComponent({ displayName: 'Spinner' }); -// components['views.messages.DateSeparator'] = stubComponent({ displayName: 'DateSeparator' }); components['views.messages.MessageTimestamp'] = stubComponent({ displayName: 'MessageTimestamp' }); components['views.messages.SenderProfile'] = stubComponent({ displayName: 'SenderProfile' }); components['views.rooms.SearchBar'] = stubComponent(); From b12b6a597c10e7cf7e7c0955dfd983b7d5cad173 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Fri, 17 Dec 2021 16:54:13 +0100 Subject: [PATCH 5/6] flip timelineDisableRelativeDates -> timelineEnableRelativeDates to fit convention Signed-off-by: Kerry Archibald --- src/components/views/messages/DateSeparator.tsx | 2 +- src/settings/Settings.tsx | 4 ++-- src/settings/UIFeature.ts | 2 +- test/components/views/messages/DateSeparator-test.tsx | 10 +++++++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/views/messages/DateSeparator.tsx b/src/components/views/messages/DateSeparator.tsx index d6800d985ee..1e3da01d7b5 100644 --- a/src/components/views/messages/DateSeparator.tsx +++ b/src/components/views/messages/DateSeparator.tsx @@ -50,7 +50,7 @@ export default class DateSeparator extends React.Component { private getLabel() { const date = new Date(this.props.ts); - const disableRelativeTimestamps = SettingsStore.getValue(UIFeature.TimelineDisableRelativeDates); + const disableRelativeTimestamps = !SettingsStore.getValue(UIFeature.TimelineEnableRelativeDates); // During the time the archive is being viewed, a specific day might not make sense, so we return the full date if (this.props.forExport || disableRelativeTimestamps) return formatFullDateNoTime(date); diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index c4d4335bf91..a96ad66e044 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -965,8 +965,8 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_UI_FEATURE, default: true, }, - [UIFeature.TimelineDisableRelativeDates]: { + [UIFeature.TimelineEnableRelativeDates]: { supportedLevels: LEVELS_UI_FEATURE, - default: false, + default: true, }, }; diff --git a/src/settings/UIFeature.ts b/src/settings/UIFeature.ts index 9c81dbe871c..225f785614f 100644 --- a/src/settings/UIFeature.ts +++ b/src/settings/UIFeature.ts @@ -32,7 +32,7 @@ export enum UIFeature { Communities = "UIFeature.communities", AdvancedSettings = "UIFeature.advancedSettings", RoomHistorySettings = "UIFeature.roomHistorySettings", - TimelineDisableRelativeDates = "UIFeature.timelineDisableRelativeDates" + TimelineEnableRelativeDates = "UIFeature.timelineEnableRelativeDates" } export enum UIComponent { diff --git a/test/components/views/messages/DateSeparator-test.tsx b/test/components/views/messages/DateSeparator-test.tsx index 13bcc776660..8ec6054cb78 100644 --- a/test/components/views/messages/DateSeparator-test.tsx +++ b/test/components/views/messages/DateSeparator-test.tsx @@ -54,10 +54,14 @@ describe("DateSeparator", () => { ], ]; + beforeEach(() => { + (SettingsStore.getValue as jest.Mock).mockReturnValue(true); + }); + it('renders the date separator correctly', () => { const component = getComponent(); expect(component).toMatchSnapshot(); - expect(SettingsStore.getValue).toHaveBeenCalledWith(UIFeature.TimelineDisableRelativeDates); + expect(SettingsStore.getValue).toHaveBeenCalledWith(UIFeature.TimelineEnableRelativeDates); }); it.each(testCases)('formats date correctly when current time is %s', (_d, ts, result) => { @@ -70,9 +74,9 @@ describe("DateSeparator", () => { }); }); - describe('when Settings.TimelineDisableRelativeDates is truthy', () => { + describe('when Settings.TimelineEnableRelativeDates is falsy', () => { beforeEach(() => { - (SettingsStore.getValue as jest.Mock).mockReturnValue(true); + (SettingsStore.getValue as jest.Mock).mockReturnValue(false); }); it.each(testCases)('formats date in full when current time is %s', (_d, ts) => { expect(getComponent({ ts, forExport: false }).text()).toEqual(formatFullDateNoTime(new Date(ts))); From e5029e5440a8f05cd361c2b81cd12780286bf154 Mon Sep 17 00:00:00 2001 From: Kerry Archibald Date: Mon, 20 Dec 2021 11:21:50 +0100 Subject: [PATCH 6/6] mock date constructor in test Signed-off-by: Kerry Archibald --- .../views/messages/DateSeparator.tsx | 10 ++----- .../views/messages/DateSeparator-test.tsx | 27 ++++++++++++++----- .../__snapshots__/DateSeparator-test.tsx.snap | 4 +-- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/components/views/messages/DateSeparator.tsx b/src/components/views/messages/DateSeparator.tsx index 1e3da01d7b5..27d3643e70e 100644 --- a/src/components/views/messages/DateSeparator.tsx +++ b/src/components/views/messages/DateSeparator.tsx @@ -38,16 +38,10 @@ function getDaysArray(): string[] { interface IProps { ts: number; forExport?: boolean; - now?: Date; } @replaceableComponent("views.messages.DateSeparator") export default class DateSeparator extends React.Component { - // use defaultProp for now to make testing easier - public static defaultProps = { - now: new Date(), - }; - private getLabel() { const date = new Date(this.props.ts); const disableRelativeTimestamps = !SettingsStore.getValue(UIFeature.TimelineEnableRelativeDates); @@ -55,8 +49,8 @@ export default class DateSeparator extends React.Component { // During the time the archive is being viewed, a specific day might not make sense, so we return the full date if (this.props.forExport || disableRelativeTimestamps) return formatFullDateNoTime(date); - const today = this.props.now; - const yesterday = new Date(this.props.now); + const today = new Date(); + const yesterday = new Date(); const days = getDaysArray(); yesterday.setDate(today.getDate() - 1); diff --git a/test/components/views/messages/DateSeparator-test.tsx b/test/components/views/messages/DateSeparator-test.tsx index 8ec6054cb78..0a3db003f19 100644 --- a/test/components/views/messages/DateSeparator-test.tsx +++ b/test/components/views/messages/DateSeparator-test.tsx @@ -32,21 +32,29 @@ describe("DateSeparator", () => { const HOUR_MS = 3600000; const DAY_MS = HOUR_MS * 24; // Friday Dec 17 2021, 9:09am - const now = new Date('Fri Dec 17 2021 09:09:00 GMT+0100 (Central European Standard Time)'); + const now = '2021-12-17T08:09:00.000Z'; + const nowMs = 1639728540000; const defaultProps = { - ts: now.getTime(), + ts: nowMs, now, }; + const RealDate = global.Date; + class MockDate extends Date { + constructor(date) { + super(date || now); + } + } + const getComponent = (props = {}) => mount(); type TestCase = [string, number, string]; const testCases: TestCase[] = [ - ['the exact same moment', now.getTime(), 'Today'], - ['same day as current day', now.getTime() - HOUR_MS, 'Today'], - ['day before the current day', now.getTime() - (HOUR_MS * 12), 'Yesterday'], - ['2 days ago', now.getTime() - DAY_MS * 2, 'Wednesday'], - ['144 hours ago', now.getTime() - HOUR_MS * 144, 'Sat, Dec 11 2021'], + ['the exact same moment', nowMs, 'Today'], + ['same day as current day', nowMs - HOUR_MS, 'Today'], + ['day before the current day', nowMs - (HOUR_MS * 12), 'Yesterday'], + ['2 days ago', nowMs - DAY_MS * 2, 'Wednesday'], + ['144 hours ago', nowMs - HOUR_MS * 144, 'Sat, Dec 11 2021'], [ '6 days ago, but less than 144h', new Date('Saturday Dec 11 2021 23:59:00 GMT+0100 (Central European Standard Time)').getTime(), @@ -55,9 +63,14 @@ describe("DateSeparator", () => { ]; beforeEach(() => { + global.Date = MockDate as unknown as DateConstructor; (SettingsStore.getValue as jest.Mock).mockReturnValue(true); }); + afterAll(() => { + global.Date = RealDate; + }); + it('renders the date separator correctly', () => { const component = getComponent(); expect(component).toMatchSnapshot(); diff --git a/test/components/views/messages/__snapshots__/DateSeparator-test.tsx.snap b/test/components/views/messages/__snapshots__/DateSeparator-test.tsx.snap index 55ca7ad2cc2..69b5a34defc 100644 --- a/test/components/views/messages/__snapshots__/DateSeparator-test.tsx.snap +++ b/test/components/views/messages/__snapshots__/DateSeparator-test.tsx.snap @@ -2,11 +2,11 @@ exports[`DateSeparator renders the date separator correctly 1`] = `