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>.
## Goals and Features
## Features
- Performant by default
- SEO Ready
@ -22,14 +22,24 @@ This project is currently <b>under development</b>.
- Dark Mode Support
## 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)
## Integrations
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
@ -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`.
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).
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
- **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.
@ -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.
People actively working on this project: @okbel & @lfades.
=======
>>>>>>> master
## Framework

View File

@ -94,7 +94,7 @@ const CartSidebarView: FC = () => {
My Cart
</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">
{items.map((item) => (
{items.map((item: any) => (
<CartItem
key={item.id}
item={item}

View File

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

View File

@ -51,6 +51,8 @@ const I18nWidget: FC = () => {
>
<button className={s.button} aria-label="Language selector">
<img
width="20"
height="20"
className="block mr-2 w-5"
src={`/${LOCALES_MAP[currentLocale].img.filename}`}
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 { CartSidebarView } from '@components/cart'
import LoginView from '@components/auth/LoginView'
import { CommerceProvider } from '@framework'
import type { Page } from '@framework/api/operations/get-all-pages'
const Loading = () => (
<div className="w-80 h-80 flex items-center text-center justify-center p-3">
<LoadingDots />
@ -22,10 +24,6 @@ const dynamicProps = {
loading: () => <Loading />,
}
const LoginView = dynamic(
() => import('@components/auth/LoginView'),
dynamicProps
)
const SignUpView = dynamic(
() => import('@components/auth/SignUpView'),
dynamicProps

View File

@ -4,7 +4,7 @@
}
.modal {
@apply bg-primary p-12 border border-accents-2;
@apply bg-primary p-12 border border-accents-2 relative;
}
.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 s from './Modal.module.css'
import { Cross } from '@components/icons'
@ -7,45 +7,55 @@ import {
enableBodyScroll,
clearAllBodyScrollLocks,
} from 'body-scroll-lock'
import FocusTrap from '@lib/focus-trap'
interface Props {
className?: string
children?: any
open?: boolean
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 handleKey = useCallback(
(e: KeyboardEvent) => {
if (e.key === 'Escape') {
return onClose()
}
},
[onClose]
)
useEffect(() => {
if (ref.current) {
if (open) {
disableBodyScroll(ref.current)
window.addEventListener('keydown', handleKey)
} else {
enableBodyScroll(ref.current)
}
}
return () => {
window.removeEventListener('keydown', handleKey)
clearAllBodyScrollLocks()
}
}, [open])
}, [open, handleKey])
return (
<Portal>
{open ? (
<div className={s.root} ref={ref}>
<div className={s.modal}>
<div className="h-7 flex items-center justify-end w-full">
<button
onClick={() => onClose()}
aria-label="Close panel"
className="hover:text-gray-500 transition ease-in-out duration-150 focus:outline-none"
>
<Cross className="h-6 w-6" />
</button>
</div>
{children}
<div className={s.root}>
<div className={s.modal} role="dialog" ref={ref}>
<button
onClick={() => onClose()}
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"
>
<Cross className="h-6 w-6" />
</button>
<FocusTrap focusFirst>{children}</FocusTrap>
</div>
</div>
) : null}

View File

@ -1,6 +1,6 @@
// Fallback to CMS Data
export const defatultPageProps = {
export const defaultPageProps = {
header: {
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,
InferGetStaticPropsType,
} from 'next'
import { Text } from '@components/ui'
import { Layout } from '@components/common'
import getSlug from '@lib/get-slug'
import { missingLocaleInPages } from '@lib/usage-warns'
import { Layout } from '@components/common'
import { Text } from '@components/ui'
import { getConfig } from '@framework/api'
import getPage from '@framework/api/operations/get-page'
import getAllPages from '@framework/api/operations/get-all-pages'
import { defatultPageProps } from '@lib/defaults'
import { defaultPageProps } from '@lib/defaults'
export async function getStaticProps({
preview,
@ -34,7 +34,7 @@ export async function getStaticProps({
}
return {
props: { ...defatultPageProps, pages, page },
props: { ...defaultPageProps, pages, page },
revalidate: 60 * 60, // Every hour
}
}

View File

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

View File

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

View File

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

7736
yarn.lock

File diff suppressed because it is too large Load Diff