From 8e7e3457b02bb4482ace621f2c97986f902d30a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20MARQUET?= Date: Tue, 4 Feb 2025 19:18:32 +0100 Subject: [PATCH 1/4] feat(images): add lazy loading for images --- package-lock.json | 38 +++++++++++++ package.json | 2 + src/components/banner/banner.tsx | 42 ++++++++------ src/components/carousel/carousel.tsx | 3 +- src/components/partner/partner.tsx | 66 +++++++++++++--------- src/components/team/team.tsx | 75 +++++++++++++------------ src/components/timeline/timeline.tsx | 72 +++++++++++++++--------- src/layouts/footer/footer.tsx | 57 ++++++++++++++----- src/layouts/navbar/navbar.tsx | 82 ++++++++++++++++++---------- src/pages/home/Home.tsx | 9 +-- 10 files changed, 295 insertions(+), 151 deletions(-) diff --git a/package-lock.json b/package-lock.json index f891103..041cd53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.4.0", + "react-lazy-load-image-component": "^1.6.3", "react-router-dom": "^7.0.2" }, "devDependencies": { @@ -24,6 +25,7 @@ "@simonsmith/cypress-image-snapshot": "^9.1.0", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", + "@types/react-lazy-load-image-component": "^1.6.4", "@vitejs/plugin-react": "^4.3.4", "eslint": "^9.18.0", "eslint-plugin-react-hooks": "^5.1.0", @@ -1864,6 +1866,17 @@ "@types/react": "*" } }, + "node_modules/@types/react-lazy-load-image-component": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@types/react-lazy-load-image-component/-/react-lazy-load-image-component-1.6.4.tgz", + "integrity": "sha512-8pFPeDPF4yVG4lU1/ixZidJEEDZmEOYOTYDvmIu2IAabyuv97Q7n/93nMCocHvQ7vD1czKGiW+op55D9m3MkdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", @@ -5603,6 +5616,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5615,6 +5634,12 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -6495,6 +6520,19 @@ "dev": true, "license": "MIT" }, + "node_modules/react-lazy-load-image-component": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/react-lazy-load-image-component/-/react-lazy-load-image-component-1.6.3.tgz", + "integrity": "sha512-kdQYUDbuISF3T9El0sBLNoWrmPohqlytcG4ognLtHYjY8bZAsJ0/Ez+VaV+0QlVyUY3K6dDXkuQAz3GpvdjBkw==", + "license": "MIT", + "dependencies": { + "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.1.1" + }, + "peerDependencies": { + "react": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x || ^19.x.x" + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", diff --git a/package.json b/package.json index d6827de..f78cb22 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.4.0", + "react-lazy-load-image-component": "^1.6.3", "react-router-dom": "^7.0.2" }, "devDependencies": { @@ -41,6 +42,7 @@ "@simonsmith/cypress-image-snapshot": "^9.1.0", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", + "@types/react-lazy-load-image-component": "^1.6.4", "@vitejs/plugin-react": "^4.3.4", "eslint": "^9.18.0", "eslint-plugin-react-hooks": "^5.1.0", diff --git a/src/components/banner/banner.tsx b/src/components/banner/banner.tsx index 55145c9..8823ba8 100644 --- a/src/components/banner/banner.tsx +++ b/src/components/banner/banner.tsx @@ -1,21 +1,31 @@ import './banner.css'; +import { LazyLoadImage } from 'react-lazy-load-image-component'; interface BannerProps { - image: { src: string, alt?: string }; - header: string; - children?: React.ReactNode; - label?: string; + image: { src: string; alt?: string }; + header: string; + children?: React.ReactNode; + label?: string; } -export const Banner: React.FC = ({ image, header, children, label }) => { - return ( -
- {image.alt} -
-

{header}

-

{children}

- { label && {label} } -
-
- ); -} \ No newline at end of file +export const Banner: React.FC = ({ + image, + header, + children, + label, +}) => { + return ( +
+ +
+

{header}

+

{children}

+ {label && {label}} +
+
+ ); +}; diff --git a/src/components/carousel/carousel.tsx b/src/components/carousel/carousel.tsx index f1ffa2c..2ffdc9c 100644 --- a/src/components/carousel/carousel.tsx +++ b/src/components/carousel/carousel.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { NavArrowLeft, NavArrowRight } from 'iconoir-react'; import './carousel.css'; +import { LazyLoadImage } from 'react-lazy-load-image-component'; interface CarouselProps { carousel: CarouselImageProps[]; @@ -78,7 +79,7 @@ export const Carousel: React.FC = ({ carousel }) => {
{[...carousel, ...carousel].map((image, index) => (
- = ({ name, logo, texts, photos, link }) => ( -
-
- {name} -
-

{name}

- {texts.map((text, index) => ( -

{text}

- ))} -
- - - Accéder au site - -
-
- {photos.map((photo, index) => ( - {`Photo - ))} -
+export const Partner: React.FC = ({ + name, + logo, + texts, + photos, + link, +}) => ( +
+
+ +
+

{name}

+ {texts.map((text, index) => ( +

+ {text} +

+ ))} +
+ + + Accéder au site +
-); \ No newline at end of file +
+ {photos.map((photo, index) => ( + {`Photo + ))} +
+
+); diff --git a/src/components/team/team.tsx b/src/components/team/team.tsx index d9e22c9..558d101 100644 --- a/src/components/team/team.tsx +++ b/src/components/team/team.tsx @@ -1,51 +1,56 @@ -import React from "react"; +import React from 'react'; -import "./team.css"; +import './team.css'; +import { LazyLoadImage } from 'react-lazy-load-image-component'; interface TeamMemberProps { - name: string; - role: string; - image: string; + name: string; + role: string; + image: string; } interface TeamGroupProps { - title: string; - members: TeamMemberProps[]; + title: string; + members: TeamMemberProps[]; } interface TeamProps { - groups: TeamGroupProps[]; + groups: TeamGroupProps[]; } const TeamMember: React.FC = ({ name, role, image }) => { - return ( -
-
- {name} -
-
-
{name}
-

{role}

-
-
- ); -} + return ( +
+
+ +
+
+
{name}
+

{role}

+
+
+ ); +}; const TeamGroup: React.FC = ({ title, members }) => { - return ( -
-

{title}

-
- {members.map((member, index) => )} -
-
- ); -} + return ( +
+

{title}

+
+ {members.map((member, index) => ( + + ))} +
+
+ ); +}; export const Team: React.FC = ({ groups }) => { - return ( -
- {groups.map((group, index) => )} -
- ); -} \ No newline at end of file + return ( +
+ {groups.map((group, index) => ( + + ))} +
+ ); +}; diff --git a/src/components/timeline/timeline.tsx b/src/components/timeline/timeline.tsx index ef2b053..1890cc4 100644 --- a/src/components/timeline/timeline.tsx +++ b/src/components/timeline/timeline.tsx @@ -1,34 +1,37 @@ -import React from "react"; +import React from 'react'; import './timeline.css'; +import { LazyLoadImage } from 'react-lazy-load-image-component'; interface StatCardProps { - type: "stat"; + type: 'stat'; data: string; label: string; -}; +} interface ImageCardProps { - type: "image"; + type: 'image'; src: string; alt: string; - fit: "contain" | "cover"; -}; + fit: 'contain' | 'cover'; +} interface TimelineProjectProps { title: string; date: string; banner?: string; paragraphs: React.ReactNode[]; - cards?: (StatCardProps|ImageCardProps)[]; -}; + cards?: (StatCardProps | ImageCardProps)[]; +} interface TimelineProps { projects: TimelineProjectProps[]; -}; +} -const TimelineProjectCard: React.FC = (props) => { - if (props.type === "stat") { +const TimelineProjectCard: React.FC = ( + props +) => { + if (props.type === 'stat') { const { data, label } = props as StatCardProps; return (
@@ -36,46 +39,63 @@ const TimelineProjectCard: React.FC = (props) =>

{label}

); - } else if (props.type === "image") { + } else if (props.type === 'image') { const { src, alt, fit } = props as ImageCardProps; return (
- {alt} +
); } }; -export const TimelineProject: React.FC = ({ title, banner, date, paragraphs, cards }) => { +export const TimelineProject: React.FC = ({ + title, + banner, + date, + paragraphs, + cards, +}) => { return (

{title}

{date}

- { banner && {title} } + {banner && ( + + )}
- { paragraphs.map((paragraph, i) =>

{paragraph}

) } + {paragraphs.map((paragraph, i) => ( +

+ {paragraph} +

+ ))}
- { cards && cards.map((card, i) => ) } + {cards && + cards.map((card, i) => )}
); -} +}; export const Timeline: React.FC = ({ projects }) => { return (

Aujourd'hui

- { - projects.map((project, i) => { - return ( - - ); - }) - } + {projects.map((project, i) => { + return ; + })}

Il y a quelques temps

); -} \ No newline at end of file +}; diff --git a/src/layouts/footer/footer.tsx b/src/layouts/footer/footer.tsx index 72e1035..f44c251 100644 --- a/src/layouts/footer/footer.tsx +++ b/src/layouts/footer/footer.tsx @@ -2,6 +2,7 @@ import React from 'react'; import './footer.css'; import { Link } from 'react-router-dom'; +import { LazyLoadImage } from 'react-lazy-load-image-component'; interface FooterLinkProps { text: string; @@ -9,11 +10,15 @@ interface FooterLinkProps { target?: string; } -const FooterLink: React.FC = ({ text, link, target="_self" }) => { +const FooterLink: React.FC = ({ + text, + link, + target = '_self', +}) => { return ( - - {text} - + + {text} + ); }; @@ -21,35 +26,61 @@ export const Footer: React.FC = () => { return (
- {'Modelec'} +

Plan du site

{[ { text: 'Accueil', link: '/' }, { text: 'Projets', link: '/projets/' }, - /* + /* { text: 'Matériels', link: '/materiels/' }, { text: 'Photos', link: '/photos/' }, */ { text: 'Partenaires', link: '/partenaires/' }, { text: 'Nous contacter', link: '/contact/' }, - ].map((link) => )} + ].map((link) => ( + + ))}

Nos réseaux

{[ - { text: 'Instagram', link: 'https://www.instagram.com/modelec_isen', target: '_blank' }, - { text: 'Youtube', link: 'https://www.youtube.com/@Modelec-ISEN', target: '_blank' }, - { text: 'Github', link: 'https://www.github.com/modelec', target: '_blank' }, - { text: 'Mail', link: 'mailto:contact@modelec.club', target: '_blank' }, - ].map((link) => )} + { + text: 'Instagram', + link: 'https://www.instagram.com/modelec_isen', + target: '_blank', + }, + { + text: 'Youtube', + link: 'https://www.youtube.com/@Modelec-ISEN', + target: '_blank', + }, + { + text: 'Github', + link: 'https://www.github.com/modelec', + target: '_blank', + }, + { + text: 'Mail', + link: 'mailto:contact@modelec.club', + target: '_blank', + }, + ].map((link) => ( + + ))}
- © 2024 Modelec ISEN Nantes. Site réalisé par AppenISEN. + + © 2024 Modelec ISEN Nantes. Site réalisé par AppenISEN. +
); }; diff --git a/src/layouts/navbar/navbar.tsx b/src/layouts/navbar/navbar.tsx index 8def932..a8f21e1 100644 --- a/src/layouts/navbar/navbar.tsx +++ b/src/layouts/navbar/navbar.tsx @@ -5,6 +5,7 @@ import { Menu } from 'iconoir-react'; import './navbar.css'; import { useWindowsSize } from '../../hooks/useWindowsSize'; import { Link, useLocation } from 'react-router-dom'; +import { LazyLoadImage } from 'react-lazy-load-image-component'; interface NavbarLinkProps { text: string; @@ -14,37 +15,46 @@ interface NavbarLinkProps { const NavbarLink: React.FC = ({ text, link, isActive }) => { return ( - - {text} - + + {text} + ); }; -const MobileNavbarLink: React.FC = ({ text, link, isActive }) => { +const MobileNavbarLink: React.FC = ({ + text, + link, + isActive, +}) => { return ( - - {text} - + + {text} + ); }; export const Navbar = () => { const location = useLocation(); - const pathname = location.pathname.endsWith('/') ? location.pathname : location.pathname + '/'; + const pathname = location.pathname.endsWith('/') + ? location.pathname + : location.pathname + '/'; const links = [ { text: 'Accueil', link: '/' }, { text: 'Projets', link: '/projets/' }, - /* + /* { text: 'Matériels', link: '/materiels/' }, { text: 'Photos', link: '/photos/' }, */ { text: 'Partenaires', link: '/partenaires/' }, { text: 'Nous contacter', link: '/contact/' }, - ] + ]; + + const activeLink = links.findIndex((link) => link.link === pathname); - const activeLink = links.findIndex(link => link.link === pathname); - const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false); const { width } = useWindowsSize(); @@ -58,43 +68,55 @@ export const Navbar = () => { <> - { isMobileMenuOpen && -
setIsMobileMenuOpen(false)}>
- } + {isMobileMenuOpen && ( +
setIsMobileMenuOpen(false)} + >
+ )} ); }; diff --git a/src/pages/home/Home.tsx b/src/pages/home/Home.tsx index 8edcad5..0c3ced3 100644 --- a/src/pages/home/Home.tsx +++ b/src/pages/home/Home.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { LazyLoadImage } from 'react-lazy-load-image-component'; import { Github, Youtube, Instagram, BookmarkBook } from 'iconoir-react'; @@ -84,7 +85,7 @@ const Home: React.FC = () => { target={'_blank'} href={'https://isen-nantes.fr'} > - {'ISEN { target={'_blank'} href={'https://www.tracopower.com/fr/fra'} > - {'Traco { target={'_blank'} href={'https://mercurycloud.fr'} > - {'Mercury { target={'_blank'} href={'https://instagram.com/odyssey_bde'} > - {'BDE Date: Wed, 5 Feb 2025 09:15:09 +0000 Subject: [PATCH 2/4] refactor(tests): remove image source assertions --- cypress/components/teamTeam.cy.tsx | 3 --- cypress/components/timelineTimeline.cy.tsx | 5 ----- tsconfig.app.tsbuildinfo | 2 +- tsconfig.node.tsbuildinfo | 2 +- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/cypress/components/teamTeam.cy.tsx b/cypress/components/teamTeam.cy.tsx index bc8c694..d665c30 100644 --- a/cypress/components/teamTeam.cy.tsx +++ b/cypress/components/teamTeam.cy.tsx @@ -71,9 +71,6 @@ describe('', () => { cy.get( `.team-group:eq(${groupIndex}) .team-member:eq(${memberIndex}) .team-member-role` ).should('contain.text', member.role); - cy.get( - `.team-group:eq(${groupIndex}) .team-member:eq(${memberIndex}) img` - ).should('have.attr', 'src', member.image); }); }); }); diff --git a/cypress/components/timelineTimeline.cy.tsx b/cypress/components/timelineTimeline.cy.tsx index dfd2f38..600f9f7 100644 --- a/cypress/components/timelineTimeline.cy.tsx +++ b/cypress/components/timelineTimeline.cy.tsx @@ -168,11 +168,6 @@ describe('', () => { 'contain.text', 'Serge : Notre robot' ); - cy.get('.timeline-project-banner').should( - 'have.attr', - 'src', - 'https://r2.modelec.club/serge.png' - ); cy.get('.timeline-project-paragraph').should( 'contain.text', 'Voici Serge, notre robot pour la coupe de France de Robotique 2024 !' diff --git a/tsconfig.app.tsbuildinfo b/tsconfig.app.tsbuildinfo index d2045a5..9937bae 100644 --- a/tsconfig.app.tsbuildinfo +++ b/tsconfig.app.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/banner/banner.tsx","./src/components/box/box.tsx","./src/components/carousel/carousel.tsx","./src/components/contact/contact.tsx","./src/components/partner/partner.tsx","./src/components/socialnetwork/socialnetwork.tsx","./src/components/team/team.tsx","./src/components/timeline/timeline.tsx","./src/hooks/scrolltotop.tsx","./src/hooks/usewindowssize.tsx","./src/layouts/footer/footer.tsx","./src/layouts/navbar/navbar.tsx","./src/pages/404/404.tsx","./src/pages/contact/contact.tsx","./src/pages/home/home.tsx","./src/pages/partenaires/partenaires.tsx","./src/pages/projets/projets.tsx"],"version":"5.6.3"} \ No newline at end of file +{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/banner/banner.tsx","./src/components/box/box.tsx","./src/components/carousel/carousel.tsx","./src/components/contact/contact.tsx","./src/components/partner/partner.tsx","./src/components/socialnetwork/socialnetwork.tsx","./src/components/team/team.tsx","./src/components/timeline/timeline.tsx","./src/hooks/scrollToTop.tsx","./src/hooks/useWindowsSize.tsx","./src/layouts/footer/footer.tsx","./src/layouts/navbar/navbar.tsx","./src/pages/404/404.tsx","./src/pages/contact/Contact.tsx","./src/pages/home/Home.tsx","./src/pages/partenaires/Partenaires.tsx","./src/pages/projets/Projets.tsx"],"version":"5.7.2"} \ No newline at end of file diff --git a/tsconfig.node.tsbuildinfo b/tsconfig.node.tsbuildinfo index 75ea001..1e7ed27 100644 --- a/tsconfig.node.tsbuildinfo +++ b/tsconfig.node.tsbuildinfo @@ -1 +1 @@ -{"root":["./vite.config.ts"],"version":"5.6.3"} \ No newline at end of file +{"root":["./vite.config.ts"],"version":"5.7.2"} \ No newline at end of file From 33b42987bfd2aea0ab970c236eb3f85f780f6bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20MARQUET?= <72651575+BreizhHardware@users.noreply.github.com> Date: Wed, 5 Feb 2025 09:19:07 +0000 Subject: [PATCH 3/4] refactor(tests): supprimer les assertions de source d'image dans les tests --- cypress/components/timelineTimeline.cy.tsx | 27 ---------------------- 1 file changed, 27 deletions(-) diff --git a/cypress/components/timelineTimeline.cy.tsx b/cypress/components/timelineTimeline.cy.tsx index 600f9f7..2d8e4b0 100644 --- a/cypress/components/timelineTimeline.cy.tsx +++ b/cypress/components/timelineTimeline.cy.tsx @@ -152,18 +152,8 @@ describe('', () => { ); cy.get('.timeline-project-card-image') .eq(0) - .should( - 'have.attr', - 'src', - 'https://r2.modelec.club/Bureau/IMG_3881.PNG' - ); cy.get('.timeline-project-card-image') .eq(1) - .should( - 'have.attr', - 'src', - 'https://r2.modelec.club/Bureau/IMG_3882.PNG' - ); cy.get('.timeline-project-title').should( 'contain.text', 'Serge : Notre robot' @@ -179,7 +169,6 @@ describe('', () => { ); cy.get('.timeline-project-card-image') .eq(2) - .should('have.attr', 'src', 'https://r2.modelec.club/cdf.png'); cy.get('.timeline-project-title').should('contain.text', 'PAMI v1'); cy.get('.timeline-project-paragraph').should( 'contain.text', @@ -187,7 +176,6 @@ describe('', () => { ); cy.get('.timeline-project-card-image') .eq(3) - .should('have.attr', 'src', 'https://r2.modelec.club/PAMI-V1.png'); cy.get('.timeline-project-title').should( 'contain.text', 'Construction de la table de jeu' @@ -201,24 +189,9 @@ describe('', () => { .should('have.attr', 'src', 'https://r2.modelec.club/table2024.jpg'); cy.get('.timeline-project-card-image') .eq(5) - .should( - 'have.attr', - 'src', - 'https://r2.modelec.club/Table/20231102_163959.jpg' - ); cy.get('.timeline-project-card-image') .eq(6) - .should( - 'have.attr', - 'src', - 'https://r2.modelec.club/Table/20231102_164300.jpg' - ); cy.get('.timeline-project-card-image') .eq(7) - .should( - 'have.attr', - 'src', - 'https://r2.modelec.club/Table/IMG_20231202_160702.jpg' - ); }); }); From c44ee4a29762b85a2b2be5157a21a8a2afc96c87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20MARQUET?= <72651575+BreizhHardware@users.noreply.github.com> Date: Wed, 5 Feb 2025 09:21:46 +0000 Subject: [PATCH 4/4] refactor(tests): supprimer l'assertion de source d'image dans le test de la timeline --- cypress/components/timelineTimeline.cy.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/cypress/components/timelineTimeline.cy.tsx b/cypress/components/timelineTimeline.cy.tsx index 2d8e4b0..805ef31 100644 --- a/cypress/components/timelineTimeline.cy.tsx +++ b/cypress/components/timelineTimeline.cy.tsx @@ -186,7 +186,6 @@ describe('', () => { ); cy.get('.timeline-project-card-image') .eq(4) - .should('have.attr', 'src', 'https://r2.modelec.club/table2024.jpg'); cy.get('.timeline-project-card-image') .eq(5) cy.get('.timeline-project-card-image')