Quellcode durchsuchen

HUE-9485 [frontend] Add a Vue TimeAgo component

Johan Ahlen vor 5 Jahren
Ursprung
Commit
557ba6eb5d

+ 149 - 0
desktop/core/src/desktop/js/components/TimeAgo.test.ts

@@ -0,0 +1,149 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you 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 { shallowMount } from '@vue/test-utils';
+import TimeAgo from './TimeAgo.vue';
+
+describe('TimeAgo.vue', () => {
+  const RealDate = Date.now;
+
+  const MOCK_NOW = 1000 * 60 * 60 * 24 * 365 * 10;
+
+  beforeAll(() => {
+    global.Date.now = jest.fn(() => MOCK_NOW);
+  });
+
+  afterAll(() => {
+    global.Date.now = RealDate;
+  });
+
+  it('should render', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW
+      }
+    });
+    expect(wrapper.element).toMatchSnapshot();
+  });
+
+  it('should render 1 second ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 1000
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('1 second ago');
+  });
+
+  it('should render 4 seconds ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 4 * 1000
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('4 seconds ago');
+  });
+
+  it('should render 1 minute ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 1000 * 60
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('1 minute ago');
+  });
+
+  it('should render 4 minutes ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 1000 * 60 * 4
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('4 minutes ago');
+  });
+
+  it('should render 1 hour ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 1000 * 60 * 60
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('1 hour ago');
+  });
+
+  it('should render 4 hours ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 1000 * 60 * 60 * 4
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('4 hours ago');
+  });
+
+  it('should render 1 day ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 1000 * 60 * 60 * 24
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('1 day ago');
+  });
+
+  it('should render 4 days ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 1000 * 60 * 60 * 24 * 4
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('4 days ago');
+  });
+
+  it('should render 1 month ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 1000 * 60 * 60 * 24 * (365 / 12)
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('1 month ago');
+  });
+
+  it('should render 4 months ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 1000 * 60 * 60 * 24 * (365 / 12) * 4
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('4 months ago');
+  });
+
+  it('should render 1 year ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 1000 * 60 * 60 * 24 * (365 / 12) * 12
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('1 year ago');
+  });
+
+  it('should render 4 years ago', () => {
+    const wrapper = shallowMount(TimeAgo, {
+      propsData: {
+        value: MOCK_NOW - 1000 * 60 * 60 * 24 * (365 / 12) * 12 * 4
+      }
+    });
+    expect(wrapper.element.firstChild?.textContent).toContain('4 years ago');
+  });
+});

+ 58 - 0
desktop/core/src/desktop/js/components/TimeAgo.vue

@@ -0,0 +1,58 @@
+<!--
+  Licensed to Cloudera, Inc. under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  Cloudera, Inc. licenses this file
+  to you 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.
+-->
+
+<template>
+  <span>{{ timeAgo }}</span>
+</template>
+
+<script lang="ts">
+  import I18n from '../utils/i18n';
+  import Vue from 'vue';
+  import Component from 'vue-class-component';
+  import { Prop } from 'vue-property-decorator';
+
+  const SECOND = { val: 1000, text: 'second' };
+  const MINUTE = { val: SECOND.val * 60, text: 'minute' };
+  const HOUR = { val: MINUTE.val * 60, text: 'hour' };
+  const DAY = { val: HOUR.val * 24, text: 'day' };
+  const MONTH = { val: DAY.val * (365 / 12), text: 'month' };
+  const YEAR = { val: DAY.val * 365, text: 'year' };
+
+  const LIMITS = [YEAR, MONTH, DAY, HOUR, MINUTE, SECOND];
+
+  @Component
+  export default class TimeAgo extends Vue {
+    @Prop({ required: true })
+    value!: number;
+
+    // TODO: Add timezone support
+    get timeAgo(): string {
+      const diff = Date.now() - this.value;
+      for (const limit of LIMITS) {
+        if (diff >= limit.val) {
+          const val = Math.round(diff / limit.val);
+          const postfix = I18n(`${limit.text}${val > 1 ? 's' : ''} ago`);
+          return `${val} ${postfix}`;
+        }
+      }
+      return I18n('now');
+    }
+  }
+</script>
+
+<style lang="scss" scoped></style>

+ 7 - 0
desktop/core/src/desktop/js/components/__snapshots__/TimeAgo.test.ts.snap

@@ -0,0 +1,7 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`TimeAgo.vue should render 1`] = `
+<span>
+  now
+</span>
+`;