Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACM-3456 Governance > Policies > Results tab #3857

Closed
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e60f581
adds translations
vishsanghishetty Sep 13, 2024
ee759a3
adds localization support to i18n
vishsanghishetty Sep 13, 2024
6699200
adds i18nextLng key to constants
vishsanghishetty Sep 13, 2024
8cf7ebd
Add functions for date and time formatting, including relative time s…
vishsanghishetty Sep 13, 2024
5678146
Add localization support and relative time formatting for creation ti…
vishsanghishetty Sep 13, 2024
34e21a5
Add localization support and relative time formatting for creation ti…
vishsanghishetty Sep 13, 2024
4cd1142
Implement function to retrieve the last selected language from localS…
vishsanghishetty Sep 13, 2024
98d749b
prettier fix
vishsanghishetty Sep 13, 2024
f9476bc
adds tests datetime
vishsanghishetty Sep 13, 2024
f276ea3
addresses sonar cloud issue
vishsanghishetty Sep 13, 2024
c685d97
Extracts the nested ternary operation into an independent statement.
vishsanghishetty Sep 13, 2024
340b7c5
merge main
vishsanghishetty Sep 14, 2024
2d8c2c4
unit test for twenty four hour time
vishsanghishetty Sep 14, 2024
5376e82
adds 2 more test cases in unit tests
vishsanghishetty Sep 14, 2024
63f7c2f
fixes failing unit test
vishsanghishetty Sep 14, 2024
b8eb2f7
fixes date formatter test
vishsanghishetty Sep 14, 2024
929a4c7
fixes missing transalations
vishsanghishetty Sep 14, 2024
f497f7b
fixes prettier issue
vishsanghishetty Sep 14, 2024
df528fc
removes commented code
vishsanghishetty Sep 14, 2024
44d30a4
removes unused function
vishsanghishetty Sep 14, 2024
396c553
fixes prettier issue
vishsanghishetty Sep 14, 2024
6a668e7
removes commented line
vishsanghishetty Sep 14, 2024
9f29273
removes console log
vishsanghishetty Sep 16, 2024
eac6e70
Add default values for history message and timestamp
vishsanghishetty Sep 16, 2024
bd17975
addresses comment space
vishsanghishetty Sep 16, 2024
b05661e
deletes date time implementation
vishsanghishetty Sep 17, 2024
ac62c1a
updates translation strings
vishsanghishetty Sep 17, 2024
a349081
Update i18n interpolation object implementation for date and number f…
vishsanghishetty Sep 17, 2024
a3a2a47
adds i18n tests
vishsanghishetty Sep 17, 2024
c4dfed2
removes earlier const used for local storage i18n
vishsanghishetty Sep 17, 2024
bd8582d
Implement i18n date localization for Credentials Page
vishsanghishetty Sep 17, 2024
09b6e4c
Implement i18n date localization for Policy Results table
vishsanghishetty Sep 17, 2024
8f1e618
updates i18n tests
vishsanghishetty Sep 17, 2024
16bcd06
updates i18n tests
vishsanghishetty Sep 17, 2024
e56a192
updates i18n tests
vishsanghishetty Sep 17, 2024
380881c
updates i18n tests and i18n
vishsanghishetty Sep 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions frontend/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
"{{count}} clusters with violations_plural": "{{count}} clusters with violations",
"{{count}} clusters without violations": "{{count}} cluster without violations",
"{{count}} clusters without violations_plural": "{{count}} clusters without violations",
"{{count}} day": "{{count}} day",
"{{count}} day_plural": "{{count}} day",
"{{count}} hour": "{{count}} hour",
"{{count}} hour_plural": "{{count}} hour",
"{{count}} minute": "{{count}} minute",
"{{count}} minute_plural": "{{count}} minute",
"{{count}} more": "{{count}} more",
"{{count}} more_plural": "{{count}} more",
"{{count}} selected": "{{count}} selected",
Expand Down Expand Up @@ -1640,6 +1646,7 @@
"Job template": "Job template",
"Joined": "Joined",
"Jump to the bottom": "Jump to the bottom",
"Just now": "Just now",
"Kind": "Kind",
"Kubernetes": "Kubernetes",
"Kubernetes type": "Kubernetes type",
Expand Down
2 changes: 2 additions & 0 deletions frontend/public/locales/es/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,7 @@
"Discover hosts to create host inventory": "Descubrir hosts para crear un inventario de hosts",
"Discovered": "Descubierto",
"Discovered clusters": "Clústeres descubiertos",
"Discovered policies": "Políticas descubiertas",
"discovery.addDiscovery": "Crear ajustes de descubrimiento",
"discovery.configureDiscovery": "Configurar los ajustes de descubrimiento",
"discovery.import": "Importar clúster",
Expand Down Expand Up @@ -1612,6 +1613,7 @@
"Job template": "Plantilla de tareas",
"Joined": "Unido",
"Jump to the bottom": "Saltar a la parte inferior",
"Just now": "Justo ahora",
"Kind": "Clase",
"Kubernetes": "Kubernetes",
"Kubernetes type": "Tipo Kubernetes",
Expand Down
2 changes: 2 additions & 0 deletions frontend/public/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,7 @@
"Discover hosts to create host inventory": "Découvrir des hôtes pour créer un inventaire d’hôtes",
"Discovered": "Découvert",
"Discovered clusters": "Clusters découverts",
"Discovered policies": "Stratégies découvertes",
"discovery.addDiscovery": "Créer des paramètres de découverte",
"discovery.configureDiscovery": "Configurer les paramètres de découverte",
"discovery.import": "Importer un cluster",
Expand Down Expand Up @@ -1612,6 +1613,7 @@
"Job template": "Modèle de tâche",
"Joined": "Rejoint",
"Jump to the bottom": "Aller en bas",
"Just now": "À l'instant",
"Kind": "Type",
"Kubernetes": "Kubernetes",
"Kubernetes type": "Type Kubernetes",
Expand Down
2 changes: 2 additions & 0 deletions frontend/public/locales/ja/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,7 @@
"Discover hosts to create host inventory": "ホストを検出してホストインベントリーを作成する",
"Discovered": "検出済み",
"Discovered clusters": "検出されたクラスター",
"Discovered policies": "検出されたポリシー",
"discovery.addDiscovery": "検出設定の作成",
"discovery.configureDiscovery": "検出設定の設定",
"discovery.import": "クラスターのインポート",
Expand Down Expand Up @@ -1598,6 +1599,7 @@
"Job template": "ジョブテンプレート",
"Joined": "参加済み",
"Jump to the bottom": "下にジャンプ",
"Just now": "たった今",
"Kind": "種類",
"Kubernetes": "Kubernetes",
"Kubernetes type": "Kubernetes タイプ",
Expand Down
2 changes: 2 additions & 0 deletions frontend/public/locales/ko/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,7 @@
"Discover hosts to create host inventory": "호스트 인벤토리를 생성할 호스트 검색",
"Discovered": "검색됨",
"Discovered clusters": "검색된 클러스터",
"Discovered policies": "검색된 정책",
"discovery.addDiscovery": "검색 설정 생성",
"discovery.configureDiscovery": "검색 설정 구성",
"discovery.import": "클러스터 가져오기",
Expand Down Expand Up @@ -1598,6 +1599,7 @@
"Job template": "작업 템플릿",
"Joined": "참여됨",
"Jump to the bottom": "맨 아래로 이동",
"Just now": "방금",
"Kind": "유형",
"Kubernetes": "Kubernetes",
"Kubernetes type": "Kubernetes 유형",
Expand Down
2 changes: 2 additions & 0 deletions frontend/public/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,7 @@
"Discover hosts to create host inventory": "发现主机以创建主机清单",
"Discovered": "发现的",
"Discovered clusters": "发现的集群",
"Discovered policies": "发现的策略",
"discovery.addDiscovery": "创建发现设置",
"discovery.configureDiscovery": "配置发现设置",
"discovery.import": "导入集群",
Expand Down Expand Up @@ -1598,6 +1599,7 @@
"Job template": "作业模板",
"Joined": "Joined",
"Jump to the bottom": "跳转到底部",
"Just now": "刚刚",
"Kind": "种类(Kind)",
"Kubernetes": "Kubernetes",
"Kubernetes type": "Kubernetes 类型",
Expand Down
18 changes: 18 additions & 0 deletions frontend/src/lib/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { initReactI18next } from 'react-i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import HttpApi from 'i18next-http-backend'
import { supportedLanguages } from './supportedLanguages'
import { dateTimeFormatter, fromNow } from '../resources/utils/datetime'

