From 8bc66b21690e0748034aef81a86619164ebc2b17 Mon Sep 17 00:00:00 2001 From: MinamiFunakoshiTR Date: Tue, 12 Aug 2025 09:17:09 -0400 Subject: [PATCH 1/5] prettifyDate done --- .github/workflows/test.yaml | 24 +++ package.json | 10 +- pnpm-lock.yaml | 308 ++++++++++++++++++++++++++++++++++++ src/test/utils.test.ts | 141 +++++++++++++++++ src/utils/index.ts | 73 +++++++++ 5 files changed, 552 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/test.yaml create mode 100644 src/test/utils.test.ts diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 00000000..e4b171f2 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,24 @@ +name: Test Graphics Kit +permissions: + contents: read + issues: write +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + build-app: + name: SvelteKit builds + strategy: + matrix: + version: [20, 22] + runs-on: ubuntu-latest + env: + TESTING: true + steps: + - id: test + name: Run tests + run: pnpm test \ No newline at end of file diff --git a/package.json b/package.json index d8598b08..19e7b1eb 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "changeset:version": "changeset version", "changeset:publish": "git add --all && changeset publish", - "knip": "knip" + "knip": "knip", + "test": "vitest" }, "license": "MIT", "files": [ @@ -102,14 +103,15 @@ "dayjs": "^1.11.13", "es-toolkit": "^1.35.0", "journalize": "^2.6.0", + "mp4box": "^0.5.4", "proper-url-join": "^2.1.2", "pym.js": "^1.3.2", "slugify": "^1.6.6", "storybook-addon-rtl": "^1.1.0", "svelte-fa": "^4.0.3", "svelte-intersection-observer": "^1.0.0", - "mp4box": "^0.5.4", - "ua-parser-js": "^2.0.3" + "ua-parser-js": "^2.0.3", + "vitest": "^3.2.4" }, "exports": { ".": { @@ -124,4 +126,4 @@ "bugs": { "url": "https://github.com/reuters-graphics/graphics-components/issues" } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae7fbfab..3b6786bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ importers: ua-parser-js: specifier: ^2.0.3 version: 2.0.3 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.14.1)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1) devDependencies: '@changesets/cli': specifier: ^2.29.2 @@ -1061,6 +1064,9 @@ packages: '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/concat-stream@2.0.3': resolution: {integrity: sha512-3qe4oQAPNwVNwK4C9c8u+VJqv9kez+2MR4qJpoPFfXtgxxif1QbFusvXzK0/Wra2VX07smostI2VMmJNSpZjuQ==} @@ -1082,6 +1088,9 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/eslint@9.6.1': resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} @@ -1225,21 +1234,50 @@ packages: '@vitest/expect@2.0.5': resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@2.0.5': resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==} '@vitest/pretty-format@2.1.9': resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + '@vitest/spy@2.0.5': resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/utils@2.0.5': resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} '@vitest/utils@2.1.9': resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + abbrev@2.0.0: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -1392,6 +1430,10 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1721,6 +1763,15 @@ packages: supports-color: optional: true + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decode-named-character-reference@1.1.0: resolution: {integrity: sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==} @@ -1898,6 +1949,9 @@ packages: resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -2085,6 +2139,10 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -2125,6 +2183,14 @@ packages: picomatch: optional: true + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -2574,6 +2640,9 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true @@ -2689,6 +2758,9 @@ packages: loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + loupe@3.2.0: + resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==} + lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -3154,6 +3226,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} @@ -3524,6 +3599,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -3592,6 +3670,12 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.9.0: + resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + storybook-addon-rtl@1.1.0: resolution: {integrity: sha512-L8JljF1M+30rcSuM4JjeIi4ZRmg9WZi/1u4T/5/EQvpDKCMOAq7uHeOKj4YS1InC4Zksnz3DrggXmO3mISXKcQ==} @@ -3669,6 +3753,9 @@ packages: resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} engines: {node: '>=14.16'} + strip-literal@3.0.0: + resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + stylis@4.3.6: resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} @@ -3784,18 +3871,40 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyglobby@0.2.12: resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + tinyrainbow@1.2.0: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + tinyspy@3.0.2: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} + engines: {node: '>=14.0.0'} + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -3973,6 +4082,11 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + vite@6.3.2: resolution: {integrity: sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -4021,6 +4135,34 @@ packages: vite: optional: true + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + walk-up-path@3.0.1: resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==} @@ -4065,6 +4207,11 @@ packages: engines: {node: ^16.13.0 || >=18.0.0} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -5098,6 +5245,10 @@ snapshots: '@types/aria-query@5.0.4': {} + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + '@types/concat-stream@2.0.3': dependencies: '@types/node': 22.14.1 @@ -5118,6 +5269,8 @@ snapshots: dependencies: '@types/ms': 2.1.0 + '@types/deep-eql@4.0.2': {} + '@types/eslint@9.6.1': dependencies: '@types/estree': 1.0.7 @@ -5299,6 +5452,22 @@ snapshots: chai: 5.2.0 tinyrainbow: 1.2.0 + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.0 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@6.3.2(@types/node@22.14.1)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.3.2(@types/node@22.14.1)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1) + '@vitest/pretty-format@2.0.5': dependencies: tinyrainbow: 1.2.0 @@ -5307,10 +5476,30 @@ snapshots: dependencies: tinyrainbow: 1.2.0 + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.0.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.17 + pathe: 2.0.3 + '@vitest/spy@2.0.5': dependencies: tinyspy: 3.0.2 + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.3 + '@vitest/utils@2.0.5': dependencies: '@vitest/pretty-format': 2.0.5 @@ -5324,6 +5513,12 @@ snapshots: loupe: 3.1.3 tinyrainbow: 1.2.0 + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.0 + tinyrainbow: 2.0.0 + abbrev@2.0.0: {} acorn-jsx@5.3.2(acorn@8.14.1): @@ -5473,6 +5668,8 @@ snapshots: buffer-from@1.1.2: {} + cac@6.7.14: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -5807,6 +6004,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.1: + dependencies: + ms: 2.1.3 + decode-named-character-reference@1.1.0: dependencies: character-entities: 2.0.2 @@ -6026,6 +6227,8 @@ snapshots: iterator.prototype: 1.1.5 safe-array-concat: 1.1.3 + es-module-lexer@1.7.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -6351,6 +6554,8 @@ snapshots: esutils@2.0.3: {} + expect-type@1.2.2: {} + extend@3.0.2: {} extendable-error@0.1.7: {} @@ -6389,6 +6594,10 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fdir@6.4.6(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -6849,6 +7058,8 @@ snapshots: js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + js-yaml@3.14.1: dependencies: argparse: 1.0.10 @@ -6964,6 +7175,8 @@ snapshots: loupe@3.1.3: {} + loupe@3.2.0: {} + lower-case@2.0.2: dependencies: tslib: 2.8.1 @@ -7720,6 +7933,8 @@ snapshots: path-type@4.0.0: {} + pathe@2.0.3: {} + pathval@2.0.0: {} picocolors@1.1.1: {} @@ -8130,6 +8345,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + signal-exit@4.1.0: {} sirv@3.0.1: @@ -8189,6 +8406,10 @@ snapshots: sprintf-js@1.0.3: {} + stackback@0.0.2: {} + + std-env@3.9.0: {} + storybook-addon-rtl@1.1.0: {} storybook@8.6.12(prettier@3.5.3): @@ -8292,6 +8513,10 @@ snapshots: strip-json-comments@5.0.1: {} + strip-literal@3.0.0: + dependencies: + js-tokens: 9.0.1 + stylis@4.3.6: {} supports-color@7.2.0: @@ -8396,15 +8621,30 @@ snapshots: tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + tinyglobby@0.2.12: dependencies: fdir: 6.4.3(picomatch@4.0.2) picomatch: 4.0.2 + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + + tinypool@1.1.1: {} + tinyrainbow@1.2.0: {} + tinyrainbow@2.0.0: {} + tinyspy@3.0.2: {} + tinyspy@4.0.3: {} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -8647,6 +8887,27 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 + vite-node@3.2.4(@types/node@22.14.1)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1): + dependencies: + cac: 6.7.14 + debug: 4.4.1 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.3.2(@types/node@22.14.1)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vite@6.3.2(@types/node@22.14.1)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1): dependencies: esbuild: 0.25.2 @@ -8666,6 +8927,48 @@ snapshots: optionalDependencies: vite: 6.3.2(@types/node@22.14.1)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1) + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.14.1)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@6.3.2(@types/node@22.14.1)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.0 + debug: 4.4.1 + expect-type: 1.2.2 + magic-string: 0.30.17 + pathe: 2.0.3 + picomatch: 4.0.2 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 6.3.2(@types/node@22.14.1)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1) + vite-node: 3.2.4(@types/node@22.14.1)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.14.1 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + walk-up-path@3.0.1: {} wcwidth@1.0.1: @@ -8733,6 +9036,11 @@ snapshots: dependencies: isexe: 3.1.1 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} wrap-ansi@7.0.0: diff --git a/src/test/utils.test.ts b/src/test/utils.test.ts new file mode 100644 index 00000000..30f26910 --- /dev/null +++ b/src/test/utils.test.ts @@ -0,0 +1,141 @@ +import { prettifyDate } from '../utils/index'; +import { describe, it, expect } from 'vitest'; + +process.env.TESTING = 'true'; + +describe('Utils tests', () => { + + it('should format full month correctly', () => { + const unformatted = 'January 1, 2023, 10:00 AM'; + const formatted = 'Jan. 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformatted)).toBe(formatted); + }); + + it('should format 3-letter abbreviated month correctly', () => { + const unformatted = 'Jan 1, 2023, 10:00 AM'; + const formatted = 'Jan. 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformatted)).toBe(formatted); + }); + + it('should format March, April, June, July correctly', () => { + const unformattedMarch = 'Mar 1, 2023, 10:00 AM'; + const formattedMarch = 'March 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedMarch)).toBe(formattedMarch); + + const unformattedApril = 'Apr 1, 2023, 10:00 AM'; + const formattedApril = 'April 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedApril)).toBe(formattedApril); + + const unformattedJune = 'Jun 1, 2023, 10:00 AM'; + const formattedJune = 'June 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedJune)).toBe(formattedJune); + + const unformattedJuly = 'Jul 1, 2023, 10:00 am'; + const formattedJuly = 'July 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedJuly)).toBe(formattedJuly); + + const unformattedSept = 'Sep 1, 2023, 10:00 AM'; + const formattedSept = 'Sept. 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedSept)).toBe(formattedSept); + }); + + it('should format months with periods correctly', () => { + const unformattedMarch = 'Mar. 1, 2023, 10:00 pm'; + const formattedMarch = 'March 1, 2023, 10:00 p.m.'; + expect(prettifyDate(unformattedMarch)).toBe(formattedMarch); + + const unformattedApril = 'Apr. 1, 2023, 10:00 AM'; + const formattedApril = 'April 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedApril)).toBe(formattedApril); + + const unformattedMay = 'May. 1, 2023, 10:00 am'; + const formattedMay = 'May 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedMay)).toBe(formattedMay); + + const unformattedJune = 'Jun. 1, 2023, 10:00 PM'; + const formattedJune = 'June 1, 2023, 10:00 p.m.'; + expect(prettifyDate(unformattedJune)).toBe(formattedJune); + + const unformattedJuly = 'Jul. 1, 2023, 10:00 PM'; + const formattedJuly = 'July 1, 2023, 10:00 p.m.'; + expect(prettifyDate(unformattedJuly)).toBe(formattedJuly); + + const unformattedSept = 'Sep. 1, 2023, 10:00 PM'; + const formattedSept = 'Sept. 1, 2023, 10:00 p.m.'; + expect(prettifyDate(unformattedSept)).toBe(formattedSept); + }); + + + it('should format months on their own properly', () => { + const unformattedMarch = 'Mar.'; + const formattedMarch = 'March'; + expect(prettifyDate(unformattedMarch)).toBe(formattedMarch); + + const unformattedApril = 'Apr.'; + const formattedApril = 'April'; + expect(prettifyDate(unformattedApril)).toBe(formattedApril); + + const unformattedMay = 'May.'; + const formattedMay = 'May'; + expect(prettifyDate(unformattedMay)).toBe(formattedMay); + + const unformattedJune = 'Jun.'; + const formattedJune = 'June'; + expect(prettifyDate(unformattedJune)).toBe(formattedJune); + + const unformattedJuly = 'Jul.'; + const formattedJuly = 'July'; + expect(prettifyDate(unformattedJuly)).toBe(formattedJuly); + + const unformattedSept = 'Sep.'; + const formattedSept = 'Sept.'; + expect(prettifyDate(unformattedSept)).toBe(formattedSept); + }); + + it('should format months with year properly', () => { + const unformattedMarch = 'Mar. 2025'; + const formattedMarch = 'March 2025'; + expect(prettifyDate(unformattedMarch)).toBe(formattedMarch); + + const unformattedApril = 'Apr. 2025'; + const formattedApril = 'April 2025'; + expect(prettifyDate(unformattedApril)).toBe(formattedApril); + + const unformattedMay = 'May. 2025'; + const formattedMay = 'May 2025'; + expect(prettifyDate(unformattedMay)).toBe(formattedMay); + + const unformattedJune = 'Jun. 2025'; + const formattedJune = 'June 2025'; + expect(prettifyDate(unformattedJune)).toBe(formattedJune); + + const unformattedJuly = 'Jul. 2025'; + const formattedJuly = 'July 2025'; + expect(prettifyDate(unformattedJuly)).toBe(formattedJuly); + + const unformattedSept = 'Sep. 2025'; + const formattedSept = 'Sept. 2025'; + expect(prettifyDate(unformattedSept)).toBe(formattedSept); + }); + + it('should fix spacing between time and am/pm', () => { + const unformattedMarch = 'Mar. 1, 2023, 10:00pm'; + const formattedMarch = 'March 1, 2023, 10:00 p.m.'; + expect(prettifyDate(unformattedMarch)).toBe(formattedMarch); + + const unformattedApril = 'Apr. 1, 2023, 10:00AM'; + const formattedApril = 'April 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedApril)).toBe(formattedApril); + + const unformattedMay = 'May. 1, 2023, 10:00am'; + const formattedMay = 'May 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedMay)).toBe(formattedMay); + + const unformattedJune = 'Jun. 1, 2023, 10:00AM'; + const formattedJune = 'June 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedJune)).toBe(formattedJune); + }); + + + +}); diff --git a/src/utils/index.ts b/src/utils/index.ts index 1cf6a635..5ad8d129 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -13,3 +13,76 @@ export const getAuthorPageUrl = (author: string): string => { const authorSlug = slugify(author.trim(), { lower: true }); return `https://www.reuters.com/authors/${authorSlug}/`; }; + +/** Formats a string containing a full or 3-letter abbreviated month, AM/PM, and am/pm to match the Reuters style. + * + * All months, full or abbreviated to 3 letters, are formatted to: + * - Jan. + * - Feb. + * - March + * - April] + * - May + * - June + * - July + * - Aug. + * - Sept. + * - Oct. + * - Nov. + * - Dec. + * + * AM and PM are formatted as lowercase. + * + */ +export const prettifyDate = (input: string) => { + // Define a object to map full month names to their Reuters style equivalents + const conversions: { [key: string]: string } = { + // full months + January: 'Jan.', + February: 'Feb.', + August: 'Aug.', + September: 'Sept.', + October: 'Oct.', + November: 'Nov.', + December: 'Dec.', + + // 3-letter abbreviations that need fixing + Jan: 'Jan.', + Feb: 'Feb.', + Mar: 'March', + Apr: 'April', + Jun: 'June', + Jul: 'July', + Sep: 'Sept.', + }; + + // If the key in conversions is found in the input, replace it with the corresponding value + let formatted = Object.keys(conversions).reduce((acc, key) => { + const regex = new RegExp(`\\b${key}\\b`, 'g'); + return acc.replace(regex, conversions[key]); + }, input); + + // Fix rogue periods in abbreviations + let fixedAbbr = formatted.replace('Mar.', 'March') + .replace('March.', 'March') + .replace('Apr.', 'April') + .replace('April.', 'April') + .replace('May.', 'May') + .replace('June.', 'June') + .replace('July.', 'July') + .replace('Sep.', 'Sept.'); + + // Replace double periods with a single period + let fixedPeriods = fixedAbbr.replace('..', '.'); + + // Fix 'Mar. 1, 2023, 10:00pm' to 'March 1, 2023, 10:00 p.m.', with a space before 'p.m.' + return prettifyAmPm(fixedPeriods) +} + +const prettifyAmPm = (text: string) => { + return text.replace(/(\d)\s*(am|AM|pm|PM)\b/g, (match, digit, timeDesignator) => { + const formattedDesignator = timeDesignator.toLowerCase() === 'am' + ? 'a.m.' + : 'p.m.'; + return `${digit} ${formattedDesignator}`; + }); +} \ No newline at end of file From 8a688d84077aa683b9dc13aefb214cd89fc266d5 Mon Sep 17 00:00:00 2001 From: MinamiFunakoshiTR Date: Tue, 12 Aug 2025 09:27:41 -0400 Subject: [PATCH 2/5] makes prettifyDate case insensitive --- src/components/Functions/Utils.mdx | 16 +++++ src/components/Functions/Utils.stories.svelte | 9 +++ src/index.ts | 3 + src/test/utils.test.ts | 24 ++++++-- src/utils/index.ts | 61 ++++++++++--------- 5 files changed, 79 insertions(+), 34 deletions(-) create mode 100644 src/components/Functions/Utils.mdx create mode 100644 src/components/Functions/Utils.stories.svelte diff --git a/src/components/Functions/Utils.mdx b/src/components/Functions/Utils.mdx new file mode 100644 index 00000000..e7f13b23 --- /dev/null +++ b/src/components/Functions/Utils.mdx @@ -0,0 +1,16 @@ +import { Meta, Canvas } from '@storybook/blocks'; + +import * as UtilFunctionStories from './Utils.stories.svelte'; + + + + +# Utils + +Utils TKTK + + +```javascript +import {prettifyDate} from '@reuters-graphics/graphics-components'; + +``` \ No newline at end of file diff --git a/src/components/Functions/Utils.stories.svelte b/src/components/Functions/Utils.stories.svelte new file mode 100644 index 00000000..fe7b28a9 --- /dev/null +++ b/src/components/Functions/Utils.stories.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/index.ts b/src/index.ts index 35ad5ae4..790e9306 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,9 @@ export { default as cssVariables } from './actions/cssVariables/index'; export { default as resizeObserver } from './actions/resizeObserver/index'; +// Utils +export { prettifyDate } from './utils/index'; + // Components export { default as Analytics, diff --git a/src/test/utils.test.ts b/src/test/utils.test.ts index 30f26910..3d120620 100644 --- a/src/test/utils.test.ts +++ b/src/test/utils.test.ts @@ -79,7 +79,7 @@ describe('Utils tests', () => { const formattedMay = 'May'; expect(prettifyDate(unformattedMay)).toBe(formattedMay); - const unformattedJune = 'Jun.'; + const unformattedJune = 'JUN.'; const formattedJune = 'June'; expect(prettifyDate(unformattedJune)).toBe(formattedJune); @@ -87,17 +87,17 @@ describe('Utils tests', () => { const formattedJuly = 'July'; expect(prettifyDate(unformattedJuly)).toBe(formattedJuly); - const unformattedSept = 'Sep.'; + const unformattedSept = 'sep.'; const formattedSept = 'Sept.'; expect(prettifyDate(unformattedSept)).toBe(formattedSept); }); it('should format months with year properly', () => { - const unformattedMarch = 'Mar. 2025'; + const unformattedMarch = 'MAR. 2025'; const formattedMarch = 'March 2025'; expect(prettifyDate(unformattedMarch)).toBe(formattedMarch); - const unformattedApril = 'Apr. 2025'; + const unformattedApril = 'apr. 2025'; const formattedApril = 'April 2025'; expect(prettifyDate(unformattedApril)).toBe(formattedApril); @@ -136,6 +136,22 @@ describe('Utils tests', () => { expect(prettifyDate(unformattedJune)).toBe(formattedJune); }); + it('should work with lower or upper case', () => { + const unformattedMarch = 'MAR. 1, 2023, 10:00pm'; + const formattedMarch = 'March 1, 2023, 10:00 p.m.'; + expect(prettifyDate(unformattedMarch)).toBe(formattedMarch); + const unformattedApril = 'APR. 1, 2023, 10:00AM'; + const formattedApril = 'April 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedApril)).toBe(formattedApril); + + const unformattedMay = 'may. 1, 2023, 10:00am'; + const formattedMay = 'May 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedMay)).toBe(formattedMay); + + const unformattedJune = 'JUN. 1, 2023, 10:00AM'; + const formattedJune = 'June 1, 2023, 10:00 a.m.'; + expect(prettifyDate(unformattedJune)).toBe(formattedJune); + }); }); diff --git a/src/utils/index.ts b/src/utils/index.ts index 5ad8d129..f854de2a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -34,49 +34,50 @@ export const getAuthorPageUrl = (author: string): string => { * */ export const prettifyDate = (input: string) => { - // Define a object to map full month names to their Reuters style equivalents + // Define an object to map full month names to their Reuters style equivalents const conversions: { [key: string]: string } = { // full months - January: 'Jan.', - February: 'Feb.', - August: 'Aug.', - September: 'Sept.', - October: 'Oct.', - November: 'Nov.', - December: 'Dec.', + january: 'Jan.', + february: 'Feb.', + august: 'Aug.', + september: 'Sept.', + october: 'Oct.', + november: 'Nov.', + december: 'Dec.', // 3-letter abbreviations that need fixing - Jan: 'Jan.', - Feb: 'Feb.', - Mar: 'March', - Apr: 'April', - Jun: 'June', - Jul: 'July', - Sep: 'Sept.', + jan: 'Jan.', + feb: 'Feb.', + mar: 'March', + apr: 'April', + jun: 'June', + jul: 'July', + sep: 'Sept.', }; - // If the key in conversions is found in the input, replace it with the corresponding value + // If the key in conversions is found in the input (case insensitive), replace it with the corresponding value let formatted = Object.keys(conversions).reduce((acc, key) => { - const regex = new RegExp(`\\b${key}\\b`, 'g'); + const regex = new RegExp(`\\b${key}\\b`, 'gi'); // Added 'i' flag for case insensitive return acc.replace(regex, conversions[key]); }, input); - // Fix rogue periods in abbreviations - let fixedAbbr = formatted.replace('Mar.', 'March') - .replace('March.', 'March') - .replace('Apr.', 'April') - .replace('April.', 'April') - .replace('May.', 'May') - .replace('June.', 'June') - .replace('July.', 'July') - .replace('Sep.', 'Sept.'); + // Fix rogue periods in abbreviations (case insensitive) + let fixedAbbr = formatted + .replace(/\bmar\./gi, 'March') + .replace(/\bmarch\./gi, 'March') + .replace(/\bapr\./gi, 'April') + .replace(/\bapril\./gi, 'April') + .replace(/\bmay\./gi, 'May') + .replace(/\bjune\./gi, 'June') + .replace(/\bjuly\./gi, 'July') + .replace(/\bsep\./gi, 'Sept.'); // Replace double periods with a single period - let fixedPeriods = fixedAbbr.replace('..', '.'); + let fixedPeriods = fixedAbbr.replace(/\.{2,}/g, '.'); - // Fix 'Mar. 1, 2023, 10:00pm' to 'March 1, 2023, 10:00 p.m.', with a space before 'p.m.' - return prettifyAmPm(fixedPeriods) -} + // Fix am/pm formatting + return prettifyAmPm(fixedPeriods); +}; const prettifyAmPm = (text: string) => { return text.replace(/(\d)\s*(am|AM|pm|PM)\b/g, (match, digit, timeDesignator) => { From 00accb59c436d19b5ab9dbc9983f8c8da8a5d1cd Mon Sep 17 00:00:00 2001 From: MinamiFunakoshiTR Date: Tue, 12 Aug 2025 09:33:18 -0400 Subject: [PATCH 3/5] adds docs --- .github/workflows/test.yaml | 24 ------------------------ src/components/Functions/Utils.mdx | 16 +++++++++++++--- src/test/utils.test.ts | 4 ++++ 3 files changed, 17 insertions(+), 27 deletions(-) delete mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml deleted file mode 100644 index e4b171f2..00000000 --- a/.github/workflows/test.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: Test Graphics Kit -permissions: - contents: read - issues: write -on: - push: - branches: - - main - pull_request: - branches: - - main -jobs: - build-app: - name: SvelteKit builds - strategy: - matrix: - version: [20, 22] - runs-on: ubuntu-latest - env: - TESTING: true - steps: - - id: test - name: Run tests - run: pnpm test \ No newline at end of file diff --git a/src/components/Functions/Utils.mdx b/src/components/Functions/Utils.mdx index e7f13b23..af20dd8f 100644 --- a/src/components/Functions/Utils.mdx +++ b/src/components/Functions/Utils.mdx @@ -5,12 +5,22 @@ import * as UtilFunctionStories from './Utils.stories.svelte'; -# Utils +# Util functions -Utils TKTK +This library provides utility functions that can be used across various components and applications. +## Prettify date in the Reuters format + + +The function `prettifyDate` formats the input string, which is expected to be in English, to format the month and time designator (AM/PM) according to the Reuters style guide. The function is case agnostic and will format both full month names and their 3-letter abbreviations (i.e. `Mar` or `Jun`) correctly. + ```javascript -import {prettifyDate} from '@reuters-graphics/graphics-components'; +import { prettifyDate } from '@reuters-graphics/graphics-components'; +// Example usage +prettifyDate('January 1, 2023, 10:00 AM'); // returns 'Jan. 1, 2023, 10:00 a.m.' +prettifyDate('Jan 1, 2023, 10:00 PM'); // returns 'Jan. 1, 2023, 10:00 p.m.' +prettifyDate('MAR. 2025'); // returns 'March 2025' +prettifyDate('sep. 1, 2023, 10:00PM'); // returns 'Sept. 1, 2023, 10:00 p.m.' ``` \ No newline at end of file diff --git a/src/test/utils.test.ts b/src/test/utils.test.ts index 3d120620..e60e0837 100644 --- a/src/test/utils.test.ts +++ b/src/test/utils.test.ts @@ -134,6 +134,10 @@ describe('Utils tests', () => { const unformattedJune = 'Jun. 1, 2023, 10:00AM'; const formattedJune = 'June 1, 2023, 10:00 a.m.'; expect(prettifyDate(unformattedJune)).toBe(formattedJune); + + const unformattedSept = 'sep. 1, 2023, 10:00PM'; + const formattedSept = 'Sept. 1, 2023, 10:00 p.m.'; + expect(prettifyDate(unformattedSept)).toBe(formattedSept); }); it('should work with lower or upper case', () => { From 68b51a109791f39512cc13065baf573c793295da Mon Sep 17 00:00:00 2001 From: MinamiFunakoshiTR Date: Tue, 12 Aug 2025 09:33:30 -0400 Subject: [PATCH 4/5] docs(changeset): Adds util function prettifyDate --- .changeset/spicy-donkeys-attend.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/spicy-donkeys-attend.md diff --git a/.changeset/spicy-donkeys-attend.md b/.changeset/spicy-donkeys-attend.md new file mode 100644 index 00000000..ded6fd82 --- /dev/null +++ b/.changeset/spicy-donkeys-attend.md @@ -0,0 +1,5 @@ +--- +'@reuters-graphics/graphics-components': patch +--- + +Adds util function prettifyDate From 48ca0871549cb48fbd85f191bfdc6d51c1c15fb0 Mon Sep 17 00:00:00 2001 From: MinamiFunakoshiTR Date: Tue, 12 Aug 2025 09:37:20 -0400 Subject: [PATCH 5/5] formats --- package.json | 2 +- src/components/Functions/Utils.mdx | 7 ++----- src/test/utils.test.ts | 3 --- src/utils/index.ts | 22 ++++++++++++---------- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 19e7b1eb..c557f173 100644 --- a/package.json +++ b/package.json @@ -126,4 +126,4 @@ "bugs": { "url": "https://github.com/reuters-graphics/graphics-components/issues" } -} \ No newline at end of file +} diff --git a/src/components/Functions/Utils.mdx b/src/components/Functions/Utils.mdx index af20dd8f..f4e1b54d 100644 --- a/src/components/Functions/Utils.mdx +++ b/src/components/Functions/Utils.mdx @@ -1,18 +1,15 @@ -import { Meta, Canvas } from '@storybook/blocks'; +import { Meta } from '@storybook/blocks'; import * as UtilFunctionStories from './Utils.stories.svelte'; - # Util functions This library provides utility functions that can be used across various components and applications. - ## Prettify date in the Reuters format - The function `prettifyDate` formats the input string, which is expected to be in English, to format the month and time designator (AM/PM) according to the Reuters style guide. The function is case agnostic and will format both full month names and their 3-letter abbreviations (i.e. `Mar` or `Jun`) correctly. ```javascript @@ -23,4 +20,4 @@ prettifyDate('January 1, 2023, 10:00 AM'); // returns 'Jan. 1, 2023, 10:00 a.m.' prettifyDate('Jan 1, 2023, 10:00 PM'); // returns 'Jan. 1, 2023, 10:00 p.m.' prettifyDate('MAR. 2025'); // returns 'March 2025' prettifyDate('sep. 1, 2023, 10:00PM'); // returns 'Sept. 1, 2023, 10:00 p.m.' -``` \ No newline at end of file +``` diff --git a/src/test/utils.test.ts b/src/test/utils.test.ts index e60e0837..c52cb30b 100644 --- a/src/test/utils.test.ts +++ b/src/test/utils.test.ts @@ -4,7 +4,6 @@ import { describe, it, expect } from 'vitest'; process.env.TESTING = 'true'; describe('Utils tests', () => { - it('should format full month correctly', () => { const unformatted = 'January 1, 2023, 10:00 AM'; const formatted = 'Jan. 1, 2023, 10:00 a.m.'; @@ -65,7 +64,6 @@ describe('Utils tests', () => { expect(prettifyDate(unformattedSept)).toBe(formattedSept); }); - it('should format months on their own properly', () => { const unformattedMarch = 'Mar.'; const formattedMarch = 'March'; @@ -157,5 +155,4 @@ describe('Utils tests', () => { const formattedJune = 'June 1, 2023, 10:00 a.m.'; expect(prettifyDate(unformattedJune)).toBe(formattedJune); }); - }); diff --git a/src/utils/index.ts b/src/utils/index.ts index f854de2a..c7207022 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -56,13 +56,13 @@ export const prettifyDate = (input: string) => { }; // If the key in conversions is found in the input (case insensitive), replace it with the corresponding value - let formatted = Object.keys(conversions).reduce((acc, key) => { + const formatted = Object.keys(conversions).reduce((acc, key) => { const regex = new RegExp(`\\b${key}\\b`, 'gi'); // Added 'i' flag for case insensitive return acc.replace(regex, conversions[key]); }, input); // Fix rogue periods in abbreviations (case insensitive) - let fixedAbbr = formatted + const fixedAbbr = formatted .replace(/\bmar\./gi, 'March') .replace(/\bmarch\./gi, 'March') .replace(/\bapr\./gi, 'April') @@ -73,17 +73,19 @@ export const prettifyDate = (input: string) => { .replace(/\bsep\./gi, 'Sept.'); // Replace double periods with a single period - let fixedPeriods = fixedAbbr.replace(/\.{2,}/g, '.'); + const fixedPeriods = fixedAbbr.replace(/\.{2,}/g, '.'); // Fix am/pm formatting return prettifyAmPm(fixedPeriods); }; const prettifyAmPm = (text: string) => { - return text.replace(/(\d)\s*(am|AM|pm|PM)\b/g, (match, digit, timeDesignator) => { - const formattedDesignator = timeDesignator.toLowerCase() === 'am' - ? 'a.m.' - : 'p.m.'; - return `${digit} ${formattedDesignator}`; - }); -} \ No newline at end of file + return text.replace( + /(\d)\s*(am|AM|pm|PM)\b/g, + (_match, digit, timeDesignator) => { + const formattedDesignator = + timeDesignator.toLowerCase() === 'am' ? 'a.m.' : 'p.m.'; + return `${digit} ${formattedDesignator}`; + } + ); +};