diff --git a/src/api/index.js b/src/api/index.js index a0911b20d..eb189709c 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -24,6 +24,7 @@ import { getQueryParams, getResourcesAPI, getTektonAPI, + isLogTimestampsEnabled, useCollection, useResource } from './utils'; @@ -104,9 +105,11 @@ export function useNamespaces(queryConfig) { } export function getPodLogURL({ container, name, namespace, follow }) { + const timestamps = isLogTimestampsEnabled(); const queryParams = { ...(container && { container }), - ...(follow && { follow }) + ...(follow && { follow }), + ...(timestamps && { timestamps }) }; const uri = `${getKubeAPI( 'pods', diff --git a/src/api/utils.js b/src/api/utils.js index e85dce2f2..d1e484e5f 100644 --- a/src/api/utils.js +++ b/src/api/utils.js @@ -210,3 +210,11 @@ export function useResource(kind, api, params, queryConfig) { useWebSocket({ kind }); return query; } + +export function isLogTimestampsEnabled() { + return localStorage.getItem('tkn-logs-timestamps') === 'true'; +} + +export function setLogTimestampsEnabled(enabled) { + localStorage.setItem('tkn-logs-timestamps', enabled); +} diff --git a/src/api/utils.test.js b/src/api/utils.test.js index 631d1ac75..1de07263b 100644 --- a/src/api/utils.test.js +++ b/src/api/utils.test.js @@ -388,3 +388,53 @@ describe('useResource', () => { expect(result.current.data).toEqual(updatedResource); }); }); + +describe('isLogTimestampsEnabled', () => { + afterEach(() => { + localStorage.removeItem('tkn-logs-timestamps'); + }); + + it('handles valid values', () => { + localStorage.setItem('tkn-logs-timestamps', true); + expect(utils.isLogTimestampsEnabled()).toBe(true); + localStorage.setItem('tkn-logs-timestamps', false); + expect(utils.isLogTimestampsEnabled()).toBe(false); + }); + + it('handles invalid values', () => { + localStorage.setItem('tkn-logs-timestamps', 'foo'); + expect(utils.isLogTimestampsEnabled()).toBe(false); + }); +}); + +describe('isLogTimestampsEnabled', () => { + afterEach(() => { + localStorage.removeItem('tkn-logs-timestamps'); + }); + + it('handles valid values', () => { + localStorage.setItem('tkn-logs-timestamps', true); + expect(utils.isLogTimestampsEnabled()).toBe(true); + localStorage.setItem('tkn-logs-timestamps', false); + expect(utils.isLogTimestampsEnabled()).toBe(false); + }); + + it('handles invalid values', () => { + localStorage.setItem('tkn-logs-timestamps', 'foo'); + expect(utils.isLogTimestampsEnabled()).toBe(false); + }); +}); + +describe('setLogTimestampsEnabled', () => { + afterEach(() => { + localStorage.removeItem('tkn-logs-timestamps'); + }); + + it('persists the specified value', () => { + utils.setLogTimestampsEnabled(true); + expect(localStorage.getItem('tkn-logs-timestamps')).toEqual('true'); + + utils.setLogTimestampsEnabled(false); + expect(localStorage.getItem('tkn-logs-timestamps')).toEqual('false'); + }); +}); diff --git a/src/containers/Settings/Settings.js b/src/containers/Settings/Settings.js index 9b226cc46..ffada8509 100644 --- a/src/containers/Settings/Settings.js +++ b/src/containers/Settings/Settings.js @@ -14,7 +14,7 @@ limitations under the License. import React from 'react'; import { injectIntl } from 'react-intl'; import { useTitleSync } from '@tektoncd/dashboard-utils'; -import { RadioTile, TileGroup } from 'carbon-components-react'; +import { RadioTile, TileGroup, Toggle } from 'carbon-components-react'; import { Asleep16 as DarkIcon, Light16 as LightIcon, @@ -22,6 +22,10 @@ import { } from '@carbon/icons-react'; import { getTheme, setTheme } from '../../utils'; +import { + isLogTimestampsEnabled, + setLogTimestampsEnabled +} from '../../api/utils'; export function Settings({ intl }) { useTitleSync({ @@ -71,6 +75,24 @@ export function Settings({ intl }) { })} + + setLogTimestampsEnabled(checked)} + /> ); diff --git a/src/containers/Settings/Settings.scss b/src/containers/Settings/Settings.scss index 117bb2263..90315d9ed 100644 --- a/src/containers/Settings/Settings.scss +++ b/src/containers/Settings/Settings.scss @@ -14,30 +14,31 @@ limitations under the License. .tkn--settings { .tkn--settings--content { .bx--tile-group { + margin-bottom: 2rem; max-width: 400px; - } - - .bx--tile { - display: flex; - align-items: center; - .bx--tile__checkmark { - top: calc(50% - 0.5rem); - } - - .bx--tile-content { + .bx--tile { display: flex; align-items: center; - svg { - fill: $icon-01; - margin-right: 0.5rem; + .bx--tile__checkmark { + top: calc(50% - 0.5rem); + } + + .bx--tile-content { + display: flex; + align-items: center; + + svg { + fill: $icon-01; + margin-right: 0.5rem; + } } } - } - .bx--tile:not(:last-child) { - margin-bottom: 0.5rem; + .bx--tile:not(:last-child) { + margin-bottom: 0.5rem; + } } } } diff --git a/src/containers/Settings/Settings.test.js b/src/containers/Settings/Settings.test.js index b0a0a9d05..a578fd04b 100644 --- a/src/containers/Settings/Settings.test.js +++ b/src/containers/Settings/Settings.test.js @@ -16,6 +16,8 @@ import { fireEvent } from '@testing-library/react'; import { render } from '../../utils/test'; import * as Utils from '../../utils'; +import * as APIUtils from '../../api/utils'; + import Settings from '.'; describe('Settings', () => { @@ -34,4 +36,18 @@ describe('Settings', () => { fireEvent.click(getByText(/dark/i)); expect(Utils.setTheme).toHaveBeenCalledWith('dark'); }); + + it('should render the log timestamp settings correctly', () => { + jest + .spyOn(APIUtils, 'isLogTimestampsEnabled') + .mockImplementation(() => true); + jest.spyOn(APIUtils, 'setLogTimestampsEnabled'); + + const { getByLabelText, getByText } = render(); + + expect(getByText(/show log timestamps/i)).toBeTruthy(); + expect(getByText(/on/i)).toBeTruthy(); + fireEvent.click(getByLabelText(/show log timestamps/i)); + expect(APIUtils.setLogTimestampsEnabled).toHaveBeenCalledWith(false); + }); }); diff --git a/src/nls/messages_de.json b/src/nls/messages_de.json index f72ee6356..544722c1e 100644 --- a/src/nls/messages_de.json +++ b/src/nls/messages_de.json @@ -145,6 +145,7 @@ "dashboard.logs.restore": "", "dashboard.logs.scrollToBottom": "", "dashboard.logs.scrollToTop": "", + "dashboard.logs.showTimestamps.label": "", "dashboard.metadata.dateCreated": "", "dashboard.metadata.labels": "", "dashboard.metadata.namespace": "", @@ -250,6 +251,8 @@ "dashboard.theme.label": "", "dashboard.theme.light": "", "dashboard.theme.system": "", + "dashboard.toggle.off": "", + "dashboard.toggle.on": "", "dashboard.tooltipDropdown.empty": "", "dashboard.trigger.noHeaders": "", "dashboard.trigger.noOverlays": "", diff --git a/src/nls/messages_en.json b/src/nls/messages_en.json index b0470342d..3da217916 100644 --- a/src/nls/messages_en.json +++ b/src/nls/messages_en.json @@ -145,6 +145,7 @@ "dashboard.logs.restore": "Return to default", "dashboard.logs.scrollToBottom": "Scroll to end of logs", "dashboard.logs.scrollToTop": "Scroll to start of logs", + "dashboard.logs.showTimestamps.label": "Show log timestamps", "dashboard.metadata.dateCreated": "Date created:", "dashboard.metadata.labels": "Labels:", "dashboard.metadata.namespace": "Namespace:", @@ -250,6 +251,8 @@ "dashboard.theme.label": "Theme", "dashboard.theme.light": "Light", "dashboard.theme.system": "System", + "dashboard.toggle.off": "Off", + "dashboard.toggle.on": "On", "dashboard.tooltipDropdown.empty": "No items found", "dashboard.trigger.noHeaders": "No headers found for this interceptor.", "dashboard.trigger.noOverlays": "No overlays found for this interceptor.", diff --git a/src/nls/messages_es.json b/src/nls/messages_es.json index 13dc763e5..53c348857 100644 --- a/src/nls/messages_es.json +++ b/src/nls/messages_es.json @@ -145,6 +145,7 @@ "dashboard.logs.restore": "", "dashboard.logs.scrollToBottom": "", "dashboard.logs.scrollToTop": "", + "dashboard.logs.showTimestamps.label": "", "dashboard.metadata.dateCreated": "", "dashboard.metadata.labels": "", "dashboard.metadata.namespace": "", @@ -250,6 +251,8 @@ "dashboard.theme.label": "", "dashboard.theme.light": "", "dashboard.theme.system": "", + "dashboard.toggle.off": "", + "dashboard.toggle.on": "", "dashboard.tooltipDropdown.empty": "", "dashboard.trigger.noHeaders": "", "dashboard.trigger.noOverlays": "", diff --git a/src/nls/messages_fr.json b/src/nls/messages_fr.json index a67ac9ae6..5684953fa 100644 --- a/src/nls/messages_fr.json +++ b/src/nls/messages_fr.json @@ -145,6 +145,7 @@ "dashboard.logs.restore": "", "dashboard.logs.scrollToBottom": "", "dashboard.logs.scrollToTop": "", + "dashboard.logs.showTimestamps.label": "", "dashboard.metadata.dateCreated": "", "dashboard.metadata.labels": "", "dashboard.metadata.namespace": "", @@ -250,6 +251,8 @@ "dashboard.theme.label": "", "dashboard.theme.light": "", "dashboard.theme.system": "", + "dashboard.toggle.off": "", + "dashboard.toggle.on": "", "dashboard.tooltipDropdown.empty": "", "dashboard.trigger.noHeaders": "", "dashboard.trigger.noOverlays": "", diff --git a/src/nls/messages_it.json b/src/nls/messages_it.json index d7a8ac4d9..6c6a56f77 100644 --- a/src/nls/messages_it.json +++ b/src/nls/messages_it.json @@ -145,6 +145,7 @@ "dashboard.logs.restore": "", "dashboard.logs.scrollToBottom": "", "dashboard.logs.scrollToTop": "", + "dashboard.logs.showTimestamps.label": "", "dashboard.metadata.dateCreated": "", "dashboard.metadata.labels": "", "dashboard.metadata.namespace": "", @@ -250,6 +251,8 @@ "dashboard.theme.label": "", "dashboard.theme.light": "", "dashboard.theme.system": "", + "dashboard.toggle.off": "", + "dashboard.toggle.on": "", "dashboard.tooltipDropdown.empty": "", "dashboard.trigger.noHeaders": "", "dashboard.trigger.noOverlays": "", diff --git a/src/nls/messages_ja.json b/src/nls/messages_ja.json index d1510a328..732259ec8 100644 --- a/src/nls/messages_ja.json +++ b/src/nls/messages_ja.json @@ -145,6 +145,7 @@ "dashboard.logs.restore": "デフォルトに戻す", "dashboard.logs.scrollToBottom": "", "dashboard.logs.scrollToTop": "", + "dashboard.logs.showTimestamps.label": "", "dashboard.metadata.dateCreated": "作成日:", "dashboard.metadata.labels": "ラベル:", "dashboard.metadata.namespace": "Namespace:", @@ -250,6 +251,8 @@ "dashboard.theme.label": "", "dashboard.theme.light": "", "dashboard.theme.system": "", + "dashboard.toggle.off": "", + "dashboard.toggle.on": "", "dashboard.tooltipDropdown.empty": "項目が見つかりません", "dashboard.trigger.noHeaders": "このinterceptorのヘッダーが見つかりません。", "dashboard.trigger.noOverlays": "このinterceptorのオーバーレイが見つかりません。", diff --git a/src/nls/messages_ko.json b/src/nls/messages_ko.json index 780ce81bb..454be7562 100644 --- a/src/nls/messages_ko.json +++ b/src/nls/messages_ko.json @@ -145,6 +145,7 @@ "dashboard.logs.restore": "", "dashboard.logs.scrollToBottom": "", "dashboard.logs.scrollToTop": "", + "dashboard.logs.showTimestamps.label": "", "dashboard.metadata.dateCreated": "", "dashboard.metadata.labels": "", "dashboard.metadata.namespace": "", @@ -250,6 +251,8 @@ "dashboard.theme.label": "", "dashboard.theme.light": "", "dashboard.theme.system": "", + "dashboard.toggle.off": "", + "dashboard.toggle.on": "", "dashboard.tooltipDropdown.empty": "", "dashboard.trigger.noHeaders": "", "dashboard.trigger.noOverlays": "", diff --git a/src/nls/messages_pt.json b/src/nls/messages_pt.json index 6e3433ef5..43779b4cf 100644 --- a/src/nls/messages_pt.json +++ b/src/nls/messages_pt.json @@ -145,6 +145,7 @@ "dashboard.logs.restore": "", "dashboard.logs.scrollToBottom": "", "dashboard.logs.scrollToTop": "", + "dashboard.logs.showTimestamps.label": "", "dashboard.metadata.dateCreated": "", "dashboard.metadata.labels": "", "dashboard.metadata.namespace": "", @@ -250,6 +251,8 @@ "dashboard.theme.label": "", "dashboard.theme.light": "", "dashboard.theme.system": "", + "dashboard.toggle.off": "", + "dashboard.toggle.on": "", "dashboard.tooltipDropdown.empty": "", "dashboard.trigger.noHeaders": "", "dashboard.trigger.noOverlays": "", diff --git a/src/nls/messages_zh-Hans.json b/src/nls/messages_zh-Hans.json index f07afe06c..b616f4eb3 100644 --- a/src/nls/messages_zh-Hans.json +++ b/src/nls/messages_zh-Hans.json @@ -145,6 +145,7 @@ "dashboard.logs.restore": "", "dashboard.logs.scrollToBottom": "", "dashboard.logs.scrollToTop": "", + "dashboard.logs.showTimestamps.label": "", "dashboard.metadata.dateCreated": "创建日期:", "dashboard.metadata.labels": "标签:", "dashboard.metadata.namespace": "Namespace:", @@ -250,6 +251,8 @@ "dashboard.theme.label": "", "dashboard.theme.light": "", "dashboard.theme.system": "", + "dashboard.toggle.off": "", + "dashboard.toggle.on": "", "dashboard.tooltipDropdown.empty": "未找到项目", "dashboard.trigger.noHeaders": "未找到该 Interceptor 的 header。", "dashboard.trigger.noOverlays": "未找到该 Interceptor 的 overlay。", diff --git a/src/nls/messages_zh-Hant.json b/src/nls/messages_zh-Hant.json index 9807f3a13..83cb5390d 100644 --- a/src/nls/messages_zh-Hant.json +++ b/src/nls/messages_zh-Hant.json @@ -145,6 +145,7 @@ "dashboard.logs.restore": "", "dashboard.logs.scrollToBottom": "", "dashboard.logs.scrollToTop": "", + "dashboard.logs.showTimestamps.label": "", "dashboard.metadata.dateCreated": "", "dashboard.metadata.labels": "", "dashboard.metadata.namespace": "", @@ -250,6 +251,8 @@ "dashboard.theme.label": "", "dashboard.theme.light": "", "dashboard.theme.system": "", + "dashboard.toggle.off": "", + "dashboard.toggle.on": "", "dashboard.tooltipDropdown.empty": "", "dashboard.trigger.noHeaders": "", "dashboard.trigger.noOverlays": "",