Skip to content
This repository has been archived by the owner on Jun 2, 2022. It is now read-only.

[FSSS-108] Feat: Add Skeleton Loading #317

Merged
merged 42 commits into from
Feb 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
faf9658
Add primitive element skeleton
lucasfp13 Feb 8, 2022
3c5ec61
Add Filter skeleton
lucasfp13 Feb 8, 2022
c6e1df4
Add ProductCard skeleton
lucasfp13 Feb 8, 2022
f44cdc3
Add ProductTile skeleton
lucasfp13 Feb 8, 2022
9a183c1
Add Shimmer component to handle shimmer effect
lucasfp13 Feb 8, 2022
3d641ff
Use skeleton components in the ProductGallery
lucasfp13 Feb 9, 2022
9247fde
Fix product card skeleton for tablet/mobile version
lucasfp13 Feb 9, 2022
70d1f29
Use skeleton components in the homepage
lucasfp13 Feb 9, 2022
65b29fc
Minor css fixes
lucasfp13 Feb 9, 2022
fddc872
Adjust styles for sections' product cards
lucasfp13 Feb 10, 2022
ee50f19
Fix PLP test
lucasfp13 Feb 10, 2022
679c98c
Trigger CI with an empty commit
lucasfp13 Feb 10, 2022
d43c3c1
Fix product card skeleton width for desktop version
lucasfp13 Feb 10, 2022
9fb05e5
Add `ITEMS_PER_SECTION`constant
lucasfp13 Feb 10, 2022
739f045
Refactor skeleton products array
lucasfp13 Feb 10, 2022
60c284a
Fix lint warnings and give a better description
lucasfp13 Feb 10, 2022
0d67095
CSS fixes
lucasfp13 Feb 10, 2022
c74e09d
Fix white transparent variable value
lucasfp13 Feb 10, 2022
8d1fc63
Rename skeleton components
lucasfp13 Feb 10, 2022
6ea4b31
Apply composition in `SkeletonElement` and `SkeletonFilter`
lucasfp13 Feb 15, 2022
0111cd8
Add ProductGrid skeleton
lucasfp13 Feb 16, 2022
0e31408
Add ProductShelf skeleton
lucasfp13 Feb 16, 2022
5878ad2
Add ProductTiles skeleton
lucasfp13 Feb 16, 2022
3eae783
Add CHANGELOG entry
lucasfp13 Feb 16, 2022
281c972
Use `translate3d` for performance sake
lucasfp13 Feb 16, 2022
c43c26f
Remove unnecessary checking
lucasfp13 Feb 17, 2022
71b2d00
Minor fixes
lucasfp13 Feb 17, 2022
a04447b
Fixes after rebase
lucasfp13 Feb 17, 2022
e7becb5
Use `PropsWithChildren` instead get `children` as prop
lucasfp13 Feb 17, 2022
efda4ae
Adjust animation to use absolute unit `vw`
lucasfp13 Feb 17, 2022
f878818
Adjust shimmer effect animation
lucasfp13 Feb 17, 2022
e9b0d52
Rename skeleton components according to its context
lucasfp13 Feb 17, 2022
d380482
Fix shimmer wrapper width
lucasfp13 Feb 17, 2022
b41b78f
Replace magic number by a temporary constant
lucasfp13 Feb 17, 2022
f60604e
Add new color token for skeleton's background
lucasfp13 Feb 18, 2022
6a326fe
Use function for skeleton components
lucasfp13 Feb 18, 2022
9f9841e
Apply suggestions from code review
lucasfp13 Feb 18, 2022
404f841
Trigger CI with an empty commit
lucasfp13 Feb 18, 2022
7754624
Remove unnecessary optional chaining operators
lucasfp13 Feb 18, 2022
7487702
Trigger CI with an empty commit
lucasfp13 Feb 18, 2022
6d0ee04
Use `PropsWithChildren` instead get `children` as prop
lucasfp13 Feb 19, 2022
15cd92e
Check css classes after applying skeletons
lucasfp13 Feb 21, 2022
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- `GatsbyLink` to `Link` ui component.
- `Skeleton` loading components.

### Changed
- Replaces page type redirects, a.k.a. `/account`, `/login` to a corresponding file in `/pages` folder
Expand Down
14 changes: 8 additions & 6 deletions cypress/integration/plp.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ describe('Search page Filters and Sorting options', () => {
// Check if the filter applied actually brought the number of products it said it would

cy.waitUntil(() => {
return cy.get('.product-grid').should('exist')
return cy.getById('total-product-count').should('exist')
}).then(() => {
cy.getById('total-product-count').then(($countDiv) => {
expect(Number($countDiv.attr('data-count'))).to.eq(
Number(quantity)
)
})
cy.getById('total-product-count')
.parent()
.then(($countDiv) => {
expect(Number($countDiv.attr('data-count'))).to.eq(
Number(quantity)
)
})
})
})
})
Expand Down
33 changes: 18 additions & 15 deletions src/components/product/ProductGrid/ProductGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import type { ProductSummary_ProductFragment } from '@generated/graphql'
import ProductGridSkeleton from 'src/components/skeletons/ProductGridSkeleton'

