Skip to content

Commit

Permalink
Integrate search-result-view into app with dummy data
Browse files Browse the repository at this point in the history
  • Loading branch information
khshourov committed Oct 30, 2024
1 parent 9722c59 commit a58b2b9
Show file tree
Hide file tree
Showing 34 changed files with 638 additions and 60 deletions.
57 changes: 50 additions & 7 deletions views/interactivity/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,52 @@ import React, { useState, useEffect } from 'react';
import './App.css';
import { DictionaryApi } from './components/api/dictionary';
import SearchBox from './components/search-box/SearchBox';
import { SearchResultView } from './components/search-result-view/SearchResultView';
import { DictionaryEntry } from './components/types';

function App() {
const [bearerToken, setBearerToken] = useState<string | null>(null);
const [bearerToken, setBearerToken] = useState<string | null>('bearer-token');
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [response, setResponse] = useState<DictionaryEntry | null>(null);
const [response, setResponse] = useState<DictionaryEntry | null>({
dictionaryWord: {
name: 'hello',
entry: {
ipaListings: {
uk: [
{
category: '',
ipa: '/heˈləʊ/',
audio:
'https://dictionary.cambridge.org/media/english/uk_pron/u/ukh/ukhef/ukheft_029.mp3',
},
],
us: [
{
category: '',
ipa: '/heˈloʊ/',
audio:
'https://dictionary.cambridge.org/media/english/us_pron/h/hel/hello/hello.mp3',
},
],
},
meanings: [
{
categories: 'exclamation, noun',
entries: [
{
meaning: 'used when meeting or greeting someone:',
examples: ["Hello, Paul. I haven't seen you for ages."],
},
],
},
],
},
},
accessSummary: {
totalAccess: 1,
lastAccessAt: new Date('2024-01-01T00:00:00.000Z'),
},
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [errorMessage, setErrorMessage] = useState<string | null>(null);

Expand All @@ -24,11 +64,14 @@ function App() {

const dictionaryApi = new DictionaryApi({ token: bearerToken });
return (
<SearchBox
dictionaryApi={dictionaryApi}
onSuccess={setResponse}
onError={setErrorMessage}
/>
<>
<SearchBox
dictionaryApi={dictionaryApi}
onSuccess={setResponse}
onError={setErrorMessage}
/>
{response && <SearchResultView dictionaryEntry={response} />}
</>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ export const responseSchema = object({
}).nullable(),
});

const ipaListingSchema = object({
export const ipaListingSchema = object({
category: string().nonNullable().defined(),
ipa: string().required(),
audio: string().url().required(),
});

const meaningEntrySchema = object({
export const meaningEntrySchema = object({
meaning: string().required(),
examples: array().of(string()).required(),
});

const meaningSchema = object({
export const meaningSchema = object({
categories: string().required(),
entries: array().of(meaningEntrySchema).required(),
});
Expand Down
55 changes: 55 additions & 0 deletions views/interactivity/src/components/general/Accordion.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.accordion > input[type='checkbox'] {
position: absolute;
left: -100vw;
}
.accordion .content {
overflow-y: hidden;
height: 0;
transition: height 0.3s ease;
}
.accordion > input[type='checkbox']:checked ~ .content {
height: auto;
overflow: visible;
}
.accordion label {
display: block;
}
.accordion {
margin-bottom: 1em;
}
.accordion > input[type='checkbox']:checked ~ .content {
padding: 1em;
border: 1px solid #e8e8e8;
border-top: 0;
}
.accordion .handle {
margin: 0;
font-size: 1.125em;
line-height: 1.2em;
}
.accordion label {
color: #333;
cursor: pointer;
font-weight: normal;
padding: 1em;
background: #e8e8e8;
}
.accordion label:hover,
.accordion label:focus {
background: #d8d8d8;
}
.accordion .handle label:before {
font-family: 'fontawesome';
content: '\f054';
display: inline-block;
margin-right: 0.625em;
font-size: 0.58em;
line-height: 1.556em;
vertical-align: middle;
}
.accordion > input[type='checkbox']:checked ~ .handle label:before {
content: '\f078';
}
.accordion {
width: 100%;
}
17 changes: 17 additions & 0 deletions views/interactivity/src/components/general/Accordion.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { Accordion } from './Accordion';

test('accordion should have a label and content block', async () => {
const id = 'accordion-id';
const label = 'label';
const Children = () => <div>Hello</div>;
render(
<Accordion id={id} label={label}>
<Children />
</Accordion>,
);

expect(screen.queryByText(label)).toBeInTheDocument();
expect(screen.queryByText('Hello')).toBeInTheDocument();
});
22 changes: 22 additions & 0 deletions views/interactivity/src/components/general/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { ReactNode } from 'react';
import './Accordion.css';

export function Accordion({
id,
label,
children,
}: {
id: string;
label: string;
children: ReactNode;
}) {
return (
<section className="accordion">
<input id={id} type="checkbox" name={label} />
<h2 className="handle">
<label htmlFor={id}>{label}</label>
</h2>
<div className="content">{children}</div>
</section>
);
}
20 changes: 20 additions & 0 deletions views/interactivity/src/components/general/AudioPlayer.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { AudioPlayer } from './AudioPlayer';

test('audio player should play audio when button is clicked', async () => {
const id = 1;
const src = 'https://domain.com/media/1.mp3';

render(<AudioPlayer id={id} src={src} />);

const audioPlayer: HTMLAudioElement = screen.getByTestId(
`audio-player-${id}`,
);
jest.spyOn(audioPlayer, 'play').mockImplementation(() => Promise.resolve());

const button = screen.getByTestId(`audio-button-${id}`);
fireEvent.click(button);

expect(audioPlayer.play).toHaveBeenCalled();
});
25 changes: 25 additions & 0 deletions views/interactivity/src/components/general/AudioPlayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { Icon } from './Icon';

export function AudioPlayer({ id, src }: { id: number; src: string }) {
const audioRef = React.useRef<HTMLAudioElement | null>(null);

const handlePlay = () => {
if (audioRef.current) {
audioRef.current.play();
}
};

return (
<>
<div
data-testid={`audio-button-${id}`}
className="icon"
onClick={handlePlay}
>
<Icon iconType="audio" />
</div>
<audio data-testid={`audio-player-${id}`} ref={audioRef} src={src} />
</>
);
}
4 changes: 4 additions & 0 deletions views/interactivity/src/components/general/Icon.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.icon {
width: 1em;
height: 1em;
}
15 changes: 15 additions & 0 deletions views/interactivity/src/components/general/Icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import IconsSvg from './icons.svg';
import './Icon.css';

export function Icon({
iconType,
}: {
iconType: 'last-access-at' | 'total-access' | 'audio';
}) {
return (
<svg className="icon">
<use href={`${IconsSvg}#icon-${iconType}`} />
</svg>
);
}
13 changes: 13 additions & 0 deletions views/interactivity/src/components/general/icons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 0 additions & 17 deletions views/interactivity/src/components/icons.svg.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.card {
box-shadow: 0 0.25em 0.5em 0 rgba(0, 0, 0, 0.2);
transition: 0.3s;
width: 100%;
padding: 0.5em;
margin-top: 0.25em;
/* width: 40%; */
}
.card:hover {
box-shadow: 0 0.5em 1em 0 rgba(0, 0, 0, 0.2);
}
.divider {
padding: 0.5em 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { SearchResultView } from './SearchResultView';

test('it should display all the dictionary entry information', () => {
const dictionaryEntry = {
dictionaryWord: {
name: 'hello',
entry: {
ipaListings: {
uk: [
{
category: '',
ipa: '/heˈləʊ/',
audio:
'https://dictionary.cambridge.org/media/english/uk_pron/u/ukh/ukhef/ukheft_029.mp3',
},
],
us: [
{
category: '',
ipa: '/heˈloʊ/',
audio:
'https://dictionary.cambridge.org/media/english/us_pron/h/hel/hello/hello.mp3',
},
],
},
meanings: [
{
categories: 'exclamation, noun',
entries: [
{
meaning: 'used when meeting or greeting someone:',
examples: ["Hello, Paul. I haven't seen you for ages."],
},
],
},
],
},
},
accessSummary: {
totalAccess: 1,
lastAccessAt: new Date('2024-01-01T00:00:00.000Z'),
},
};

render(<SearchResultView dictionaryEntry={dictionaryEntry} />);

expect(screen.queryByTestId('header-container')).toBeInTheDocument();
expect(screen.getByTestId('divider')).toBeInTheDocument();
expect(screen.queryByTestId('ipa-container')).toBeInTheDocument();
expect(screen.getByText('Meanings')).toBeInTheDocument();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { DictionaryEntry } from '../types';
import { SearchResultHeader } from './header/SearchResultHeader';
import { IPAContainer } from './ipa/IPAContainer';
import { CategoryMeaningView } from './meaning/CategoryMeaningView';
import './SearchResultView.css';

export function SearchResultView({
dictionaryEntry,
}: {
dictionaryEntry: DictionaryEntry;
}) {
return (
<div className="card">
<SearchResultHeader
word={dictionaryEntry.dictionaryWord.name}
accessSummary={dictionaryEntry.accessSummary}
/>
<div className="divider">
<hr data-testid="divider" />
</div>
<IPAContainer
ipaListings={dictionaryEntry.dictionaryWord.entry.ipaListings}
/>
<CategoryMeaningView
categoryMeanings={dictionaryEntry.dictionaryWord.entry.meanings}
/>
</div>
);
}
Loading

0 comments on commit a58b2b9

Please sign in to comment.