File tree Expand file tree Collapse file tree 9 files changed +163
-0
lines changed Expand file tree Collapse file tree 9 files changed +163
-0
lines changed Original file line number Diff line number Diff line change
1
+ import React from 'react' ;
2
+
3
+ export function Icons ( ) {
4
+ return (
5
+ < svg style = { { display : 'none' } } >
6
+ < symbol id = "icon-last-access-at" viewBox = "0 0 448 512" >
7
+ < path d = "M96 32l0 32L48 64C21.5 64 0 85.5 0 112l0 48 448 0 0-48c0-26.5-21.5-48-48-48l-48 0 0-32c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 32L160 64l0-32c0-17.7-14.3-32-32-32S96 14.3 96 32zM448 192L0 192 0 464c0 26.5 21.5 48 48 48l352 0c26.5 0 48-21.5 48-48l0-272z" > </ path >
8
+ </ symbol >
9
+ < symbol id = "icon-total-access" viewBox = "0 0 384 512" >
10
+ < path d = "M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-384c0-35.3-28.7-64-64-64L64 0zM96 64l192 0c17.7 0 32 14.3 32 32l0 32c0 17.7-14.3 32-32 32L96 160c-17.7 0-32-14.3-32-32l0-32c0-17.7 14.3-32 32-32zm32 160a32 32 0 1 1 -64 0 32 32 0 1 1 64 0zM96 352a32 32 0 1 1 0-64 32 32 0 1 1 0 64zM64 416c0-17.7 14.3-32 32-32l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-96 0c-17.7 0-32-14.3-32-32zM192 256a32 32 0 1 1 0-64 32 32 0 1 1 0 64zm32 64a32 32 0 1 1 -64 0 32 32 0 1 1 64 0zm64-64a32 32 0 1 1 0-64 32 32 0 1 1 0 64zm32 64a32 32 0 1 1 -64 0 32 32 0 1 1 64 0zM288 448a32 32 0 1 1 0-64 32 32 0 1 1 0 64z" > </ path >
11
+ </ symbol >
12
+ < symbol id = "icon-audio" viewBox = "0 0 640 512" >
13
+ < path d = "M533.6 32.5C598.5 85.2 640 165.8 640 256s-41.5 170.7-106.4 223.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C557.5 398.2 592 331.2 592 256s-34.5-142.2-88.7-186.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zM473.1 107c43.2 35.2 70.9 88.9 70.9 149s-27.7 113.8-70.9 149c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C475.3 341.3 496 301.1 496 256s-20.7-85.3-53.2-111.8c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zm-60.5 74.5C434.1 199.1 448 225.9 448 256s-13.9 56.9-35.4 74.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C393.1 284.4 400 271 400 256s-6.9-28.4-17.7-37.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zM301.1 34.8C312.6 40 320 51.4 320 64l0 384c0 12.6-7.4 24-18.9 29.2s-25 3.1-34.4-5.3L131.8 352 64 352c-35.3 0-64-28.7-64-64l0-64c0-35.3 28.7-64 64-64l67.8 0L266.7 40.1c9.4-8.4 22.9-10.4 34.4-5.3z" > </ path >
14
+ </ symbol >
15
+ </ svg >
16
+ ) ;
17
+ }
Original file line number Diff line number Diff line change
1
+ .access-info-container {
2
+ display : flex;
3
+ flex-direction : row;
4
+ align-items : center;
5
+ justify-content : flex-left;
6
+ gap : 1em ;
7
+ }
8
+ .access-info-container > .access-info {
9
+ display : flex;
10
+ flex-direction : row;
11
+ align-items : center;
12
+ justify-content : center;
13
+ gap : 0.5em ;
14
+ }
15
+ .icon {
16
+ width : 1em ;
17
+ height : 1em ;
18
+ }
19
+ .access-info > .access-content {
20
+ margin-top : 0.35em ;
21
+ }
Original file line number Diff line number Diff line change
1
+ import { render , screen } from '@testing-library/react' ;
2
+ import React from 'react' ;
3
+ import { SearchResultAccessSummary } from './SearchResultAccessSummary' ;
4
+
5
+ test ( 'access-summary block should show last access date and total access for searched word' , async ( ) => {
6
+ const accessSummary = {
7
+ lastAccessAt : '2024-01-01T00:00:00.000Z' ,
8
+ totalAccess : 313 ,
9
+ } ;
10
+
11
+ render ( < SearchResultAccessSummary accessSummary = { accessSummary } /> ) ;
12
+
13
+ expect ( screen . getByText ( '2024-01-01 00:00:00' ) ) . toBeDefined ( ) ;
14
+ expect ( screen . getByText ( '313' ) ) . toBeDefined ( ) ;
15
+ } ) ;
Original file line number Diff line number Diff line change
1
+ import React from 'react' ;
2
+ import { Utils } from '../../../lib/utils' ;
3
+ import { Icons } from '../../icons.svg' ;
4
+ import { AccessSummary } from '../../types' ;
5
+ import './SearchResultAccessSummary.css' ;
6
+
7
+ export function SearchResultAccessSummary ( {
8
+ accessSummary,
9
+ } : {
10
+ accessSummary : AccessSummary ;
11
+ } ) {
12
+ return (
13
+ < div className = "access-info-container" data-testid = "access-summary" >
14
+ < div className = "access-info" >
15
+ < Icons />
16
+ < div className = "icon" >
17
+ < svg >
18
+ < use id = "#icon-last-access-at" />
19
+ </ svg >
20
+ </ div >
21
+ < span className = "access-content" >
22
+ { new Utils ( ) . iso8601ToDateTime ( accessSummary . lastAccessAt ) }
23
+ </ span >
24
+ < div className = "icon" >
25
+ < svg >
26
+ < use id = "#icon-total-access" />
27
+ </ svg >
28
+ </ div >
29
+ < span className = "access-content" > { accessSummary . totalAccess } </ span >
30
+ </ div >
31
+ </ div >
32
+ ) ;
33
+ }
Original file line number Diff line number Diff line change
1
+ .header-container {
2
+ display : flex;
3
+ flex-direction : row;
4
+ align-items : center;
5
+ justify-content : space-between;
6
+ gap : 2em ;
7
+ }
8
+ .word {
9
+ padding : 0.5em 0 ;
10
+ max-width : 60% ;
11
+ word-wrap : anywhere;
12
+ }
Original file line number Diff line number Diff line change
1
+ import { render , screen } from '@testing-library/react' ;
2
+ import React from 'react' ;
3
+ import { SearchResultHeader } from './SearchResultHeader' ;
4
+
5
+ test ( 'header should have the searched word and access-summary section (when provided)' , async ( ) => {
6
+ const searchedWord = 'hello' ;
7
+ const accessSummary = {
8
+ totalAccess : 3 ,
9
+ lastAccessAt : '2024-01-01T00:00:00.000Z' ,
10
+ } ;
11
+
12
+ render (
13
+ < SearchResultHeader word = { searchedWord } accessSummary = { accessSummary } /> ,
14
+ ) ;
15
+
16
+ const word = screen . getByText ( searchedWord ) ;
17
+ const accessSummaryBlock = screen . queryByTestId ( 'access-summary' ) ;
18
+
19
+ expect ( word ) . toBeDefined ( ) ;
20
+ expect ( accessSummaryBlock ) . not . toBeNull ( ) ;
21
+ } ) ;
22
+
23
+ test ( 'header should not have any access-summary section for first time access of a word' , async ( ) => {
24
+ const searchedWord = 'hello' ;
25
+
26
+ render ( < SearchResultHeader word = { searchedWord } accessSummary = { null } /> ) ;
27
+
28
+ const accessSummaryBlock = screen . queryByTestId ( 'access-summary' ) ;
29
+
30
+ expect ( accessSummaryBlock ) . toBeNull ( ) ;
31
+ } ) ;
Original file line number Diff line number Diff line change
1
+ import React from 'react' ;
2
+ import { AccessSummary } from '../../types' ;
3
+ import { SearchResultAccessSummary } from './SearchResultAccessSummary' ;
4
+ import './SearchResultHeader.css' ;
5
+
6
+ export function SearchResultHeader ( {
7
+ word,
8
+ accessSummary,
9
+ } : {
10
+ word : string ;
11
+ accessSummary : AccessSummary ;
12
+ } ) {
13
+ return (
14
+ < div className = "header-container" data-testid = "header-container" >
15
+ < div className = "word" > { word } </ div >
16
+ { accessSummary && (
17
+ < SearchResultAccessSummary accessSummary = { accessSummary } />
18
+ ) }
19
+ </ div >
20
+ ) ;
21
+ }
Original file line number Diff line number Diff line change @@ -24,3 +24,11 @@ describe('decodeBase64Gzip()', () => {
24
24
expect ( uncompressed ) . toBe ( 'some text' ) ;
25
25
} ) ;
26
26
} ) ;
27
+
28
+ describe ( 'iso8601ToDateTime()' , ( ) => {
29
+ test ( 'function should return datetime string when iso 8601 datetime is given' , ( ) => {
30
+ expect ( utils . iso8601ToDateTime ( '2024-01-01T00:00:00.000Z' ) ) . toBe (
31
+ '2024-01-01 00:00:00' ,
32
+ ) ;
33
+ } ) ;
34
+ } ) ;
Original file line number Diff line number Diff line change @@ -7,4 +7,9 @@ export class Utils {
7
7
) ;
8
8
return pako . inflate ( binaryData , { to : 'string' } ) ;
9
9
}
10
+
11
+ iso8601ToDateTime ( iso8601 : string ) {
12
+ // iso 8601: YYYY-MM-DDThh:mm:ss.SSSZ
13
+ return iso8601 . split ( '.' ) [ 0 ] . replace ( 'T' , ' ' ) ;
14
+ }
10
15
}
You can’t perform that action at this time.
0 commit comments