i18n
// pass the i18n instance to react-i18next
Expand All @@ -28,6 +29,23 @@ i18n
keySeparator: false, // this repo will use single level json
interpolation: {
escapeValue: false, // react handles this already
format: function (value, format, lng, options) {
if (format === 'number') {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat#Browser_compatibility
return new Intl.NumberFormat(lng).format(value)
}
if (value instanceof Date) {
if (format === 'fromNow') {
const fromNowOptions = {
includeSeconds: options?.includeSeconds || false, // Extract or set defaults
addSuffix: options?.addSuffix || false,
}
return fromNow(value, undefined, fromNowOptions)
}
return dateTimeFormatter(lng).format(value)
}
return value
},
},
defaultNS: 'translation', // the default file for strings when using useTranslation, etc
nsSeparator: '~',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/resources/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ export enum HypershiftCloudPlatformType {
PowerVS = 'PowerVS',
KubeVirt = 'KubeVirt',
}

export const LAST_LANGUAGE_LOCAL_STORAGE_KEY = 'i18nextLng' // Key for storing the last selected language in local storage.
155 changes: 155 additions & 0 deletions frontend/src/resources/utils/datetime.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// /* Copyright Contributors to the Open Cluster Management project */
import { fromNow, getDuration, isValid, timeFormatter, twentyFourHourTime } from './datetime'
import { getLastLanguage } from './getLastLanguage'

