);
}
diff --git a/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.scss b/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.scss
new file mode 100644
index 00000000000..bb7939d11e7
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.scss
@@ -0,0 +1 @@
+// To keep StyleLint happy. Remove when file contains actual code.
diff --git a/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.tsx b/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.tsx
index 1f3cd87cfe9..c8ab9627d5d 100644
--- a/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.tsx
+++ b/plugins/woocommerce-admin/client/marketplace/components/extensions/extensions.tsx
@@ -5,11 +5,13 @@
/**
* Internal dependencies
*/
+import ProductList from '../product-list/product-list';
+import './extensions.scss';
-export default function Extensions() {
+export default function Extensions(): JSX.Element {
return (
-
-
Extensions
+
);
}
diff --git a/plugins/woocommerce-admin/client/marketplace/components/footer/footer.scss b/plugins/woocommerce-admin/client/marketplace/components/footer/footer.scss
new file mode 100644
index 00000000000..e71e61341e0
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/footer/footer.scss
@@ -0,0 +1,51 @@
+@import '../../stylesheets/_variables.scss';
+
+.woocommerce-admin-page__extensions .woocommerce-layout__footer {
+ background: #f6f7f7;
+ // Undo default fixed footer style used in WC Admin
+ position: unset;
+ width: 100%;
+}
+
+.woocommerce-marketplace__footer {
+ box-sizing: content-box;
+ max-width: $content-max-width;
+ margin: auto;
+ padding: 48px $content-spacing-large;
+
+ &-title {
+ color: $gutenberg-gray-900;
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: 28px;
+ max-width: 389px;
+ margin: 0 0 $content-spacing-large;
+ }
+
+ a {
+ text-decoration: none;
+ }
+
+ &-columns {
+ display: flex;
+ flex-direction: column;
+ gap: $large-gap;
+ }
+
+ &-logo {
+ color: $woo-purple-50;
+ display: flex;
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 20px;
+ gap: $small-gap;
+ margin: 48px 0 0;
+ }
+}
+
+@media screen and (min-width: $breakpoint-medium) {
+ .woocommerce-marketplace__footer-columns {
+ flex-direction: row;
+ }
+}
diff --git a/plugins/woocommerce-admin/client/marketplace/components/footer/footer.tsx b/plugins/woocommerce-admin/client/marketplace/components/footer/footer.tsx
new file mode 100644
index 00000000000..a6e23d073a2
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/footer/footer.tsx
@@ -0,0 +1,89 @@
+/**
+ * External dependencies
+ */
+import { WooFooterItem } from '@woocommerce/admin-layout';
+import { __ } from '@wordpress/i18n';
+import { check, commentContent, lock } from '@wordpress/icons';
+import { createInterpolateElement } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import './footer.scss';
+import IconWithText from '../icon-with-text/icon-with-text';
+import WooIcon from '../../assets/images/woo-icon.svg';
+
+const refundPolicyTitle = createInterpolateElement(
+ __( '30 day
money back guarantee', 'woocommerce' ),
+ {
+ // eslint-disable-next-line jsx-a11y/anchor-has-content
+ a:
,
+ }
+);
+
+const supportTitle = createInterpolateElement(
+ __( '
Support teams across the world', 'woocommerce' ),
+ {
+ // eslint-disable-next-line jsx-a11y/anchor-has-content
+ a:
,
+ }
+);
+
+const paymentTitle = createInterpolateElement(
+ __( '
Safe & Secure online payment', 'woocommerce' ),
+ {
+ // eslint-disable-next-line jsx-a11y/anchor-has-content
+ a:
,
+ }
+);
+
+function FooterContent(): JSX.Element {
+ return (
+
+
+ { __(
+ 'Grow your business with hundreds of solutions for your store.',
+ 'woocommerce'
+ ) }
+
+
+
+
+
+
+
+
+
{ __( 'Woo Marketplace', 'woocommerce' ) }
+
+
+ );
+}
+
+export default function Footer(): JSX.Element {
+ return (
+
+
+
+ );
+}
diff --git a/plugins/woocommerce-admin/client/marketplace/components/icon-with-text/icon-with-text.scss b/plugins/woocommerce-admin/client/marketplace/components/icon-with-text/icon-with-text.scss
new file mode 100644
index 00000000000..d3a996d1490
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/icon-with-text/icon-with-text.scss
@@ -0,0 +1,29 @@
+@import '../../stylesheets/_variables.scss';
+
+.woocommerce-marketplace {
+ &__icon-group {
+ flex: 1;
+ max-width: 382px;
+
+ &-headline {
+ display: flex;
+ gap: $small-gap;
+ }
+
+ &-title {
+ color: #101517;
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 20px;
+ margin: 0 0 8px;
+ }
+
+ &-description {
+ color: $wp-gray-50;
+ font-size: 13px;
+ font-weight: 400;
+ line-height: 20px;
+ margin: 0;
+ }
+ }
+}
diff --git a/plugins/woocommerce-admin/client/marketplace/components/icon-with-text/icon-with-text.tsx b/plugins/woocommerce-admin/client/marketplace/components/icon-with-text/icon-with-text.tsx
new file mode 100644
index 00000000000..1eec0b67a9d
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/icon-with-text/icon-with-text.tsx
@@ -0,0 +1,37 @@
+/**
+ * External dependencies
+ */
+import { Icon } from '@wordpress/icons';
+import { ReactElement } from 'react';
+
+/**
+ * Internal dependencies
+ */
+import './icon-with-text.scss';
+
+export interface IconWithTextProps {
+ icon: JSX.Element;
+ title: ReactElement;
+ description: string;
+}
+
+export default function IconWithText( props: IconWithTextProps ): JSX.Element {
+ const { icon, title, description } = props;
+ return (
+
+
+
+
+ { title }
+
+
+
+ { description }
+
+
+ );
+}
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
new file mode 100644
index 00000000000..7ef7a8b7a84
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/product-list-content/product-list-content.scss
@@ -0,0 +1,40 @@
+@import '../../stylesheets/_variables.scss';
+
+.woocommerce-marketplace {
+
+ &__product-list-content {
+ display: grid;
+ gap: $medium-gap;
+ }
+
+ &__extension-card {
+ background-color: #3c3c3c;
+ height: 270px;
+ }
+}
+
+@media screen and (min-width: $breakpoint-medium) {
+ .woocommerce-marketplace {
+ &__product-list-content {
+ gap: $large-gap;
+ grid-template-columns: repeat(2, 1fr);
+ }
+ }
+}
+
+@media screen and (min-width: $breakpoint-large) {
+ .woocommerce-marketplace {
+ &__product-list-content {
+ gap: $large-gap;
+ grid-template-columns: repeat(3, 1fr);
+ }
+ }
+}
+
+@media screen and (min-width: $breakpoint-xlarge) {
+ .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
new file mode 100644
index 00000000000..70c39bf4227
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/product-list-content/product-list-content.tsx
@@ -0,0 +1,19 @@
+/**
+ * External dependencies
+ */
+
+/**
+ * Internal dependencies
+ */
+import './product-list-content.scss';
+
+export default function ProductListContent(): JSX.Element {
+ return (
+
+ );
+}
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
new file mode 100644
index 00000000000..a824101108c
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/product-list-header/product-list-header.scss
@@ -0,0 +1,11 @@
+.woo-marketplace__product-list-header {
+ color: $gray-900;
+
+ h2 {
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: 28px;
+ margin-top: 0;
+ }
+}
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
new file mode 100644
index 00000000000..85d28e5102c
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/product-list-header/product-list-header.tsx
@@ -0,0 +1,24 @@
+/**
+ * External dependencies
+ */
+
+/**
+ * Internal dependencies
+ */
+import './product-list-header.scss';
+
+interface ProductListHeaderProps {
+ title: string;
+}
+
+export default function ProductListHeader(
+ props: ProductListHeaderProps
+): JSX.Element {
+ const { title } = props;
+
+ return (
+
+
{ title }
+
+ );
+}
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
new file mode 100644
index 00000000000..0aaca9de173
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/product-list/product-list.tsx
@@ -0,0 +1,24 @@
+/**
+ * External dependencies
+ */
+
+/**
+ * Internal dependencies
+ */
+import ProductListContent from '../product-list-content/product-list-content';
+import ProductListHeader from '../product-list-header/product-list-header';
+
+interface ProductListProps {
+ title: string;
+}
+
+export default function ProductList( props: ProductListProps ): JSX.Element {
+ const { title } = props;
+
+ return (
+
+ );
+}
diff --git a/plugins/woocommerce-admin/client/marketplace/components/search/search.scss b/plugins/woocommerce-admin/client/marketplace/components/search/search.scss
new file mode 100644
index 00000000000..a37dc8b263b
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/search/search.scss
@@ -0,0 +1,36 @@
+@import '../../stylesheets/_variables.scss';
+
+.marketplace-search-form {
+ background-color: $gutenberg-gray-100;
+ padding: 9px 5px;
+ display: flex;
+ .search-form__search-input {
+ background-color: transparent;
+ border: none;
+ color: $gutenberg-gray-900;
+ display: inline;
+ fill: $woo-purple-50;
+ flex: 1;
+ font-size: 13px;
+ line-height: 20px;
+
+ ::placeholder {
+ color: $gutenberg-gray-700;
+ }
+ }
+ input[type='text']:focus {
+ border: none;
+ box-shadow: none;
+ }
+ .search-form__search-button {
+ background-color: transparent;
+ border: none;
+ border-radius: 0;
+ cursor: pointer;
+ fill: currentColor;
+ font-size: 1em;
+ line-height: 0;
+ text-align: center;
+ }
+}
+
diff --git a/plugins/woocommerce-admin/client/marketplace/components/search/search.tsx b/plugins/woocommerce-admin/client/marketplace/components/search/search.tsx
new file mode 100644
index 00000000000..e479f75238a
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/components/search/search.tsx
@@ -0,0 +1,94 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { Icon, search } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import './search.scss';
+
+const searchPlaceholder = __( 'Search extensions and themes', 'woocommerce' );
+
+const marketplaceAPI = 'https://woocommerce.com/wp-json';
+
+export interface SearchProps {
+ locale?: string | 'en_US';
+ country?: string | undefined;
+}
+
+/**
+ * Search component.
+ *
+ * @param {SearchProps} props - Search props: locale and country.
+ * @return {JSX.Element} Search component.
+ */
+function Search( props: SearchProps ): JSX.Element {
+ const locale = props.locale ?? 'en_US';
+ const country = props.country ?? '';
+
+ const build_parameter_string = (
+ query_string: string,
+ query_country: string,
+ query_locale: string
+ ): string => {
+ const params = new URLSearchParams();
+ params.append( 'term', query_string );
+ params.append( 'country', query_country );
+ params.append( 'locale', query_locale );
+ return params.toString();
+ };
+
+ const runSearch = () => {
+ const query = (
+ document.getElementById( 'search-query' ) as HTMLInputElement
+ ).value.trim();
+
+ const params = build_parameter_string( query, country, locale );
+ fetch( marketplaceAPI + '?' + params, {
+ method: 'GET',
+ } )
+ .then( ( response ) => response.json() )
+ .then( ( response ) => {
+ return response;
+ } );
+ return [];
+ };
+
+ const handleKeyUp = ( event: { key: string } ) => {
+ if ( event.key === 'Enter' ) {
+ runSearch();
+ }
+ };
+
+ const renderSearch = () => {
+ return (
+
+
+
+
+
+ );
+ };
+
+ return
{ renderSearch() }
;
+}
+
+export default Search;
diff --git a/plugins/woocommerce-admin/client/marketplace/components/tabs/tabs.scss b/plugins/woocommerce-admin/client/marketplace/components/tabs/tabs.scss
index 6579a242624..72afcc7fe87 100644
--- a/plugins/woocommerce-admin/client/marketplace/components/tabs/tabs.scss
+++ b/plugins/woocommerce-admin/client/marketplace/components/tabs/tabs.scss
@@ -1,6 +1,22 @@
-.woocommerce-marketplace__tabs {
- .woocommerce-marketplace__tab-button {
- border-bottom: 3.5px solid transparent;
+@import '../../stylesheets/_variables.scss';
+
+.woocommerce-marketplace {
+ &__tabs {
+ margin: auto;
+ padding: 0 $content-spacing-small;
+ box-sizing: content-box;
+ }
+
+ &__tab-button {
+ border-bottom: 1.5px solid transparent;
+ border-radius: 0;
+ padding: 1.5rem 0;
+ margin: 0 1.5rem 0 0;
+ color: $mauve-light-12;
+ font-size: 13px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: 16px;
&:focus:not(:disabled) {
box-shadow: none;
@@ -11,3 +27,9 @@
}
}
}
+
+@media screen and (min-width: $breakpoint-medium) {
+ .woocommerce-marketplace__tabs {
+ padding: 0 $content-spacing-large;
+ }
+}
diff --git a/plugins/woocommerce-admin/client/marketplace/index.tsx b/plugins/woocommerce-admin/client/marketplace/index.tsx
index d9d45577b1f..95285c253cc 100644
--- a/plugins/woocommerce-admin/client/marketplace/index.tsx
+++ b/plugins/woocommerce-admin/client/marketplace/index.tsx
@@ -15,12 +15,14 @@ export default function Marketplace() {
const [ selectedTab, setSelectedTab ] = useState( DEFAULT_TAB_KEY );
return (
- <>
-
+
);
}
diff --git a/plugins/woocommerce-admin/client/marketplace/marketplace.scss b/plugins/woocommerce-admin/client/marketplace/marketplace.scss
index ec11a5d9db9..1c2ca06267b 100644
--- a/plugins/woocommerce-admin/client/marketplace/marketplace.scss
+++ b/plugins/woocommerce-admin/client/marketplace/marketplace.scss
@@ -1 +1,18 @@
-/* This is for Stylelint. Delete this comment when we add styles here! */
+@import './stylesheets/_variables.scss';
+
+.woocommerce-admin-page__extensions {
+ background: #fff;
+
+ .woocommerce-layout__primary {
+ margin: 0;
+ }
+
+ .woocommerce-layout__main {
+ padding: 0;
+ }
+}
+
+.woocommerce-marketplace__header {
+ border-bottom: 1px solid $gutenberg-gray-300;
+}
+
diff --git a/plugins/woocommerce-admin/client/marketplace/stylesheets/_variables.scss b/plugins/woocommerce-admin/client/marketplace/stylesheets/_variables.scss
new file mode 100644
index 00000000000..aa4d83b3fc8
--- /dev/null
+++ b/plugins/woocommerce-admin/client/marketplace/stylesheets/_variables.scss
@@ -0,0 +1,28 @@
+// Spacings
+// Taken from base style system
+// @wordpress/base-styles/_variables.scss
+$small-gap: $grid-unit-10; // 8px
+$medium-gap: $grid-unit-20; // 16px
+$large-gap: $grid-unit-30; // 24px
+$xlarge-gap: $grid-unit-40; // 32px
+
+// Layout
+$content-spacing-small: $grid-unit-20;
+$content-spacing-large: $grid-unit-40;
+$content-spacing-small: $medium-gap;
+$content-spacing-large: $xlarge-gap;
+$content-max-width: 1600px;
+
+// Breakpoints
+$breakpoint-medium: 769px;
+$breakpoint-large: 1024px;
+$breakpoint-xlarge: 1500px;
+
+// Colours
+$gutenberg-gray-100: #f0f0f0;
+$gutenberg-gray-300: $gray-300;
+$gutenberg-gray-700: #757575;
+$gutenberg-gray-900: $gray-900;
+$mauve-light-12: #1a1523;
+$woo-purple-50: #7f54b3;
+$wp-gray-50: #646970;