Skip to content

万宝锐旗直招-ethan-frontend #1841

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@
- [TypeScript Frontend](frontend/)
- [Swift](swift/)
- [UI 设计师](design/)
- [TypeScript Backend](backend/)
- [TypeScript Fullstack](fullstack/)

北京:
- [TypeScript Backend](backend/)
- [TypeScript Fullstack(偏 Backend)](fullstack/)
深圳/香港:
- [Support Engineer](it-support-engineer/)

链接中有更为具体的 JD。

Expand Down Expand Up @@ -129,4 +126,4 @@ Fork 当前仓库并在相关文件夹下找到你的作业。完成作业后提

1. 面试时我们会考察编程语言的掌握能力(TS/Swift)。
2. 我们看重解决问题的能力,以及发散思维的能力。
3. 所有办公地点在市区,交通便利。
3. 所有办公地点在市区,交通便利。
42 changes: 36 additions & 6 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
.App {
text-align: center;
position: relative;
width: 100vw;
height: 100vh;
}

.title {
font-size: 56px;
font-weight: 600;
.container {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding-top: 170px;
padding-bottom: 100px;
box-sizing: border-box;
background-position: center bottom 100px;
background-size: auto 60%;
background-repeat: no-repeat;
}

.text {
font-size: 28px;
font-weight: 400;
.container .text-content {
position: relative;
z-index: 1;
}

.container .text-content .title {
font-size: 60px;
line-height: 60px;
color: inherit;
margin: 0;
text-align: center;
white-space: pre;
}

.container .text-content .content {
font-size: 34px;
text-align: center;
line-height: 50px;
white-space: pre;
}
15 changes: 9 additions & 6 deletions frontend/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React from 'react';
import React from 'react'
import { render } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
describe('App', () => {
it('component should be render correctly', () => {
const { rerender } = render(<App />);
expect(() => {
rerender(<App />);
}).not.toBeNull();
});
});
53 changes: 50 additions & 3 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,54 @@
import './App.css';
import Carousel from './components/Carousel/index'
import imgIPhone from './assets/iphone.png'
import imgTablet from './assets/tablet.png'
import imgAirPods from './assets/airpods.png'

import './App.css'

export interface CarouselDataItem{
id: string
color: string
backgroundColor: string
title?: string[]
contents?: string[]
bg: string
}


const data: CarouselDataItem[] = [
{
id: '1',
color: '#fff',
backgroundColor: '#111111',
title: ['xPhone'],
contents: ['Lots to love. Less to spend.', 'Starting at $399.'],
bg: imgIPhone
},
{
id: '2',
color: '#000',
backgroundColor: '#FAFAFA',
title: ['Tablet'],
contents: ['Just the right amount of everything.'],
bg: imgTablet
},
{
id: '3',
color: '#000',
backgroundColor: '#F1F1F1',
title: ['Buy a Tablet or xPhone for college.', 'Get airPods.'],
bg: imgAirPods
}
]

function App() {
return <div className='App'>{/* write your component here */}</div>;
return (
<div className="App">
<Carousel
items={data}
/>
</div>
)
}

export default App;
export default App;
81 changes: 81 additions & 0 deletions frontend/src/components/Carousel/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
@keyframes progress {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}

.carousel {
width: 100%;
height: 100%;
overflow-x: hidden;
}

.carousel .carousel-wrapper {
width: 100%;
height: 100%;
display: flex;
flex-wrap: nowrap;
padding: 0;
margin: 0;
transition: transform 0.3s;
}

.carousel .carousel-wrapper .carousel-item {
width: 100%;
height: 100%;
flex-shrink: 0;
position: relative;
list-style: none;
}

.carousel .indicators {
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 30px;
display: flex;
align-items: center;
padding: 0;
}

.carousel .indicators .indicator {
list-style: none;
box-sizing: border-box;
width: 40px;
height: 20px;
border-radius: 1px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}

.carousel .indicators .indicator + .indicator {
margin-left: 8px;
}

.carousel .indicators .indicator .indicator__track {
display: block;
width: 100%;
height: 2px;
background: rgb(140, 140, 140);
}

.carousel .indicators .indicator .indicator__bar{
display: block;
border-radius: 1px;
width: 100%;
height: 2px;
background-color: #fff;
transform: scaleX(0);
transform-origin: left;
}


.carousel .indicators .indicator.active .indicator__bar {
animation: progress linear;
animation-duration: var(--indicator-animation-duration);
}
70 changes: 70 additions & 0 deletions frontend/src/components/Carousel/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* *
* @author Ethan
* @email [email protected]
* @datetime 2023/04/19
* */

import { type FC, type CSSProperties, useEffect, useState } from 'react'
import './index.css'

import { CarouselDataItem } from '../../App'

export interface CarouselProps {
items: CarouselDataItem[]
delay?: number
}

const Carousel: FC<CarouselProps> = ({ items, delay = 3000}) => {
const [active, setActive] = useState(0)
useEffect(() => {
const timer = setTimeout(() => setActive((active + 1) % items.length), delay)
return () => clearTimeout(timer)
}, [items, delay, active])

return (
<div
className="carousel"
style={{ '--indicator-animation-duration': `${delay}ms` } as CSSProperties}
>
<ul className="carousel-wrapper" style={{ transform: `translateX(-${active * 100}%)` }}>
{items.map((item, idx) => (
<li
key={item.id}
className={`${idx === active ? 'carousel-item active' : 'carousel-item'}`}
>
<div
className="container"
style={{
backgroundColor: item.backgroundColor,
backgroundImage: `url(${item.bg})`
}}
>
<div className="text-content" style={{ color: item.color }}>
{item.title && <h2 className="title">{item.title.join('\r\n')}</h2>}
{item.contents && <h4 className="content">{item.contents.join('\r\n')}</h4>}
</div>
</div>
</li>
))}
</ul>
<ul className="indicators">
{items.map((item, idx) => (
<li
key={item.id}
className={`indicator${idx === active ? ' active' : ''}`}
onClick={() => {
setActive(idx)
}
}
>
<span className="indicator__track">
<span className="indicator__bar"/>
</span>
</li>
))}
</ul>
</div>
)
}

export default Carousel
50 changes: 50 additions & 0 deletions frontend/src/components/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import Carousel from '../components/Carousel/index'
import imgIPhone from '../assets/iphone.png'
import imgTablet from '../assets/tablet.png'
import imgAirPods from '../assets/airpods.png'

beforeEach(()=>{
jest.clearAllMocks();
})

describe('renders carousel component correctly', () => {
const Component = () => (
<Carousel items={[{
id: '1',
color: '#fff',
backgroundColor: '#111111',
title: ['xPhone'],
contents: ['Lots to love. Less to spend.', 'Starting at $399.'],
bg: imgIPhone
},
{
id: '2',
color: '#000',
backgroundColor: '#FAFAFA',
title: ['Tablet'],
contents: ['Just the right amount of everything.'],
bg: imgTablet
},
{
id: '3',
color: '#000',
backgroundColor: '#F1F1F1',
title: ['Buy a Tablet or xPhone for college.', 'Get airPods.'],
bg: imgAirPods
}]} delay={3000}/>
)
test('click dots',()=>{
const { container } = render(<Component />)
const dots = container.children[0].querySelectorAll(".indicator__track");
//click dots[0] show first page, the title should be xPhone
fireEvent.click(dots[0]);
expect(screen.getByText("xPhone")).not.toBeNull()
//click dots[1] show second page, the title should be Tablet
fireEvent.click(dots[1]);
expect(screen.getByText("Tablet")).not.toBeNull()
//click dots[2] show second page, the title should be Tablet
fireEvent.click(dots[2]);
expect(screen.getByText("Buy a Tablet or xPhone for college. Get airPods.")).not.toBeNull()
})});