// Mocking i18n for translation functions
jest.mock('i18next', () => ({
t: (key: string, options?: any) => {
if (key === 'Just now') return 'Just now'
return `${options.count} ${key}`
},
}))

describe('fromNow', () => {
it('should return "Just now" for very recent dates (1 ms)', () => {
const now = new Date()
const recentDate = new Date(now.getTime() - 1)
expect(fromNow(recentDate, now)).toBe('Just now')
})

it('should return correct relative time for past dates', () => {
const now = new Date()
const oneDayAgo = new Date(now.getTime() - 86400000)
expect(fromNow(oneDayAgo, now)).toBe('1 day ago')
})

it('should return "-" for future dates', () => {
const now = new Date()
const futureDate = new Date(now.getTime() + 86400000)
expect(fromNow(futureDate, now)).toBe('-')
})

it.skip('should handle options like omitSuffix', () => {
const now = new Date()
const oneDayAgo = new Date(now.getTime() - 86400000)
expect(fromNow(oneDayAgo, now, { omitSuffix: true })).toBe('1 day')
})
})
describe('getDuration', () => {
it('should correctly calculate duration in days, hours, minutes, and seconds', () => {
const ms = 90061000
const result = getDuration(ms)
expect(result).toEqual({ days: 1, hours: 1, minutes: 1, seconds: 1 })
})

it('should return zero for negative or null values', () => {
expect(getDuration(-1000)).toEqual({ days: 0, hours: 0, minutes: 0, seconds: 0 })
expect(getDuration(0)).toEqual({ days: 0, hours: 0, minutes: 0, seconds: 0 })
})
})

describe('isValid', () => {
it('should return true for valid Date objects', () => {
const validDate = new Date()
expect(isValid(validDate)).toBe(true)
})

it('should return false for invalid Date objects', () => {
const invalidDate = new Date('invalid date')
expect(isValid(invalidDate)).toBe(false)
})
})

describe('Formatters', () => {
it('should format time correctly using timeFormatter', () => {
const date = new Date('2024-09-09T12:44:00')
expect(timeFormatter.format(date)).toBe('12:44 PM')
})

it('should format date correctly using dateFormatter', () => {
const date = new Date(Date.UTC(2024, 8, 8)) // Setting date in UTC (Sep 8, 2024)

const dateFormatter = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
timeZone: 'UTC', // Ensures the formatter uses UTC
})

expect(dateFormatter.format(date)).toBe('Sep 8, 2024')
})
})

describe('twentyFourHourTime', () => {
it('should return time in HH:mm format when showSeconds is false or undefined', () => {
const date = new Date('2024-09-12T14:30:00') // 14:30
expect(twentyFourHourTime(date)).toBe('14:30')
})

it('should return time in HH:mm:ss format when showSeconds is true', () => {
const date = new Date('2024-09-12T14:30:45') // 14:30:45
expect(twentyFourHourTime(date, true)).toBe('14:30:45')
})

it('should return time in HH:mm format when showSeconds is false', () => {
const date = new Date('2024-09-12T14:30:45') // 14:30:45
expect(twentyFourHourTime(date, false)).toBe('14:30')
})

it('should correctly handle midnight (00:00)', () => {
const date = new Date('2024-09-12T00:00:00') // 00:00
expect(twentyFourHourTime(date)).toBe('00:00')
})

it('should correctly handle single digit hours and minutes', () => {
const date = new Date('2024-09-12T07:08:00') // 07:08
expect(twentyFourHourTime(date)).toBe('07:08')
})

it('should correctly handle seconds when showSeconds is true', () => {
const date = new Date('2024-09-12T07:08:09') // 07:08:09
expect(twentyFourHourTime(date, true)).toBe('07:08:09')
})

it('should correctly handle noon (12:00) without seconds', () => {
const date = new Date('2024-09-12T12:00:00') // 12:00
expect(twentyFourHourTime(date)).toBe('12:00')
})

it('should correctly handle noon (12:00) with seconds', () => {
const date = new Date('2024-09-12T12:00:00') // 12:00:00
expect(twentyFourHourTime(date, true)).toBe('12:00:00')
})
})

jest.mock('./getLastLanguage')

describe('language selection logic', () => {
let i18n: { language?: string }

beforeEach(() => {
// Reset the i18n mock before each test
i18n = {}
jest.resetAllMocks()
})

it('should return i18n.language when it is defined', () => {
i18n.language = 'fr'
const language = (i18n.language || getLastLanguage() || 'en').split('-')[0]
expect(language).toBe('fr')
})

it('should return getLastLanguage when i18n.language is undefined', () => {
;(getLastLanguage as jest.Mock).mockReturnValue('es')

const language = (i18n.language || getLastLanguage() || 'en').split('-')[0]
expect(language).toBe('es')
})

it('should return "en" when both i18n.language and getLastLanguage return undefined', () => {
;(getLastLanguage as jest.Mock).mockReturnValue(undefined)

const language = (i18n.language || getLastLanguage() || 'en').split('-')[0]
expect(language).toBe('en')
})
})
Loading