Skip to content

Commit b280c66

Browse files
author
“kebean”
committed
Testimonials redesign
1 parent 4321d94 commit b280c66

File tree

2 files changed

+214
-88
lines changed

2 files changed

+214
-88
lines changed

Diff for: src/pages/About.tsx

+136-84
Original file line numberDiff line numberDiff line change
@@ -10,124 +10,174 @@ import person from '../assets/person.png';
1010
import person2 from '../assets/person2.png';
1111
import ur from '../assets/ur.png';
1212

13+
const testimonials = [
14+
{
15+
id: 1,
16+
name: 'Bernard Dushimimana',
17+
role: 'Sr.Manager',
18+
organization: 'Andela',
19+
image: person,
20+
content: `I'm extremely impressed with Pulse and their performance management platform.
21+
Since using their services, it has been a game-changer for our organization.
22+
The platform is intuitive, easy to navigate, and packed with powerful features.`,
23+
},
24+
{
25+
id: 2,
26+
name: 'Susan',
27+
role: 'Sr.Manager',
28+
organization: 'Andela',
29+
image: person2,
30+
content: `I'm delighted to share my positive experience with Pulse and their exceptional
31+
performance management platform. Implementing their services has led to remarkable
32+
improvements in our performance tracking and management processes.`,
33+
},
34+
{
35+
id: 3,
36+
name: 'Dr. Jack',
37+
role: 'Director',
38+
organization: 'University of Rwanda',
39+
image: ur,
40+
content: `
41+
We are thrilled with the services provided by Pulse. Their performance management platform
42+
has exceeded our expectations in every way. The user-friendly interface and comprehensive
43+
features have made tracking and monitoring our performance metrics a breeze.
44+
`,
45+
},
46+
{
47+
id: 4,
48+
name: 'Dr. Jack',
49+
role: 'Director',
50+
organization: 'University of Rwanda',
51+
image: ur,
52+
content: `
53+
We are thrilled with the services provided by Pulse. Their performance management platform
54+
has exceeded our expectations in every way. The user-friendly interface and comprehensive
55+
features have made tracking and monitoring our performance metrics a breeze.
56+
`,
57+
},
58+
{
59+
id: 5,
60+
name: 'Dr. Jack',
61+
role: 'Director',
62+
organization: 'University of Rwanda',
63+
image: ur,
64+
content: `
65+
We are thrilled with the services provided by Pulse. Their performance management platform
66+
has exceeded our expectations in every way. The user-friendly interface and comprehensive
67+
features have made tracking and monitoring our performance metrics a breeze.
68+
`,
69+
},
70+
{
71+
id: 6,
72+
name: 'Dr. Jack',
73+
role: 'Director',
74+
organization: 'University of Rwanda',
75+
image: ur,
76+
content: `
77+
We are thrilled with the services provided by Pulse. Their performance management platform
78+
has exceeded our expectations in every way. The user-friendly interface and comprehensive
79+
features have made tracking and monitoring our performance metrics a breeze.
80+
`,
81+
},
82+
];
83+
1384
function Testimonial() {
14-
const { t } = useTranslation();
1585
const [currentIndex, setCurrentIndex] = useState(0);
1686

17-
const testimonials = [
18-
{
19-
id: 1,
20-
name: 'Bernard Dushimimana',
21-
role: t('Sr.Manager'),
22-
organization: t('Andela'),
23-
image: person,
24-
content: t('Content1'),
25-
},
26-
{
27-
id: 2,
28-
name: 'Susan',
29-
role: t('Sr.Manager'),
30-
organization: t('Andela'),
31-
image: person2,
32-
content: t('Content2'),
33-
},
34-
{
35-
id: 3,
36-
name: 'Dr. Jack',
37-
role: t('Director'),
38-
organization: t('University of Rwanda'),
39-
image: ur,
40-
content: t('Content3'),
41-
},
42-
];
43-
4487
const prevTestimonial = () => {
45-
setCurrentIndex(
46-
(prevIndex) =>
47-
(prevIndex - 1 + testimonials.length) % testimonials.length,
88+
setCurrentIndex((prevIndex) =>
89+
prevIndex === 0 ? testimonials.length - 1 : prevIndex - 1
4890
);
4991
};
5092

5193
const nextTestimonial = () => {
52-
setCurrentIndex((prevIndex) => (prevIndex + 1) % testimonials.length);
94+
setCurrentIndex((prevIndex) =>
95+
prevIndex === testimonials.length - 1 ? 0 : prevIndex + 1
96+
);
5397
};
5498

5599
useEffect(() => {
56100
const interval = setInterval(() => {
57-
setCurrentIndex((prevIndex) => (prevIndex + 1) % testimonials.length);
101+
setCurrentIndex((prevIndex) =>
102+
prevIndex === testimonials.length - 1 ? 0 : prevIndex + 1
103+
);
58104
}, 10000);
59105

60106
return () => clearInterval(interval);
61107
}, []);
62108

63109
return (
64110
<>
65-
<div className="block md:hidden flex-col lg:mx-10 md:mx-20 my-20 gap-10 relative font-serif">
66-
{testimonials.map((testimonial, index) => (
111+
<div className="block md:hidden relative font-serif my-20">
112+
<div className="overflow-hidden">
67113
<div
68-
key={testimonial.id}
69-
className={`bg-indigo-100 dark:bg-dark-bg dark:text-slate-300 lg:w-1/3 p-8 md:w-full rounded-b-3xl sm:mx-3 rounded-t-3xl ${
70-
currentIndex === index ? 'visible' : 'hidden'
71-
}`}
114+
className="flex transition-transform duration-500 ease-in-out"
115+
style={{ transform: `translateX(-${currentIndex * 100}%)` }}
72116
>
73-
<div className="flex flex-col sm:flex-row mb-6 items-center">
74-
<img className="sm:w-1/4" src={testimonial.image} alt="" />
75-
76-
<ul>
77-
<li className="text-xs ml-3 dark:text-slate-300 text-neutral-600">
78-
{testimonial.name}
79-
</li>
80-
<li className="text-xs mt-2 dark:text-slate-300 ml-3">
81-
{testimonial.role}, {testimonial.organization}
82-
</li>
83-
</ul>
84-
</div>
85-
<p className="text-base dark:text-slate-300 text-neutral-900">
86-
{testimonial.content}
87-
</p>
117+
{testimonials.map((testimonial) => (
118+
<div
119+
key={testimonial.id}
120+
className="flex-shrink-0 w-full bg-indigo-100 dark:bg-dark-bg dark:text-slate-300 p-8 rounded-3xl mx-3"
121+
>
122+
<div className="flex flex-col sm:flex-row mb-6 items-center">
123+
<img className="sm:w-1/4" src={testimonial.image} alt="" />
124+
<ul>
125+
<li className="text-xs ml-3 dark:text-slate-300 text-neutral-600">
126+
{testimonial.name}
127+
</li>
128+
<li className="text-xs mt-2 dark:text-slate-300 ml-3">
129+
{testimonial.role}, {testimonial.organization}
130+
</li>
131+
</ul>
132+
</div>
133+
<p className="text-base dark:text-slate-300 text-neutral-900">
134+
{testimonial.content}
135+
</p>
136+
</div>
137+
))}
88138
</div>
89-
))}
139+
</div>
140+
141+
<button
142+
type="button"
143+
className="absolute top-1/2 left-3 transform -translate-y-1/2 text-neutral-600 rounded-full bg-slate-50 p-1 drop-shadow-2xl opacity-50 hover:opacity-75 transition-opacity z-10"
144+
onClick={prevTestimonial}
145+
>
146+
<AiOutlineLeft size={20} />
147+
</button>
90148

91-
<span className="absolute top-1/2 left-3 transform -translate-y-1/2 text-neutral-600 rounded-full bg-slate-50 p-1 drop-shadow-2xl opacity-50 ">
92-
<AiOutlineLeft
93-
size={20}
94-
onClick={prevTestimonial}
95-
className="cursor-pointer"
96-
/>
97-
</span>
98-
99-
<span className="absolute top-1/2 right-3 transform -translate-y-1/2 text-neutral-600 rounded-full bg-slate-50 p-1 drop-shadow-2xl opacity-50 ">
100-
<AiOutlineRight
101-
size={20}
102-
onClick={nextTestimonial}
103-
className="cursor-pointer"
104-
/>
105-
</span>
149+
<button
150+
type="button"
151+
className="absolute top-1/2 right-3 transform -translate-y-1/2 text-neutral-600 rounded-full bg-slate-50 p-1 drop-shadow-2xl opacity-50 hover:opacity-75 transition-opacity z-10"
152+
onClick={nextTestimonial}
153+
>
154+
<AiOutlineRight size={20} />
155+
</button>
106156
</div>
107157

108-
<div className="hidden md:flex md:flex-row flex-col lg:mx-0 md:mx-0 my-20 gap-10 md:flex-wrap lg:flex-nowrap font-serif">
109-
{testimonials.map((testimonial, index) => (
158+
{/* Desktop view remains the same */}
159+
<div className="hidden md:flex overflow-x-auto md:flex-row flex-col lg:mx-0 md:mx-0 my-20 gap-10 font-serif">
160+
{testimonials.map((testimonial) => (
110161
<div
111162
key={testimonial.id}
112-
className="bg-indigo-100 dark:bg-dark-bg lg:w-1/3 p-8 md:w-full rounded-b-3xl sm:mx-3 rounded-t-3xl "
163+
className="bg-indigo-100 dark:bg-dark-bg lg:w-1/3 p-8 md:w-full rounded-b-3xl sm:mx-3 rounded-t-3xl flex-shrink-0"
113164
>
114-
<div className="flex flex-col sm:flex-row mb-6 items-center">
165+
<div className="flex flex-col sm:flex-row mb-6 items-center">
115166
<img
116167
className="md:w-1/5 sm:w-1/3"
117168
src={testimonial.image}
118169
alt=""
119170
/>
120-
121171
<ul>
122-
<li className="text-sm ml-3 dark:text-slate-300 text-neutral-600">
172+
<li className="text-sm ml-3 dark:text-slate-300 text-neutral-600">
123173
{testimonial.name}
124174
</li>
125-
<li className="text-sm dark:text-slate-300 mt-2 ml-3">
175+
<li className="text-sm dark:text-slate-300 mt-2 ml-3">
126176
{testimonial.role}, {testimonial.organization}
127177
</li>
128178
</ul>
129179
</div>
130-
<p className="text-base dark:text-slate-300 text-neutral-900">
180+
<p className="text-base dark:text-slate-300 text-neutral-900">
131181
{testimonial.content}
132182
</p>
133183
</div>
@@ -168,20 +218,20 @@ function About({ styles }: any) {
168218
];
169219

170220
return (
171-
<div className=" bg-white mt-auto dark:bg-dark-frame-bg font-serif">
172-
<div className=" justify-between w-full h-full pb-5">
221+
<div className="bg-white mt-auto dark:bg-dark-frame-bg font-serif">
222+
<div className="justify-between w-full h-full pb-5">
173223
<div>
174-
<h1 className="text-center text-neutral-700 mb-5 font lg:text-4xl md:text-2xl pt-10 pb-4 dark:text-slate-100">
224+
<h1 className="text-center text-neutral-700 mb-5 font lg:text-4xl md:text-2xl pt-10 pb-4 dark:text-slate-100">
175225
{t('hero2')}
176226
</h1>
177227
</div>
178228

179229
{aboutCards.map(({ title, body, img, orientation }) => (
180230
<div
181231
key={body}
182-
className={`mt-10 card-image lg:mx-10 sm:mx-5 md:flex ${orientation} `}
232+
className={`mt-10 card-image lg:mx-10 sm:mx-5 md:flex ${orientation}`}
183233
>
184-
<div className="shadow-md sm:shadow-none mx-auto md:mx-0 bg-indigo-100 items-center md:items-start flex-col dark:bg-dark-bg md:w-[45%] md:py-10 md:px-4 max-w-[35em] sm:w-full sm:rounded-3xl md:mr-2">
234+
<div className="shadow-md sm:shadow-none mx-auto md:mx-0 bg-indigo-100 items-center md:items-start flex-col dark:bg-dark-bg md:w-[45%] md:py-10 md:px-4 max-w-[35em] sm:w-full sm:rounded-3xl md:mr-2">
185235
<h2 className="pt-3 header-style lg:px-5 lg:text-2xl sm:text-xl lg:text-start sm:text-center md:text-start dark:text-slate-200">
186236
{title}
187237
</h2>
@@ -207,12 +257,14 @@ function About({ styles }: any) {
207257
<div className="px-5 mt-20">
208258
<h1 className="font text-center text-neutral-700 lg:text-4xl sm:text-2xl dark:text-slate-100">
209259
{' '}
210-
{t('Come shape the future together')}
260+
Come shape the future together{' '}
211261
</h1>
212262
</div>
213263
<Testimonial />
214264
</div>
215265
</div>
216266
);
217267
}
268+
218269
export { About };
270+

Diff for: tests/pages/About.test.tsx

+78-4
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,94 @@
11
import React from 'react';
22
import { MemoryRouter } from 'react-router-dom';
3-
import renderer from 'react-test-renderer';
3+
import renderer, { act } from 'react-test-renderer';
44
import '@testing-library/jest-dom';
55
import { MockedProvider as ApolloProvider } from '@apollo/client/testing';
66
import { About } from '../../src/pages/About';
7+
import { render, screen, fireEvent } from '@testing-library/react';
8+
import { useTranslation } from 'react-i18next';
9+
10+
jest.mock('react-i18next', () => ({
11+
useTranslation: () => ({
12+
t: (key) => key,
13+
}),
14+
}));
715

816
describe('About page', () => {
9-
it('renders the about page', () => {
17+
it('renders the about page with translations and testimonials', () => {
1018
const elem = renderer.create(
1119
<MemoryRouter>
1220
<ApolloProvider>
1321
<About />
1422
</ApolloProvider>
15-
</MemoryRouter>,
23+
</MemoryRouter>
1624
);
17-
1825
expect(elem.toJSON()).toMatchSnapshot();
1926
});
27+
28+
it('renders about cards with correct titles and descriptions', () => {
29+
render(
30+
<MemoryRouter>
31+
<ApolloProvider>
32+
<About />
33+
</ApolloProvider>
34+
</MemoryRouter>
35+
);
36+
37+
const titles = ['performance', 'analytics', 'continuos', 'goal'];
38+
titles.forEach((title) => {
39+
expect(screen.getByText(title)).toBeInTheDocument();
40+
});
41+
});
42+
43+
it('displays testimonial content and navigates through testimonials', () => {
44+
render(
45+
<MemoryRouter>
46+
<ApolloProvider>
47+
<About />
48+
</ApolloProvider>
49+
</MemoryRouter>
50+
);
51+
52+
const firstTestimonial = screen.getByText(
53+
"I'm extremely impressed with Pulse and their performance management platform."
54+
);
55+
expect(firstTestimonial).toBeInTheDocument();
56+
57+
const nextButton = screen.getByRole('button', { name: /next/i });
58+
fireEvent.click(nextButton);
59+
60+
const secondTestimonial = screen.getByText(
61+
"I'm delighted to share my positive experience with Pulse and their exceptional performance management platform."
62+
);
63+
expect(secondTestimonial).toBeInTheDocument();
64+
});
65+
66+
it('automatically moves to the next testimonial every 10 seconds', () => {
67+
jest.useFakeTimers();
68+
69+
render(
70+
<MemoryRouter>
71+
<ApolloProvider>
72+
<About />
73+
</ApolloProvider>
74+
</MemoryRouter>
75+
);
76+
77+
const firstTestimonial = screen.getByText(
78+
"I'm extremely impressed with Pulse and their performance management platform."
79+
);
80+
81+
expect(firstTestimonial).toBeInTheDocument();
82+
83+
act(() => {
84+
jest.advanceTimersByTime(10000);
85+
});
86+
87+
const secondTestimonial = screen.getByText(
88+
"I'm delighted to share my positive experience with Pulse and their exceptional performance management platform."
89+
);
90+
expect(secondTestimonial).toBeInTheDocument();
91+
92+
jest.useRealTimers();
93+
});
2094
});

0 commit comments

Comments
 (0)