4
0
forked from crowetic/commerce

Merge branch 'master' into agnostic

This commit is contained in:
Belen Curcio 2021-01-11 12:15:26 -03:00
commit 9ffc60ec95
14 changed files with 131 additions and 7777 deletions

View File

@ -9,7 +9,7 @@ Demo live at: [demo.vercel.store](https://demo.vercel.store/)
This project is currently <b>under development</b>. This project is currently <b>under development</b>.
## Goals and Features ## Features
- Performant by default - Performant by default
- SEO Ready - SEO Ready
@ -22,14 +22,24 @@ This project is currently <b>under development</b>.
- Dark Mode Support - Dark Mode Support
## Work in progress ## Work in progress
We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1) We're using Github Projects to keep track of issues in progress and todo's. Here is our [Board](https://github.com/vercel/commerce/projects/1)
## Integrations ## Integrations
Next.js Commerce integrates out-of-the-box with BigCommerce. We plan to support all major ecommerce backends. Next.js Commerce integrates out-of-the-box with BigCommerce. We plan to support all major ecommerce backends.
If you wish to work on a new cms/headless ecommerce provider, please check this repo https://github.com/vercel/commerce-framework and open a PR with your functions and utilities under the standard mentioned in the README.
## Goals
* **Next.js Commerce** should have a completely data **agnostic** UI
* **Aware of schema**: should ship with the right data schemas and types.
* All providers should return the right data types and schemas to blend correctly with Next.js Commerce.
* `@framework` will be the alias utilized in commerce and it will map to the ecommerce provider of preference- e.g BigCommerce, Shopify, Swell. All providers should expose the same standardized functions. _Note that the same applies for recipes using a CMS + an ecommerce provider._
There is a `framework` folder in the root folder that will contain multiple ecommerce providers.
Additionally, we need to ensure feature parity (not all providers have e.g. wishlist) we will also have to build a feature API to disable/enable features in the UI.
People actively working on this project: @okbel & @lfades.
## Troubleshoot ## Troubleshoot
@ -80,10 +90,10 @@ Our commitment to Open Source can be found [here](https://vercel.com/oss).
6. Add proper store values to `.env.local`. 6. Add proper store values to `.env.local`.
7. Run `yarn dev` to build and watch for code changes 7. Run `yarn dev` to build and watch for code changes
8. The development branch is `development` (this is the branch pull requests should be made against). 8. The development branch is `development` (this is the branch pull requests should be made against).
On a release, the relevant parts of the changes in the `staging` branch are rebased into `master`. On a release, `develop` branch is rebased into `master`.
## Goals
<<<<<<< HEAD
- **Next.js Commerce** should have a completely data **agnostic** UI - **Next.js Commerce** should have a completely data **agnostic** UI
- **Aware of schema**: should ship with the right data schemas and types. - **Aware of schema**: should ship with the right data schemas and types.
- All providers should return the right data types and schemas to blend correctly with Next.js Commerce. - All providers should return the right data types and schemas to blend correctly with Next.js Commerce.
@ -94,6 +104,8 @@ There is a `framework` folder in the root folder that will contain multiple ecom
Additionally, we need to ensure feature parity (not all providers have e.g. wishlist) we will also have to build a feature API to disable/enable features in the UI. Additionally, we need to ensure feature parity (not all providers have e.g. wishlist) we will also have to build a feature API to disable/enable features in the UI.
People actively working on this project: @okbel & @lfades. People actively working on this project: @okbel & @lfades.
=======
>>>>>>> master
## Framework ## Framework

View File

@ -94,7 +94,7 @@ const CartSidebarView: FC = () => {
My Cart My Cart
</h2> </h2>
<ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-3 border-t border-accents-3"> <ul className="py-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-accents-3 border-t border-accents-3">
{items.map((item) => ( {items.map((item: any) => (
<CartItem <CartItem
key={item.id} key={item.id}
item={item} item={item}

View File

@ -102,6 +102,7 @@ const Footer: FC<Props> = ({ className, pages }) => {
<div className="flex items-center text-primary"> <div className="flex items-center text-primary">
<span className="text-primary">Crafted by</span> <span className="text-primary">Crafted by</span>
<a <a
rel="noopener"
href="https://vercel.com" href="https://vercel.com"
aria-label="Vercel.com Link" aria-label="Vercel.com Link"
target="_blank" target="_blank"

View File

@ -51,6 +51,8 @@ const I18nWidget: FC = () => {
> >
<button className={s.button} aria-label="Language selector"> <button className={s.button} aria-label="Language selector">
<img <img
width="20"
height="20"
className="block mr-2 w-5" className="block mr-2 w-5"
src={`/${LOCALES_MAP[currentLocale].img.filename}`} src={`/${LOCALES_MAP[currentLocale].img.filename}`}
alt={LOCALES_MAP[currentLocale].img.alt} alt={LOCALES_MAP[currentLocale].img.alt}

View File

@ -9,9 +9,11 @@ import { useAcceptCookies } from '@lib/hooks/useAcceptCookies'
import { Sidebar, Button, Modal, LoadingDots } from '@components/ui' import { Sidebar, Button, Modal, LoadingDots } from '@components/ui'
import { CartSidebarView } from '@components/cart' import { CartSidebarView } from '@components/cart'
import LoginView from '@components/auth/LoginView'
import { CommerceProvider } from '@framework' import { CommerceProvider } from '@framework'
import type { Page } from '@framework/api/operations/get-all-pages' import type { Page } from '@framework/api/operations/get-all-pages'
const Loading = () => ( const Loading = () => (
<div className="w-80 h-80 flex items-center text-center justify-center p-3"> <div className="w-80 h-80 flex items-center text-center justify-center p-3">
<LoadingDots /> <LoadingDots />
@ -22,10 +24,6 @@ const dynamicProps = {
loading: () => <Loading />, loading: () => <Loading />,
} }
const LoginView = dynamic(
() => import('@components/auth/LoginView'),
dynamicProps
)
const SignUpView = dynamic( const SignUpView = dynamic(
() => import('@components/auth/SignUpView'), () => import('@components/auth/SignUpView'),
dynamicProps dynamicProps

View File

@ -4,7 +4,7 @@
} }
.modal { .modal {
@apply bg-primary p-12 border border-accents-2; @apply bg-primary p-12 border border-accents-2 relative;
} }
.modal:focus { .modal:focus {

View File

@ -1,4 +1,4 @@
import { FC, useRef, useEffect } from 'react' import { FC, useRef, useEffect, useCallback } from 'react'
import Portal from '@reach/portal' import Portal from '@reach/portal'
import s from './Modal.module.css' import s from './Modal.module.css'
import { Cross } from '@components/icons' import { Cross } from '@components/icons'
@ -7,45 +7,55 @@ import {
enableBodyScroll, enableBodyScroll,
clearAllBodyScrollLocks, clearAllBodyScrollLocks,
} from 'body-scroll-lock' } from 'body-scroll-lock'
import FocusTrap from '@lib/focus-trap'
interface Props { interface Props {
className?: string className?: string
children?: any children?: any
open?: boolean open?: boolean
onClose: () => void onClose: () => void
onEnter?: () => void | null
} }
const Modal: FC<Props> = ({ children, open, onClose }) => { const Modal: FC<Props> = ({ children, open, onClose, onEnter = null }) => {
const ref = useRef() as React.MutableRefObject<HTMLDivElement> const ref = useRef() as React.MutableRefObject<HTMLDivElement>
const handleKey = useCallback(
(e: KeyboardEvent) => {
if (e.key === 'Escape') {
return onClose()
}
},
[onClose]
)
useEffect(() => { useEffect(() => {
if (ref.current) { if (ref.current) {
if (open) { if (open) {
disableBodyScroll(ref.current) disableBodyScroll(ref.current)
window.addEventListener('keydown', handleKey)
} else { } else {
enableBodyScroll(ref.current) enableBodyScroll(ref.current)
} }
} }
return () => { return () => {
window.removeEventListener('keydown', handleKey)
clearAllBodyScrollLocks() clearAllBodyScrollLocks()
} }
}, [open]) }, [open, handleKey])
return ( return (
<Portal> <Portal>
{open ? ( {open ? (
<div className={s.root} ref={ref}> <div className={s.root}>
<div className={s.modal}> <div className={s.modal} role="dialog" ref={ref}>
<div className="h-7 flex items-center justify-end w-full"> <button
<button onClick={() => onClose()}
onClick={() => onClose()} aria-label="Close panel"
aria-label="Close panel" className="hover:text-gray-500 transition ease-in-out duration-150 focus:outline-none absolute right-0 top-0 m-6"
className="hover:text-gray-500 transition ease-in-out duration-150 focus:outline-none" >
> <Cross className="h-6 w-6" />
<Cross className="h-6 w-6" /> </button>
</button> <FocusTrap focusFirst>{children}</FocusTrap>
</div>
{children}
</div> </div>
</div> </div>
) : null} ) : null}

View File

@ -1,6 +1,6 @@
// Fallback to CMS Data // Fallback to CMS Data
export const defatultPageProps = { export const defaultPageProps = {
header: { header: {
links: [ links: [
{ {

64
lib/focus-trap.tsx Normal file
View File

@ -0,0 +1,64 @@
import React, { useEffect, RefObject } from 'react'
import { tabbable } from 'tabbable'
interface Props {
children: React.ReactNode | any
focusFirst?: boolean
}
export default function FocusTrap({ children, focusFirst = false }: Props) {
const root: RefObject<any> = React.useRef()
const anchor: RefObject<any> = React.useRef(document.activeElement)
const returnFocus = () => {
// Returns focus to the last focused element prior to trap.
if (anchor) {
anchor.current.focus()
}
}
const trapFocus = () => {
// Focus the container element
if (root.current) {
root.current.focus()
if (focusFirst) {
selectFirstFocusableEl()
}
}
}
const selectFirstFocusableEl = () => {
// Try to find focusable elements, if match then focus
// Up to 6 seconds of load time threshold
let match = false
let end = 60 // Try to find match at least n times
let i = 0
const timer = setInterval(() => {
if (!match !== i > end) {
match = !!tabbable(root.current).length
if (match) {
// Attempt to focus the first el
tabbable(root.current)[0].focus()
}
i = i + 1
} else {
// Clear interval after n attempts
clearInterval(timer)
}
}, 100)
}
useEffect(() => {
setTimeout(trapFocus, 20)
return () => {
returnFocus()
}
}, [root, children])
return React.createElement('div', {
ref: root,
children,
className: 'outline-none focus-trap',
tabIndex: -1,
})
}

View File

@ -3,14 +3,14 @@ import type {
GetStaticPropsContext, GetStaticPropsContext,
InferGetStaticPropsType, InferGetStaticPropsType,
} from 'next' } from 'next'
import { Text } from '@components/ui'
import { Layout } from '@components/common'
import getSlug from '@lib/get-slug' import getSlug from '@lib/get-slug'
import { missingLocaleInPages } from '@lib/usage-warns' import { missingLocaleInPages } from '@lib/usage-warns'
import { Layout } from '@components/common'
import { Text } from '@components/ui'
import { getConfig } from '@framework/api' import { getConfig } from '@framework/api'
import getPage from '@framework/api/operations/get-page' import getPage from '@framework/api/operations/get-page'
import getAllPages from '@framework/api/operations/get-all-pages' import getAllPages from '@framework/api/operations/get-all-pages'
import { defatultPageProps } from '@lib/defaults' import { defaultPageProps } from '@lib/defaults'
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
@ -34,7 +34,7 @@ export async function getStaticProps({
} }
return { return {
props: { ...defatultPageProps, pages, page }, props: { ...defaultPageProps, pages, page },
revalidate: 60 * 60, // Every hour revalidate: 60 * 60, // Every hour
} }
} }

View File

@ -1,10 +1,4 @@
import Document, { import Document, { Head, Html, Main, NextScript } from 'next/document'
DocumentContext,
Head,
Html,
Main,
NextScript,
} from 'next/document'
class MyDocument extends Document { class MyDocument extends Document {
render() { render() {

View File

@ -6,8 +6,12 @@ import { Layout } from '@components/common'
import { Heart } from '@components/icons' import { Heart } from '@components/icons'
import { Text, Container } from '@components/ui' import { Text, Container } from '@components/ui'
import { WishlistCard } from '@components/wishlist' import { WishlistCard } from '@components/wishlist'
<<<<<<< HEAD
import { defatultPageProps } from '@lib/defaults' import { defatultPageProps } from '@lib/defaults'
import { useCustomer } from '@framework/customer' import { useCustomer } from '@framework/customer'
=======
import { defaultPageProps } from '@lib/defaults'
>>>>>>> master
export async function getStaticProps({ export async function getStaticProps({
preview, preview,
@ -16,7 +20,7 @@ export async function getStaticProps({
const config = getConfig({ locale }) const config = getConfig({ locale })
const { pages } = await getAllPages({ config, preview }) const { pages } = await getAllPages({ config, preview })
return { return {
props: { ...defatultPageProps, pages }, props: { ...defaultPageProps, pages },
} }
} }

View File

@ -8,6 +8,11 @@ module.exports = {
'./pages/**/*.{js,ts,jsx,tsx}', './pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}',
], ],
options: {
safelist: {
standard: ['outline-none'],
},
},
}, },
theme: { theme: {
extend: { extend: {

7736
yarn.lock

File diff suppressed because it is too large Load Diff