import ProductCard from '../ProductCard'
import './product-grid.scss'
Expand All @@ -12,21 +13,23 @@ interface Props {

function ProductGrid({ products, page, pageSize }: Props) {
return (
<ul className="product-grid">
{products.map((product, idx) => (
<li key={`${product.id}`}>
<ProductCard
product={product}
index={pageSize * page + idx + 1}
bordered
outOfStock={
product.offers.offers?.[0].availability !==
'https://schema.org/InStock'
}
/>
</li>
))}
</ul>
<ProductGridSkeleton loading={products.length === 0}>
<ul className="product-grid">
{products.map((product, idx) => (
<li key={`${product.id}`}>
<ProductCard
product={product}
index={pageSize * page + idx + 1}
bordered
outOfStock={
product.offers.offers?.[0].availability !==
'https://schema.org/InStock'
}
/>
</li>
))}
</ul>
</ProductGridSkeleton>
)
}

Expand Down
64 changes: 37 additions & 27 deletions src/components/sections/ProductGallery/ProductGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import React, { useState } from 'react'
import Filter from 'src/components/search/Filter'
import Sort from 'src/components/search/Sort'
import Button, { LinkButton } from 'src/components/ui/Button'
import SkeletonElement from 'src/components/skeletons/SkeletonElement'
import FilterSkeleton from 'src/components/skeletons/FilterSkeleton'
import ProductGrid from 'src/components/product/ProductGrid'

import GalleryPage from './ProductGalleryPage'
import { useGalleryQuery } from './useGalleryQuery'
Expand All @@ -33,35 +36,39 @@ function ProductGallery({ title, slug }: Props) {
<div className="product-listing / grid-content-full">
<div className="product-listing__content-grid / grid-content">
<div className="product-listing__filters">
<Filter
slug={slug}
isOpen={isFilterOpen}
facets={orderedFacets}
onDismiss={() => setIsFilterOpen(false)}
/>
<FilterSkeleton loading={orderedFacets?.length === 0}>
<Filter
slug={slug}
isOpen={isFilterOpen}
facets={orderedFacets}
onDismiss={() => setIsFilterOpen(false)}
/>
</FilterSkeleton>
</div>

<div
className="product-listing__results-count"
data-testid="total-product-count"
data-count={totalCount}
>
<h2>{totalCount} Results</h2>
<div className="product-listing__results-count" data-count={totalCount}>
<SkeletonElement shimmer type="text" loading={!data}>
<h2 data-testid="total-product-count">{totalCount} Results</h2>
</SkeletonElement>
</div>

<div className="product-listing__sort">
<Sort />
<SkeletonElement shimmer type="text" loading={!data}>
<Sort />
</SkeletonElement>

<Button
variant="tertiary"
data-testid="open-filter-button"
icon={<FadersHorizontalIcon size={16} />}
iconPosition="left"
aria-label="Open Filters"
onClick={() => setIsFilterOpen(!isFilterOpen)}
>
Filters
</Button>
<SkeletonElement shimmer type="button" loading={!data}>
<Button
variant="tertiary"
data-testid="open-filter-button"
icon={<FadersHorizontalIcon size={16} />}
iconPosition="left"
aria-label="Open Filters"
onClick={() => setIsFilterOpen(!isFilterOpen)}
>
Filters
</Button>
</SkeletonElement>
</div>

<div className="product-listing__results">
Expand All @@ -87,9 +94,9 @@ function ProductGallery({ title, slug }: Props) {
)}

{/* Render ALL products */}
<div className="product-listing__data-grid">
{data &&
pages.map((page) => (
{data ? (
<>
{pages.map((page) => (
<GalleryPage
key={`gallery-page-${page}`}
showSponsoredProducts={false}
Expand All @@ -98,7 +105,10 @@ function ProductGallery({ title, slug }: Props) {
title={title}
/>
))}
</div>
</>
) : (
<ProductGrid page={0} pageSize={0} products={[]} />
)}

{/* Prefetch Previous and Next pages */}
{prev !== false && (
Expand Down
28 changes: 13 additions & 15 deletions src/components/sections/ProductGallery/product-gallery.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
@import "src/styles/scaffold";

// Check it after apply Skeletons.
.product-listing {
--product-listing-row-height: var(--space-6);

Expand Down Expand Up @@ -29,19 +28,6 @@
}
}

.product-listing__data-grid {
--product-listing-grid-height: 1750px;
--product-listing-row-height: var(--space-6);

min-height: calc(var(--product-listing-grid-height) - var(--product-listing-row-height));
contain-intrinsic-size: calc(var(--product-listing-grid-height) - var(--product-listing-row-height));
content-visibility: auto;

@include media(">=notebook") {
--product-listing-grid-height: 1016px;
}
}

.product-listing__filters {
@include media(">=notebook") {
position: sticky;
Expand All @@ -62,6 +48,11 @@
padding: var(--space-1) var(--space-3) var(--space-2);
background-color: var(--bg-neutral-lightest);

[data-element-variant="text"] {
min-width: rem(225px);
min-height: var(--space-5);
}

@include media(">=notebook") {
grid-column: 6 / span 7;
justify-content: flex-end;
Expand All @@ -72,6 +63,10 @@
.button[data-store-button] {
display: none;
}

[data-element-variant="button"] {
display: none;
}
}
}

Expand All @@ -92,9 +87,12 @@
padding: 0;
background-color: unset;
}

[data-element-variant="text"] {
min-width: rem(130px);
}
}

// Check it after apply Skeletons.
.product-listing__results {
--padding: var(--space-1);

Expand Down
17 changes: 10 additions & 7 deletions src/components/sections/ProductShelf/ProductShelf.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react'
import type { ProductSummary_ProductFragment } from '@generated/graphql'
import ProductShelfSkeleton from 'src/components/skeletons/ProductShelfSkeleton'

import ProductCard from '../../product/ProductCard'

Expand All @@ -11,13 +12,15 @@ interface ProductShelfProps {

function ProductShelf({ products }: ProductShelfProps) {
return (
<ul data-product-shelf className="grid-content">
{products.map((product, idx) => (
<li key={`${product.id}`}>
<ProductCard product={product} index={idx + 1} />
</li>
))}
</ul>
<ProductShelfSkeleton loading={products.length === 0}>
<ul data-product-shelf className="grid-content">
{products.map((product, idx) => (
<li key={`${product.id}`}>
<ProductCard product={product} index={idx + 1} />
</li>
))}
</ul>
</ProductShelfSkeleton>
)
}

Expand Down
29 changes: 16 additions & 13 deletions src/components/sections/ProductTiles/ProductTiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react'
import Tiles, { Tile } from 'src/components/ui/Tiles'
import ProductCard from 'src/components/product/ProductCard'
import type { ProductSummary_ProductFragment } from '@generated/graphql'
import ProductTilesSkeleton from 'src/components/skeletons/ProductTilesSkeleton'

interface TilesProps {
products: ProductSummary_ProductFragment[]
Expand All @@ -26,19 +27,21 @@ const getRatio = (products: number, idx: number) => {

const ProductTiles = ({ products }: TilesProps) => {
return (
<Tiles>
{products.map((product, idx) => (
<Tile key={product.id}>
<ProductCard
data-testid="tile-card"
product={product}
index={idx + 1}
variant="horizontal"
aspectRatio={getRatio(products.length, idx)}
/>
</Tile>
))}
</Tiles>
<ProductTilesSkeleton variant="horizontal" loading={products.length === 0}>
<Tiles>
{products.map((product, idx) => (
<Tile key={product.id}>
<ProductCard
data-testid="tile-card"
product={product}
index={idx + 1}
variant="horizontal"
aspectRatio={getRatio(products.length, idx)}
/>
</Tile>
))}
</Tiles>
</ProductTilesSkeleton>
)
}

Expand Down
32 changes: 32 additions & 0 deletions src/components/skeletons/FilterSkeleton/FilterSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react'
import type { PropsWithChildren } from 'react'

import Shimmer from '../Shimmer'
import SkeletonElement from '../SkeletonElement'
import './filter-skeleton.scss'

interface Props {
loading?: boolean
}

function FilterSkeleton({
children,
loading = true,
}: PropsWithChildren<Props>) {
return loading ? (
<div data-store-filter-skeleton>
<SkeletonElement shimmer type="text" />

<div data-filter-skeleton-content>
<SkeletonElement type="text" />
<SkeletonElement type="text" />
<SkeletonElement type="text" />
<Shimmer />
</div>
</div>
) : (
<>{children}</>
)
}

export default FilterSkeleton
28 changes: 28 additions & 0 deletions src/components/skeletons/FilterSkeleton/filter-skeleton.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@import "src/styles/scaffold";

[data-store-filter-skeleton] {
margin-top: var(--space-1);

@include media("<notebook") { display: none; }

[data-filter-skeleton-content] {
position: relative;
display: flex;
flex-direction: column;
padding: var(--space-1) var(--space-1) var(--space-0);
overflow: hidden;
border: var(--border-width-0) solid var(--color-border-display);
border-radius: var(--border-radius-default);

[data-element-variant="text"] {
min-width: 100%;
min-height: var(--space-8);
margin-bottom: var(--space-0);
}
}

[data-element-variant="text"] {
max-width: 30%;
margin-bottom: var(--space-2);
}
}
1 change: 1 addition & 0 deletions src/components/skeletons/FilterSkeleton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './FilterSkeleton'
Loading