From b56654867afc565c54f1d6e6e412228c0116e5c7 Mon Sep 17 00:00:00 2001 From: And Finally Date: Tue, 8 Aug 2023 15:29:08 +0100 Subject: [PATCH] Changes from `add/marketplace-product-card-component` after rebasing on feature branch. --- .../category-selector/category-selector.tsx | 2 +- .../components/discover/discover.scss | 20 +++- .../components/discover/discover.tsx | 29 ++++- .../components/extensions/extensions.scss | 7 +- .../components/extensions/extensions.tsx | 10 +- .../components/product-card/product-card.scss | 108 ++++++++++++++++++ .../components/product-card/product-card.tsx | 86 ++++++++++++++ .../product-list-content.scss | 3 +- .../product-list-content.tsx | 48 ++++++-- .../product-list-header.scss | 31 ++++- .../product-list-header.tsx | 20 +++- .../components/product-list/product-list.tsx | 14 ++- .../marketplace/stylesheets/_variables.scss | 2 + .../client/marketplace/utils/functions.tsx | 33 ++++++ .../client/payments-welcome/index.tsx | 3 + 15 files changed, 382 insertions(+), 34 deletions(-) create mode 100644 plugins/woocommerce-admin/client/marketplace/components/product-card/product-card.scss create mode 100644 plugins/woocommerce-admin/client/marketplace/components/product-card/product-card.tsx create mode 100644 plugins/woocommerce-admin/client/marketplace/utils/functions.tsx diff --git a/plugins/woocommerce-admin/client/marketplace/components/category-selector/category-selector.tsx b/plugins/woocommerce-admin/client/marketplace/components/category-selector/category-selector.tsx index 711f9e37231..6330bd63521 100644 --- a/plugins/woocommerce-admin/client/marketplace/components/category-selector/category-selector.tsx +++ b/plugins/woocommerce-admin/client/marketplace/components/category-selector/category-selector.tsx @@ -25,7 +25,7 @@ export type CategoryAPIItem = { }; function fetchCategories(): Promise< CategoryAPIItem[] > { - return fetch( MARKETPLACE_URL + 'wp-json/wccom-extensions/1.0/categories' ) + return fetch( MARKETPLACE_URL + '/wp-json/wccom-extensions/1.0/categories' ) .then( ( response ) => { if ( ! response.ok ) { throw new Error( response.statusText ); diff --git a/plugins/woocommerce-admin/client/marketplace/components/discover/discover.scss b/plugins/woocommerce-admin/client/marketplace/components/discover/discover.scss index bb7939d11e7..c0085e86638 100644 --- a/plugins/woocommerce-admin/client/marketplace/components/discover/discover.scss +++ b/plugins/woocommerce-admin/client/marketplace/components/discover/discover.scss @@ -1 +1,19 @@ -// To keep StyleLint happy. Remove when file contains actual code. +@import '../../stylesheets/_variables.scss'; + +.woocommerce-marketplace { + &__discover { + display: flex; + flex-direction: column; + align-items: center; + gap: 40px; + padding: 0 $content-spacing-small; + } +} + +@media screen and (min-width: $breakpoint-medium) { + .woocommerce-marketplace { + &__discover { + padding: 0 $content-spacing-large; + } + } +} diff --git a/plugins/woocommerce-admin/client/marketplace/components/discover/discover.tsx b/plugins/woocommerce-admin/client/marketplace/components/discover/discover.tsx index 7b96bf6195d..b16ecb68098 100644 --- a/plugins/woocommerce-admin/client/marketplace/components/discover/discover.tsx +++ b/plugins/woocommerce-admin/client/marketplace/components/discover/discover.tsx @@ -1,16 +1,41 @@ /** * External dependencies */ +import { useEffect, useState } from '@wordpress/element'; /** * Internal dependencies */ +import ProductList from '../product-list/product-list'; +import { fetchDiscoverPageData, ProductGroup } from '../../utils/functions'; import './discover.scss'; -export default function Discover(): JSX.Element { +export default function Discover(): JSX.Element | null { + const [ productGroups, setProductGroups ] = useState< + Array< ProductGroup > + >( [] ); + + useEffect( () => { + fetchDiscoverPageData().then( ( products: Array< ProductGroup > ) => { + setProductGroups( products ); + } ); + }, [] ); + + if ( ! productGroups.length ) { + return null; + } + + const groupsList = productGroups.flatMap( ( group ) => group ); return (
-

Discover Our Favorites

+ { groupsList.map( ( groups ) => ( + + ) ) }
); } diff --git a/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.scss b/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.scss index bb7939d11e7..b9bf02128ac 100644 --- a/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.scss +++ b/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.scss @@ -1 +1,6 @@ -// To keep StyleLint happy. Remove when file contains actual code. +.woocommerce-marketplace { + &__extensions { + display: flex; + flex-direction: column; + } +} diff --git a/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.tsx b/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.tsx index c8ab9627d5d..b373015704c 100644 --- a/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.tsx +++ b/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.tsx @@ -5,13 +5,19 @@ /** * Internal dependencies */ -import ProductList from '../product-list/product-list'; import './extensions.scss'; +import CategorySelector from '../category-selector/category-selector'; export default function Extensions(): JSX.Element { return (
- + +
+
+
+
+
+
); } diff --git a/plugins/woocommerce-admin/client/marketplace/components/product-card/product-card.scss b/plugins/woocommerce-admin/client/marketplace/components/product-card/product-card.scss new file mode 100644 index 00000000000..4a6b25cf22f --- /dev/null +++ b/plugins/woocommerce-admin/client/marketplace/components/product-card/product-card.scss @@ -0,0 +1,108 @@ +@import '../../stylesheets/_variables.scss'; + +.woocommerce-marketplace { + &__product-card { + padding: $large-gap; + border-radius: $grid-unit-05 !important; + margin-top: $large-gap; + + &__content { + display: grid; + align-items: flex-start; + gap: $medium-gap; + justify-content: space-between; + height: 100%; + grid-template-rows: auto 1fr 20px; + } + + &__header { + align-self: stretch; + } + + &__icon { + width: $grid-unit-60; + height: $grid-unit-60; + flex-shrink: 0; + border-radius: $grid-unit; + } + + &__details { + display: flex; + justify-content: flex-start; + align-items: flex-start; + gap: $medium-gap; + background: $white; + } + + &__meta { + display: flex; + flex-direction: column; + gap: 2px; + color: $gray-900; + } + + &__title { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + color: $gray-900; + font-size: $editor-font-size; + font-style: normal; + font-weight: 600; + line-height: $large-gap; + margin: -4px 0 0; + padding: 0; + overflow: hidden; + text-overflow: ellipsis; + } + + &__vendor { + display: flex; + gap: $grid-unit-05; + margin: 0; + padding: 0; + } + + &__vendor a { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + text-decoration: none; + } + + &__description { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + text-overflow: ellipsis; + overflow: hidden; + } + + &__price { + align-items: flex-end; + gap: $grid-unit-05; + align-self: stretch; + text-decoration: none !important; + color: $gray-900 !important; + font-style: normal; + font-weight: 600; + line-height: $medium-gap; + } + + &__price-billing { + color: $gray-600; + font-size: $default-font-size; + font-style: normal; + font-weight: 400; + line-height: $medium-gap; + } + } +} + +@media screen and (min-width: $breakpoint-medium) { + .woocommerce-marketplace { + &__product-card { + margin-top: 0; + } + } +} diff --git a/plugins/woocommerce-admin/client/marketplace/components/product-card/product-card.tsx b/plugins/woocommerce-admin/client/marketplace/components/product-card/product-card.tsx new file mode 100644 index 00000000000..0e4cafe9094 --- /dev/null +++ b/plugins/woocommerce-admin/client/marketplace/components/product-card/product-card.tsx @@ -0,0 +1,86 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { Button, Card } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import './product-card.scss'; +import { Product } from '../product-list-content/product-list-content'; +import { getAdminSetting } from '../../../../client/utils/admin-settings'; + +export interface ProductCardProps { + type?: string; + product: Product; +} + +function ProductCard( props: ProductCardProps ): JSX.Element { + const { product } = props; + const currencySymbol = getAdminSetting( 'currency' )?.symbol; + + let productVendor: string | JSX.Element | null = product?.vendorName; + if ( product?.vendorName && product?.vendorUrl ) { + productVendor = ( + + { product.vendorName } + + ); + } + + return ( + +
+
+
+ { product.icon && ( + { + ) } +
+

+ { product.title } +

+ { productVendor && ( +

+ { __( 'By ', 'woocommerce' ) } + { productVendor } +

+ ) } +
+
+
+

+ { product.description } +

+ +
+
+ ); +} + +export default ProductCard; diff --git a/plugins/woocommerce-admin/client/marketplace/components/product-list-content/product-list-content.scss b/plugins/woocommerce-admin/client/marketplace/components/product-list-content/product-list-content.scss index bbbfefdb9c7..869cb402184 100644 --- a/plugins/woocommerce-admin/client/marketplace/components/product-list-content/product-list-content.scss +++ b/plugins/woocommerce-admin/client/marketplace/components/product-list-content/product-list-content.scss @@ -1,7 +1,6 @@ @import '../../stylesheets/_variables.scss'; .woocommerce-marketplace { - &__product-list-content { display: grid; gap: $medium-gap; @@ -32,7 +31,7 @@ } } -@media screen and (min-width: $breakpoint-xlarge) { +@media screen and (min-width: $breakpoint-huge) { .woocommerce-marketplace { &__product-list-content { grid-template-columns: repeat(4, 1fr); diff --git a/plugins/woocommerce-admin/client/marketplace/components/product-list-content/product-list-content.tsx b/plugins/woocommerce-admin/client/marketplace/components/product-list-content/product-list-content.tsx index 70c39bf4227..840d21add22 100644 --- a/plugins/woocommerce-admin/client/marketplace/components/product-list-content/product-list-content.tsx +++ b/plugins/woocommerce-admin/client/marketplace/components/product-list-content/product-list-content.tsx @@ -1,19 +1,49 @@ -/** - * External dependencies - */ - /** * Internal dependencies */ import './product-list-content.scss'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars, @woocommerce/dependency-group +import ProductCard from '../product-card/product-card'; -export default function ProductListContent(): JSX.Element { +export interface Product { + id?: number; + title: string; + description: string; + vendorName: string; + vendorUrl: string; + icon: string; + url: string; + price: string | number; + productType?: string; + averageRating?: number | null; + reviewsCount?: number | null; + currency?: string; +} +interface ProductListContentProps { + products: Product[]; +} + +export default function ProductListContent( + props: ProductListContentProps +): JSX.Element { + const { products } = props; return (
-
-
-
-
+ { products.map( ( product ) => ( + + ) ) }
); } diff --git a/plugins/woocommerce-admin/client/marketplace/components/product-list-header/product-list-header.scss b/plugins/woocommerce-admin/client/marketplace/components/product-list-header/product-list-header.scss index a824101108c..2aa5d4b0d5d 100644 --- a/plugins/woocommerce-admin/client/marketplace/components/product-list-header/product-list-header.scss +++ b/plugins/woocommerce-admin/client/marketplace/components/product-list-header/product-list-header.scss @@ -1,11 +1,32 @@ -.woo-marketplace__product-list-header { - color: $gray-900; +@import '../../stylesheets/_variables.scss'; - h2 { +.woocommerce-marketplace { + &__product-list-header { + display: flex; + justify-content: center; + gap: $medium-gap; + align-self: stretch; + } + + &__product-list-title { + flex: 1 0 0; font-size: 20px; font-style: normal; font-weight: 500; - line-height: 28px; - margin-top: 0; + } + + &__product-list-link { + display: flex; + justify-content: center; + align-items: center; + gap: 12px; + } + + &__product-list-link a { + display: flex; + justify-content: center; + align-items: center; + text-decoration: none; } } + diff --git a/plugins/woocommerce-admin/client/marketplace/components/product-list-header/product-list-header.tsx b/plugins/woocommerce-admin/client/marketplace/components/product-list-header/product-list-header.tsx index 85d28e5102c..83aaf5af862 100644 --- a/plugins/woocommerce-admin/client/marketplace/components/product-list-header/product-list-header.tsx +++ b/plugins/woocommerce-admin/client/marketplace/components/product-list-header/product-list-header.tsx @@ -1,7 +1,8 @@ /** * External dependencies */ - +import { Link } from '@woocommerce/components'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ @@ -9,16 +10,25 @@ import './product-list-header.scss'; interface ProductListHeaderProps { title: string; + groupURL: string; } export default function ProductListHeader( props: ProductListHeaderProps ): JSX.Element { - const { title } = props; - + const { title, groupURL } = props; return ( -
-

{ title }

+
+

+ { title } +

+ { groupURL !== null && ( + + + { __( 'See more', 'woocommerce' ) } + + + ) }
); } diff --git a/plugins/woocommerce-admin/client/marketplace/components/product-list/product-list.tsx b/plugins/woocommerce-admin/client/marketplace/components/product-list/product-list.tsx index 8537915f64a..c28188c515b 100644 --- a/plugins/woocommerce-admin/client/marketplace/components/product-list/product-list.tsx +++ b/plugins/woocommerce-admin/client/marketplace/components/product-list/product-list.tsx @@ -5,22 +5,24 @@ /** * Internal dependencies */ -import CategorySelector from '../category-selector/category-selector'; -import ProductListContent from '../product-list-content/product-list-content'; +import ProductListContent, { + Product, +} from '../product-list-content/product-list-content'; import ProductListHeader from '../product-list-header/product-list-header'; interface ProductListProps { title: string; + products: Product[]; + groupURL: string; } export default function ProductList( props: ProductListProps ): JSX.Element { - const { title } = props; + const { title, products, groupURL } = props; return (
- - - + +
); } diff --git a/plugins/woocommerce-admin/client/marketplace/stylesheets/_variables.scss b/plugins/woocommerce-admin/client/marketplace/stylesheets/_variables.scss index 24c2ace259c..251db7b91c0 100644 --- a/plugins/woocommerce-admin/client/marketplace/stylesheets/_variables.scss +++ b/plugins/woocommerce-admin/client/marketplace/stylesheets/_variables.scss @@ -13,12 +13,14 @@ $content-spacing-small: $grid-unit-20; $content-spacing-large: $grid-unit-40; $content-spacing-small: $medium-gap; $content-spacing-large: $xlarge-gap; +$content-spacing-xlarge: $grid-unit-60; $content-max-width: 1600px; // Breakpoints $breakpoint-medium: 769px; $breakpoint-large: 1024px; $breakpoint-xlarge: 1500px; +$breakpoint-huge: 1920px; // Header $header-height-desktop: 89px; diff --git a/plugins/woocommerce-admin/client/marketplace/utils/functions.tsx b/plugins/woocommerce-admin/client/marketplace/utils/functions.tsx new file mode 100644 index 00000000000..c285c4610e9 --- /dev/null +++ b/plugins/woocommerce-admin/client/marketplace/utils/functions.tsx @@ -0,0 +1,33 @@ +/** + * Internal dependencies + */ +import { Product } from '../components/product-list-content/product-list-content'; +import { MARKETPLACE_URL } from '../components/constants'; + +interface ProductGroup { + id: number; + title: string; + items: Product[]; + url: string; +} + +// Fetch data for the discover page from the WooCommerce.com API +const fetchDiscoverPageData = async (): Promise< Array< ProductGroup > > => { + const fetchUrl = MARKETPLACE_URL + '/wp-json/wccom-extensions/2.0/featured'; + + return fetch( fetchUrl ) + .then( ( response ) => { + if ( ! response.ok ) { + throw new Error( response.statusText ); + } + return response.json(); + } ) + .then( ( json ) => { + return json; + } ) + .catch( () => { + return []; + } ); +}; + +export { fetchDiscoverPageData, ProductGroup }; diff --git a/plugins/woocommerce-admin/client/payments-welcome/index.tsx b/plugins/woocommerce-admin/client/payments-welcome/index.tsx index db5a7f3d699..185aef9fd46 100644 --- a/plugins/woocommerce-admin/client/payments-welcome/index.tsx +++ b/plugins/woocommerce-admin/client/payments-welcome/index.tsx @@ -33,6 +33,9 @@ declare global { cta_label: string; tc_url: string; }; + currency?: { + symbol: string; + }; }; }; }